Skip to content

Commit bce0ef7

Browse files
sametbasbugclaude
andcommitted
Close the five alerts from the first CodeQL run
js/polynomial-redos in slugify was real as a pattern but unreachable with this input: the preceding replace collapses every run of non-alphanumerics to a single dash, so `-+` can never match more than one character. Measured it -- 50k dashes plus 50k punctuation marks collapse to "-a-" before the second replace ever sees them. Rewrote it as `^-|-$`, which is backtracking-free and equivalent, and wrote the invariant into the comment because the equivalence depends on that first collapse staying there. Checked both forms against all 579 live titles: 0 differences. js/file-system-race in the mirror was an existsSync-then-read pair. Now a single read that treats ENOENT as absence and rethrows everything else -- swallowing a permission error as "no file" would have silently rewritten the archive. Mirror is still idempotent: 579 unchanged. js/file-access-to-http in the e2e tools: both take their base URL from argv and default to plain http, then post local fixtures -- including .cases/orbit-tokens.json -- to it. They now refuse a non-local target unless it is https. py/incomplete-url-substring-sanitization was a fake HTTP client matching `"api.pexels.com" in url`, which any URL carrying that string in a query would satisfy. Now startswith on the full origin. 212 tests pass, both builds complete. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent ae865e0 commit bce0ef7

5 files changed

Lines changed: 54 additions & 6 deletions

File tree

‎newsroom/tests/test_hero.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ def json(self):
142142

143143
class Client:
144144
def get(self, url, **kwargs):
145-
if "api.pexels.com" in url:
145+
# `in` yerine `startswith`: alt dize kontrolü
146+
# "https://kotu.example/?x=api.pexels.com" adresini de
147+
# Pexels sanardı (py/incomplete-url-substring-sanitization).
148+
if url.startswith("https://api.pexels.com/"):
146149
return Response(payload={"photos": photos})
147150
return Response(content=image)
148151

‎scripts/haber-ayna.mjs‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
* aksi halde ayrı veritabanlarına bakıyor.
2626
*/
2727
import { execFileSync } from "node:child_process";
28-
import { readFileSync, writeFileSync, readdirSync, unlinkSync, existsSync } from "node:fs";
28+
import { readFileSync, writeFileSync, readdirSync, unlinkSync } from "node:fs";
2929
import { join } from "node:path";
3030

