text-processing-utils/src/spellcheck/tests/fetch-loader-security.test.ts

50 lines
2 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { FetchDictionaryLoader } from '../dictionaries/loaders/fetch-loader';
describe('FetchDictionaryLoader — URL injection protection', () => {
const loader = new FetchDictionaryLoader('https://cdn.example.com/dictionaries');
it('rejects ../ path traversal', async () => {
await expect(loader.loadText('../../admin/secret')).rejects.toThrow('Path traversal detected');
});
it('rejects paths with embedded ..', async () => {
await expect(loader.loadText('foo/../../../etc/passwd')).rejects.toThrow(
'Path traversal detected',
);
});
it('rejects absolute paths starting with /', async () => {
await expect(loader.loadText('/admin/secret')).rejects.toThrow('Absolute paths are not allowed');
});
it('exists() returns false for traversal attempts instead of throwing', async () => {
const result = await loader.exists('../../admin/secret');
expect(result).toBe(false);
});
it('exists() returns false for absolute paths', async () => {
const result = await loader.exists('/admin/secret');
expect(result).toBe(false);
});
it('resolves normal relative paths correctly', () => {
const loader2 = new FetchDictionaryLoader('https://cdn.example.com/dictionaries');
// loadText will fail on fetch (no server), but should NOT throw injection errors
// We test the URL resolution by expecting a fetch error, not a security error
expect(loader2.loadText('english/words.txt')).rejects.toThrow();
});
it('handles base URL with trailing slash consistently', () => {
const loaderWithSlash = new FetchDictionaryLoader('https://cdn.example.com/dict/');
const loaderNoSlash = new FetchDictionaryLoader('https://cdn.example.com/dict');
// Both should reject traversal identically
expect(loaderWithSlash.loadText('../../secret')).rejects.toThrow('Path traversal detected');
expect(loaderNoSlash.loadText('../../secret')).rejects.toThrow('Path traversal detected');
});
});