Files
wehub-resource-sync 7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

141 lines
5.3 KiB
JavaScript

/**
* JavaScript tests for safe-fetch.js
* Run with: npm test tests/infrastructure_tests/test_safe_fetch.test.js
*
* Tests the safeFetch function which wraps fetch() with URL validation.
*/
// Mock fetch
global.fetch = jest.fn(() => Promise.resolve({ ok: true }));
// Mock SafeLogger (used by url-validator.js)
global.SafeLogger = {
warn: jest.fn(),
error: jest.fn(),
info: jest.fn(),
debug: jest.fn()
};
// Mock window.location for URL parsing
global.window = {
location: {
href: 'http://localhost:5000/',
origin: 'http://localhost:5000'
}
};
// Load the URL validator first (dependency) and expose as global
const URLValidator = require('../../src/local_deep_research/web/static/js/security/url-validator.js');
global.URLValidator = URLValidator;
// Load safe-fetch module and expose as global
const { safeFetch } = require('../../src/local_deep_research/web/static/js/security/safe-fetch.js');
global.safeFetch = safeFetch;
describe('safeFetch', () => {
beforeEach(() => {
jest.clearAllMocks();
global.fetch.mockClear();
});
describe('Internal URLs (starting with /)', () => {
test('should allow internal API URLs without validation', async () => {
await safeFetch('/api/settings');
expect(global.fetch).toHaveBeenCalledWith('/api/settings', {});
});
test('should allow internal page URLs', async () => {
await safeFetch('/history');
expect(global.fetch).toHaveBeenCalledWith('/history', {});
});
test('should pass options through for internal URLs', async () => {
const options = { method: 'POST', body: JSON.stringify({ key: 'value' }) };
await safeFetch('/api/data', options);
expect(global.fetch).toHaveBeenCalledWith('/api/data', options);
});
test('should allow deep internal paths', async () => {
await safeFetch('/api/v1/research/123/status');
expect(global.fetch).toHaveBeenCalledWith('/api/v1/research/123/status', {});
});
});
describe('External URLs with safe schemes', () => {
test('should allow https URLs', async () => {
await safeFetch('https://example.com/api');
expect(global.fetch).toHaveBeenCalledWith('https://example.com/api', {});
});
test('should allow http URLs', async () => {
await safeFetch('http://example.com/api');
expect(global.fetch).toHaveBeenCalledWith('http://example.com/api', {});
});
});
describe('Unsafe URLs', () => {
test('should block javascript: URLs', async () => {
await expect(safeFetch('javascript:alert(1)')).rejects.toThrow('Blocked unsafe URL');
expect(global.fetch).not.toHaveBeenCalled();
});
test('should block data: URLs', async () => {
await expect(safeFetch('data:text/html,<script>alert(1)</script>')).rejects.toThrow('Blocked unsafe URL');
expect(global.fetch).not.toHaveBeenCalled();
});
test('should block vbscript: URLs', async () => {
await expect(safeFetch('vbscript:msgbox(1)')).rejects.toThrow('Blocked unsafe URL');
expect(global.fetch).not.toHaveBeenCalled();
});
test('should block javascript: with mixed case', async () => {
await expect(safeFetch('JavaScript:alert(1)')).rejects.toThrow('Blocked unsafe URL');
expect(global.fetch).not.toHaveBeenCalled();
});
test('should block javascript: with leading whitespace', async () => {
await expect(safeFetch(' javascript:alert(1)')).rejects.toThrow('Blocked unsafe URL');
expect(global.fetch).not.toHaveBeenCalled();
});
});
describe('Edge cases', () => {
test('should handle empty options object', async () => {
await safeFetch('/api/test', {});
expect(global.fetch).toHaveBeenCalledWith('/api/test', {});
});
test('should handle URLs with query parameters', async () => {
await safeFetch('/api/search?q=test&limit=10');
expect(global.fetch).toHaveBeenCalledWith('/api/search?q=test&limit=10', {});
});
test('should handle URLs with fragments', async () => {
await safeFetch('/page#section');
expect(global.fetch).toHaveBeenCalledWith('/page#section', {});
});
});
});
describe('URLValidator integration', () => {
test('URLValidator should be available globally', () => {
expect(global.URLValidator).toBeDefined();
expect(typeof global.URLValidator.isSafeUrl).toBe('function');
});
test('URLValidator.isSafeUrl should validate https URLs', () => {
expect(global.URLValidator.isSafeUrl('https://example.com')).toBe(true);
});
test('URLValidator.isSafeUrl should reject javascript URLs', () => {
expect(global.URLValidator.isSafeUrl('javascript:alert(1)')).toBe(false);
});
test('URLValidator.isUnsafeScheme should detect unsafe schemes', () => {
expect(global.URLValidator.isUnsafeScheme('javascript:void(0)')).toBe(true);
expect(global.URLValidator.isUnsafeScheme('data:text/html,test')).toBe(true);
expect(global.URLValidator.isUnsafeScheme('https://example.com')).toBe(false);
});
});