diff --git a/src/rules.ts b/src/rules.ts index 779cf09..496bfe1 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -201,11 +201,18 @@ export function checkReferrerPolicy(headers: RawHeaders): HeaderFinding { // no-referrer-when-downgrade is intentionally excluded: it sends the full URL // (path + query) to every cross-origin HTTPS destination. It was the historical // browser default precisely because it was the least restrictive option. - const strongValues = ['no-referrer', 'strict-origin', 'strict-origin-when-cross-origin', 'same-origin']; - const isStrong = strongValues.includes(raw.toLowerCase().trim()); - const score = isStrong ? 10 : 5; - return { header: 'Referrer-Policy', score, maxScore: 10, status: isStrong ? 'good' : 'warning', raw, - findings: isStrong ? [] : [`Value '${raw}' may leak referrer information`], + const strongValues = new Set(['no-referrer', 'strict-origin', 'strict-origin-when-cross-origin', 'same-origin']); + // Referrer-Policy supports a comma-separated fallback list; browsers use the last recognised value. + // e.g. "unsafe-url, strict-origin-when-cross-origin" is effectively strict-origin-when-cross-origin. + const allValidPolicies = new Set([ + 'no-referrer', 'no-referrer-when-downgrade', 'origin', 'origin-when-cross-origin', + 'same-origin', 'strict-origin', 'strict-origin-when-cross-origin', 'unsafe-url', '', + ]); + const tokens = raw.split(',').map(t => t.trim().toLowerCase()); + const effective = tokens.filter(t => allValidPolicies.has(t)).pop() ?? tokens[tokens.length - 1] ?? raw.toLowerCase().trim(); + const isStrong = strongValues.has(effective); + return { header: 'Referrer-Policy', score: isStrong ? 10 : 5, maxScore: 10, status: isStrong ? 'good' : 'warning', raw, + findings: isStrong ? [] : [`Value '${effective}' may leak referrer information`], recommendations: isStrong ? [] : ['Use: strict-origin-when-cross-origin'] }; } diff --git a/test/analyzer.test.ts b/test/analyzer.test.ts index ed22923..30022a8 100644 --- a/test/analyzer.test.ts +++ b/test/analyzer.test.ts @@ -390,6 +390,24 @@ describe('checkReferrerPolicy', () => { expect(r.score).toBe(5); expect(r.status).toBe('warning'); }); + + it('comma-separated fallback list: last recognized strong value earns full score', () => { + const r = checkReferrerPolicy({ 'referrer-policy': 'unsafe-url, strict-origin-when-cross-origin' }); + expect(r.score).toBe(10); + expect(r.status).toBe('good'); + }); + + it('comma-separated fallback list: last recognized weak value gives warning', () => { + const r = checkReferrerPolicy({ 'referrer-policy': 'strict-origin-when-cross-origin, unsafe-url' }); + expect(r.score).toBe(5); + expect(r.status).toBe('warning'); + }); + + it('finding reports the effective value, not the full fallback list string', () => { + const r = checkReferrerPolicy({ 'referrer-policy': 'strict-origin-when-cross-origin, unsafe-url' }); + expect(r.findings[0]).toContain('unsafe-url'); + expect(r.findings[0]).not.toContain('strict-origin-when-cross-origin'); + }); }); describe('checkPermissionsPolicy', () => {