Security review flagged unsanitized request `ref` flowing into the lookup argv. Defense in depth: (1) validate ref at the HTTP boundary against a bounded safe charset with no leading '-' (isValidRef → 400 invalid_ref), and (2) pass both values as the unambiguous --opt=value form so a token can never be re-parsed as a flag by argparse even if a row reached the queue unsanitized. Extracted pure validators (cleanPhone/isValidRef) to validate.ts. 12 service tests (incl. smuggling cases) + typecheck; spawn drain re-smoked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
30 lines
1.1 KiB
TypeScript
30 lines
1.1 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
|
|
import { cleanPhone, isValidRef } from './validate.ts';
|
|
|
|
describe('cleanPhone', () => {
|
|
test('normalizes spacing/punctuation and keeps a leading +', () => {
|
|
expect(cleanPhone('+1 (555) 123-4567')).toBe('+15551234567');
|
|
expect(cleanPhone('6315304426')).toBe('6315304426');
|
|
});
|
|
test('rejects too-short / too-long / non-numeric', () => {
|
|
expect(cleanPhone('123')).toBeNull();
|
|
expect(cleanPhone('abc')).toBeNull();
|
|
expect(cleanPhone('+1234567890123456')).toBeNull(); // 16 digits
|
|
});
|
|
});
|
|
|
|
describe('isValidRef (argv-injection hardening)', () => {
|
|
test('accepts ordinary correlation ids', () => {
|
|
for (const r of ['prospect-9', 'lead_42', 'abc.def:123', 'A1']) expect(isValidRef(r)).toBe(true);
|
|
});
|
|
test('rejects flag-smuggling and unsafe shapes', () => {
|
|
for (const r of ['--device', '-x', '--phone=+1999', 'a b', 'a;rm -rf', '$(whoami)', '']) {
|
|
expect(isValidRef(r)).toBe(false);
|
|
}
|
|
});
|
|
test('rejects over-long refs (>128)', () => {
|
|
expect(isValidRef('a'.repeat(129))).toBe(false);
|
|
expect(isValidRef('a'.repeat(128))).toBe(true);
|
|
});
|
|
});
|