Skip to content

fix(security): remove unsafe in-process transform executor fallback#5

Open
BentWorks wants to merge 1 commit intorookiestar28:mainfrom
BentWorks:fix/cve-transform-executor-thread-escape
Open

fix(security): remove unsafe in-process transform executor fallback#5
BentWorks wants to merge 1 commit intorookiestar28:mainfrom
BentWorks:fix/cve-transform-executor-thread-escape

Conversation

@BentWorks
Copy link
Contributor

Summary

  • Vulnerability: get_transform_executor() silently fell back to the in-process TransformExecutor when TransformProcessRunner (S35) was unavailable. The in-process executor uses threading.Thread with join(timeout=…), but Python threads cannot be forcefully terminated. A malicious transform module could ignore the timeout and continue executing indefinitely in the host process with full access to its address space, network, and filesystem.
  • Fix: Changed get_transform_executor() to raise RuntimeError when TransformProcessRunner is unavailable, following the codebase's established fail-closed security posture. The TransformProcessRunner uses subprocess.run(timeout=…) which kills the child process on timeout, and the worker subprocess denies network access via socket monkeypatching.
  • Tests: Added TestGetTransformExecutorFailClosed class verifying both the fail-closed behavior and the normal subprocess runner path.

Files Changed

  • services/constrained_transforms.py — Replaced silent fallback with RuntimeError; added deprecation docs to TransformExecutor
  • tests/test_s35_transform_isolation.py — Added TestGetTransformExecutorFailClosed test class

Test plan

  • All existing TestS35TransformIsolation tests continue to pass (3/3)
  • New TestGetTransformExecutorFailClosed tests pass (2/2)
  • Verify that transforms still work normally when TransformProcessRunner is available

Disclosure

This vulnerability was identified during an AI-assisted security audit using Claude Code. The patch was also authored with AI assistance.

🤖 Generated with Claude Code

The `get_transform_executor()` function fell back to the in-process
`TransformExecutor` when `TransformProcessRunner` was unavailable. The
in-process executor uses `threading.Thread` with `join(timeout=…)`,
but Python threads cannot be forcefully terminated — a malicious or
runaway transform could escape the timeout constraint and continue
executing indefinitely in the host process with full access to its
address space.

This patch:
- Changes `get_transform_executor()` to raise `RuntimeError` instead
  of silently falling back to the unsafe thread-based executor,
  following the codebase's fail-closed security posture.
- Adds deprecation documentation to `TransformExecutor` class explaining
  why it is unsafe and that `TransformProcessRunner` (S35) should
  always be used.
- Adds regression tests verifying the fail-closed behavior when the
  subprocess runner is unavailable and the normal case when it is.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rookiestar28
Copy link
Owner

rookiestar28 commented Feb 15, 2026

Thanks for the security hardening direction. I agree with removing the unsafe in-process fallback, but this PR is not merge-ready yet due to an error-handling contract gap.

Why this should not be merged yet

  1. Runtime error contract mismatch

    • constrained_transforms.py changes get_transform_executor() to raise RuntimeError when TransformProcessRunner is unavailable.
    • security_doctor.py (check_hardening_wave2) currently catches only ImportError.
    • Result: if subprocess runner import/init fails, Security Doctor can abort instead of reporting a structured FAIL result.
  2. Potential behavior regression at runtime

    • The failure may bubble into /openclaw/security/doctor and produce a 500 path, instead of a normal report containing s35_isolation=fail.
    • We should fail closed for transform execution, but Security Doctor should remain diagnostically stable.
  3. Test coverage is incomplete for this new failure mode

    • Added tests focus on get_transform_executor behavior.
    • Missing regression tests for Security Doctor handling RuntimeError from get_transform_executor.

Required changes before merge

  1. Update check_hardening_wave2 in security_doctor.py:

    • Handle RuntimeError from get_transform_executor (and preferably unexpected exceptions for this check scope).
    • Emit a structured FAIL check (s35_isolation) with actionable remediation, not a hard crash.
  2. Add regression tests in test_s30_security_doctor_wave2.py:

    • Patch get_transform_executor to raise RuntimeError.
    • Assert check_hardening_wave2() still completes and records s35_isolation as FAIL with meaningful detail/remediation.
  3. (Recommended) Introduce a dedicated exception type (e.g., TransformIsolationUnavailable) to avoid broad exception coupling and make diagnostics explicit.

  4. Re-run required verification gates after patch:

    • pre-commit run detect-secrets --all-files
    • pre-commit run --all-files --show-diff-on-failure
    • Full unit tests
    • E2E per SOP

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.

2 participants