Skip to content

fix: typing UX — input/output separation + cleaner hint line#99

Merged
Desperado merged 13 commits into
mainfrom
Desperado/fix-hint-line
May 15, 2026
Merged

fix: typing UX — input/output separation + cleaner hint line#99
Desperado merged 13 commits into
mainfrom
Desperado/fix-hint-line

Conversation

@Desperado
Copy link
Copy Markdown
Contributor

@Desperado Desperado commented May 15, 2026

Two typing-related fixes bundled together.

1. Separate user input from agent output in the queue reader

Bug: When the user typed while the agent was working, the agent's concurrent stdout writes (StreamText, FinishMarkdown, PrintToolIcon) would shove the typing line onto new rows or, worse, erase the typed characters entirely (FinishMarkdown does \033[1A\033[2K per counted newline).

Fix: Drop per-character echo. On the first keystroke, print a single static status line — "⌨ typing — Enter to queue, Ctrl+C to cancel" — and silently buffer runes. The user's input has no visual presence the agent can stomp on; the full text reveals itself in the existing "✓ queued [N]: …" confirmation when Enter is pressed.

Trade-off: you can't see typos as you type, but you also can't get the typing line mangled.

2. Hint line tidy-up

Below the slash prompt, the hint now reads:

`Ctrl+O output: compact • ↑↓ history • Ctrl+X×3 clear • Opt+←/→ words • Ctrl+C cancel • / commands`

  • Added ↑↓ history — was supported but undiscovered
  • Removed Ctrl+←/→ — still works, just not surfaced

🤖 Generated with Claude Code

Desperado and others added 6 commits May 15, 2026 14:57
… word-jump

Three UX fixes for the interactive prompt:

