fix(config url): Hardening fetch options. (#5985)

This commit is contained in:
Joe Boccanfuso 2026-04-29 11:57:33 -04:00 committed by GitHub
parent 90abd00936
commit 468e5734a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 19 deletions

View File

@ -97,16 +97,14 @@ function resolveConfigFetchPolicy(rawUrl, policy = {}) {
} }
async function fetchConfigJson(normalizedPolicy) { async function fetchConfigJson(normalizedPolicy) {
const { normalizedUrl, isAuthenticated, isSameOrigin } = normalizedPolicy; const { normalizedUrl } = normalizedPolicy;
const response = isAuthenticated || isSameOrigin const response = await fetch(normalizedUrl, {
? await fetch(normalizedUrl) method: 'GET',
: await fetch(normalizedUrl, { mode: 'cors',
method: 'GET', credentials: 'same-origin',
mode: 'cors', redirect: 'error',
credentials: 'omit', referrerPolicy: 'no-referrer',
redirect: 'error', });
referrerPolicy: 'no-referrer',
});
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to fetch dynamic datasource configuration (${response.status})`); throw new Error(`Failed to fetch dynamic datasource configuration (${response.status})`);
@ -114,7 +112,4 @@ async function fetchConfigJson(normalizedPolicy) {
return response.json(); return response.json();
} }
export { export { resolveConfigFetchPolicy, fetchConfigJson };
resolveConfigFetchPolicy,
fetchConfigJson,
};

View File

@ -103,14 +103,14 @@ describe('secureConfigFetch', () => {
expect.objectContaining({ expect.objectContaining({
method: 'GET', method: 'GET',
mode: 'cors', mode: 'cors',
credentials: 'omit', credentials: 'same-origin',
redirect: 'error', redirect: 'error',
referrerPolicy: 'no-referrer', referrerPolicy: 'no-referrer',
}) })
); );
}); });
it('uses simple fetch for unauthenticated same-origin requests', async () => { it('uses hardened fetch options for unauthenticated same-origin requests', async () => {
global.fetch.mockResolvedValue({ global.fetch.mockResolvedValue({
status: 200, status: 200,
ok: true, ok: true,
@ -124,11 +124,18 @@ describe('secureConfigFetch', () => {
}); });
expect(global.fetch).toHaveBeenCalledWith( expect(global.fetch).toHaveBeenCalledWith(
`${window.location.origin}/protected/config.json` `${window.location.origin}/protected/config.json`,
expect.objectContaining({
method: 'GET',
mode: 'cors',
credentials: 'same-origin',
redirect: 'error',
referrerPolicy: 'no-referrer',
})
); );
}); });
it('uses simple fetch in authenticated environments', async () => { it('uses hardened fetch options in authenticated environments', async () => {
global.fetch.mockResolvedValue({ global.fetch.mockResolvedValue({
status: 200, status: 200,
ok: true, ok: true,
@ -141,7 +148,16 @@ describe('secureConfigFetch', () => {
isSameOrigin: false, isSameOrigin: false,
}); });
expect(global.fetch).toHaveBeenCalledWith('https://trusted.example.com/config.json'); expect(global.fetch).toHaveBeenCalledWith(
'https://trusted.example.com/config.json',
expect.objectContaining({
method: 'GET',
mode: 'cors',
credentials: 'same-origin',
redirect: 'error',
referrerPolicy: 'no-referrer',
})
);
}); });
}); });
}); });