Skip to content

Commit 7a05536

Browse files
dahliacodex
andcommitted
Handle extensionless image URLs in downloadImage safely
Use the last path segment to derive an extension and fall back to jpg for extensionless single-segment paths like /image. Keep rejecting unsafe or ambiguous paths by bailing out when no valid extension can be derived. This keeps the path-traversal safeguard intact while allowing the extensionless URL shape called out in review. #608 (comment) Co-Authored-By: Codex <codex@openai.com>
1 parent 9303408 commit 7a05536

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

packages/cli/src/imagerenderer.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,22 @@ test("downloadImage - rejects unsafe extension containing path traversal", async
189189
globalThis.fetch = originalFetch;
190190
}
191191
});
192+
193+
test("downloadImage - falls back to jpg when URL has no extension", async () => {
194+
const originalFetch = globalThis.fetch;
195+
globalThis.fetch =
196+
((_input: URL | RequestInfo) =>
197+
Promise.resolve(new Response(new Uint8Array([1, 2, 3])))) as typeof fetch;
198+
199+
let result: string | null = null;
200+
try {
201+
result = await downloadImage("https://198.51.100.10/image");
202+
assert.notEqual(result, null);
203+
assert.equal(path.extname(result!), ".jpg");
204+
} finally {
205+
globalThis.fetch = originalFetch;
206+
if (result != null) {
207+
await rm(path.dirname(result), { recursive: true, force: true });
208+
}
209+
}
210+
});

packages/cli/src/imagerenderer.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,17 @@ export async function downloadImage(url: string): Promise<string | null> {
131131
return null;
132132
}
133133
const imageData = new Uint8Array(await response.arrayBuffer());
134-
const extension = new URL(targetUrl).pathname.split(".").pop() || "jpg";
134+
const pathname = new URL(targetUrl).pathname;
135+
const pathSegments = pathname.split("/").filter((segment) =>
136+
segment !== ""
137+
);
138+
const filename = pathSegments[pathSegments.length - 1] ?? "";
139+
const extension = filename.includes(".")
140+
? path.extname(filename).slice(1)
141+
: pathSegments.length === 1
142+
? "jpg"
143+
: "";
144+
if (extension.length < 1) return null;
135145
if (
136146
extension.includes("/") || extension.includes("\\") ||
137147
extension.includes("..")

0 commit comments

Comments
 (0)