Merge pull request #34285 from backstage/rugvip/cimd-metadata-size-cap

auth-backend: cap CIMD metadata response size
This commit is contained in:
Patrik Oldsberg
2026-05-18 17:53:32 +02:00
committed by GitHub
3 changed files with 52 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
Limit the size of fetched client ID metadata documents to prevent oversized responses from being accepted.
@@ -328,6 +328,29 @@ describe('CimdClient', () => {
).rejects.toThrow('Invalid client metadata document');
});
it('should throw for oversized JSON without content-length', async () => {
const oversizedMetadata = {
client_id: 'https://example.com/oauth-metadata.json',
client_name: 'x'.repeat(64 * 1024),
redirect_uris: ['http://localhost:8080/callback'],
};
const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValue(
new Response(JSON.stringify(oversizedMetadata), {
headers: { 'content-type': 'application/json' },
}),
);
try {
await expect(
fetchCimdMetadata({
clientId: 'https://example.com/oauth-metadata.json',
}),
).rejects.toThrow('Client metadata document too large');
} finally {
fetchMock.mockRestore();
}
});
it('should throw for client_id mismatch', async () => {
const mismatchedMetadata = {
client_id: 'https://different.com/metadata',
+24 -2
View File
@@ -187,6 +187,24 @@ function validateMetadata(
}
}
async function readCappedResponseBody(response: Response): Promise<string> {
if (!response.body) {
return '';
}
const chunks: Buffer[] = [];
let received = 0;
for await (const chunk of response.body) {
received += chunk.byteLength;
if (received > MAX_RESPONSE_BYTES) {
throw new InputError('Client metadata document too large');
}
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks).toString('utf8');
}
/**
* Fetches and validates a CIMD metadata document.
* @throws InputError if fetching or validation fails
@@ -228,8 +246,12 @@ export async function fetchCimdMetadata(opts: {
let metadata: CimdMetadata;
try {
metadata = await response.json();
} catch {
const responseBody = await readCappedResponseBody(response);
metadata = JSON.parse(responseBody) as CimdMetadata;
} catch (error) {
if (isError(error) && error.name === 'InputError') {
throw error;
}
throw new InputError('Invalid client metadata document');
}