Add extensible AST linting and secret-leak taint analysis by pelikhan · Pull Request #69813 · github/gh-aw · GitHub
Skip to content

Add extensible AST linting and secret-leak taint analysis - #69813

Merged
pelikhan merged 43 commits into
mainfrom
pelikhan-secret-leak-analysis
Oct 18, 2026
Merged

pelikhan merged 43 commits into
mainfrom
pelikhan-secret-leak-analysis

Conversation

@pelikhan

@pelikhan pelikhan commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Add 18 AST-only linters, an extensible rule registry, and reusable source/sink/filter secret-taint analysis. Rules consume parsed AW or Actions ASTs; file discovery and workflow parsing belong to the CLI adapter. No rule recompiles a workflow, fabricates an execution graph, retrieves secret values, or executes analyzed programs.

  • Accept AW .md, native Actions .yml/.yaml, and compiled .lock.yml inputs without compilation. AW-only checks inspect authored execution nodes and prompts and retain unavailable generated-flow coverage. Directory scans avoid duplicate lock/source pairs; Markdown never reaches external actionlint.
  • Declare supported AST targets, stable IDs, default severities, deterministic value-free JSON diagnostics, cancellation, and independent failure handling. gh aw lint runs registered checks alongside actionlint on YAML; --rules selects AST-only operation. Existing extension rules default to Actions-only targets.
  • Track symbolic flows through expressions, environments, step/job outputs, Bash/sh, files, AW prompts, and explicitly configured JavaScript ASTs. Fix the Gemini inventory API key-in-URL exposure using an authentication header.
  • Hide only secret-leak coverage warnings in normal text/JSON output; lint --debug restores them. Internal AST diagnostics, actual errors, analysis failures, and other rules' warnings remain intact. Shape-preserving JavaScript object rest avoids contaminating public timeout fields with nested credential environment members while retaining real secret flows and shallow aliases.
  • Require positive/negative contracts for every registered linter and supported AST: 86 functional cases cover strict/non-strict behavior, exact diagnostics, repeatability, and AST immutability.
  • Add per-rule performance markers and execution-trace regions, 100 benchmark cases / 300 baseline samples, and ordinary-test runtime/allocation-growth guards.
  • Update CLI/API documentation, threat-spec mappings through version 1.0.59, and the major changeset. Merge main's test fixes and preserve the concurrent correction for optional malformed AW lock companions.

Linters

Linter AST inputs Threat mapping Severity Observable check
secret-leak AW, Actions CTR-017 subset Error; internal coverage warnings visible with --debug Symbolic secrets reaching modeled logs, summaries, outputs, network fields, publication inputs, cache keys, or artifacts. AW-only input retains missing generated-flow warnings. JavaScript is opt-in.
action-pin AW, Actions CTR-002 subset Strict policy Immutable remote action, Docker action, and reusable-workflow references. Local references are exempt; authored references are not resolved by compilation.
template-injection AW, Actions CTR-006 subset Error Known attacker-controlled expressions interpolated into executable run text, including environment aliases. Quoted shell-variable consumption is not direct interpolation.
workflow-run-scope AW, Actions CTR-021 subset Error / strict policy Required upstream workflow selection and positive branch scope, excluding empty, wildcard-only, and negated-only filters. Does not prove complete glob-language trust.
job-timeout AW, Actions CTR-026 subset Error Explicit workflow/job timeouts are literal positive integers. Omission is allowed; compiler defaults are not invented.
permission-scope AW, Actions CTR-001 subset Error / strict policy Blanket write-all declarations and invalid OIDC id-token: read. Scoped writes and OIDC issuance are not automatically violations.
version-check AW CTR-018 Strict policy Authored configuration explicitly disables check-for-updates.
conditional-import AW CTR-020 Error Configuration import entries contain if, including false/null values. Runtime prompt conditionals are unaffected.
private-to-public AW CTR-031 Strict policy GitHub-tool configuration opts out of private-to-public protection, globally or for selected servers.
install-scripts AW CTR-014 Strict policy Node runtime configuration enables dependency install hooks. Does not infer arbitrary install commands or imported policy.
allowed-labels AW CTR-015 Error Bare * in creation/discussion-update label allowlists. Narrow patterns and literal required-label names remain valid.
network-scope AW CTR-011 subset Strict policy Bare wildcard network allowances, including alongside defaults. Domain wildcards remain valid; omission is not missing-firewall evidence.
push-target AW CTR-012 subset Warning Wildcard PR-branch pushes lack authored title/label constraints. Respects legacy aliases and defers absent constraints when imports may supply them.
tool-scope AW CTR-003 subset Strict policy Shell refusal conflicts with CLI/gh proxies, minimum-integrity bypass lacks explicit shell intent, or cache memory uses repository-wide scope. Missing prerequisites defer for unresolved imports.
sandbox-config AW CTR-004 subset Strict policy Explicit agent sandbox disablement or nonempty internal agent/MCP command, environment, argument, image, or version overrides. Does not prove effective sandbox enforcement.
image-argument AW, Actions CTR-013 subset Error Option-prefixed job/service or authored MCP container image operands. No registry queries, inferred package operands, or guessed dynamic image values.
checkout-mode AW CTR-033 subset Error Explicit boolean expansion mode, literal list/single-mode conflicts, multi-mode path/allowlist prerequisites, bare allowlist wildcards, and forbidden LFS/submodule/current/wiki options. Runtime selector trust and visibility remain separate.
fork-association AW CTR-034 subset Error Explicit fork association admission uses GitHub's uppercase enum. Empty association lists and repository wildcard patterns are valid; generated admission guards are not reconstructed.

