Skip to content
Closed
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
25 changes: 25 additions & 0 deletions review-enrichment/src/analyzers/iac-misconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ const DOCKER_SOCKET_MOUNT_RE =
const HSTS_DISABLED_RE = /\bStrict-Transport-Security\b[^\n]*\bmax-age\s*=\s*0\b/i;
const REFERRER_UNSAFE_URL_RE = /\bReferrer-Policy\b[^\n]*\bunsafe-url\b/i;
const COOKIE_NOT_HTTPONLY_RE = /\bhttp[_-]?only\b[\s"'=:,-]*false\b/i;
const COOP_UNSAFE_NONE_RE =
/\bCross-Origin-Opener-Policy\b[^\n]*\bunsafe-none\b/i;
// The value must be exactly `0` (filter off), not `10`/`01` — `(?![0-9])` rejects a digit continuation.
// Accepts both `X-XSS-Protection: 0` and nginx `add_header X-XSS-Protection "0"`.
const XSS_PROTECTION_OFF_RE =
/\bX-XSS-Protection\b(?:\s*:\s*|\s+)["']?0(?![0-9])/i;
const FRAME_OPTIONS_ALLOWALL_RE = /\bX-Frame-Options\b[^\n]*\bALLOWALL\b/i;

function* patchLines(patch: string): Generator<string> {
let start = 0;
Expand Down Expand Up @@ -541,6 +548,24 @@ export function scanPatchForIacMisconfig(
) {
return findings;
}
if (
COOP_UNSAFE_NONE_RE.test(body) &&
pushFinding(findings, seen, path, newLine, "coop-unsafe-none", maxFindings)
) {
return findings;
}
if (
XSS_PROTECTION_OFF_RE.test(body) &&
pushFinding(findings, seen, path, newLine, "x-xss-protection-off", maxFindings)
) {
return findings;
}
if (
FRAME_OPTIONS_ALLOWALL_RE.test(body) &&
pushFinding(findings, seen, path, newLine, "frame-options-allowall", maxFindings)
) {
return findings;
}

newLine++;
}
Expand Down
6 changes: 6 additions & 0 deletions review-enrichment/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,12 @@ export function renderBrief(
return "sets `Referrer-Policy: unsafe-url`, leaking the full URL (path and query) to cross-origin destinations";
case "cookie-not-httponly":
return "sets `httpOnly: false` on a cookie, exposing it to JavaScript so an XSS can read it";
case "coop-unsafe-none":
return "sets `Cross-Origin-Opener-Policy: unsafe-none`, allowing cross-origin pages to retain opener access";
case "x-xss-protection-off":
return "disables the legacy XSS filter with `X-XSS-Protection: 0`";
case "frame-options-allowall":
return "sets `X-Frame-Options: ALLOWALL`, permitting any site to embed this page in a frame (clickjacking risk)";
}
};

Expand Down
5 changes: 4 additions & 1 deletion review-enrichment/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,10 @@ export interface IacMisconfigFinding {
| "docker-socket-mount"
| "hsts-disabled"
| "referrer-policy-leak"
| "cookie-not-httponly";
| "cookie-not-httponly"
| "coop-unsafe-none"
| "x-xss-protection-off"
| "frame-options-allowall";
}

/** A newly-added dependency whose install compiles native code (npm node-gyp addon) or has no prebuilt wheel
Expand Down
8 changes: 8 additions & 0 deletions review-enrichment/test/iac-misconfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ test("scanPatchForIacMisconfig flags insecure HTTP security-header settings", ()
["+ add_header Strict-Transport-Security \"max-age=0\";", "hsts-disabled"],
["+ add_header Referrer-Policy \"unsafe-url\";", "referrer-policy-leak"],
["+ httpOnly: false", "cookie-not-httponly"],
["+ add_header Cross-Origin-Opener-Policy \"unsafe-none\";", "coop-unsafe-none"],
["+ add_header X-XSS-Protection \"0\";", "x-xss-protection-off"],
["+ add_header X-Frame-Options \"ALLOWALL\";", "frame-options-allowall"],
];
for (const [added, kind] of cases) {
const findings = scanPatchForIacMisconfig(
Expand All @@ -482,6 +485,11 @@ test("scanPatchForIacMisconfig does not flag secure HTTP header values (incl. Ca
"+ add_header Cache-Control \"max-age=0\";",
"+ add_header Referrer-Policy \"strict-origin-when-cross-origin\";",
"+ httpOnly: true",
"+ add_header Cross-Origin-Opener-Policy \"same-origin\";",
"+ add_header X-XSS-Protection \"1; mode=block\";",
"+ add_header X-Frame-Options \"SAMEORIGIN\";",
// A bare `unsafe-none` config key without the COOP header token must NOT fire the COOP rule.
"+ unsafe-none = false",
];
for (const added of safe) {
assert.deepEqual(
Expand Down
Loading