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:284lower.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

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