From ad7c9facaf0dcab6d7d861727d983a4d02c9d0af Mon Sep 17 00:00:00 2001 From: mika kuns Date: Fri, 17 Apr 2026 14:25:15 +0200 Subject: [PATCH] fix(worker): escape newline/tab in CLI args --- .../Runner/ClaudeArgsBuilder.cs | 10 ++++++-- .../Runner/ClaudeArgsBuilderTests.cs | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/ClaudeDo.Worker/Runner/ClaudeArgsBuilder.cs b/src/ClaudeDo.Worker/Runner/ClaudeArgsBuilder.cs index 1d612cc..26cc9e1 100644 --- a/src/ClaudeDo.Worker/Runner/ClaudeArgsBuilder.cs +++ b/src/ClaudeDo.Worker/Runner/ClaudeArgsBuilder.cs @@ -55,9 +55,15 @@ public sealed class ClaudeArgsBuilder private static string Escape(string value) { - if (value.Contains(' ') || value.Contains('"') || value.Contains('\'')) + if (value.Contains(' ') || value.Contains('"') || value.Contains('\'') + || value.Contains('\t') || value.Contains('\n') || value.Contains('\r')) { - var escaped = value.Replace("\\", "\\\\").Replace("\"", "\\\""); + var escaped = value + .Replace("\\", "\\\\") + .Replace("\"", "\\\"") + .Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\t", "\\t"); return $"\"{escaped}\""; } return value; diff --git a/tests/ClaudeDo.Worker.Tests/Runner/ClaudeArgsBuilderTests.cs b/tests/ClaudeDo.Worker.Tests/Runner/ClaudeArgsBuilderTests.cs index 8887f87..2d38a5a 100644 --- a/tests/ClaudeDo.Worker.Tests/Runner/ClaudeArgsBuilderTests.cs +++ b/tests/ClaudeDo.Worker.Tests/Runner/ClaudeArgsBuilderTests.cs @@ -68,4 +68,28 @@ public sealed class ClaudeArgsBuilderTests var args = _builder.Build(new ClaudeRunConfig(null, """Don't say "hello".""", null, null)); Assert.Contains("--append-system-prompt", args); } + + [Fact] + public void Build_quotes_system_prompt_with_newline() + { + var args = _builder.Build(new ClaudeRunConfig( + Model: null, + SystemPrompt: "line1\nline2", + AgentPath: null, + ResumeSessionId: null)); + + Assert.Contains("--append-system-prompt \"line1\\nline2\"", args); + } + + [Fact] + public void Build_quotes_system_prompt_with_tab() + { + var args = _builder.Build(new ClaudeRunConfig( + Model: null, + SystemPrompt: "col1\tcol2", + AgentPath: null, + ResumeSessionId: null)); + + Assert.Contains("\"col1", args); + } }