Strict policy: error for authored strict AW (the default), warning for explicit non-strict AW, and warning for native Actions where applicable. Missing workflow selection and invalid OIDC reads are always errors; wildcard push-target findings remain warnings. AW-only checks accept a standalone AW AST or a .lock.yml companion and skip native Actions without source evidence. CLI/repository overrides and imported configurations are not reconstructed. These are observable subsets, not assertions of full CTR conformance.

Current analysis results

Both completed scans cover all 18 rules across 344 compiled workflows and companion sources on published tree 242e891bfd5c8825b039ff8d1b7e613b67fe06f7:

Configuration Errors Internal/debug coverage warnings
Default, without native JavaScript configuration 0 472
Native JavaScript compiler and source/runtime mappings enabled 0 633,973

Normal default CLI JSON is []. Coverage warnings are normally hidden and restored with --debug; the underlying AST diagnostics remain available. The configured run completed all 344 runners and 6,192 rule checks, with zero runner failures and zero compiler-unavailable/incompatible diagnostics. The other 17 linters report no findings in either scan.

Default coverage gaps are 341 unavailable-JavaScript boundaries, seven other external-command boundaries and 124 unsupported/indeterminate shell boundaries. Earlier source-matched shell/file refinements reduced 1,605 warnings to 472; uninspected helpers were not allowlisted.

The new configured total supersedes the earlier child-tree count of 630,179 after main's runtime/source changes. The 14 historical timeout findings were corrected by preserving JavaScript object-rest member provenance, not by suppressing error diagnostics. No interrupted or input-racing partial scan contributes to these final totals. Warnings are incomplete coverage, not vulnerability counts or evidence of safety.

Optional JavaScript inspection uses the actual Go TypeScript compiler through pinned upstream-exported native-preview AST APIs. It confines source reads, preserves cancellation, follows available modules, and retains gaps for unknown calls, cycles, syntax, dynamic reads, and traversal/control-flow limits. Genuine escaped single-record stdout masking commands do not sanitize captured stdout or later uses. Public length/index results require proven types; arbitrary object properties and numeric coercions remain tainted. Authentication inputs are intentional credential consumption; endpoint trust is not inferred.

Remaining compiler obligations require additional trusted evidence: job-purpose authorization, effective sandbox/firewall/output-route semantics, engine enforcement capabilities, package operand semantics, manifests, guarded checkout provenance, repository visibility, successful-job cache gating, runtime bot/App identity, and publisher API provenance. Rules do not infer these through raw YAML, helper basenames, or command substring matching. An empty errors-only report is not proof of whole-workflow safety or complete coverage.

Performance tracking

DEBUG=workflow:lint:perf DEBUG_COLORS=0 gh aw lint --rules= --json writes value-free per-rule start/completion markers, elapsed nanoseconds, diagnostic counts and failure status to stderr. Separate markers measure normalization and total runner time. JSON output and coverage-warning visibility are unchanged. Go execution traces include workflow:lint, workflow:lint:rule:<id>, and workflow:lint:diagnostics regions.