1. **Typing while agent works shows only one character** — the spinner's `\r`
   line-rewrite was overwriting OS-echoed chars in canonical mode.  Fixed by
   switching stdin to half-raw mode (ICANON+ECHO off, OPOST kept so agent
   streaming output isn't broken), managing echo manually, and telling the
   spinner to pause its writes while the user is typing (`Terminal.userTyping`
   atomic flag checked before each 80 ms tick).

2. **Enter can't interrupt thinking** — `StartQueueReader` now accepts a
   `cancelFn` that is called when the user presses Enter with non-empty text.
   For the direct API path this cancels the streaming context; for CC/Codex
   the new `CLIAgent.Cancel()` method cancels the subprocess context. The
   queued text is processed on the very next REPL iteration.

3. **Option+Arrow doesn't jump words on macOS** — macOS Terminal.app sends
   ESC+b/f for Option+Left/Right; iTerm2 and xterm-compatible terminals send
   `\x1b[1;3D`/`\x1b[1;3C` (`KeyLeft/Right{Alt:true}`).  Both sequences now
   invoke `previousWordBoundary` / `nextWordBoundary`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…buf alloc

- agent.go: protect all three a.cancel write sites with cancelMu so the
  CancelCurrent() goroutine can never race with RunStreaming/tool-call/ollama
  paths (was a data race flagged by security reviewer)
- stdin_queue_unix.go: add ESC-sequence state machine so Option+Arrow during
  agent thinking no longer inserts stray 'b'/'f' characters into the typed
  line; also pre-allocate the read buffer outside the hot loop; fix
  utf8.RuneError size==1 check so genuine U+FFFD (size==3) passes through
- input.go: mention Opt+←/→ in the keyboard hint line

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the user presses Enter to interrupt the running agent, Cancel() fires
context.CancelFunc which sends SIGKILL to the CC/Codex subprocess. cmd.Wait()
then returns "signal: killed", which was surfaced to the user as:

  ✗ claude exited with error: signal: killed

Check ctx.Err() before treating a non-zero exit as an error — if the context
was already cancelled the kill was intentional, so return whatever output was
collected (possibly empty) without an error. The REPL then cleanly picks up
the next queued prompt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the queue reader echoed each keystroke inline. When the agent
concurrently called StreamText / FinishMarkdown / PrintToolIcon, the writes
landed wherever the cursor happened to be, displacing the typing line into
new rows. FinishMarkdown's \033[1A\033[2K loop could even erase the typed
characters outright.

New approach: print a single static status line on the first keystroke
("⌨ typing — Enter to queue, Ctrl+C to cancel") and silently buffer the
runes with no per-character echo. The user's input has no visual presence
the agent can stomp on, and the full text reveals itself in the existing
"✓ queued [N]: …" confirmation when Enter is pressed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Desperado Desperado changed the title fix: update input hint line — add history, drop Ctrl+←/→ fix: typing UX — input/output separation + cleaner hint line May 15, 2026
Copy link
Copy Markdown

@qualitymaxapp qualitymaxapp Bot left a comment

Choose a reason for hiding this comment

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

QualityMax Security Scan

Found 1 security issue(s).

Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax security findings in this PR:\n- injection in internal/agent/cc_agent.go:293: The ccSessionID is passed directly as a command-line argument via --resume without validation. If ccSessionID is user-controlled or derived from untrusted sources, it could be exploited for command injection."


// Expose cancel so Cancel() can interrupt from another goroutine.
a.runMu.Lock()
a.runCancel = cancel
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: The ccSessionID is passed directly as a command-line argument via --resume without validation. If ccSessionID is user-controlled or derived from untrusted sources, it could be exploited for command injection.

Fix: Validate ccSessionID to ensure it matches an expected format (e.g., alphanumeric only). Implement strict whitelist validation before passing to exec.CommandContext.

CWE-78 | A03:2021-Injection

Fix with your LLM agent
<your-llm-agent> "Fix the injection issue in internal/agent/cc_agent.go at line 293: The ccSessionID is passed directly as a command-line argument via --resume without validation. If ccSessionID is user-controlled or derived from untrusted sources, it could be exploited for command injection."

@qualitymaxapp
Copy link
Copy Markdown

qualitymaxapp Bot commented May 15, 2026

✅ QualityMax BOLA Audit — No new HIGH risk routes

No new RED (missing authorization) findings introduced by this PR.

@qualitymaxapp
Copy link
Copy Markdown

qualitymaxapp Bot commented May 15, 2026

⚠️ QualityMax Diff Analysis — WARN

Session ID validation added but command injection risk remains in exec.CommandContext usage; concurrency safety improved with mutexes.

🟡 Finding 1: security

File: internal/agent/cc_agent.go:282 | Severity: warning

What: Session ID is validated with isSafeCCSessionID() before being passed to --resume flag, but the validation allows hyphens which could be misinterpreted as flag prefixes if the session ID starts with '--'. The check at line 298 prevents leading hyphens, but this occurs AFTER the args slice is already constructed at line 282.

Fix: Move the isSafeCCSessionID() validation to occur before appending to args slice, or ensure the validation is applied at the point where ccSessionID is set (line 433). Current code has validation at line 282 but it's checking the wrong variable scope.

Fix with your LLM agent
<your-llm-agent> "Fix the security issue in internal/agent/cc_agent.go at line 282: Session ID is validated with isSafeCCSessionID() before being passed to --resume flag, but the validation allows hyphens which could be misinterpreted as flag prefixes if the session ID starts with '--'. The check at line 298 prevents leading hyphens, but this occurs AFTER the args slice is already constructed at line 282."

🟡 Finding 2: security

File: internal/agent/cc_agent.go:304 | Severity: warning

What: exec.CommandContext is called with a.claudeBin which could be user-controlled via config. While session ID is now validated, if claudeBin path is attacker-controlled, arbitrary command execution is possible.

Fix: Validate that a.claudeBin is an absolute path to an expected binary location, or restrict it to a known allowlist of paths during CCAgent initialization.

Fix with your LLM agent
<your-llm-agent> "Fix the security issue in internal/agent/cc_agent.go at line 304: exec.CommandContext is called with a.claudeBin which could be user-controlled via config. While session ID is now validated, if claudeBin path is attacker-controlled, arbitrary command execution is possible."

🟡 Finding 3: logic

File: internal/agent/cc_agent.go:319 | Severity: warning

What: Context cancellation is now treated as success (returns result with nil error at line 321), but this means partial/incomplete results from interrupted operations are returned as if they succeeded. This could mask incomplete tool executions or partial responses.

Fix: Return a distinct error type for user-initiated cancellation (e.g., ErrCancelled) so callers can distinguish between successful completion and interruption. Alternatively, document this behavior clearly if it's intentional.

Fix with your LLM agent
<your-llm-agent> "Fix the logic issue in internal/agent/cc_agent.go at line 319: Context cancellation is now treated as success (returns result with nil error at line 321), but this means partial/incomplete results from interrupted operations are returned as if they succeeded. This could mask incomplete tool executions or partial responses."

🔵 Finding 4: logic

File: internal/agent/codex_agent.go:201 | Severity: info

What: Same context cancellation handling as cc_agent.go - treats cancellation as success and returns partial results.

Fix: Apply the same fix as suggested for cc_agent.go for consistency.

Fix with your LLM agent
<your-llm-agent> "Fix the logic issue in internal/agent/codex_agent.go at line 201: Same context cancellation handling as cc_agent.go - treats cancellation as success and returns partial results."

🔵 Finding 5: test_coverage

File: internal/agent/cc_agent_session_test.go:212 | Severity: info

What: New test TestSafeCCSessionIDValidation covers validation logic well, but doesn't test the actual command construction path where the validated ID is used with --resume flag.

Fix: Add an integration test that verifies session IDs are properly escaped/quoted when passed to exec.CommandContext, or that malicious IDs are rejected before command construction.

Fix with your LLM agent
<your-llm-agent> "Fix the test_coverage issue in internal/agent/cc_agent_session_test.go at line 212: New test TestSafeCCSessionIDValidation covers validation logic well, but doesn't test the actual command construction path where the validated ID is used with --resume flag."
Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax review findings in this PR:\n- security in internal/agent/cc_agent.go:282: Session ID is validated with isSafeCCSessionID() before being passed to --resume flag, but the validation allows hyphens which could be misinterpreted as flag prefixes if the session ID starts with '--'. The check at line 298 prevents leading hyphens, but this occurs AFTER the args slice is already constructed at line 282.\n- security in internal/agent/cc_agent.go:304: exec.CommandContext is called with a.claudeBin which could be user-controlled via config. While session ID is now validated, if claudeBin path is attacker-controlled, arbitrary command execution is possible.\n- logic in internal/agent/cc_agent.go:319: Context cancellation is now treated as success (returns result with nil error at line 321), but this means partial/incomplete results from interrupted operations are returned as if they succeeded. This could mask incomplete tool executions or partial responses.\n- logic in internal/agent/codex_agent.go:201: Same context cancellation handling as cc_agent.go - treats cancellation as success and returns partial results.\n- test_coverage in internal/agent/cc_agent_session_test.go:212: New test TestSafeCCSessionIDValidation covers validation logic well, but doesn't test the actual command construction path where the validated ID is used with --resume flag."

💡 Skills that might have caught this earlier: sast-triage-qm. React 👍 on this comment if the review helped, 👎 if it missed something real or hallucinated. Or use qmax skill-feedback <skill> --good|--bad "<note>" for details. Feeds the skill-postmortem-qm review queue.

Analyzed commit 7c31001f with claude-sonnet-4-5-20250929 (12 files, 5538 tokens) | Customize review preferences

Copy link
Copy Markdown

@qualitymaxapp qualitymaxapp Bot left a comment

Choose a reason for hiding this comment

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

QualityMax Security Scan

Found 1 security issue(s).

Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax security findings in this PR:\n- path-traversal in internal/agent/cc_agent.go:283: The mcpConfigPath is constructed using os.TempDir() and os.Getpid(), then written with os.WriteFile(). While the path construction itself is safe, the file is created with 0600 permissions in a world-writable temp directory. An attacker could potentially create a symlink at the expected path before the file is written, leading to arbitrary file write."

Comment thread internal/agent/cc_agent.go Outdated
}
if ccSessionID != "" {
if !isSafeCCSessionID(ccSessionID) {
return "", fmt.Errorf("invalid Claude session ID")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: The mcpConfigPath is constructed using os.TempDir() and os.Getpid(), then written with os.WriteFile(). While the path construction itself is safe, the file is created with 0600 permissions in a world-writable temp directory. An attacker could potentially create a symlink at the expected path before the file is written, leading to arbitrary file write.

Fix: Use os.CreateTemp() instead of os.WriteFile() to atomically create the file with secure permissions, preventing symlink attacks. Example: f, err := os.CreateTemp(os.TempDir(), "qmax-mcp-*.json"); defer f.Close(); f.WriteString(string(data))

CWE-22 | A01:2021-Broken Access Control

Fix with your LLM agent
<your-llm-agent> "Fix the path-traversal issue in internal/agent/cc_agent.go at line 283: The mcpConfigPath is constructed using os.TempDir() and os.Getpid(), then written with os.WriteFile(). While the path construction itself is safe, the file is created with 0600 permissions in a world-writable temp directory. An attacker could potentially create a symlink at the expected path before the file is written, leading to arbitrary file write."

@qualitymaxapp
Copy link
Copy Markdown

qualitymaxapp Bot commented May 15, 2026

⚠️ QualityMax Diff Analysis — WARN

Concurrency safety improvements look good; one command injection risk in session ID validation requires attention.

🟡 Finding 1: security

File: internal/agent/cc_agent.go:293 | Severity: warning

What: Session ID validation (isSafeCCSessionID) is called AFTER the ID is already used in --resume flag construction. If validation fails, the command has already been built with a potentially malicious session ID that could contain shell metacharacters or command injection payloads.

Fix: Move the isSafeCCSessionID check to before line 292 where args are constructed: validate ccSessionID immediately after the if ccSessionID != "" check, and return an error before appending to args if validation fails.

Fix with your LLM agent
<your-llm-agent> "Fix the security issue in internal/agent/cc_agent.go at line 293: Session ID validation (isSafeCCSessionID) is called AFTER the ID is already used in --resume flag construction. If validation fails, the command has already been built with a potentially malicious session ID that could contain shell metacharacters or command injection payloads."

🔵 Finding 2: logic

File: internal/agent/cc_agent.go:231 | Severity: info

What: WriteMCPConfig now uses os.CreateTemp for atomic file creation, which is good. However, the file permissions are not explicitly set to 0600. CreateTemp uses 0600 by default on Unix, but this is not guaranteed across all platforms.

Fix: After f.Close(), explicitly call os.Chmod(path, 0600) to ensure the config file is only readable by the owner, preventing other local users from reading MCP server configurations.

Fix with your LLM agent
<your-llm-agent> "Fix the logic issue in internal/agent/cc_agent.go at line 231: WriteMCPConfig now uses os.CreateTemp for atomic file creation, which is good. However, the file permissions are not explicitly set to 0600. CreateTemp uses 0600 by default on Unix, but this is not guaranteed across all platforms."

🔵 Finding 3: test_coverage

File: internal/agent/cc_agent_session_test.go:212 | Severity: info

What: TestSafeCCSessionIDValidation tests the validation function but does not verify that invalid session IDs are actually rejected in the Run() method before being passed to the subprocess.

Fix: Add an integration test that calls CCAgent.Run() with an invalid session ID (e.g., containing shell metacharacters) and verifies that it returns an error without executing the command.

Fix with your LLM agent
<your-llm-agent> "Fix the test_coverage issue in internal/agent/cc_agent_session_test.go at line 212: TestSafeCCSessionIDValidation tests the validation function but does not verify that invalid session IDs are actually rejected in the Run() method before being passed to the subprocess."
Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax review findings in this PR:\n- security in internal/agent/cc_agent.go:293: Session ID validation (isSafeCCSessionID) is called AFTER the ID is already used in --resume flag construction. If validation fails, the command has already been built with a potentially malicious session ID that could contain shell metacharacters or command injection payloads.\n- logic in internal/agent/cc_agent.go:231: WriteMCPConfig now uses os.CreateTemp for atomic file creation, which is good. However, the file permissions are not explicitly set to 0600. CreateTemp uses 0600 by default on Unix, but this is not guaranteed across all platforms.\n- test_coverage in internal/agent/cc_agent_session_test.go:212: TestSafeCCSessionIDValidation tests the validation function but does not verify that invalid session IDs are actually rejected in the Run() method before being passed to the subprocess."

💡 Skills that might have caught this earlier: sast-triage-qm. React 👍 on this comment if the review helped, 👎 if it missed something real or hallucinated. Or use qmax skill-feedback <skill> --good|--bad "<note>" for details. Feeds the skill-postmortem-qm review queue.

Analyzed commit d344918e with claude-sonnet-4-5-20250929 (12 files, 5961 tokens) | Customize review preferences

Copy link
Copy Markdown

@qualitymaxapp qualitymaxapp Bot left a comment

Choose a reason for hiding this comment

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

QualityMax Security Scan

Found 1 security issue(s).

Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax security findings in this PR:\n- injection in internal/agent/cc_agent.go:330: User-controlled ccSessionID is passed to exec.CommandContext without proper validation. The isSafeCCSessionID() check is called but its implementation is not visible, making it impossible to verify if it prevents command injection."

result := a.parseStream(stdout, term)

if err := cmd.Wait(); err != nil {
// Intentional cancel (user pressed Enter to interrupt) — not an error.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: User-controlled ccSessionID is passed to exec.CommandContext without proper validation. The isSafeCCSessionID() check is called but its implementation is not visible, making it impossible to verify if it prevents command injection.

Fix: Ensure isSafeCCSessionID() uses a strict whitelist (alphanumeric + hyphens/underscores only) and validate that the session ID matches expected format before appending to command arguments. Consider using a more restrictive approach such as UUID validation.

CWE-78 | A03:2021-Injection

Fix with your LLM agent
<your-llm-agent> "Fix the injection issue in internal/agent/cc_agent.go at line 330: User-controlled ccSessionID is passed to exec.CommandContext without proper validation. The isSafeCCSessionID() check is called but its implementation is not visible, making it impossible to verify if it prevents command injection."

Copy link
Copy Markdown

@qualitymaxapp qualitymaxapp Bot left a comment

Choose a reason for hiding this comment

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

QualityMax Security Scan

Found 2 security issue(s).

Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax security findings in this PR:\n- path-traversal in internal/agent/cc_agent.go:265: The mcpConfigPath is created in OS temp directory but passed to external 'claude' CLI. If an attacker can predict or influence the temp directory location, they could potentially swap the config file with a malicious one before it's used.\n- injection in internal/agent/cc_agent.go:330: User-controlled 'userMsg' is passed directly to 'claude --print' command without sanitization. An attacker can inject arbitrary command-line arguments or shell metacharacters."

for i, r := range id {
switch i {
case 8, 13, 18, 23:
if r != '-' {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: The mcpConfigPath is created in OS temp directory but passed to external 'claude' CLI. If an attacker can predict or influence the temp directory location, they could potentially swap the config file with a malicious one before it's used.

Fix: While os.CreateTemp() is used (which is good), ensure the file permissions (0600) are set before writing sensitive data. Additionally, validate the file hasn't been modified before passing it to the claude CLI by checking its inode/modification time.

CWE-22 | A01:2021-Broken Access Control

Fix with your LLM agent
<your-llm-agent> "Fix the path-traversal issue in internal/agent/cc_agent.go at line 265: The mcpConfigPath is created in OS temp directory but passed to external 'claude' CLI. If an attacker can predict or influence the temp directory location, they could potentially swap the config file with a malicious one before it's used."


// Expose cancel so Cancel() can interrupt from another goroutine.
a.runMu.Lock()
a.runCancel = cancel
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: User-controlled 'userMsg' is passed directly to 'claude --print' command without sanitization. An attacker can inject arbitrary command-line arguments or shell metacharacters.

Fix: Use exec.Command with separate arguments array (already done correctly) but validate/sanitize userMsg content. Consider restricting special characters or using a whitelist of allowed input patterns.

CWE-78 | A03:2021-Injection

Fix with your LLM agent
<your-llm-agent> "Fix the injection issue in internal/agent/cc_agent.go at line 330: User-controlled 'userMsg' is passed directly to 'claude --print' command without sanitization. An attacker can inject arbitrary command-line arguments or shell metacharacters."

@qualitymaxapp
Copy link
Copy Markdown

qualitymaxapp Bot commented May 15, 2026

✅ QualityMax Pipeline

Gate Result
🔍 AI Review ✅ Clean · claude-sonnet-4-5-20250929
🧪 Repo Tests ✅ 287/287 passed (go)
🤖 AI Tests ✅ 28/32 passed

Powered by QualityMax — AI-Powered Test Automation

# Conflicts:
#	internal/session/stdin_queue_unix.go
#	internal/tui/input.go
Copy link
Copy Markdown

@qualitymaxapp qualitymaxapp Bot left a comment

Choose a reason for hiding this comment

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

QualityMax Security Scan

Found 1 security issue(s).

Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax security findings in this PR:\n- injection in internal/agent/cc_agent.go:280: User input 'userMsg' is passed directly to exec.CommandContext as stdin without sanitization. While stdin is generally safer than command-line arguments, if the claude CLI or downstream tools parse stdin unsafely, this could enable command injection or other attacks."

}
return os.SameFile(expected, current) &&
current.Mode().Perm() == 0600 &&
current.Size() == expected.Size() &&
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: User input 'userMsg' is passed directly to exec.CommandContext as stdin without sanitization. While stdin is generally safer than command-line arguments, if the claude CLI or downstream tools parse stdin unsafely, this could enable command injection or other attacks.

Fix: Validate and sanitize userMsg before passing to the subprocess. Consider implementing input validation to reject or escape potentially dangerous patterns.

CWE-78 | A03:2021-Injection

Fix with your LLM agent
<your-llm-agent> "Fix the injection issue in internal/agent/cc_agent.go at line 280: User input 'userMsg' is passed directly to exec.CommandContext as stdin without sanitization. While stdin is generally safer than command-line arguments, if the claude CLI or downstream tools parse stdin unsafely, this could enable command injection or other attacks."

Copy link
Copy Markdown

@qualitymaxapp qualitymaxapp Bot left a comment

Choose a reason for hiding this comment

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

QualityMax Security Scan

Found 1 security issue(s).

Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax security findings in this PR:\n- path-traversal in internal/agent/cc_agent.go:335: The mcpPath variable (from WriteMCPConfig) is passed directly to exec.CommandContext as a command-line argument. While WriteMCPConfig creates the file securely, the path itself is not validated before being passed to the claude CLI, which could be exploited if the path contains special characters or is manipulated."

mcpInfo := a.mcpConfigInfo
a.mu.Unlock()
if !sameMCPConfigFile(mcpPath, mcpInfo) {
return "", fmt.Errorf("MCP config changed before claude launch")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: The mcpPath variable (from WriteMCPConfig) is passed directly to exec.CommandContext as a command-line argument. While WriteMCPConfig creates the file securely, the path itself is not validated before being passed to the claude CLI, which could be exploited if the path contains special characters or is manipulated.

Fix: Validate that mcpPath is an absolute path and contains only expected characters before passing to exec.CommandContext. Consider using filepath.Abs() and filepath.Clean().

CWE-22 | A01:2021-Broken Access Control

Fix with your LLM agent
<your-llm-agent> "Fix the path-traversal issue in internal/agent/cc_agent.go at line 335: The mcpPath variable (from WriteMCPConfig) is passed directly to exec.CommandContext as a command-line argument. While WriteMCPConfig creates the file securely, the path itself is not validated before being passed to the claude CLI, which could be exploited if the path contains special characters or is manipulated."

Copy link
Copy Markdown

@qualitymaxapp qualitymaxapp Bot left a comment

Choose a reason for hiding this comment

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

QualityMax Security Scan

Found 2 security issue(s).

Fix all findings with your LLM agent
<your-llm-agent> "Fix all QualityMax security findings in this PR:\n- path-traversal in internal/agent/cc_agent.go:265: The validateMCPConfigPathForClaude function attempts to validate file paths but uses a character whitelist that may be insufficient. The check allows '/', '.', '_', '-' characters which could potentially be combined to create path traversal sequences like '../' or '/./'.\n- injection in internal/agent/cc_agent.go:330: User input 'userMsg' is passed to claude CLI via stdin without sufficient validation. While sanitizeCCUserPrompt filters some control characters, it does not prevent injection of command-line arguments or special sequences that could be interpreted by the shell or claude CLI."


a.mu.Lock()
a.mcpConfigPath = path
a.mcpConfigInfo = info
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: The validateMCPConfigPathForClaude function attempts to validate file paths but uses a character whitelist that may be insufficient. The check allows '/', '.', '_', '-' characters which could potentially be combined to create path traversal sequences like '../' or '/./'.

Fix: Instead of character-by-character validation, use filepath.Abs() and filepath.Clean() (already done), then verify the result is within an expected directory. Additionally, ensure the path does not contain '..' sequences: add a check like if strings.Contains(clean, "..") { return "", fmt.Errorf("invalid path") }

CWE-22 | A01:2021-Broken Access Control

Fix with your LLM agent
<your-llm-agent> "Fix the path-traversal issue in internal/agent/cc_agent.go at line 265: The validateMCPConfigPathForClaude function attempts to validate file paths but uses a character whitelist that may be insufficient. The check allows '/', '.', '_', '-' characters which could potentially be combined to create path traversal sequences like '../' or '/./'."


func sanitizeCCUserPrompt(prompt string) (string, error) {
if strings.ContainsRune(prompt, '\x00') {
return "", fmt.Errorf("invalid prompt: contains NUL byte")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH: User input 'userMsg' is passed to claude CLI via stdin without sufficient validation. While sanitizeCCUserPrompt filters some control characters, it does not prevent injection of command-line arguments or special sequences that could be interpreted by the shell or claude CLI.

Fix: The current approach of passing user input via stdin is relatively safe, but ensure that the claude binary itself cannot be manipulated. Consider using exec.Command with explicit argument arrays (already done correctly here) and verify that stdin-based input cannot trigger unintended CLI behavior. The sanitization function should be reviewed to ensure it blocks all potentially dangerous sequences.

CWE-78 | A03:2021-Injection

Fix with your LLM agent
<your-llm-agent> "Fix the injection issue in internal/agent/cc_agent.go at line 330: User input 'userMsg' is passed to claude CLI via stdin without sufficient validation. While sanitizeCCUserPrompt filters some control characters, it does not prevent injection of command-line arguments or special sequences that could be interpreted by the shell or claude CLI."

@Desperado Desperado merged commit 8323bca into main May 15, 2026
5 of 6 checks passed
@Desperado Desperado deleted the Desperado/fix-hint-line branch May 15, 2026 16:47
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