Skip to content

CORS scanner over-correction: regex misses 13/14 common framework patterns + negation filter false negatives #488

Description

@ajianaz

Problem

PR #484 fixed the original false positive (#483) but over-corrected, introducing 3 new issues identified in post-merge audit.

Finding 1 — Regex misses common real-world patterns (MAJOR)

The narrowed regex only catches 3 narrow forms. 13 of 14 common framework CORS patterns are MISSED:

Pattern Framework Currently Detected?
Access-Control-Allow-Origin: * HTTP Header ✅
cors = "*" Generic ✅
origin: * Generic ✅
add_header Access-Control-Allow-Origin *; nginx ❌
CORS_ALLOW_ALL_ORIGINS = True Django ❌
.allowedOrigins("*") Spring Boot ❌
AllowAnyOrigin() .NET ❌
.allow_origin(Any) tower-http (Rust) ❌
allow_origins=["*"] FastAPI/Starlette ❌
cors({ origin: true }) Express.js ❌
AllowOrigins: ["*"] Go Fiber ❌
cors_origins = ["*"] Rust config ❌
set_header Access-Control-Allow-Origin "*" actix-web ❌

Finding 2 — Negation filter false negatives (MAJOR)

is_false_positive_cors() in builtin.rs:258-278 uses unanchored .* patterns that match across comment→code boundaries:

"except.*wildcard",    // Matches: "Except for wildcard: cors = \"*\"" (suppresses real finding!)
"not.*wildcard",       // Matches: "if not_wildcard_mode { origin = \"*\" }"

Real-world cases wrongly suppressed:

  • # Except for wildcard endpoints: cors = "*" — actual wildcard after comment
  • if not_wildcard_mode { origin = "*" } — variable name triggers negation match
  • // no wildcard for now, but origin = "*" — actual code after comment

Finding 3 — CORS_CONFIG env-key suppression is too broad (MAJOR)

builtin.rs:284 — lower.contains("cors_config") suppresses any line containing that substring, including actual dangerous assignments:

// Suppressed (correctly — env var read):
let origins = env::var("CORS_CONFIG").unwrap();

// Also suppressed (WRONG — real wildcard assignment):
CORS_CONFIG = "*"

Finding 4 — Performance: 12 regexes compiled per call (MINOR)

builtin.rs:272-277 compiles 12 regexes inside is_false_positive_cors() on every match. Several markers are plain strings that could use str::contains(). Regex compilation should use LazyLock<Vec<Regex>> or std::sync::OnceLock.

Suggested Fix

  1. Widen regex to cover framework patterns (nginx add_header, Django CORS_ALLOW_ALL, Spring allowedOrigins, etc.)
  2. Anchor negation patterns — require negation marker at line start (after comment trim), not anywhere on line
  3. Scope env-key suppression — only suppress when env::var("...") or getenv("...") pattern is present
  4. Pre-compile regexes — use LazyLock for static regex patterns

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions