Skip to content

bug(terminal): safety filter false-positives on background wrapper keywords inside quoted strings #20064

@kshitijk4poor

Description

@kshitijk4poor

Bug Description

The terminal tool's _foreground_background_guidance() function uses a naive regex to detect shell-level background wrappers:

_SHELL_LEVEL_BACKGROUND_RE = re.compile(r"\b(?:nohup|disown|setsid)\b", re.IGNORECASE)

This regex fires on any occurrence of the word in the full command string, including inside quoted strings, Python code, commit messages, git operations, and PR body text. This causes false positives that block legitimate commands.

Steps to Reproduce

Any of these get blocked:

# Python code mentioning the word in a string
python3 -c "x = 'preexec_fn=os.setsid'"

# Git commit messages
git commit -m "fix: replace preexec_fn=os.setsid with process_group=0"

# PR creation with body text
gh pr create --body "We removed preexec_fn=os.setsid..."

# Even echo or grep
echo "The function os.setsid() creates a new session"

Expected Behavior

The filter should only block commands where nohup/disown/setsid are used as actual shell-level process management commands, e.g.:

setsid my_server          # actual setsid usage → should block
nohup ./script.sh &       # actual nohup usage → should block

It should NOT block when these words appear in:

  • Quoted strings (single or double)
  • Here-documents
  • Command arguments (commit messages, PR bodies, echo text)
  • Python/Ruby/etc. code passed via -c

Actual Behavior

All commands containing the word anywhere in the text are blocked with:

Foreground command uses shell-level background wrappers (nohup/disown/setsid).
Use terminal(background=true) so Hermes can track the process...

Proposed Fix

The regex should only match when the keyword appears as the first word of the command or immediately after ;, |, &&, ||, or at the start of a subshell. A possible approach:

# Match only when the keyword is used as a command (not inside quotes or arguments)
_SHELL_LEVEL_BACKGROUND_RE = re.compile(
    r"(?:^|[;&|]\s*|&&\s*|\|\|\s*|\(\s*)(?:nohup|disown|setsid)\b",
    re.IGNORECASE | re.MULTILINE,
)

This is conservative but covers the main false-positive cases. A more robust fix would be to strip quoted content before matching.

Environment

  • macOS 26.3.1 (Tahoe)
  • hermes-agent main (commit 7c0fa26)
  • Affects: tools/terminal_tool.py line 1542

Impact

This is a significant usability issue — any session dealing with process management code (reviewing PRs, writing commit messages, debugging subprocess issues) hits this repeatedly, requiring workarounds like writing content to files instead of passing inline.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existstool/terminalTerminal execution and process managementtype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions