[sergo] Sergo Report: YAML Workflow Name Injection + Dead Parameter Audit - 2026-03-24 #22753
Closed
Replies: 1 comment
-
|
This discussion has been marked as outdated by Sergo - Serena Go Expert. A newer discussion is available at Discussion #22971. |
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.
-
Overview
Run 30 of Sergo analyzed the Go codebase using a dual strategy: extending the high-scoring YAML injection audit from run 26 (score 9) into the
compiler_yaml.goworkflow name field, combined with a new dead/unused parameter audit applied to the freshest code in the repo (commit586e5bf, merged today). Three actionable findings were identified spanning two files introduced in the latest commit plus an existing context propagation gap.The headline finding is that
compiler_yaml.go:195embeds the user-supplied workflow name directly inside a YAML double-quoted string (name: "...") without escaping"characters. A workflow whose H1 header orname:frontmatter field contains a double-quote character produces syntactically invalid GitHub Actions YAML that will fail at runner parse time. This is the same injection surface that produced the run 26 critical MCP tool-name findings, now extended one level up to the workflow name itself.Serena Tools Snapshot
grep,find,bash,find_symbol,get_symbols_overview,list_dir,read_fileStrategy Selection
Cached Reuse Component (50%)
Previous Strategy Adapted:
mcp-scripts-shell-injection-plus-yaml-analysis(run 26, score 9)toolNameinmcp_setup_generator.goheredocs. The same root cause pattern (user-controlled string embedded in generated YAML without escaping) appears elsewhere in the codebase.toolNamein MCP setup steps, today I targeteddata.Name(workflow name) in the top-level GitHub Actionsname:field andGH_AW_INFO_WORKFLOW_NAMEenv var. I also traced provenance fromExtractWorkflowNameFromMarkdownBody(H1 header) andextractStringFromMap(frontmatter, "name")to confirm no sanitization before YAML emission.New Exploration Component (50%)
Novel Approach: Dead/Unused Parameter Audit of today's commit (
586e5bf)git show,grep,read_fileengine_firewall_support.go,compiler_yaml_artifacts.go) — prime candidates.pkg/workflow/engine_firewall_support.go(all 189 lines, brand-new),pkg/cli/update_check.go(context propagation gap in goroutine)Combined Strategy Rationale
The cached component ensures breadth (covering the same injection vector across a wider surface), while the new component ensures timeliness (catching bugs in code committed hours before this analysis). Together they surface issues at different lifecycle stages: a long-standing design gap (workflow name escaping) and a fresh oversight (dead parameter in today's commit).
Codebase Context
pkg/)compiler_yaml.go,engine_firewall_support.go,compiler_yaml_main_job.go,compiler_orchestrator_workflow.go,compiler_orchestrator_tools.go,frontmatter_extraction_yaml.go,parser/frontmatter_content.go,update_check.go586e5bf— "fix: include policy-manifest.json and audit directory in firewall-audit-logs artifact" (Copilot-authored, today)Findings Summary
compiler_yaml.go:195,679"produces invalid GitHub Actions YAMLengine_firewall_support.go:143workflowNameparameter received but never used (dead param, latest commit)update_check.go:220client.Get()ignores context;GetWithContext(ctx)available but unusedDetailed Findings
Finding 1 — Workflow Name Unescaped in Double-Quoted YAML (HIGH)
Issue Type: YAML Injection / Robustness
Problem:
compiler_yaml.goembedsdata.Name(the user workflow name) raw inside"..."YAML double-quoted scalars at two sites without escaping"characters. A workflow namedMy "AI" Assistantproduces:which is syntactically invalid YAML — the GitHub Actions runner rejects it at parse time with a YAML syntax error, preventing the workflow from ever running. Same pattern appears in the env var block:
Evidence of unescaped path (tracing
data.Name):compiler_yaml.go:195—fmt.Fprintf(yaml, "name: \"%s\"\n", data.Name)← no escapingcompiler_yaml.go:679—fmt.Fprintf(yaml, " GH_AW_INFO_WORKFLOW_NAME: \"%s\"\n", data.Name)← no escapingcompiler_orchestrator_tools.go:264-266—frontmatterName := extractStringFromMap(result.Frontmatter, "name", nil)→ if set, overridesworkflowName; no sanitizationparser/frontmatter_content.go:158-163— H1 header extracted as raw text; no char filteringContrast with correct sites that already use
%q(Go-quoted string with proper backslash escaping):compiler_activation_job.go:229—fmt.Sprintf(" GH_AW_WORKFLOW_NAME: %q\n", data.Name)✅compiler_safe_outputs_job.go:468—envVars["GH_AW_WORKFLOW_NAME"] = fmt.Sprintf("%q", data.Name)✅Impact: Any user with a workflow whose name contains
"(e.g.,My "Project" Workflow,"Quoted" Name) gets invalid compiled YAML that fails immediately at parse-time on the GitHub Actions runner. The"is a naturally occurring character in workflow names (especially those auto-generated from markdown H1 headers).Location(s):
pkg/workflow/compiler_yaml.go:195pkg/workflow/compiler_yaml.go:679Recommendation: Use YAML-safe double-quote escaping. In YAML double-quoted scalars,
"is escaped as\"and\as\\:Alternatively, extract a helper
yamlDoubleQuote(s string) stringused across all double-quoted scalar sites incompiler_yaml.go.Finding 2 — Dead
workflowNameParameter in New Firewall Function (MEDIUM)Issue Type: Dead Code / Inconsistent API
Problem:
generateFirewallLogParsingStep(workflowName string)was introduced in today's commit586e5bfas part of the newengine_firewall_support.gofile. The function signature acceptsworkflowNamebut the parameter is never referenced in the function body — the step name is hardcoded as"Print firewall logs"with no workflow identification.Contrast with the sibling function in the same file that correctly uses the name:
The call site
compiler_yaml_main_job.go:465passesdata.Nameas a dead argument:Impact: (a) The unused parameter is dead code carrying cognitive overhead. (b) In
workflow_callscenarios where multiple workflows are nested, all "Print firewall logs" steps are indistinguishable in the GitHub Actions UI — users cannot tell which firewall log step belongs to which workflow. (c) If the intent was to include the workflow name in the step name (consistent with the artifact naming pattern), it was accidentally dropped during implementation.Location(s):
pkg/workflow/engine_firewall_support.go:143,152pkg/workflow/compiler_yaml_main_job.go:465Recommendation: Either use
workflowNamein the step name for consistency withgenerateSquidLogsUploadStep, or remove the parameter entirely if intentionally unused:Finding 3 —
CheckForUpdatesAsyncContext Not Propagated to HTTP Call (MEDIUM)Issue Type: Context Propagation Gap
Problem:
CheckForUpdatesAsyncaccepts acontext.Contextparameter with the documented intent that "the context can be used to cancel the update check if the program is shutting down" (update_check.go:231). The goroutine checksctx.Err()before callingcheckForUpdates(), butcheckForUpdates()does not receive the context, andgetLatestRelease()usesclient.Get()instead of the availableclient.GetWithContext(ctx)fromgo-ghv2.13.0:Evidence:
go-ghv2 (v2.13.0pergo.mod) exposesRESTClient.GetWithContext(ctx context.Context, path string, response interface{}) error. The goroutine checks context twice (ctx.Err()at line 244), then abandons cancellation for the actual network call.Impact: On slow or rate-limited networks, if the user Ctrl+C's the
gh aw validatecommand, the update-check goroutine continues its GitHub API call indefinitely until the OS kills the process. This is low severity in practice (CLI exits and the OS kills all goroutines), but violates the stated contract of the function and wastes network resources.Location(s):
pkg/cli/update_check.go:213,220(getLatestRelease)pkg/cli/update_check.go:248(call tocheckForUpdateswithout ctx)Recommendation:
Improvement Tasks
Task 1: Fix Workflow Name Escaping in Double-Quoted YAML Scalars
Issue Type: YAML Injection / Robustness
Problem:
compiler_yaml.goembedsdata.Namein"..."YAML strings without escaping"→ invalid compiled YAML for any workflow name containing a double-quote.Location(s):
pkg/workflow/compiler_yaml.go:195—name:fieldpkg/workflow/compiler_yaml.go:679—GH_AW_INFO_WORKFLOW_NAME:env varImpact:
name:frontmatter field contains"Before:
After:
Validation:
",\, and bothactionlintoryqparse)fmt.Fprintf(yaml, ...\"%s\"..., data.Name)patterns to find similar sitesEstimated Effort: Small
Task 2: Fix Dead
workflowNameParameter ingenerateFirewallLogParsingStepIssue Type: Dead Code / Unused Parameter
Problem:
engine_firewall_support.go:143— new function from586e5bfhas an unusedworkflowNameparameter; step name is hardcoded, inconsistent with siblinggenerateSquidLogsUploadStep.Location(s):
pkg/workflow/engine_firewall_support.go:143,152pkg/workflow/compiler_yaml_main_job.go:465Impact:
workflow_callscenariosBefore:
After (Option A — use name for step identification):
After (Option B — remove parameter if name is intentionally omitted):
Validation:
go test ./pkg/workflow/...)generateSquidLogsUploadStepalso needs alignmentEstimated Effort: Small
Task 3: Propagate Context Through
CheckForUpdatesAsync→getLatestReleaseIssue Type: Context Propagation Gap
Problem:
update_check.go:220—client.Get()ignores the context passed toCheckForUpdatesAsync;GetWithContext(ctx)is available ingo-ghv2.13 but unused, breaking the stated contract.Location(s):
pkg/cli/update_check.go:213,220—getLatestRelease()pkg/cli/update_check.go:143,162,248—checkForUpdates()and its caller in goroutineImpact:
gh aw validateupdate check goroutineBefore:
After:
Validation:
api.RESTClient.GetWithContextsignature ingo-ghv2.13checkForUpdates/getLatestReleaseare affectedEstimated Effort: Small
Success Metrics
This Run
Score Reasoning:
Historical Context
Strategy Performance (Last 10 Runs)
Cumulative Statistics
mcp-scripts-shell-injection-plus-yaml-analysis(score 9, run 26)Long-standing Unfixed Issues (Top 5 by Run Count)
Recommendations
Immediate Actions
yamlDoubleQuoteEscapehelper and apply tocompiler_yaml.go:195,679. Small effort, prevents invalid compiled workflows for a non-trivial class of workflow names.generateFirewallLogParsingStep(MEDIUM) — introduced today; clean up before it accumulates technical debt. Choose whether the step name should include the workflow name (consistent withgenerateSquidLogsUploadStep).CheckForUpdatesAsync.GetWithContextis available, change is purely mechanical.Long-term Improvements
The YAML injection surface (Finding 1) suggests a systemic gap:
data.Nameis used at least 6 times incompiler_yaml.goand many more across compiler files, but only some sites use%q. A one-time audit to find allfmt.Fprintf(yaml, ..., data.Name)calls and verify each uses safe quoting would be worthwhile. The correct sites already use%q; the remaining"%s"sites should follow suit or use a YAML-safe helper.Next Run Preview
Suggested Focus Areas
compiler_main_job.go,compiler_activation_job.go, andcompiler_safe_outputs_job.gostep-name fields — anyfmt.Fprintf(yaml, " - name: %s\n", data.Name)patterns where the name is embedded unquoted in YAML step namesgenerateUnifiedArtifactUpload(paths []string, prefix string)in the newcompiler_yaml_artifacts.go— trace whereprefixcomes from and whetherpathsslice elements are user-controlled or constantStrategy Evolution
The dead-parameter audit of new commits proved highly efficient (found a finding in the first file examined from today's commit). Consider making "audit the most recently changed files" a standing 25% component of every run.
References:
Generated by Sergo — The Serena Go Expert | Run ID: 23510452858 | Strategy: yaml-workflow-name-injection-plus-dead-param-audit
Beta Was this translation helpful? Give feedback.
All reactions