Skip to content

fix(core): resolve CORS regex logic bug and implement value-based regex testing#1375

Open
jess-tech-lab wants to merge 10 commits intoOWASP:masterfrom
jess-tech-lab:fix-cors-origin
Open

fix(core): resolve CORS regex logic bug and implement value-based regex testing#1375
jess-tech-lab wants to merge 10 commits intoOWASP:masterfrom
jess-tech-lab:fix-cors-origin

Conversation

@jess-tech-lab
Copy link
Copy Markdown
Contributor

Proposed change

This PR fixes a logic bug in the CORS reflection module and upgrades the testing infrastructure to prevent "silent failures" in YAML modules.

  1. Security Module Fix (nettacker/modules/vuln/http_cors.yaml)
  • Removed a trailing space in the Access-Control-Allow-Origin regex to ensure the module correctly identifies vulnerabilities when the origin is reflected.
  1. Testing Suite Enhancement (tests/test_yaml_regexes.py)
  • Improved the test suite to validate regex patterns against actual header value with reflection header mapping - added logic to extract the Origin from the request fuzzer and verify the response regex against it.

Closes #1374

Type of change

  • New core framework functionality
  • Bugfix (non-breaking change which fixes an issue)
  • Code refactoring without any functionality changes
  • New or existing module/payload change
  • Documentation/localization improvement
  • Test coverage improvement
  • Dependency upgrade
  • Other improvement (best practice, cleanup, optimization, etc)

Checklist

  • I've followed the contributing guidelines
  • I have digitally signed all my commits in this PR
  • I've run make pre-commit and confirm it didn't generate any warnings/changes
  • I've run make test, I confirm all tests passed locally
  • I've added/updated any relevant documentation in the docs/ folder
  • I've linked this PR with an open issue
  • I've tested and verified that my code works as intended and resolves the issue as described
  • I have attached screenshots demonstrating my code works as intended
  • I've checked all other open PRs to avoid submitting duplicate work
  • I confirm that the code and comments in this PR are not direct unreviewed outputs of AI
  • I confirm that I am the Sole Responsible Author for every line of code, comment, and design decision

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 8, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved CORS vulnerability detection to avoid false positives by tightening header-matching behavior.
  • Tests

    • Strengthened test validations for response-header patterns, including checks that response rules align with corresponding request header values.

Walkthrough

Removed a trailing space from the Access-Control-Allow-Origin regex in http_cors.yaml and extended test logic to validate response-header regexes against resolved expected request header values.

Changes

Cohort / File(s) Summary
HTTP CORS Regex Fix
nettacker/modules/vuln/http_cors.yaml
Fixed the origin reflected payload regex by removing a trailing space in the Access-Control-Allow-Origin pattern (`"(http
Header-aware regex tests
tests/test_yaml_schema_and_regex.py
Added REFLECTED_HEADERS mapping and resolve_input_format(); changed regex helpers to return (regex, expected_header_value) tuples; extended is_valid_regex() to optionally check that a regex matches a provided header value; updated test loop and failure messages accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • arkid15r
  • securestep9
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately captures the main changes: fixing a CORS regex bug and implementing value-based regex testing.
Description check ✅ Passed The PR description clearly explains both the security module fix and testing suite enhancement, directly addressing the bug and improvements.
Linked Issues check ✅ Passed The PR fully addresses both objectives from issue #1374: removing the trailing space from the CORS regex and enhancing test suite validation.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issue: only the CORS regex fix and test suite enhancements are included.
Docstring Coverage ✅ Passed Docstring coverage is 93.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/test_yaml_regexes.py (2)

25-37: Render the sample from nettacker_fuzzer.data, not fixed defaults.

resolve_input_format() currently ignores data, prefix, and suffix, so the new semantic check is only accurate for modules that happen to use http, 80, and testpath. Building the sample from the YAML fuzzer config keeps the assertion aligned with the value the request step would actually emit.

♻️ Suggested refactor
 def resolve_input_format(value):
-    """Resolve a literal or nettacker_fuzzer header value to a concrete string by substituting
-    fuzzer double-brace variables with representative values. Single-brace placeholders like
-    {target} are left as-is so they remain valid literals in both the test string and the regex."""
-    if isinstance(value, dict) and "nettacker_fuzzer" in value:
-        fmt = value["nettacker_fuzzer"]["input_format"]
-    else:
-        fmt = str(value)
-    return (
-        fmt.replace("{{schema}}", "http")
-        .replace("{{ports}}", "80")
-        .replace("{{paths}}", "testpath")
-    )
+    """Resolve a literal or nettacker_fuzzer header value to a representative concrete string."""
+    if not (isinstance(value, dict) and "nettacker_fuzzer" in value):
+        return str(value)
+
+    fuzzer = value["nettacker_fuzzer"]
+    rendered = fuzzer.get("input_format", "")
+    for key, candidates in fuzzer.get("data", {}).items():
+        sample = candidates[0] if isinstance(candidates, list) and candidates else candidates
+        if sample is not None:
+            rendered = rendered.replace(f"{{{{{key}}}}}", str(sample))
+
+    return f'{fuzzer.get("prefix", "")}{rendered}{fuzzer.get("suffix", "")}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_yaml_regexes.py` around lines 25 - 37, resolve_input_format
currently substitutes {{schema}}, {{ports}}, {{paths}} with fixed defaults;
change it to build the sample from the nettacker_fuzzer config: read
value["nettacker_fuzzer"]["data"] (and also use prefix and suffix when present)
and replace the double-brace variables in input_format with the corresponding
entries from that data dict instead of hard-coded "http", "80", "testpath", then
return prefix + rendered_input_format + suffix so the sample matches what the
request step would actually emit. Use the existing resolve_input_format function
and the keys input_format, data, prefix, suffix to locate fields.

98-98: Use str | None for the explicit type annotation.

The parameter header_value has an implicit Optional type (annotation str with default None), which is inconsistent and reduces type clarity. Since the project targets Python 3.10+, use str | None instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_yaml_regexes.py` at line 98, Update the type annotation for the
parameter in the is_valid_regex function to explicitly use the modern union
syntax: change header_value's annotation from implicit Optional (defaulting to
None) to "str | None" so the signature reads is_valid_regex(regex: str,
header_value: str | None = None) to match Python 3.10+ typing style and improve
clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_yaml_regexes.py`:
- Around line 25-37: resolve_input_format currently substitutes {{schema}},
{{ports}}, {{paths}} with fixed defaults; change it to build the sample from the
nettacker_fuzzer config: read value["nettacker_fuzzer"]["data"] (and also use
prefix and suffix when present) and replace the double-brace variables in
input_format with the corresponding entries from that data dict instead of
hard-coded "http", "80", "testpath", then return prefix + rendered_input_format
+ suffix so the sample matches what the request step would actually emit. Use
the existing resolve_input_format function and the keys input_format, data,
prefix, suffix to locate fields.
- Line 98: Update the type annotation for the parameter in the is_valid_regex
function to explicitly use the modern union syntax: change header_value's
annotation from implicit Optional (defaulting to None) to "str | None" so the
signature reads is_valid_regex(regex: str, header_value: str | None = None) to
match Python 3.10+ typing style and improve clarity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5515219-1c5b-4d19-991c-fef36cf95392

📥 Commits

Reviewing files that changed from the base of the PR and between 2310a83 and 0952736.

📒 Files selected for processing (2)
  • nettacker/modules/vuln/http_cors.yaml
  • tests/test_yaml_regexes.py

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_yaml_regexes.py (1)

102-102: Use str | None for the optional header_value parameter.

The function parameter header_value: str = None triggers Ruff's RUF013 warning. Change it to header_value: str | None = None to make the optional type explicit. Python 3.10+ is the minimum supported version, so the union syntax is appropriate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_yaml_regexes.py` at line 102, Update the is_valid_regex function
signature to use the explicit optional union type for header_value to address
Ruff RUF013: change the parameter annotation from header_value: str = None to
header_value: str | None = None in the def is_valid_regex(...) declaration so
the type is explicit (Python 3.10+ union syntax) and adjust any type hints/usage
accordingly if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_yaml_regexes.py`:
- Around line 27-39: The test helper resolve_input_format currently collapses a
nettacker_fuzzer block into a single synthetic string (only "http", "80",
"testpath"); update it to fully expand all fuzzer-emitted variants (e.g.,
multiple schemas like "http"/"https", arrays in "ports"/"paths"/"data", and any
"prefix"/"suffix") and produce all concrete header values instead of one. Change
resolve_input_format to detect when value is a dict with "nettacker_fuzzer" and
iterate over each variant field (schema(s), ports, paths, prefix/suffix, data)
to build the Cartesian product of emitted strings, return a list of concrete
values, and update the test assertion logic that iterates (regex, header_value)
pairs so it asserts the response regex matches each concrete header_value for
every variant.

---

Nitpick comments:
In `@tests/test_yaml_regexes.py`:
- Line 102: Update the is_valid_regex function signature to use the explicit
optional union type for header_value to address Ruff RUF013: change the
parameter annotation from header_value: str = None to header_value: str | None =
None in the def is_valid_regex(...) declaration so the type is explicit (Python
3.10+ union syntax) and adjust any type hints/usage accordingly if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b6b35765-a666-47cb-9cce-86b60e47e6e6

📥 Commits

Reviewing files that changed from the base of the PR and between 0952736 and 396070f.

📒 Files selected for processing (1)
  • tests/test_yaml_regexes.py

Comment thread tests/test_yaml_regexes.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/test_yaml_schema_and_regex.py (1)

44-52: Use explicit | None type hint instead of implicit Optional.

PEP 484 prohibits implicit Optional when using = None as a default. The type hint should explicitly indicate the parameter can be None.

Proposed fix
-def is_valid_regex(regex: str, header_value: str = None) -> bool:
+def is_valid_regex(regex: str, header_value: str | None = None) -> bool:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_yaml_schema_and_regex.py` around lines 44 - 52, The parameter
annotation for is_valid_regex should explicitly show it can be None; update the
signature to use the union operator (header_value: str | None = None) instead of
implicit Optional via default, keeping the default = None and leaving the
function body unchanged so type checkers follow PEP 484/PEP 604; reference the
is_valid_regex function to locate and update only the header_value type hint.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_yaml_schema_and_regex.py`:
- Around line 44-52: The parameter annotation for is_valid_regex should
explicitly show it can be None; update the signature to use the union operator
(header_value: str | None = None) instead of implicit Optional via default,
keeping the default = None and leaving the function body unchanged so type
checkers follow PEP 484/PEP 604; reference the is_valid_regex function to locate
and update only the header_value type hint.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9db8fd79-0fda-4a60-8268-d3ca40e789b4

📥 Commits

Reviewing files that changed from the base of the PR and between 396070f and 36b5a2c.

📒 Files selected for processing (2)
  • nettacker/modules/vuln/http_cors.yaml
  • tests/test_yaml_schema_and_regex.py
✅ Files skipped from review due to trivial changes (1)
  • nettacker/modules/vuln/http_cors.yaml

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/test_yaml_schema_and_regex.py (1)

232-236: Rename unused loop variable to _header_name.

The static analysis correctly identifies that header_name is not used within the loop body. Per Python convention, prefix with underscore to indicate intentional disuse.

♻️ Proposed fix
-                    for header_name, header_block in value.items():
+                    for _header_name, header_block in value.items():
                         if isinstance(header_block, dict) and "regex" in header_block:
                             regexes.append((header_block["regex"], None))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_yaml_schema_and_regex.py` around lines 232 - 236, Rename the
unused loop variable header_name to _header_name in the loop under the
conditional if field == "headers" and isinstance(value, dict) so static analysis
won't flag it; update the for loop header for for header_name, header_block in
value.items(): to use _header_name while leaving header_block and
regexes.append((header_block["regex"], None)) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_yaml_schema_and_regex.py`:
- Around line 232-236: Rename the unused loop variable header_name to
_header_name in the loop under the conditional if field == "headers" and
isinstance(value, dict) so static analysis won't flag it; update the for loop
header for for header_name, header_block in value.items(): to use _header_name
while leaving header_block and regexes.append((header_block["regex"], None))
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1bf6dd5f-5574-4a4a-b5f1-584a5365bd3f

📥 Commits

Reviewing files that changed from the base of the PR and between 36b5a2c and fabe6f3.

📒 Files selected for processing (1)
  • tests/test_yaml_schema_and_regex.py

@jess-tech-lab
Copy link
Copy Markdown
Contributor Author

Hi @securestep9 and @arkid15r, I've rebased this branch against the latest changes to resolve conflicts with PR #1319. Please review when you have a chance. Thanks.

@jess-tech-lab
Copy link
Copy Markdown
Contributor Author

@arkid15r and @securestep9 - can you review? Thanks.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Silent failure in http_cors.yaml due to unvalidated regex logic

1 participant