[sergo] Sergo Report: MCP Scripts Shell Injection Audit - 2026-03-19 #21867
Closed
Replies: 1 comment
-
|
This discussion has been marked as outdated by Sergo - Serena Go Expert. A newer discussion is available at Discussion #22033. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Executive Summary
Today's analysis completed a deep audit of the
mcp-scriptssubsystem, combining a 50% extension of run 25's tool-name path traversal investigation with a 50% new exploration of howtoolNamepropagates into the compiled GitHub Actions YAML output. The investigation uncovered a critical shell injection vulnerability inmcp_setup_generator.gowhere unvalidated YAML frontmatter tool names are embedded verbatim into shellcat > FILE << 'DELIM'commands inside compiled.lock.ymlfiles, enabling arbitrary command execution on CI/CD runners. This compounds two previously-identified high severity findings (local path traversal and comment newline injection inmcp inspect) that remain unfixed from run 25. All three vulnerabilities share a single root cause:parseMCPScriptsMap()stores YAML map keys astoolNamewithout any validation.The codebase has a clear precedent for this kind of sanitization —
compiler_safe_outputs_job.go:614callsstringutil.NormalizeSafeOutputIdentifier()before constructing heredoc delimiters — butmcp_setup_generator.goomits this step entirely. Success score: 9/10.🛠️ Serena Tools Update
Tools Snapshot
Tool Capabilities Used Today
find_symbol— retrieved function bodies frommcp_scripts_generator.go,mcp_scripts_parser.go,mcp_setup_generator.go,stringutil/sanitize.go,strings.gofind_referencing_symbols— traced callers ofgenerateMCPScriptsToolsConfigto discovermcp_setup_generator.gousageget_symbols_overview— scannedmcp_inspect_mcp_scripts_files.goandmcp_scripts_generator.gobash/grep— searched for normalization/sanitization calls, goroutine patterns,math/randusage, and heredoc delimiter generation📊 Strategy Selection
Cached Reuse Component (50%)
Previous Strategy Adapted:
tempfile-pathtrav-plus-pr-cwd-revisit(run 25, score 8)mcp_inspect_mcp_scripts_files.go:94path traversal andmcp_scripts_generator.go:188,245,262newline injection — both still unfixed. Following the compile path oftoolNameintomcp_setup_generator.gowas the natural next step.mcp inspectcommand to the compiler output — the generated.lock.ymlGitHub Actions workflow. Read all sixfmt.Fprintfcall sites ingenerateMCPSetup.New Exploration Component (50%)
Novel Approach: Tracing
generateMCPScriptsToolsConfig's referencing symbols to discover(*Compiler).generateMCPSetupand auditing the shell command generation in compiled YAML output.find_referencing_symbols(traced callers),bash sed(read mcp_setup_generator.go lines 335–440)pkg/workflow/mcp_setup_generator.go,pkg/workflow/strings.go,pkg/stringutil/identifiers.goCombined Strategy Rationale
The cached component confirmed the local attack surface; the new component discovered the far more severe CI/CD attack surface. Together they map the full injection chain from YAML frontmatter parsing → local file writing → compiled YAML output.
🔍 Analysis Execution
Codebase Context
pkg/workflow(mcp_setup_generator, mcp_scripts_generator, mcp_scripts_parser, strings, compiler_safe_outputs_job),pkg/cli(mcp_inspect_mcp_scripts_files),pkg/stringutilmcp-scriptssubsystem compile and inspect pathsFindings Summary
📋 Detailed Findings
Critical Issue — Shell Injection in Compiled GitHub Actions YAML
Location:
pkg/workflow/mcp_setup_generator.go:377, 385, 391, 395, 401, 405The
generateMCPSetupmethod generates theSetup MCP Scripts Tool Filesstep in the compiled GitHub Actions workflow. For each mcp-scripts tool, it writes:toolNameis the raw YAML map key from themcp-scripts:frontmatter section — no sanitization, no quoting, no validation. A tool name containing shell metacharacters produces a malicious compiled workflow:When GitHub Actions executes this shell step, bash parses the
;-separated commands and executes them. Additionally, the heredoc delimiter is constructed asGH_AW_MCP_SCRIPTS_SH_+strings.ToUpper(toolName)+_EOF— iftoolNamecontains spaces, the delimiter becomes syntactically invalid (GH_AW_MCP_SCRIPTS_SH_EVIL TOOL_SH_EOF).Contrast:
compiler_safe_outputs_job.go:614correctly callsstringutil.NormalizeSafeOutputIdentifier(scriptName)before generating its heredoc delimiter.mcp_setup_generator.gohas no equivalent normalization.Severity: CRITICAL — arbitrary command execution in GitHub Actions CI/CD runner via a crafted workflow frontmatter.
High Priority Issues — Local Path Traversal + Comment Code Injection (unfixed from run 25)
Locations:
pkg/cli/mcp_inspect_mcp_scripts_files.go:94pkg/workflow/mcp_scripts_generator.go:188, 245, 262Path Traversal (
mcp inspect):filepath.Join("/tmp/gh-aw-mcp-scripts-abc123", "../../.ssh/authorized_keys.sh")→/home/user/.ssh/authorized_keys.sh(0755 executable).Newline Comment Injection (all three script generators):
A
toolConfig.Namecontaining\nterminates the comment, and the content after the newline is interpreted as executable code. This affects all three script types. Note:formatMultiLineComment()correctly handles\ninDescriptionby prefixing each line — butNameis embedded raw.Severity: HIGH — local filesystem write outside temp dir (when running
gh aw mcp inspect) and code injection in generated scripts.View Medium Priority Issue — Inconsistent Tool Name Normalization
Location:
pkg/workflow/mcp_setup_generator.go:376vspkg/workflow/compiler_safe_outputs_job.go:614compiler_safe_outputs_job.go:614normalizes script names before using them in heredoc delimiters and paths:mcp_setup_generator.go:376does NOT normalize:Even if
NormalizeSafeOutputIdentifier(which only replaces-with_) were applied, it would be insufficient to prevent shell injection. The real fix is validation at parse time inparseMCPScriptsMap(). But the inconsistency highlights thatmcp-scriptswas added without porting the defensive patterns from the safersafe-outputssubsystem.✅ Improvement Tasks Generated
Task 1: Add Tool Name Validation in
parseMCPScriptsMap(CRITICAL)Issue Type: Security — Shell Injection / Path Traversal
Problem:
parseMCPScriptsMapinpkg/workflow/mcp_scripts_parser.gostores YAML map keys astoolNamewithout any validation. These names flow into: (a) shell commands in compiled GitHub Actions YAML viamcp_setup_generator.go, (b)filepath.Joininmcp inspectviamcp_inspect_mcp_scripts_files.go:94, and (c) single-line comment strings in generated scripts viamcp_scripts_generator.go:188,245,262.Location(s):
pkg/workflow/mcp_scripts_parser.go:71—for toolName, toolValue := range mcpScriptsMap(no validation)pkg/workflow/mcp_setup_generator.go:377,385,391,395,401,405— sixfmt.Fprintfcalls embedding rawtoolNamepkg/cli/mcp_inspect_mcp_scripts_files.go:94—filepath.Join(dir, toolName+extension)pkg/workflow/mcp_scripts_generator.go:188,245,262— name embedded in comment without\nguardImpact:
mcp inspectRecommendation: Reject tool names that don't match
^[a-zA-Z][a-zA-Z0-9_-]{0,63}$at parse time inparseMCPScriptsMap, returning a compiler error (not silent drop). This is a consistent approach with other field validation in the codebase.Before (
pkg/workflow/mcp_scripts_parser.go):After:
Validation:
"evil; cmd"rejected at parse time"../../etc/passwd"rejected at parse time\nrejected at parse timego test ./pkg/workflow/...Estimated Effort: Small
Task 2: Shell-Safe File Writing in
mcp_setup_generator.go(CRITICAL — defense-in-depth)Issue Type: Security — Shell Injection in Compiled YAML Output
Problem: Even after adding parse-time validation (Task 1), the YAML generation in
mcp_setup_generator.goshould defensively validate/sanitize tool names before embedding them in shell commands. Currently,toolNameappears six times in unquoted shell contexts withincat > PATH << 'DELIM'heredocs.Location(s):
pkg/workflow/mcp_setup_generator.go:377,385,391,395,401,405Impact:
Recommendation: Apply
filepath.Base(stringutil.SanitizeForFilename(toolName))or an equivalent sanitizer before usingtoolNameinfmt.Fprintfcalls that embed it in shell paths. Also assert the sanitized name matches the validated format.Before:
After:
Validation:
tool-one— verify output YAML unchangedSanitizeForFilenameorfilepath.Basestrips/and..go test ./pkg/workflow/...Estimated Effort: Small
Task 3: Guard
toolConfig.NameEmbedding in Script Comment Lines (HIGH)Issue Type: Security — Newline Code Injection in Generated Scripts
Problem: In
mcp_scripts_generator.go,toolConfig.Name(which equalstoolName) is concatenated directly into single-line comment strings without newline sanitization. A name containing\nterminates the comment early, and everything after becomes executable code in the generated.sh,.py, or.cjsfile.Location(s):
pkg/workflow/mcp_scripts_generator.go:188— JS:"// Auto-generated mcp-script tool: " + toolConfig.Name + "\n\n"pkg/workflow/mcp_scripts_generator.go:245— Shell:"# Auto-generated mcp-script tool: " + toolConfig.Name + "\n"pkg/workflow/mcp_scripts_generator.go:262— Python:"# Auto-generated mcp-script tool: " + toolConfig.Name + "\n"Impact:
Recommendation: Use
strings.ReplaceAll(toolConfig.Name, "\n", " ")(inline newline removal) at the comment generation sites, or useformatMultiLineComment(already available in the same file) which handles this correctly.Before (shell generator, line 245):
After:
Validation:
Namecontaining\ngenerates a single comment linego test ./pkg/workflow/...Estimated Effort: Small
📈 Success Metrics
This Run
Reasoning for Score
📊 Historical Context
Strategy Performance (last 5 runs)
Cumulative Statistics
🎯 Recommendations
Immediate Actions
parseMCPScriptsMap— blocks all three injection vectors at the rootmcp_setup_generator.gobeforefmt.Fprintfheredoc generationtoolConfig.Namenewlines ingenerateMCPScriptShellToolScript,generateMCPScriptPythonToolScript,generateMCPScriptJavaScriptToolScriptLong-term Improvements
ValidateMCPScriptsConfigcompiler validation pass (alongside existingvalidate*.gofiles) that runs tool name validation as part of the normal compilation pipeline, rather than inside the parserparseMCPScriptsMap(analogous totemplate_injection_validation_fuzz_test.gowhich already exists for expression injection)🔄 Next Run Preview
Suggested Focus Areas
pr_command.go:629,660,691os.Chdir without CWD restoration (unfixed since run 16, 10 runs)getLatestWorkflowRunWithRetrycontext propagation (unfixed since run 9, 17 runs)GetSupportedEngines/GetAllEnginesnon-deterministic map iteration (unfixed since run 2, 24 runs — near-record persistence)Strategy Evolution
fmt.Fprintf(yaml, ...)calls that embed user-controlled strings could surface similar issues in other subsystems (MCP config, safe-outputs, codemod)References:
Generated by Sergo — The Serena Go Expert | Run ID: 23319384899 | Strategy: mcp-scripts-shell-injection-plus-yaml-analysis
Beta Was this translation helpful? Give feedback.
All reactions