Skip to content

fix(executor): re-verify parser.py integrity immediately before exec_module#281

Open
advikdivekar wants to merge 3 commits into
utksh1:mainfrom
advikdivekar:security/plugin-parser-rce
Open

fix(executor): re-verify parser.py integrity immediately before exec_module#281
advikdivekar wants to merge 3 commits into
utksh1:mainfrom
advikdivekar:security/plugin-parser-rce

Conversation

@advikdivekar
Copy link
Copy Markdown
Contributor

What is the problem

Closes #202

executor._parse_results() executed parser.py via importlib.util.exec_module() after only verifying the file's integrity at plugin load time (server startup). This creates a TOCTOU (Time-of-Check-Time-of-Use) window:

Startup:  load_plugins() → _verify_plugin_integrity() → ✓ OK
          ...time passes, requests come in...
Attack:   attacker writes malicious parser.py to plugins/foo/parser.py
Next req: _parse_results() → parser_path.exists() → True → exec_module()
          → arbitrary code executes inside the server process

Any process or user with write access to the plugin directory (a misconfigured container, another compromised service, a path traversal elsewhere) could exploit this window.

What was changed

File Change
backend/secuscan/plugins.py Added verify_parser_at_exec_time() — re-computes the plugin digest with compute_plugin_digest() and compares it against plugin.checksum using hmac.compare_digest immediately before execution. Blocks when checksum is absent and enforce_plugin_signatures is True.
backend/secuscan/executor.py In _parse_results(), call verify_parser_at_exec_time() before the importlib block. If it returns False, log and skip the parser rather than executing it.
testing/backend/unit/test_plugin_parser_rce.py 7 tests: checksum match allows execution, tampered parser blocked, no-checksum + enforcement-off allowed (with warning), no-checksum + enforcement-on blocked, digest compute failure returns safe default, executor skips exec_module when check fails, executor calls exec_module when check passes

Why this approach

  • Re-checking at exec time is the canonical fix for TOCTOU — the window between check and use is eliminated by re-checking immediately before use
  • hmac.compare_digest prevents timing oracle attacks on the checksum comparison
  • Graceful degradation: plugins without checksums (legacy or development) still work when enforce_plugin_signatures=False — only blocked in hardened deployments where the operator has explicitly opted in
  • No file locking or inotify required — a second digest computation is cheap and portable

How to test

# Simulate TOCTOU tamper:
# 1. Start server normally (parser.py has correct checksum)
# 2. Replace plugins/myplugin/parser.py with malicious content
# 3. Trigger a scan — parser must be SKIPPED, not executed

# Check logs for:
# ERROR: SECURITY: Parser integrity check failed for plugin myplugin
# ERROR: Skipping custom parser for plugin myplugin: integrity check failed at exec time

Edge cases covered

  • Digest compute fails (e.g. permission error) → safe default (reject)
  • Plugin has no checksum + enforce_plugin_signatures=False → allowed with warning
  • Plugin has no checksum + enforce_plugin_signatures=True → blocked
  • Parser is replaced with content that still has a parse() function → still blocked by digest mismatch
  • Executor falls through to built-in parsers when custom parser is skipped

Verification checklist

  • pytest testing/backend/ — 245 passed (1 pre-existing failure in test_route_rejects_task_when_limiter_full)
  • 7 new RCE-prevention tests — all pass
  • All existing executor and plugin integrity tests — pass

…module

The executor previously ran parser.py after only verifying it at plugin
load time (startup). An attacker who could write to the plugin directory
after startup could replace parser.py between the load-time check and
the actual execution — a TOCTOU vulnerability leading to arbitrary code
execution inside the server process.

Changes:
- plugins.py: add verify_parser_at_exec_time() — re-computes the plugin
  digest and compares it against the stored checksum using
  hmac.compare_digest immediately before exec_module is called; blocks
  execution if checksum is absent and enforce_plugin_signatures is on
- executor.py: call verify_parser_at_exec_time() in _parse_results()
  before the importlib exec_module block; parser is skipped on failure
- test_plugin_parser_rce.py: 7 new tests covering: checksum match allows,
  tampered parser blocked, no-checksum with enforcement on blocked, digest
  compute failure returns safe default, executor skips exec_module when
  check fails, executor calls exec_module when check passes

Fixes utksh1#202 (plugin parser.py dynamically executed without runtime signature
enforcement — RCE via TOCTOU).
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7a566dfdc4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +185 to +192
if not plugin.checksum:
if settings.enforce_plugin_signatures:
logger.error(
"Refusing to execute parser for plugin %s: no checksum present "
"and signature enforcement is enabled",
plugin.id,
)
return False
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Allow signature-only plugins at parser exec time

verify_parser_at_exec_time() now rejects any plugin without plugin.checksum, but _verify_plugin_integrity() explicitly treats checksum and signature as alternative integrity mechanisms (it only fails enforcement when both are missing). In a deployment with enforce_plugin_signatures=True, a plugin that was validly loaded via signature verification (no checksum, signature present) will always have its custom parser skipped at runtime, creating a regression for hardened signature-based setups. The exec-time check should mirror load-time policy by accepting signature-verified plugins instead of hard-failing on missing checksum alone.

Useful? React with 👍 / 👎.

@advikdivekar
Copy link
Copy Markdown
Contributor Author

@utksh1 please review it and I have asked to claim few issues, please assign those issues to me under GSSoC 2026

@advikdivekar
Copy link
Copy Markdown
Contributor Author

@utksh1 all test passed now, please review it

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.

[Security] Plugin parser.py Dynamically Executed Without Signature Enforcement — Remote Code Execution

1 participant