Merge commit from fork

* fix: prevent SSRF via redirect in CIMD metadata fetch

* fix: prevent SSRF via redirect in CIMD metadata fetch

* fix: add redirect target listener to SSRF redirect test
This commit is contained in:
Ben Lambert
2026-03-11 13:34:25 +01:00
committed by GitHub
parent 4f5ed06dd1
commit e9b6e978f1
3 changed files with 42 additions and 0 deletions
@@ -300,6 +300,42 @@ describe('CimdClient', () => {
});
});
describe('redirect protection', () => {
it('should reject redirects to prevent SSRF via redirect bypass', async () => {
const redirectTarget = jest.fn();
server.use(
rest.get(
'https://example.com/oauth-metadata.json',
(_req, res, ctx) => {
return res(
ctx.status(302),
ctx.set('Location', 'http://127.0.0.1:8080/internal'),
);
},
),
rest.get('http://127.0.0.1:8080/internal', (req, res, ctx) => {
redirectTarget();
return res(
ctx.json({
client_id: 'https://example.com/oauth-metadata.json',
client_name: 'Sneaky Client',
redirect_uris: ['http://localhost:8080/callback'],
}),
);
}),
);
await expect(
fetchCimdMetadata({
clientId: 'https://example.com/oauth-metadata.json',
}),
).rejects.toThrow('Failed to fetch client metadata');
expect(redirectTarget).not.toHaveBeenCalled();
});
});
describe('HTTP error handling', () => {
it('should throw for network errors', async () => {
server.use(
@@ -224,6 +224,7 @@ export async function fetchCimdMetadata(opts: {
method: 'GET',
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
redirect: 'error',
});
} catch {
throw new InputError('Failed to fetch client metadata');