fix(configuration): Harden dynamic datasource URL trust boundaries and credential handling. (#5963)

* fix(configuration): Harden dynamic datasource URL trust boundaries and credential handling.

* Remove testing configuration.

* Update policies for runtime ?url=... datasource loading.

* PR feedback.
This commit is contained in:
Joe Boccanfuso authored and GitHub committed 2026-04-21 11:55:14 -04:00
1 parent 51e6b35dbb
commit eede569a88
6 files changed
+334 -13

No files matched your search

@@ -0,0 +1,113 @@
// @ts-nocheck
import { resolveConfigFetchPolicy, fetchConfigJson } from './secureConfigFetch';
describe('secureConfigFetch', () => {
describe('resolveConfigFetchPolicy', () => {
it('allows arbitrary origin in unauthenticated environments', () => {
const result = resolveConfigFetchPolicy('https://untrusted.example.com/config.json', {
userAuthenticationService: {
getAuthorizationHeader: () => ({}),
},
});
expect(result.normalizedUrl).toBe('https://untrusted.example.com/config.json');
expect(result.isAuthenticated).toBe(false);
});
it('blocks non-allowlisted origins in authenticated environments', () => {
expect(() =>
resolveConfigFetchPolicy('https://untrusted.example.com/config.json', {
allowedOrigins: ['https://trusted.example.com'],
userAuthenticationService: {
getAuthorizationHeader: () => ({ Authorization: 'Bearer token123' }),
},
})
).toThrow('Blocked remote configuration origin');
});
it('allows allowlisted origin in authenticated environments', () => {
const result = resolveConfigFetchPolicy('http://localhost:5000/config.json', {
allowedOrigins: ['http://localhost:5000', 'https://trusted.example.com'],
userAuthenticationService: {
getAuthorizationHeader: () => ({ Authorization: 'Bearer token123' }),
},
});
expect(result.normalizedUrl).toBe('http://localhost:5000/config.json');
expect(result.isAuthenticated).toBe(true);
});
it('blocks authenticated fetch when allowlist is missing', () => {
expect(() =>
resolveConfigFetchPolicy('https://noTrustList.example.com/config.json', {
userAuthenticationService: {
getAuthorizationHeader: () => ({ Authorization: 'Bearer token123' }),
},
})
).toThrow('Blocked remote configuration origin');
});
it('rejects embedded userinfo in config URLs', () => {
expect(() =>
resolveConfigFetchPolicy('https://user:pass@trusted.example.com/config.json', {
allowedOrigins: ['https://trusted.example.com'],
userAuthenticationService: {
getAuthorizationHeader: () => ({ Authorization: 'Bearer token123' }),
},
})
).toThrow('URL userinfo is not allowed for dynamic datasource configuration');
});
});
describe('fetchConfigJson', () => {
const originalFetch = global.fetch;
beforeEach(() => {
global.fetch = jest.fn();
});
afterEach(() => {
jest.restoreAllMocks();
global.fetch = originalFetch;
});
it('uses hardened fetch options in unauthenticated environments', async () => {
global.fetch.mockResolvedValue({
status: 200,
ok: true,
json: async () => ({ ok: true }),
});
await fetchConfigJson({
normalizedUrl: 'https://example.com/config.json',
isAuthenticated: false,
});
expect(global.fetch).toHaveBeenCalledWith(
'https://example.com/config.json',
expect.objectContaining({
method: 'GET',
mode: 'cors',
credentials: 'omit',
redirect: 'error',
referrerPolicy: 'no-referrer',
})
);
});
it('uses simple fetch in authenticated environments', async () => {
global.fetch.mockResolvedValue({
status: 200,
ok: true,
json: async () => ({ ok: true }),
});
await fetchConfigJson({
normalizedUrl: 'https://trusted.example.com/config.json',
isAuthenticated: true,
});
expect(global.fetch).toHaveBeenCalledWith('https://trusted.example.com/config.json');
});
});
});