Skip to content

Commit 5976728

Browse files
committed
feat(proxy-favicon): disallow SVG favicons and add unit tests for handling
1 parent 9673aba commit 5976728

File tree

4 files changed

+72
-9
lines changed

4 files changed

+72
-9
lines changed

__tests__/proxyFavicon.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2+
import { onRequestGet, Env } from '../functions/api/proxy-favicon';
3+
4+
describe('proxy-favicon handler', () => {
5+
const originalFetch = global.fetch;
6+
let fetchMock: ReturnType<typeof vi.fn>;
7+
8+
beforeEach(() => {
9+
fetchMock = vi.fn();
10+
global.fetch = fetchMock as unknown as typeof global.fetch;
11+
});
12+
13+
afterEach(() => {
14+
global.fetch = originalFetch;
15+
vi.restoreAllMocks();
16+
});
17+
18+
it('streams through raster favicons unchanged', async () => {
19+
fetchMock.mockResolvedValueOnce(new Response('png-data', {
20+
status: 200,
21+
headers: { 'content-type': 'image/png' },
22+
}));
23+
24+
const request = new Request('https://example.com/api/proxy-favicon?url=https://remote.test/favicon.png');
25+
const response = await onRequestGet({ request, env: {} as Env });
26+
27+
expect(fetchMock).toHaveBeenCalledOnce();
28+
expect(response.status).toBe(200);
29+
expect(response.headers.get('content-type')).toBe('image/png');
30+
expect(await response.text()).toBe('png-data');
31+
});
32+
33+
it('rejects SVG favicons with UNSUPPORTED_TYPE', async () => {
34+
fetchMock.mockResolvedValueOnce(new Response('<svg></svg>', {
35+
status: 200,
36+
headers: { 'content-type': 'image/svg+xml' },
37+
}));
38+
39+
const request = new Request('https://example.com/api/proxy-favicon?url=https://remote.test/favicon.svg');
40+
const response = await onRequestGet({ request, env: {} as Env });
41+
42+
expect(fetchMock).toHaveBeenCalledOnce();
43+
expect(response.status).toBe(415);
44+
expect(response.headers.get('x-error-code')).toBe('UNSUPPORTED_TYPE');
45+
const body = await response.json();
46+
expect(body).toMatchObject({ error: true, code: 'UNSUPPORTED_TYPE' });
47+
});
48+
});
Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
---
22
id: task-16
33
title: Disallow SVG favicons in proxy
4-
status: To Do
5-
assignee: []
4+
status: Done
5+
assignee:
6+
- '@assistant'
67
created_date: '2025-09-16 12:10'
7-
updated_date: '2025-09-16 12:16'
8+
updated_date: '2025-09-16 13:08'
89
labels:
910
- security
1011
dependencies: []
@@ -17,7 +18,23 @@ Tighten the favicon proxy to reject image/svg+xml responses.
1718

1819
## Acceptance Criteria
1920
<!-- AC:BEGIN -->
20-
- [ ] #1 Remove image/svg+xml from the allowed content types.
21-
- [ ] #2 Return 415 with UNSUPPORTED_TYPE when an SVG favicon is requested.
22-
- [ ] #3 Existing raster favicon handling remains unchanged.
21+
- [x] #1 Remove image/svg+xml from the allowed content types.
22+
- [x] #2 Return 415 with UNSUPPORTED_TYPE when an SVG favicon is requested.
23+
- [x] #3 Existing raster favicon handling remains unchanged.
2324
<!-- AC:END -->
25+
26+
27+
## Implementation Plan
28+
29+
1. Remove image/svg+xml from the allowed content types in proxy-favicon.
30+
2. Add unit tests ensuring SVG responses return 415 with UNSUPPORTED_TYPE.
31+
3. Add test confirming png/jpeg responses continue to stream successfully.
32+
4. Run Vitest suite to validate.
33+
34+
35+
## Implementation Notes
36+
37+
Implementation:
38+
- Removed image/svg+xml from ALLOWED_CONTENT_TYPES so SVG favicons return UNSUPPORTED_TYPE 415.
39+
- Added proxyFavicon vitest coverage for raster passthrough and SVG rejection.
40+
- Ran pnpm test.

backlog/tasks/task-2 - Audit-external-assets-for-SRI-and-CSP-alignment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Audit external assets for SRI and CSP alignment
44
status: To Do
55
assignee: []
66
created_date: '2025-09-16 12:09'
7-
updated_date: '2025-09-16 12:15'
7+
updated_date: '2025-09-16 13:03'
88
labels:
99
- security
1010
dependencies: []

functions/api/proxy-favicon.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const ALLOWED_CONTENT_TYPES = new Set([
1313
'image/gif',
1414
'image/jpeg',
1515
'image/webp',
16-
'image/svg+xml',
1716
]);
1817

1918
function badRequest(message: string) {
@@ -70,4 +69,3 @@ export const onRequestGet = async (context: { request: Request; env: Env }) => {
7069

7170
return new Response(resp.body, { status: 200, headers: outHeaders });
7271
};
73-

0 commit comments

Comments
 (0)