Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,22 @@ export function checkXFrameOptions(headers: RawHeaders): HeaderFinding {
};
}
const val = (raw ?? '').toUpperCase().trim();
const score = (val === 'DENY' || val === 'SAMEORIGIN') ? 15 : 8;
return { header: 'X-Frame-Options', score, maxScore: 15, status: score === 15 ? 'good' : 'warning', raw,
findings: score < 15 ? [`X-Frame-Options value '${raw}' is not DENY or SAMEORIGIN`] : [],
recommendations: score < 15 ? ['Use DENY or SAMEORIGIN'] : [] };
if (val === 'DENY' || val === 'SAMEORIGIN') {
return { header: 'X-Frame-Options', score: 15, maxScore: 15, status: 'good', raw, findings: [], recommendations: [] };
}
// ALLOW-FROM was dropped by every current browser engine (Chrome, Firefox,
// Safari, Edge) years ago — it is silently ignored, not honored, so it
// provides zero real clickjacking protection despite looking like a valid directive.
if (val.startsWith('ALLOW-FROM')) {
return {
header: 'X-Frame-Options', score: 0, maxScore: 15, status: 'warning', raw,
findings: ['ALLOW-FROM is non-standard and ignored by all current browsers — provides no clickjacking protection'],
recommendations: ["Use DENY or SAMEORIGIN, or CSP frame-ancestors to allowlist specific origins"],
};
}
return { header: 'X-Frame-Options', score: 8, maxScore: 15, status: 'warning', raw,
findings: [`X-Frame-Options value '${raw}' is not DENY or SAMEORIGIN`],
recommendations: ['Use DENY or SAMEORIGIN'] };
}

export function checkXContentTypeOptions(headers: RawHeaders): HeaderFinding {
Expand Down
14 changes: 13 additions & 1 deletion test/analyzer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,20 @@ describe('checkXFrameOptions', () => {
expect(r.score).toBe(15);
});

it('invalid value returns score 8', () => {
it('ALLOW-FROM is ignored by all current browsers and scores 0', () => {
const r = checkXFrameOptions({ 'x-frame-options': 'ALLOW-FROM https://example.com' });
expect(r.score).toBe(0);
expect(r.status).toBe('warning');
expect(r.findings.some(f => /ignored by all current browsers/i.test(f))).toBe(true);
});

it('ALLOW-FROM matching is case-insensitive', () => {
const r = checkXFrameOptions({ 'x-frame-options': 'allow-from https://example.com' });
expect(r.score).toBe(0);
});

it('other invalid value returns score 8', () => {
const r = checkXFrameOptions({ 'x-frame-options': 'garbage-value' });
expect(r.score).toBe(8);
expect(r.status).toBe('warning');
});
Expand Down
Loading