3131
const ARGS = process.argv.slice(2);
@@ -115,7 +115,16 @@ for (const a of makaleler) {
115115
const yol = join(DIZIN, `${a.slug}.md`);
116116
beklenen.add(`${a.slug}.md`);
117117
const yeni = markdownUret(a, etiketler.get(a.slug) ?? [], kaynaklar.get(a.slug) ?? []);
118-
const eski = existsSync(yol) ? readFileSync(yol, "utf-8") : null;
118+
/* Önce `existsSync` sorup sonra okumak iki ayrı sistem çağrısı: arada
119+
* dosya kaybolursa okuma patlar. Doğrudan okuyup ENOENT'i yokluk sayıyoruz
120+
* — tek çağrı, yarış yok (js/file-system-race). Diğer hatalar yutulmuyor:
121+
* izin hatasını "dosya yok" saymak, arşivi sessizce yeniden yazdırırdı. */
122+
let eski = null;
123+
try {
124+
eski = readFileSync(yol, "utf-8");
125+
} catch (hata) {
126+
if (hata.code !== "ENOENT") throw hata;
127+
}
119128
if (eski === yeni) { degismeyen++; continue; }
120129
if (eski !== null && eski.trimEnd() === yeni.trimEnd()) sadeceSonBosluk++;
121130
else if (eski !== null) { farkliIcerik++; if (farkliIcerik <= 5) console.log(` İÇERİK FARKI: ${a.slug}`); }

‎worker/src/index.ts‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,17 @@ export function slugify(title: string): string {
9292
.join("")
9393
.toLowerCase();
9494

95-
let slug = mapped.replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "");
95+
/* İkinci kalıpta `-+` değil `-` var ve bu bilinçli.
96+
*
97+
* İlk `replace` alfanümerik olmayan her DİZİYİ tek bir tireye indiriyor,
98+
* yani çıktısında ardışık tire bulunması imkânsız. `-+$` yazmak, hiç
99+
* oluşamayacak bir diziyi geri izlemeyle aramak demek: CodeQL bunu
100+
* polinom ReDoS olarak işaretliyor (js/polynomial-redos) ve kalıp olarak
101+
* haklı, yalnız buradaki girdiyle erişilemiyor.
102+
*
103+
* Tek tire yazmak hem denk hem geri izlemesiz. Ama denklik ilk `replace`'in
104+
* daraltmasına DAYANIYOR: o kaldırılırsa burası da bozulur. */
105+
let slug = mapped.replace(/[^a-z0-9]+/gu, "-").replace(/^-|-$/gu, "");
96106

97107
if (slug.length > MAX_SLUG_LENGTH) {
98108
const cut = slug.slice(0, MAX_SLUG_LENGTH);

‎worker/tools/e2e-orbit.mjs‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,20 @@
77
*/
88
import { readFileSync } from "node:fs";
99

10-
const BASE = process.argv[2] ?? "http://localhost:8788";
10+
/* Bu araç yerel test verisini — test token'ları ve yayın külliyatı — gövdeye koyup BASE'e gönderiyor.
11+
* BASE komut satırından geliyor ve varsayılanı düz `http`. Yanlış bir adres
12+
* verilirse test verisi şifresiz olarak oraya gider (js/file-access-to-http).
13+
* Bu yüzden hedef ya yereldir ya da `https` olmak zorunda. */
14+
function hedefiDogrula(base) {
15+
const url = new URL(base);
16+
const yerel = ["localhost", "127.0.0.1", "[::1]", "::1"].includes(url.hostname);
17+
if (!yerel && url.protocol !== "https:") {
18+
throw new Error(`Yerel olmayan hedefe düz http ile gidilmez: ${base}`);
19+
}
20+
return base;
21+
}
22+
23+
const BASE = hedefiDogrula(process.argv[2] ?? "http://localhost:8788");
1124
const tokens = JSON.parse(readFileSync(new URL("../.cases/orbit-tokens.json", import.meta.url), "utf-8"));
1225

1326
const results = [];

‎worker/tools/e2e.mjs‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,20 @@
1111
import { readFileSync } from "node:fs";
1212
import { fileURLToPath } from "node:url";
1313

14-
const BASE = process.argv[2] ?? "http://localhost:8790";
14+
/* Bu araç yerel test verisini — yayın külliyatı ve üretilmiş görseller — gövdeye koyup BASE'e gönderiyor.
15+
* BASE komut satırından geliyor ve varsayılanı düz `http`. Yanlış bir adres
16+
* verilirse test verisi şifresiz olarak oraya gider (js/file-access-to-http).
17+
* Bu yüzden hedef ya yereldir ya da `https` olmak zorunda. */
18+
function hedefiDogrula(base) {
19+
const url = new URL(base);
20+
const yerel = ["localhost", "127.0.0.1", "[::1]", "::1"].includes(url.hostname);
21+
if (!yerel && url.protocol !== "https:") {
22+
throw new Error(`Yerel olmayan hedefe düz http ile gidilmez: ${base}`);
23+
}
24+
return base;
25+
}
26+
27+
const BASE = hedefiDogrula(process.argv[2] ?? "http://localhost:8790");
1528
const TOKEN = process.env.DEV_PUBLISH_TOKEN ?? "yerel-gelistirme-tokeni-degistirilecek";
1629
const ROOT = fileURLToPath(new URL("../..", import.meta.url));
1730

0 commit comments

Comments
 (0)