diff --git a/review-enrichment/src/analyzers/iac-misconfig.ts b/review-enrichment/src/analyzers/iac-misconfig.ts index fb565e33b5..af70a0df66 100644 --- a/review-enrichment/src/analyzers/iac-misconfig.ts +++ b/review-enrichment/src/analyzers/iac-misconfig.ts @@ -12,12 +12,14 @@ const CORS_CREDENTIALS_RE = /\b(?:access-control-allow-credentials|allow_credentials|credentials)\b[\s"'=:,-]*(?:true|yes|on)\b/i; const OPEN_INGRESS_RE = /\b(?:cidr_blocks|source_ranges|ipv4_cidr_blocks|cidr|ip_range|value)\b[^\n#]*0\.0\.0\.0\/0\b|\b0\.0\.0\.0\/0\b/i; +const HOST_NETWORK_RE = + /\bhostNetwork\b[\s"'=:,-]*true\b|\bnetwork_mode\b[\s"'=:,-]*host\b/i; const PUBLIC_BUCKET_RE = /(?:(?:["'])?(?:bucket_)?acl(?:["'])?\s*[=:]\s*["']public-(?:read|read-write)["']|(?:["'])?public_access(?:["'])?\s*[=:]\s*true\b|(?:["'])?public(?:["'])?\s*[=:]\s*true\b|(?:["'])?block_public_(?:acls|policy)(?:["'])?\s*[=:]\s*false\b)/i; const SAME_SITE_NONE_RE = /\bsameSite\b[\s"'=:,-]*["']?none["']?\b/i; const SECURE_FALSE_RE = /\bsecure\b[\s"'=:,-]*false\b/i; const TLS_DISABLED_RE = - /\brejectUnauthorized\b[\s"'=:,-]*false\b|\bverify\s*=\s*False\b|\bssl_verify\b[\s"'=:,-]*false\b/i; + /\brejectUnauthorized\b[\s"'=:,-]*false\b|\bverify\s*=\s*False\b|\bssl_verify\b[\s"'=:,-]*false\b|\binsecureSkipTLSVerify\b[\s"'=:,-]*true\b|\bskipTLSVerify\b[\s"'=:,-]*true\b/i; const PROD_RE = /\b(?:NODE_ENV|ENVIRONMENT|APP_ENV)\b[\s"'=:,-]*production\b|\bproduction\s*:/i; const DEBUG_TRUE_RE = /\bdebug\b[\s"'=:,-]*true\b|\bDEBUG\b[\s"'=:,-]*true\b/i; @@ -157,7 +159,7 @@ export function scanPatchForIacMisconfig( } if ( - OPEN_INGRESS_RE.test(body) && + (OPEN_INGRESS_RE.test(body) || HOST_NETWORK_RE.test(body)) && pushFinding(findings, seen, path, newLine, "open-ingress", maxFindings) ) { return findings; diff --git a/review-enrichment/test/iac-misconfig.test.ts b/review-enrichment/test/iac-misconfig.test.ts new file mode 100644 index 0000000000..70b41cc2c7 --- /dev/null +++ b/review-enrichment/test/iac-misconfig.test.ts @@ -0,0 +1,57 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; + +import { scanPatchForIacMisconfig } from "../dist/analyzers/iac-misconfig.js"; + +test("scanPatchForIacMisconfig flags hostNetwork and compose host network mode", () => { + const k8s = scanPatchForIacMisconfig( + "deploy/k8s/app.yaml", + ["@@ -10,0 +10,2 @@", "+ hostNetwork: true", "+ dnsPolicy: ClusterFirstWithHostNet"].join("\n"), + ); + assert.deepEqual(k8s, [{ file: "deploy/k8s/app.yaml", line: 10, kind: "open-ingress" }]); + + const compose = scanPatchForIacMisconfig( + "docker-compose.yml", + ["@@ -1,0 +5,1 @@", "+ network_mode: host"].join("\n"), + ); + assert.deepEqual(compose, [{ file: "docker-compose.yml", line: 5, kind: "open-ingress" }]); +}); + +test("scanPatchForIacMisconfig flags K8s and Helm TLS skip settings", () => { + const k8s = scanPatchForIacMisconfig( + "values.yaml", + ["@@ -20,0 +20,1 @@", "+ insecureSkipTLSVerify: true"].join("\n"), + ); + assert.deepEqual(k8s, [{ file: "values.yaml", line: 20, kind: "tls-verification-disabled" }]); + + const helm = scanPatchForIacMisconfig( + "charts/app/values.yaml", + ["@@ -3,0 +3,1 @@", '+ skipTLSVerify: "true"'].join("\n"), + ); + assert.deepEqual(helm, [{ file: "charts/app/values.yaml", line: 3, kind: "tls-verification-disabled" }]); +}); + +test("scanPatchForIacMisconfig ignores unchanged lines and honors maxFindings", () => { + assert.deepEqual( + scanPatchForIacMisconfig("docker-compose.yml", "@@ -1,1 +1,1 @@\n network_mode: host"), + [], + ); + assert.deepEqual( + scanPatchForIacMisconfig("docker-compose.yml", "@@ -1,0 +1,1 @@\n+ network_mode: host", { + maxFindings: 0, + }), + [], + ); +}); + +test("scanPatchForIacMisconfig aborts when the signal is aborted", () => { + const controller = new AbortController(); + controller.abort(); + assert.throws( + () => + scanPatchForIacMisconfig("docker-compose.yml", "@@ -1,0 +1,1 @@\n+ network_mode: host", { + signal: controller.signal, + }), + /analyzer_aborted/, + ); +});