BenchmarkLintRules exercises safe/unsafe fixtures for every registered rule and supported AST at 32 and 256 jobs, reporting ns/op, B/op, and allocs/op. The captured baseline contains 100 cases with three samples each. The slowest median was 2.06 ms for secret-leak on the 256-job Actions unsafe fixture (local Apple M5 Pro; not a portable performance promise).

TestLintPerformanceBudget runs in the ordinary suite. It enforces a 250 ms per-check ceiling and bounds the eight-fold workload increase to 12 times the allocations plus 1,000 and 24 times the runtime plus 5 ms. Safe/unsafe fixture checks prevent skipped rule execution from passing as a fast result. Workflows/frontmatter are parsed before timing; shell/expression parsing inside checks remains measured. The synthetic guard excludes optional external JavaScript compilation and is not a proof of asymptotic complexity.

Measured rule-check wall time over the 344-workflow unconfigured corpus (excluding input parsing and output; debug instrumentation enabled):

Rules Checks Total rule time Maximum single check
secret-leak 344 2,577.7 ms 13.34 ms
template-injection 344 404.8 ms 2.55 ms
action-pin 344 48.3 ms 0.60 ms
Remaining 15 rules 344 each Under 8 ms combined Under 0.10 ms each

The configured native-JavaScript scan identifies the expensive path: aggregate runner wall time was 550.66 s, of which 550.00 s belonged to secret-leak; its slowest individual check took 21.56 s. Template-injection took 0.350 s in total and action-pin 0.046 s. These are measured experimental whole-program-analysis costs, not covered by the small in-process fixture budget and not evidence that every configuration is fast.

CPU, allocation and execution-trace artifacts were captured. The allocation profile exposes internal shell parsing and binding-map cloning as substantial secret-flow costs; no speculative semantic changes or unsafe caching were introduced. Native compiler wall time is included in its calling rule's marker; external compiler CPU work is outside Go CPU profiles.

Dependency license review

mvdan.cc/sh/v3 v3.14.1 is BSD-3-Clause, compatible with MIT subject to preserving its copyright, conditions, and disclaimer; endorsement requires permission. Release notice packaging was not audited.

Optional @typescript/native-preview@7.0.0-dev.20260707.2 declares Apache-2.0 and ships its license/NOTICE. It is installed separately, not bundled into the Go CLI. Redistribution requires applicable licenses/notices and identification of modified upstream files. Its API is explicitly unstable.

Validation

  • Required final make agent-report-progress passed after integration, including formatting, custom Go lint, schema freshness, impacted unit tests, and successful recompilation/drift checks for all 344 workflows.
  • All 86 functional contract cases, performance budgets repeated three times, all 100 benchmark cases sampled three times, and actual CLI corpus runs with performance markers. Marker regressions cover opt-in/off behavior, failures, cancellation, skipped AST targets, deduplication, and absence of private diagnostic/error/AST contents in logs. Execution traces contain the expected regions.
  • Full taint/lint race suites, native compiler regressions, CLI lint tests, and WASM builds passed. Formal threat-catalog mapping tests passed.
  • Main's fixes are integrated. Both formerly failing regression tests, TestContributionCheckWorkflowAllowsRequiredShellCommands and TestSessionParserSourcesIncludeLocalDependencies, pass with CGO enabled. This is a local reproduction result, not a claim that remote CI has finished.
  • Fresh full configured/default measurements completed on the published tree; there are zero error findings and zero configured runner failures. The worktree is clean and the remote branch matches the published SHA.
  • The concurrent published malformed-companion correction is preserved without force-pushing; explicit input parse failures remain failures, while an invalid optional AW companion does not prevent analysis of a valid Actions lock.

pelikhan and others added 2 commits October 17, 2026 18:32
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review October 18, 2026 01:59
Copilot AI max review requested due to automatic review settings October 18, 2026 01:59
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Ponytail Reviewer

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Ponytail Reviewer · gpt56 · 13.6 AIC · ⌖ 5.28 AIC · ⊞ 14.6K · ◷

Generated by Ponytail Reviewer for #69813

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Test Quality Sentinel · sonnet50 · 15 AIC · ⌖ 15.1 AIC · ◇ 6.53 AIC · ⊞ 12.6K · ◷

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

Commit pushed: 7c37b01

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR adds 2,073 new lines to core business logic under pkg/, which is over the 100-line threshold. It does not link an Architecture Decision Record (ADR).

