Skip to content

fix(slm): replace shell denylist with allowlist to prevent command injection (#3421)#3444

Merged
mrveiss merged 1 commit intoDev_new_guifrom
fix/issue-3421
Apr 4, 2026
Merged

fix(slm): replace shell denylist with allowlist to prevent command injection (#3421)#3444
mrveiss merged 1 commit intoDev_new_guifrom
fix/issue-3421

Conversation

@mrveiss
Copy link
Copy Markdown
Owner

@mrveiss mrveiss commented Apr 4, 2026

Summary

  • Replaces the trivially-bypassed _INJECTION_PATTERNS denylist with ALLOWED_EXECUTABLES — a strict frozenset of ~30 permitted executable names
  • _validate_command() uses shlex.split() and checks Path(tokens[0]).name against the allowlist; anything not listed returns HTTP 400
  • _run_command() passes the pre-split token list to asyncio.create_subprocess_exec(*tokens)shell=False by construction, no interpreter string
  • _run_via_ssh() likewise passes tokens as individual SSH arguments, never a shell string
  • SSH StrictHostKeyChecking=no replaced with yes (known_hosts present) or accept-new (first contact)
  • Endpoint dependency changed from get_current_userrequire_admin
  • language field removed from request schema (interpreter selection was another injection vector)
  • Co-located test file added covering: allowlist pass/fail, absolute-path stripping, shlex errors, empty commands, SSH key-checking logic

Closes #3421

Security

The prior denylist was bypassed by semicolons, &&, shell newlines, python3 -c, eval, and dozens of other vectors. An allowlist with shlex.split() + shell=False eliminates the entire class.

Test plan

  • pytest autobot-slm-backend/api/nodes_execution_test.py -v — all tests pass
  • Non-allowlisted executable (e.g. bash -c whoami) returns HTTP 400
  • Admin-authenticated request to valid command executes correctly
  • Non-admin request returns HTTP 403

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 4, 2026

AutoBot Phase Validation Results

System Maturity: 0.0%

Phase Status:

Recommendations:

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 4, 2026

✅ SSOT Configuration Compliance: Passing

🎉 No hardcoded values detected that have SSOT config equivalents!

@mrveiss mrveiss changed the base branch from main to Dev_new_gui April 4, 2026 16:44
@mrveiss
Copy link
Copy Markdown
Owner Author

mrveiss commented Apr 4, 2026

Code review

Found 1 issue:

  1. _run_via_ssh sets UserKnownHostsFile=/dev/null with StrictHostKeyChecking=accept-new when the known_hosts file is absent — keys accepted are immediately discarded, so every connection is treated as first contact and a MITM can substitute a different host key on each call without detection. The comment says "accept and persist the key on first connection" but /dev/null prevents any persistence.

known_hosts_file = str(known_hosts_path)
else:
# Accept and persist the key on first connection; never silently
# accept a changed key (this is safer than StrictHostKeyChecking=no).
host_key_checking = "accept-new"
known_hosts_file = "/dev/null"
logger.warning(
"known_hosts file not found at %s — using accept-new for %s",
_SSH_KNOWN_HOSTS_PATH,
ip,
)
cmd = [
"ssh",
"-p", str(ssh_port),

Fix: when the known_hosts file does not exist, create it (or use a writable path) so SSH can actually persist the accepted key, or fail hard rather than degrading silently to the same security level as StrictHostKeyChecking=no.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@mrveiss mrveiss merged commit 140c164 into Dev_new_gui Apr 4, 2026
9 of 15 checks passed
@mrveiss mrveiss deleted the fix/issue-3421 branch April 4, 2026 16:55
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.

bug(slm): remote shell execution denylist is trivially bypassed — arbitrary command injection possible

1 participant