Files
MyIP/tests/referer-check.test.js
T
jason5ng32andClaude Fable 5 5df6cec80b Fix(api): reject malformed referer instead of throwing
Scanners send garbage Referer headers; new URL() threw and surfaced as an
unhandled 500. Parse failure now means "not an allowed origin" → 403.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 21:32:42 +08:00

87 lines
3.3 KiB
JavaScript

import assert from 'node:assert/strict';
import { describe, it, afterEach, beforeEach } from 'node:test';
import { refererCheck } from '../common/referer-check.js';
// backup ALLOWED_DOMAINS before each test, restore after each test
let backup;
beforeEach(() => { backup = process.env.ALLOWED_DOMAINS; });
afterEach(() => {
if (backup === undefined) delete process.env.ALLOWED_DOMAINS;
else process.env.ALLOWED_DOMAINS = backup;
});
describe('refererCheck — base cases', () => {
it('returns false for missing referer', () => {
assert.equal(refererCheck(), false);
assert.equal(refererCheck(undefined), false);
assert.equal(refererCheck(null), false);
});
it('returns false for empty string referer', () => {
// Falsy short-circuit: not a URL parse attempt, just false
assert.equal(refererCheck(''), false);
});
it('always whitelists localhost regardless of ALLOWED_DOMAINS', () => {
delete process.env.ALLOWED_DOMAINS;
assert.equal(refererCheck('http://localhost/'), true);
assert.equal(refererCheck('http://localhost:5173/tools'), true);
assert.equal(refererCheck('https://localhost:443/'), true);
});
});
describe('refererCheck — ALLOWED_DOMAINS parsing', () => {
it('accepts a single configured domain', () => {
process.env.ALLOWED_DOMAINS = 'example.com';
assert.equal(refererCheck('https://example.com/'), true);
assert.equal(refererCheck('https://example.com/sub/path?q=1'), true);
});
it('accepts any of several comma-separated domains', () => {
process.env.ALLOWED_DOMAINS = 'a.com,b.net,c.org';
assert.equal(refererCheck('https://a.com/'), true);
assert.equal(refererCheck('https://b.net/'), true);
assert.equal(refererCheck('https://c.org/'), true);
});
it('rejects subdomains that are not explicitly listed', () => {
// Current implementation uses exact hostname match, not suffix match
process.env.ALLOWED_DOMAINS = 'example.com';
assert.equal(refererCheck('https://sub.example.com/'), false);
});
it('rejects look-alike / unknown domains', () => {
process.env.ALLOWED_DOMAINS = 'example.com';
assert.equal(refererCheck('https://example.net/'), false);
assert.equal(refererCheck('https://evil.example.com.attacker.xyz/'), false);
});
it('trims URL properly (hostname extraction ignores port, path, query)', () => {
process.env.ALLOWED_DOMAINS = 'example.com';
assert.equal(refererCheck('https://example.com:8443/deep/path?x=1#frag'), true);
});
it('treats unset ALLOWED_DOMAINS as "only localhost allowed"', () => {
delete process.env.ALLOWED_DOMAINS;
assert.equal(refererCheck('https://example.com/'), false);
assert.equal(refererCheck('http://localhost/'), true);
});
it('empty ALLOWED_DOMAINS still permits localhost', () => {
process.env.ALLOWED_DOMAINS = '';
assert.equal(refererCheck('http://localhost/'), true);
assert.equal(refererCheck('https://example.com/'), false);
});
});
describe('refererCheck — malformed inputs', () => {
it('returns false on non-URL strings instead of throwing', () => {
// Scanners send garbage Referer headers; parse failure means denial,
// not an exception bubbling up to a 500.
assert.equal(refererCheck('not-a-url'), false);
assert.equal(refererCheck('http://'), false);
assert.equal(refererCheck('%%%'), false);
});
});