It adds three things that need a recorded decision: a new in-process lint rule registry (pkg/workflow/lint), a source/sink/filter taint engine (pkg/workflow/taint), and a new third-party dependency (mvdan.cc/sh/v3). It also changes what gh aw lint runs by default.

📄 Draft ADR committed: docs/adr/69813-in-process-ast-lint-registry-with-secret-leak-taint-analysis.md. Please review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 What to do next
  1. Review the draft ADR on your branch. It was generated from the PR diff and covers the registry, the taint engine, the shell parser dependency, and the new default gh aw lint behavior.
  2. Complete the missing sections. Check the alternatives (extending actionlint or external scanners, a single hard-coded checker, regex matching) and the trade-offs (partial coverage reported as warnings, gh aw lint failing in cases where it previously passed).
  3. Commit the finalized ADR to docs/adr/ on your branch.
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-69813: In-Process AST Lint Rule Registry with Secret-Leak Taint Analysis

Once an ADR is linked in the PR body, this gate will re-run and check that the implementation matches the decision.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context: What is the problem? What forces are at play?
  • Decision: What did you decide, and why?
  • Alternatives Considered: What else could have been done?
  • Consequences: What are the trade-offs, positive and negative?

ADRs are stored in docs/adr/ as Markdown files numbered by PR number.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · opus55 · 29.9 AIC · ⌖ 40.5 AIC · ⊞ 1.9K · ◷
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

Peek at saved memory (pr-code-quality-reviewer)
reviewed_at: 2026-10-18T02:02:31.832+00:00
review_event: REQUEST_CHANGES
top_themes:
  - shell taint model false positives and false negatives
  - companion markdown parsing coupled to lock-file linting
files_reviewed:
  - pkg/cli/lint_command.go
  - pkg/cli/lint_workflow.go
  - pkg/workflow/lint/repository_workflows_test.go
  - pkg/workflow/taint/shell.go
  - pkg/workflow/taint/taint.go
  - pkg/workflow/taint/workflow.go
comment_count: 3

Note

This comment is managed by comment memory.

Expand the saved memory block to view or edit the persistent context for this thread.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 126.6 AIC · ⌖ 6.14 AIC · ⊞ 15.9K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Request changes

The new taint-linting framework still has a few correctness regressions that I would block on: env subcommands are modeled as log dumps, the add-mask shortcut can suppress a real disclosure in the same command, and a malformed companion .md now aborts linting of an otherwise-valid .lock.yml.

Blocking themes
  • The shell taint model currently introduces both false positives and false negatives around command handling.
  • Lock-file linting is now coupled too tightly to companion source parsing, which regresses normal local iteration on authored workflows.
  • Static review only: I did not execute repository code, tests, or builds.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 126.6 AIC · ⌖ 6.14 AIC · ⊞ 15.9K
Comment /review to run again

Comment thread pkg/workflow/taint/shell.go Outdated
}
}
case "env", "printenv":
if len(cmd.Args) == 1 || name == "env" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Treating every env ... invocation as a dump of the full environment is incorrect: env FOO=... cmd executes cmd, it does not print the environment, so this will generate false-positive log leaks and miss the real downstream sink semantics.

💡 Why this matters

The condition len(cmd.Args) == 1 || name == "env" is always true for env, which means any secret passed through env is reported as a log disclosure even when the command is something like env TOKEN=$TOKEN curl --oauth2-bearer .... That is noisy, and worse, it prevents the analyzer from modeling the actual subcommand (for example curl) as a network sink.

Suggested fix:

case "env":
    if len(cmd.Args) == 1 {
        // dump environment
        ...
        break
    }
    // skip leading NAME=VALUE assignments / flags, then analyze the subcommand
case "printenv":
    ...

At minimum, split env from printenv and only treat bare env as stdout output.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

env now runs the wrapped command with a temporary environment overlay; it only models environment listing when no command is supplied and reports a coverage gap for unmodeled credential-bearing commands. Regression cases pass (2afe4c3).

