Skip to content

Conversation

@badri-singhal
Copy link
Contributor

@badri-singhal badri-singhal commented Sep 28, 2025

This commit refactors the fallback mechanism for creating voice agent subprocesses to eliminate a potential command injection vulnerability.

Previously, user-provided data was passed as command-line arguments when launching a new agent process directly. This exposed the application to argument injection risks.

The new implementation aligns the fallback behavior with the more secure pattern used by the process pool:

  • Session configuration is now passed as a single JSON object via the subprocess's stdin.
  • The agent script (app.agents.voice.automatic) now handles being launched with no arguments by reading this configuration from stdin.

This change ensures that user-controlled data is no longer part of the command line, mitigating the security risk and making the process creation mechanism more robust and consistent.

Summary by CodeRabbit

  • New Features
    • Early single-session startup mode that reads JSON session config from stdin when no arguments are provided.
    • New configuration options: function confirmation timeout, HITL actions list, charting enable flag, and maximum charts per turn.
  • Refactor
    • Standardized pool sizing to AUTOMATIC_* environment variables for voice agents and rooms.
    • Fallback connection path now launches via module invocation and passes configuration through stdin.
  • Documentation
    • Updated pool implementation guide to reflect new AUTOMATIC_* environment variables.

@coderabbitai
Copy link

coderabbitai bot commented Sep 28, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Introduces a stdin-based configuration path for the voice automatic agent when no CLI args are provided. Renames pool-related config env vars to AUTOMATIC_* and adds new config constants. Updates main fallback to spawn a module subprocess and pass JSON config via stdin. Documentation reflects env var renames.

Changes

Cohort / File(s) Summary
Voice automatic early-mode
app/agents/voice/automatic/__init__.py
Adds early single-session path: asynchronously read one JSON config line from stdin, parse, and run handle_session; logs errors and returns early on failure, bypassing arg-driven flow.
Config renames & additions
app/core/config.py
Renames pool env vars to AUTOMATIC_* variants; adds FUNCTION_CONFIRMATION_TIMEOUT, HITL_ACTIONS, ENABLE_CHARTS, MAX_CHARTS_PER_TURN; maintains existing logic otherwise.
Main fallback and pools
app/main.py
Switches to AUTOMATIC_* pool constants; on pool acquisition failure, launches module via python3 -m ..., writes JSON config to stdin, closes pipe, updates logging and control flow.
Docs update
docs/POOL_IMPLEMENTATION.md
Replaces env var names with AUTOMATIC_* variants; updates auto-scaling references.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Starter as Process (no CLI args)
  participant Stdin as stdin
  participant Agent as Voice Automatic Module
  participant Session as handle_session

  Starter->>Agent: start()
  Note over Agent: Early mode detection (no args)
  Agent->>Stdin: readLineAsync()
  alt got JSON config
    Agent->>Agent: parse JSON
    Agent->>Session: handle_session(config)
    Session-->>Agent: complete
    Agent-->>Starter: return (early path done)
  else empty/invalid
    Agent->>Agent: log error
    Agent-->>Starter: return (no session)
  end
Loading
sequenceDiagram
  autonumber
  participant Main as app/main.py
  participant Pool as Resource Pools
  participant Sub as Subprocess (python -m ...)
  participant Pipe as stdin (PIPE)

  Main->>Pool: acquire()
  alt acquired
    Main->>Main: normal flow
  else failed
    Main->>Sub: spawn(module, stdin=PIPE)
    Main->>Pipe: write JSON config
    Main->>Pipe: close()
    Note over Main,Sub: Config passed via stdin instead of CLI args
    Sub-->>Main: run lifecycle / exit
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A rabbit taps the stdin line,
One JSON nibble—works just fine.
Pools renamed, the burrows neat,
New knobs to tweak with thumping feet.
If queues run dry, we spawn and spin—
A module hops, we pipe it in.
Carrots logged; ship the win. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the primary intent of the changeset—mitigating a command injection vulnerability in the fallback subprocess launch—and clearly reflects the main security improvement without unnecessary detail.

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

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79f3ba5 and 516a90f.

📒 Files selected for processing (4)
  • app/agents/voice/automatic/__init__.py (1 hunks)
  • app/core/config.py (1 hunks)
  • app/main.py (4 hunks)
  • docs/POOL_IMPLEMENTATION.md (2 hunks)
🧰 Additional context used
🪛 Ruff (0.13.1)
app/agents/voice/automatic/__init__.py

103-103: Do not catch blind exception: Exception

(BLE001)

app/main.py

280-280: subprocess call: check for execution of untrusted input

(S603)


292-292: Do not catch blind exception: Exception

(BLE001)

Comment on lines +288 to 294
if proc.stdin:
try:
proc.stdin.write(config_json.encode("utf-8"))
proc.stdin.close()
except Exception as e:
logger.error(f"Failed to write to subprocess stdin: {e}")
bot_procs[proc.pid] = (proc, room_url, session_id, "direct")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Propagate fallback stdin write failures instead of returning success.

Right now if proc.stdin.write(...) blows up (e.g., the agent process crashes before reading), we just log the error and still hand the client a “success” response. That leaves them with a Daily room but no agent ever coming online. Mirror the pool path: kill the subprocess, re-raise (and return the room to the pool if applicable) so the caller gets a failure instead of a silent noop.

         if proc.stdin:
             try:
                 proc.stdin.write(config_json.encode("utf-8"))
                 proc.stdin.close()
             except Exception as e:
                 logger.error(f"Failed to write to subprocess stdin: {e}")
+                proc.kill()
+                raise
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if proc.stdin:
try:
proc.stdin.write(config_json.encode("utf-8"))
proc.stdin.close()
except Exception as e:
logger.error(f"Failed to write to subprocess stdin: {e}")
bot_procs[proc.pid] = (proc, room_url, session_id, "direct")
if proc.stdin:
try:
proc.stdin.write(config_json.encode("utf-8"))
proc.stdin.close()
except Exception as e:
logger.error(f"Failed to write to subprocess stdin: {e}")
proc.kill()
raise
bot_procs[proc.pid] = (proc, room_url, session_id, "direct")
🧰 Tools
🪛 Ruff (0.13.1)

292-292: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In app/main.py around lines 288 to 294, the code currently logs exceptions from
proc.stdin.write/config_json and continues as success; instead, on any exception
when writing to the subprocess stdin you must: stop registering the process,
kill/terminate the subprocess (proc.kill() or proc.terminate() then proc.wait())
to avoid zombie/half-started agents, return the room to the pool if this room
was taken from a pool (restore whatever pool state or call the pool-return
helper), and re-raise the exception so the caller receives a failure; ensure
bot_procs[proc.pid] is only set after a successful write/close and that cleanup
happens inside the except before re-raising.

@badri-singhal badri-singhal changed the title Use stdin for voice agent config to improve security Prevent command injection vulnerability Sep 28, 2025
@badri-singhal badri-singhal force-pushed the prevent-command-injection-vulnerability branch from 516a90f to e626198 Compare September 29, 2025 07:29
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