Skip to content

security fixes by removing the unnecessary use of "exec" in the codebase#1525

Open
pUrGe12 wants to merge 15 commits intoOWASP:masterfrom
pUrGe12:sec-fix
Open

security fixes by removing the unnecessary use of "exec" in the codebase#1525
pUrGe12 wants to merge 15 commits intoOWASP:masterfrom
pUrGe12:sec-fix

Conversation

@pUrGe12
Copy link
Copy Markdown
Contributor

@pUrGe12 pUrGe12 commented Apr 20, 2026

Proposed change

This PR is to fix some potential security issues in Nettacker w.r.t to the user of exec in the codebase. The following places used exec as a "lazy" method and not a necessity:

  1. The generate_new_sub_steps function
  2. The apply_data_functions function
  3. The fuzzer_repeater_perform function

Type of change

  • New core framework functionality
  • Bugfix (non-breaking change that 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've 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 and 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've attached screenshots demonstrating that my code works as intended (if applicable)
  • 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 Apr 20, 2026

Summary by CodeRabbit

  • Refactor

    • Replaced dynamic code-evaluation with deterministic, safer processing for nested data assignment and interceptor sequencing.
    • Unified data-function application into a single, whitelisted flow.
  • Bug Fixes

    • Added explicit errors for unknown interceptors and stricter interceptor validation.
  • Tests

    • Added comprehensive tests for nested assignments, data-function behavior, interceptor handling, and error scenarios.

Walkthrough

Replaces exec/dynamic code in utils with deterministic helpers and allowlists: adds set_nested_value(), computes array names once for sub-step generation, enforces AVAILABLE_DATA_FUNCTIONS for data functions, and introduces ALLOWED_INTERCEPTORS with explicit interceptor application and erroring on unknown names. Tests added.

Changes

Cohort / File(s) Summary
Core utils refactor
nettacker/core/utils/common.py
Removed dynamic exec usage in generate_new_sub_steps, apply_data_functions, and fuzzer_repeater_perform. Added set_nested_value(d, key_path, value) and ALLOWED_INTERCEPTORS. Precomputes array names, assigns nested values by splitting / paths, restricts data functions via AVAILABLE_DATA_FUNCTIONS, and replaces runtime-generated interceptor chains with ordered, whitelisted function application that raises ValueError for unknown interceptors.
Tests
tests/core/utils/test_common.py
Added pytest cases for set_nested_value (various path edge cases), non-mutating generate_new_sub_steps, apply_data_functions behavior with unknown and allowed functions (including mocking read_from_file), and fuzzer_repeater_perform behavior for empty, allowed, and disallowed interceptors. Also asserts ALLOWED_INTERCEPTORS contents.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: removing unnecessary uses of exec from the codebase for security purposes.
Description check ✅ Passed The description is directly related to the changeset, identifying the three specific functions where exec was removed and explaining the security rationale.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nettacker/core/utils/common.py (1)

322-342: ⚠️ Potential issue | 🟡 Minor

Strip whitespace from interceptor names before the allowlist check.

interceptors.split(",") preserves any surrounding whitespace from the YAML value (e.g., "generate_and_replace_md5, other"["generate_and_replace_md5", " other"]), so a benign configuration style that previously "just worked" (or failed silently under exec) now hard-fails with ValueError: Interceptor ' other' is not allowed. A .strip() avoids this footgun without loosening the allowlist.

🛠 Proposed fix
-        if interceptors:
-            interceptors = interceptors.split(",")
+        if interceptors:
+            interceptors = [i.strip() for i in interceptors.split(",") if i.strip()]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/core/utils/common.py` around lines 322 - 342, The interceptors list
may contain surrounding whitespace after interceptors.split(",") causing
ALLOWED_INTERCEPTORS lookups to fail; update the processing in common.py so that
after checking if interceptors is truthy you replace interceptors =
interceptors.split(",") with a trimmed list (e.g., map/ comprehension that calls
.strip() on each entry) before the allowlist check and before applying
ALLOWED_INTERCEPTORS to interceptors_function_processed; ensure you still
validate membership against ALLOWED_INTERCEPTORS and apply the interceptor
functions (referencing the interceptors variable, ALLOWED_INTERCEPTORS, and
interceptors_function_processed).
🧹 Nitpick comments (1)
nettacker/core/utils/common.py (1)

182-186: Consider documenting set_nested_value and failure modes.

The helper assumes every intermediate segment in key_path already exists as a dict in d; a missing or non-dict intermediate raises KeyError/TypeError. In the current call site from generate_new_sub_steps, paths come from find_repeaters traversing the same structure, so this is safe today. A short docstring (plus optional type hints) would make the contract explicit for future callers.

✏️ Proposed docstring
-def set_nested_value(d, key_path, value):
-    keys = [k for k in key_path.split("/") if k]
-    for key in keys[:-1]:
-        d = d[key]
-    d[keys[-1]] = value
+def set_nested_value(d: dict, key_path: str, value) -> None:
+    """Assign ``value`` into ``d`` at the nested location described by ``key_path``.
+
+    ``key_path`` is a "/"-delimited string (leading/trailing/empty segments are ignored).
+    All intermediate segments must already exist as dicts; otherwise ``KeyError`` or
+    ``TypeError`` is raised.
+    """
+    keys = [k for k in key_path.split("/") if k]
+    for key in keys[:-1]:
+        d = d[key]
+    d[keys[-1]] = value

As per coding guidelines: "Keep functions small, use type hints where practical, and add docstrings for public APIs".

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

In `@nettacker/core/utils/common.py` around lines 182 - 186, The helper
set_nested_value currently assumes every intermediate segment in key_path exists
and is a dict which can raise KeyError/TypeError; add a concise docstring to
set_nested_value explaining its contract (key_path is "/"-separated,
intermediate keys must exist and be dicts), add type hints to the signature
(e.g., d: dict, key_path: str, value: Any -> None), and add explicit validation
of intermediate segments inside set_nested_value that raises a clear,
descriptive exception (or a ValueError/TypeError with the failing key and full
path) instead of allowing raw KeyError/TypeError to bubble up; mention
generate_new_sub_steps and find_repeaters in the docstring as the current safe
callers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@nettacker/core/utils/common.py`:
- Around line 322-342: The interceptors list may contain surrounding whitespace
after interceptors.split(",") causing ALLOWED_INTERCEPTORS lookups to fail;
update the processing in common.py so that after checking if interceptors is
truthy you replace interceptors = interceptors.split(",") with a trimmed list
(e.g., map/ comprehension that calls .strip() on each entry) before the
allowlist check and before applying ALLOWED_INTERCEPTORS to
interceptors_function_processed; ensure you still validate membership against
ALLOWED_INTERCEPTORS and apply the interceptor functions (referencing the
interceptors variable, ALLOWED_INTERCEPTORS, and
interceptors_function_processed).

---

Nitpick comments:
In `@nettacker/core/utils/common.py`:
- Around line 182-186: The helper set_nested_value currently assumes every
intermediate segment in key_path exists and is a dict which can raise
KeyError/TypeError; add a concise docstring to set_nested_value explaining its
contract (key_path is "/"-separated, intermediate keys must exist and be dicts),
add type hints to the signature (e.g., d: dict, key_path: str, value: Any ->
None), and add explicit validation of intermediate segments inside
set_nested_value that raises a clear, descriptive exception (or a
ValueError/TypeError with the failing key and full path) instead of allowing raw
KeyError/TypeError to bubble up; mention generate_new_sub_steps and
find_repeaters in the docstring as the current safe callers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f76e53d6-7bf3-4bc0-8420-aad312e50129

📥 Commits

Reviewing files that changed from the base of the PR and between 50c0be2 and 4d0711a.

📒 Files selected for processing (1)
  • nettacker/core/utils/common.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

🤖 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/core/utils/test_common.py`:
- Around line 242-245: The test
test_fuzzer_repeater_perform_disallowed_interceptor_raises uses
pytest.raises(match="os.system") which treats the string as a regex (the dot is
a metacharacter); update the match to a literal match by escaping the dot (e.g.,
use a raw string like r"os\.system") or use re.escape on the string returned by
_fuzzer_arrays so that common_utils.fuzzer_repeater_perform is asserted against
the exact "os.system" text rather than a regex.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2befcfa7-3956-4f55-914f-e919bdb194b5

📥 Commits

Reviewing files that changed from the base of the PR and between 4d0711a and 9ef86a2.

📒 Files selected for processing (1)
  • tests/core/utils/test_common.py

Comment thread tests/core/utils/test_common.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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nettacker/core/utils/common.py (1)

333-354: ⚠️ Potential issue | 🟡 Minor

Trim interceptor tokens before allowlist lookup.

The code splits interceptors by comma without stripping whitespace, which would reject valid declarations like "generate_and_replace_md5, generate_and_replace_md5" due to the leading space in the second token. Apply the suggested fix to handle spacing and empty tokens gracefully.

Suggested fix
         interceptors = copy.deepcopy(arrays[array_name]["nettacker_fuzzer"]["interceptors"])
         if interceptors:
-            interceptors = interceptors.split(",")
+            interceptors = [
+                interceptor.strip()
+                for interceptor in interceptors.split(",")
+                if interceptor.strip()
+            ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nettacker/core/utils/common.py` around lines 333 - 354, The interceptors list
is created by splitting a string but tokens are not stripped, causing matches
against ALLOWED_INTERCEPTORS to fail for tokens with surrounding whitespace;
update the processing around interceptors (the interceptors variable and the
loop that iterates over it, and where you check ALLOWED_INTERCEPTORS) to trim
each token and skip empty tokens (e.g., map/strip each token after split and
filter out empty strings) before performing the allowlist lookup and applying
the interceptor functions.
♻️ Duplicate comments (1)
tests/core/utils/test_common.py (1)

279-282: ⚠️ Potential issue | 🟡 Minor

Escape the dot in the regex match.

Ruff still flags this because "os.system" is a regex where . matches any character; use a literal regex to keep pre-commit clean.

Suggested fix
-    with pytest.raises(ValueError, match="os.system"):
+    with pytest.raises(ValueError, match=r"os\.system"):
         common_utils.fuzzer_repeater_perform(arrays)
#!/bin/bash
set -euo pipefail

# Description: Re-run the specific Ruff rule that reported this warning.
# Expected: No RUF043 findings remain after escaping the dot.
ruff check tests/core/utils/test_common.py --select RUF043
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/utils/test_common.py` around lines 279 - 282, The test
test_fuzzer_repeater_perform_disallowed_interceptor_raises uses
pytest.raises(match="os.system") which treats the dot as a wildcard; update the
match string passed to pytest.raises in that test to use a literal-dot regex
(escape the dot) so the assertion matches "os.system" exactly when calling
common_utils.fuzzer_repeater_perform on the arrays returned by
_fuzzer_arrays("os.system").
🧹 Nitpick comments (1)
tests/core/utils/test_common.py (1)

235-239: Remove create=True from the patch since read_from_file is a real function.

The read_from_file function is defined in nettacker/core/fuzzer.py and does not need to be created by the mock. Omitting create=True ensures the test fails immediately if the function is renamed or removed in production.

Suggested fix
-    with patch("nettacker.core.fuzzer.read_from_file", return_value=["a", "b"], create=True):
+    with patch("nettacker.core.fuzzer.read_from_file", return_value=["a", "b"]):
         result = common_utils.apply_data_functions(data)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/utils/test_common.py` around lines 235 - 239, Update the test
test_apply_data_functions_invokes_allowed_function to remove the unnecessary
create=True from the patch call: patch("nettacker.core.fuzzer.read_from_file",
return_value=["a", "b"], create=True) should be changed to
patch("nettacker.core.fuzzer.read_from_file", return_value=["a", "b"]) so the
mock targets the real function (nettacker.core.fuzzer.read_from_file) and will
fail fast if that function is renamed or removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@nettacker/core/utils/common.py`:
- Around line 333-354: The interceptors list is created by splitting a string
but tokens are not stripped, causing matches against ALLOWED_INTERCEPTORS to
fail for tokens with surrounding whitespace; update the processing around
interceptors (the interceptors variable and the loop that iterates over it, and
where you check ALLOWED_INTERCEPTORS) to trim each token and skip empty tokens
(e.g., map/strip each token after split and filter out empty strings) before
performing the allowlist lookup and applying the interceptor functions.

---

Duplicate comments:
In `@tests/core/utils/test_common.py`:
- Around line 279-282: The test
test_fuzzer_repeater_perform_disallowed_interceptor_raises uses
pytest.raises(match="os.system") which treats the dot as a wildcard; update the
match string passed to pytest.raises in that test to use a literal-dot regex
(escape the dot) so the assertion matches "os.system" exactly when calling
common_utils.fuzzer_repeater_perform on the arrays returned by
_fuzzer_arrays("os.system").

---

Nitpick comments:
In `@tests/core/utils/test_common.py`:
- Around line 235-239: Update the test
test_apply_data_functions_invokes_allowed_function to remove the unnecessary
create=True from the patch call: patch("nettacker.core.fuzzer.read_from_file",
return_value=["a", "b"], create=True) should be changed to
patch("nettacker.core.fuzzer.read_from_file", return_value=["a", "b"]) so the
mock targets the real function (nettacker.core.fuzzer.read_from_file) and will
fail fast if that function is renamed or removed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: aca42227-b250-4110-954f-2ea2e1eab9da

📥 Commits

Reviewing files that changed from the base of the PR and between 9ef86a2 and 8f59a5c.

📒 Files selected for processing (2)
  • nettacker/core/utils/common.py
  • tests/core/utils/test_common.py

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.

1 participant