Comment thread pkg/cli/lint_workflow.go Outdated
if readErr == nil {
document, parseErr := aw.Parse(string(source))
if parseErr != nil {
return nil, errors.New("could not parse companion AW document")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hard-failing the lint run when a sibling companion .md does not parse makes gh aw lint unusable for otherwise-valid .lock.yml files during source edits and for lock files that are linted without a trustworthy authoring source.

💡 Why this matters

This command is documented as linting existing lock files from disk. After this change, a transient parse error in foo.md aborts linting of foo.lock.yml entirely, even though the compiled workflow itself may be perfectly valid and actionlint could still provide useful signal. That is a pretty rough regression for local iteration and for consumers who only care about the execution graph.

Suggested fix: treat companion-document parse failures as a warning or coverage gap, then continue linting the lock file. Reserve a hard error for cases where the user explicitly asks to lint authored AW sources rather than compiled lock files.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A malformed companion AW document is now ignored as unavailable source evidence, while linting continues on the valid Actions AST. The regression test verifies this (9787c5a).

pelikhan and others added 8 commits October 17, 2026 19:11
Implement observable CTR-021 trigger selection checks with AW-aware severity, deterministic value-free AST-path diagnostics, cancellation, and parsed/constructed AST tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Inspect direct executable interpolation across shell variants while preserving safe environment expansion. Reuse the Actions parser through a generic value-free text taint API.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve explicit zero job timeouts and lint action pin integrity and explicit job timeout safety with value-free, source-path diagnostics.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /codebase-design (via pr-triage: new_feature). This is a well-structured addition — a clean, symbolic taint-tracking engine (sources/sinks/filters) layered over a new extensible lint-rule registry, with solid test coverage across Bash/sh propagation, Actions expressions, job/env/output propagation, reusable workflow inputs, and 344 real compiled workflows (repository_workflows_test.go). I verified the claimed Gemini API-key regression fix (daily-model-inventory) against the actual committed HEAD and confirmed go build/go test pass cleanly for pkg/workflow/taint, pkg/workflow/lint, and the CLI integration.

📋 Key Themes & Highlights

Key Themes

  • One correctness gap found: artifactContains in shell.go relies on filepath.Match, which doesn't support ** as "match any depth" — a common upload-artifact pattern like dist/** will miss nested files, creating a false negative in the exact kind of leak this PR is designed to catch. Flagged inline with a reproduction and suggested fix.
  • Everything else reviewed (CLI flag validation in lint_command.go, Actions-expression taint in expression.go, rule registry design in lint.go) is clear, well-tested, and consistent with existing codebase conventions.

Positive Highlights

  • ✅ Symbolic-only taint tracking (never touches real secret values) is a sound security design choice
  • ✅ Extensive, well-named test cases covering branch-union, loop-carried taint, and action credential outputs
  • ✅ Documentation updates in compilation-process.md and cli.md clearly explain the new secret-leak rule semantics

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 201 AIC · ⌖ 14.8 AIC · ⊞ 10.1K
Comment /matt to run again

Comment thread pkg/workflow/taint/shell.go Outdated
pelikhan and others added 5 commits October 17, 2026 19:17
Extract branch-scope evaluation below the custom function-size limit and use strings.CutPrefix without changing diagnostics or trigger behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Extract per-step analysis, compare bounds-safe reference prefixes, and use ErrorIs for cancellation assertions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The default lint path still has blocking correctness and reliability issues.

18 open issues
What changed in this PR

Adds extensible Actions/AW AST linting to gh-aw, including symbolic secret-flow analysis.

Changes:

  • Add rule registration, selection, and structured diagnostics.
  • Integrate AST checks into gh aw lint alongside actionlint.
  • Add regression tests and documentation, and move Gemini authentication into a header.
File Description
pkg/​workflow/​taint/​workflow.go Traverse workflow scopes and propagate taint.
pkg/​workflow/​taint/​taint.go Define taint rules, sources, and findings.
pkg/​workflow/​taint/​taint_test.go Test secret-flow analysis.
pkg/​workflow/​taint/​shell.go Analyze shell commands and file flows.
pkg/​workflow/​taint/​secrets.go Define secret sources and disclosure sinks.
pkg/​workflow/​taint/​expression.go Track Actions expression taint.
pkg/​workflow/​lint/​secrets.go Convert taint results into diagnostics.
pkg/​workflow/​lint/​repository_workflows_test.go Check existing compiled workflows.
pkg/​workflow/​lint/​lint.go Add the extensible rule runner.
pkg/​workflow/​lint/​lint_test.go Test registration and diagnostic handling.
pkg/​cli/​lint_workflow.go Load workflows and report AST diagnostics.
pkg/​cli/​lint_workflow_test.go Test CLI integration and reporting.
pkg/​cli/​lint_command.go Add rule-selection and reporting options.
go.sum Record dependency checksums.
go.mod Add the shell parser dependency.
docs/​src/​content/​docs/​setup/​cli.md Document lint usage and limitations.
docs/​src/​content/​docs/​reference/​glossary.md Update the lint command definition.
docs/​src/​content/​docs/​reference/​compilation-process.md Document rule-extension APIs.
.github/​workflows/​daily-model-inventory.md Send the Gemini key through a header.
.github/​workflows/​daily-model-inventory.lock.yml Regenerate the inventory workflow.
.changeset/​minor-workflow-ast-secret-leak-linter.md Describe the release changes.

Review effort: Max

Comment thread pkg/workflow/taint/shell.go Outdated
Comment thread pkg/workflow/taint/shell.go Outdated
Comment thread pkg/workflow/taint/shell.go Outdated
Comment thread pkg/workflow/taint/workflow.go Outdated
Comment thread pkg/workflow/taint/workflow.go Outdated
Comment on lines +446 to +450
name, _, found := strings.Cut(payload, "=")
if !found {
name, _, _ = strings.Cut(payload, "<<")
}
name = strings.TrimSpace(name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Output-record name extraction handles printf assignments and heredoc records; unknown dynamic GITHUB_ENV names produce a coverage gap. The existing propagation tests pass (2afe4c3).

}
case "", "/dev/null":
default:
s.files[target] = union(s.files[target], sources)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Modeled file paths resolve against the effective step/job/workflow working directory and match artifact paths in workspace-relative coordinates; inherited and step-level cases are covered (2afe4c3).

Comment thread pkg/workflow/taint/shell.go Outdated
Comment thread pkg/workflow/taint/workflow.go Outdated
Comment thread pkg/workflow/taint/workflow.go Outdated
pelikhan and others added 3 commits October 17, 2026 23:54
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot address the following outstanding work in one pass:

  1. Update this branch with the latest main using make merge-main, resolving any conflicts and preserving the intended changes.
  2. Review (pkg/workflow/taint/shell.go:282): Treating every env ... invocation as a dump of the full environment is incorrect: env FOO=... cmd executes cmd, it does not print the environment, so this will generate false-positive log leaks and miss the real downstream sink semantics. - Add extensible AST linting and secret-leak taint analysis #69813 (comment)
  3. Review (pkg/cli/lint_workflow.go:80): Hard-failing the lint run when a sibling companion .md does not parse makes gh aw lint unusable for otherwise-valid .lock.yml files during source edits and for lock files that are linted without a trustworthy authoring source. - Add extensible AST linting and secret-leak taint analysis #69813 (comment)
  4. Review (pkg/workflow/taint/shell.go:1221): payload contains joined command arguments, not emitted environment/output records. For printf '%s\\n' \"COPY=$TOKEN\" >> \"$GITHUB_ENV\", this records %s\\n COPY instead of COPY, so a later echo \"$COPY\" passes without a finding or warning. Dynamic names and separately appended heredoc value lines similarly become unusable keys. Extract names from modeled output records; when a name is unknown, report a GITHUB_ENV coverage gap or use the existing wildcard for step outputs rather than storing an invalid name. - Add extensible AST linting and secret-leak taint analysis #69813 (comment)
  5. Review (pkg/workflow/taint/shell.go:1241): File targets are stored without the run step's working directory. A step with working-directory: subdir that writes $TOKEN to token.txt is tracked as token.txt, so a later upload of subdir/token.txt silently misses the secret-bearing file. Resolve modeled reads/writes against the effective step/job/workflow working directory and compare artifact paths in the same workspace-relative coordinate system. - Add extensible AST linting and secret-leak taint analysis #69813 (comment)
  6. Fix failing check impacted-go-tests (FAILURE): https://github.com/github/gh-aw/actions/runs/39106806171/job/117614417962.
  7. Fix failing check Unit tests (A-C) (FAILURE): https://github.com/github/gh-aw/actions/runs/39106806171/job/117614669586.
  8. Fix failing check Unit tests (S-Z and other) (FAILURE): https://github.com/github/gh-aw/actions/runs/39106806171/job/117614669658.

Push the necessary fixes, reply to each listed review thread and resolve it when addressed. Ignore feedback already answered or resolved. Use the pr-finisher skill and stop when only human review or CI remains; do not trigger CI.

Sous-chef head: b733245
Sous-chef work: 42e5d572d9d75b7168fe7d0aeda0717cc07e65ed240f6d299e6fec661214f1ac 48a183c3e95bbf17204364ee4c81061de61a873e8585852342c81727b022959c 627fd9aee94d024ba6d51703addf78a228828fa3354163553af07501e2a758c0 97669ba4ec7791004bfc42d6d85b425d7bf004712258180cf92049f62cd4eb88 d29f7921e318849a088bdc1bab66f681422df7f0967cd0a66a012881345a0afb deaef94d1d3ab784f1596f554418c8a949f82fd8646f0c6e6be12f5037e9a06a e7fbfb294e31416234509bf89ca3007d7f80a93c44de3324a7a4f30ab68e8f37
Sous-chef state: eaf7e80d0fc668f8fed41c04e13e74d3f8c1793a1d80c6a28b01456ed01bc0ec

Generated by 👨‍🍳 PR Sous Chef · pi · haiku45 · 6.94 AIC · ⌖ 7.81 AIC · ⊞ 1.1K · ◷
Comment /souschef to run again

pelikhan and others added 15 commits October 18, 2026 08:33
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…analysis

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Initial plan

* Add shared /reflect fixture for model resolution

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Resolve configured model aliases with one shared step on every engine

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Switch smokes to aliases, recompile locks, document shared model resolution

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Apply remaining changes

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Remove stray import cache file committed from a test run

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Keep pi's audit-classified startup phrase ahead of the shared model_unavailable message

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Restore pkg/workflow/.github files from main removed with the stray import cache

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Fix typecheck errors in shared model resolution callers

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Expect the pi smoke's gpt-5-mini sub-agent alias to resolve to the newest live match

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Keep buildInitialWorkflowData at main's length for largefunc lint

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Prefer a live exact model id over an alias with the same name; restore pi smoke gpt-5-mini expectation

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Merge main; regenerate frontmatter docs for models: aliases

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Pass bundled model ids through without AWF, emit GH_AW_MODEL_ALIASES for every harness, validate imported aliases

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Ship built-in model aliases with the setup action; emit only workflow aliases

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Recompile lock files without built-in alias maps

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

* Fix resolver JSDoc types; document alias env var reduction

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Select the workflow's previously successful GPT-5.4 model through the existing Copilot provider. The failing run's complete live catalog advertises this model and its Responses endpoint, but no Gemini models. Preserve Pi, Actions-token authentication, tools, limits and isolation; regenerate the lock.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
…9870)

Keep the auto-selected main model and gpt-6-luna delegates while allowing each model to use its supported endpoint. Cover workflow configuration, emitted SDK wiring, model qualification, and prompt preservation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
* fix: attribute safe-output footers to work queue claims

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix: deploy report dependencies for claim footers

Include the report renderer and its transitive runtime dependencies in the isolated safe-output bundle. Add a regression that deploys the real bundle and renders a Claim-scoped footer.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
…map rule (#69831)

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Add opt-in per-rule duration markers and execution trace regions. Benchmark every registered rule on safe/unsafe AW and Actions ASTs at 32 and 256 jobs; enforce wall-time and growth budgets in ordinary tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

@copilot address the following outstanding work in one pass:

  1. Update this branch with the latest main using make merge-main, resolving any conflicts and preserving the intended changes....

Merged latest main and completed the requested follow-ups. The companion parse failure is fixed in 9787c5a; env, GITHUB_ENV record propagation, and working-directory artifact handling are covered by existing tests (2afe4c3). Final publication checks passed.

@copilot Fix the code for all comments in this review thread.

When a review comment includes a suggested change, apply the s...

The requested feedback is addressed: the remaining malformed-companion failure is fixed in 9787c5a; prior shell/taint findings were implemented in 2afe4c3 and regression-tested. Latest main is merged.

pelikhan and others added 2 commits October 18, 2026 09:06
Preserve the concurrent remote correction for optional malformed AW lock companions alongside performance instrumentation and main's test fixes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan merged commit b16a128 into main Oct 18, 2026
46 checks passed
@pelikhan
pelikhan deleted the pelikhan-secret-leak-analysis branch October 18, 2026 17:55
@github-actions

Copy link
Copy Markdown
Contributor

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.

4 participants