Harden dry-run compilation against hosted execution effects by pelikhan · Pull Request #70851 · github/gh-aw · GitHub
Skip to content

Harden dry-run compilation against hosted execution effects - #70851

Merged
pelikhan merged 5 commits into
mainfrom
pelikhan-silver-winner
Oct 21, 2026
Merged

pelikhan merged 5 commits into
mainfrom
pelikhan-silver-winner

Conversation

@pelikhan

@pelikhan pelikhan commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Why

The hosted-readiness audit found that staging built-in safe outputs did not prevent custom jobs, publishers, reusable workflows, or host-side extensions from retaining real execution and credentials. Successful compilation was not a sufficient signal for unattended hosted execution.

This change makes those diagnostic boundaries fail closed without claiming that dry-run is a general execution sandbox.

ADR: ADR-70851: Fail Closed in Dry-Run Compilation for Host Execution Effects

Changes

  • Replace authored custom jobs, custom safe-output jobs, and managed reusable calls with disabled, credential-free placeholders. Preserve dependency and declared/referenced output names, but remove executable targets, inherited secrets, permissions, and staging overrides. Static placeholder runners respect CLI, environment, and repository self-hosted policies without retaining removed matrix expressions.
  • Reject authored host steps, custom MCP servers/scripts, custom safe-output scripts/actions, safe-output and threat-detection steps, secret-masking steps, enabled Bash graders, and authored OIDC permissions, including resolved imported declarations. Built-in overrides use a fail-closed allowlist to reject future execution fields and restore-memory bypasses.
  • Keep compiler-owned pull-request stack gates separate from authored/imported pre-steps so valid max-stack workflows continue to compile in dry-run mode.
  • Fail requested scanner gates on existing as well as added medium-or-higher or unclassified findings. Preserve differential counts and canonicalize scanner file identities across symlinked paths.
  • Preserve trigger comments when adding manual dispatch, fix reusable telemetry-secret contracts, and avoid actionlint constant-condition errors.

Compatibility and migration

Breaking change: diagnostic workflows containing unsupported effects now fail compilation rather than leaving those effects unverified. Custom jobs have empty outputs and can cause dependent jobs to skip. Use a separate diagnostic workflow without unsupported declarations and resolve actionable scanner findings before live testing.

Ordinary compilation is unchanged. Agent shell execution, custom engines, custom credentials, and hosted identity/runner behavior still require independent review and isolation. A major changeset, migration guidance, CLI/MCP descriptions, and reference documentation are included.

Validation

  • Initial local ./gh-aw fleet audit: ordinary compilation succeeded for all 346 workflows; hardened dry-run emitted 138 diagnostic locks and intentionally rejected 208 workflows with unsupported effects. These counts predate the additional review hardening and are historical audit results, not current fleet certification.
  • Initial full scanner verification passed actionlint, zizmor, and shellcheck on emitted inputs. Fleet admission remained failed because of an existing poutine runner finding and compiler warnings; no findings were waived.
  • After review fixes, combined workflow/CLI regression suites passed, covering resolved/imported effect rejection, all three ShellCheck expectation cases, optional built-in restore-memory, reusable output references, runner policy, and generated stack gates. Placeholder output-contract coverage includes actionlint.
  • All 15 guidance contract tests, build, and formatting passed.
  • The required pre-publication gate was attempted. It remains blocked by baseline custom-linter diagnostics and shared golangci-lint contention; it is not reported as passed.
  • No workflows were dispatched.

Security review note

No credentials, action dependencies, or runtime privileges were added. Diagnostic output intentionally removes custom executable code and credential forwarding. Existing safe-update warnings about removed actions remain blocking rather than being approved or suppressed. Review follow-ups close the reported resolved-field and optional-built-in execution paths; this is not a claim that arbitrary agent code is sandboxed.

Disable custom jobs and reusable calls without retaining credentials, reject unsupported host execution and OIDC declarations, fail on existing actionable scanner findings, and preserve trigger comments and reusable telemetry contracts. Document the breaking diagnostic-policy change and migration.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review October 21, 2026 11:39
Copilot AI max review requested due to automatic review settings October 21, 2026 11:39
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Oct 21, 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 21, 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 · 11.9 AIC · ⌖ 15.1 AIC · ◇ 6.64 AIC · ⊞ 12.7K · ◷

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 21, 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: f62a24e

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Oct 21, 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 · 10 AIC · ⌖ 5.37 AIC · ⊞ 14.8K · ◷

Generated by Ponytail Reviewer for #70851

@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.

Verdict

No actionable blocking issues found in the changed lines. The review was limited to static inspection of the pre-fetched diff; tests were not run per review instructions.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · 1.87 AIC · ⌖ 0.668 AIC · ⊞ 1.6K
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (633 new lines under pkg/) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/70851-fail-closed-dry-run-compilation-for-host-execution-effects.md — review and complete it before merging.

The draft captures the decision to make --dry-run fail closed: reject unsupported host effects (validateDryRunEffects), turn custom/reusable jobs into inert credential-free placeholders, and fail the scanner gate on existing as well as added findings.

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

📋 What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI couldn't infer, refine the decision rationale, and list real alternatives you considered
  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-70851: Fail Closed in Dry-Run Compilation for Host Execution Effects

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

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

📋 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? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

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

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

Expect existing actionable findings to remain blocking in synthetic and native ShellCheck regressions while asserting stable script-location identities and zero added findings.

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 /codebase-design and /improve-codebase-architecture (per pr-triage: architecture_change) focused on compiler_development_safety.go, compiler_safe_output_jobs.go, and the yaml.Node rewrite in compiler_development_telemetry.go.

Overall this is solid, well-tested security-hardening work — the new validateDryRunEffects/makeDryRunJobInert logic has thorough test coverage (compiler_development_safety_test.go), including the "builtin job name bypass" edge case, and the YAML trigger-preservation rewrite is backed by dedicated round-trip and idempotency tests. No blocking issues found; left 3 non-blocking suggestions.

📋 Key Themes
  • Manually-enumerated field list drift risk (compiler_development_safety.go): the rejected builtin-job-override fields (setup-steps, pre-steps, steps, permissions, uses, with, secrets) are a separate list from the augmentation allowlist in compiler_builtin_job_augmentation.go (needs, if, permissions, timeout-minutes, continue-on-error). Today there's no exploitable gap (no augmentation path reads env for builtin jobs), but the two lists aren't coupled, so a future augmentation addition could silently bypass this safety net.
  • Early-return divergence in buildCallWorkflowJob's dry-run branch (compiler_safe_output_jobs.go): correct and well-tested, but the dry-run/normal branches now diverge more in control flow than before — a short comment explaining why skipping addCallWorkflowSecrets/addCallWorkflowPermissions is safe would help future maintainers.
  • Mixed YAML abstraction levels in prepareDryRunWorkflowDispatch: the function now mixes raw yaml.Node manipulation with higher-level goyaml.Unmarshal/MarshalWithFieldOrder calls in the same function. Justified by the comment-preservation goal and covered by tests, but worth a doc comment for future readers.

Positive Highlights

  • ✅ Excellent new test coverage for both the safety-check logic and the YAML trigger rewrite (round-trip, idempotency, and rejection-of-invalid-input tests).
  • ✅ Documentation (.github/aw/debug-agentic-workflow.md, CHANGELOG.md, changeset) is updated consistently with the breaking-change scope.
  • ✅ Scanner gate tightening (existing+added findings) is a deliberate, well-documented breaking change with updated tests.

Investigation was bounded to the pre-fetched diff plus targeted cross-references into compiler_builtin_job_augmentation.go and the job schema; no remaining uncertainty beyond what's noted above.

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

Comment thread pkg/workflow/compiler_development_safety.go Outdated
Comment thread pkg/workflow/compiler_safe_output_jobs.go
Comment thread pkg/workflow/compiler_development_telemetry.go

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

Authored host execution and OIDC grants still bypass dry-run rejection, and compilation regressions remain.

6 open issues
What changed in this PR

Hardens gh-aw dry-run compilation against retained execution and credentials, without treating diagnostic workflows as a sandbox.

Changes:

  • Replace custom jobs and managed reusable calls with inert placeholders.
  • Reject unsupported host effects and fail existing actionable scanner findings.
  • Preserve trigger comments and update tests, documentation, and migration guidance.
File Description
pkg/​workflow/​compiler_yaml.go Integrate safety validation and job disabling.
pkg/​workflow/​compiler_safe_output_jobs.go Replace dry-run reusable calls.
pkg/​workflow/​compiler_development_test.go Update disabled-call expectations.
pkg/​workflow/​compiler_development_telemetry.go Preserve triggers during dispatch insertion.
pkg/​workflow/​compiler_development_telemetry_test.go Cover telemetry and reusable-secret isolation.
pkg/​workflow/​compiler_development_safety.go Validate effects and create inert jobs.
pkg/​workflow/​compiler_development_safety_test.go Test rejection and job disabling.
pkg/​workflow/​compiler_development_dispatch_test.go Test trigger and comment preservation.
pkg/​cli/​mcp_tools_readonly.go Update the compile tool contract.
pkg/​cli/​compile_scanner_differential.go Gate existing findings and normalize paths.
pkg/​cli/​compile_scanner_differential_test.go Update scanner gate expectations.
pkg/​cli/​compile_development.go Update blocking diagnostic wording.
pkg/​cli/​compile_development_test.go Check authored OIDC rejection.
pkg/​cli/​compile_development_report.go Report hardened scope and scanner gates.
pkg/​cli/​compile_development_report_test.go Update report assertions.
docs/​src/​content/​docs/​setup/​cli.md Document dry-run restrictions.
docs/​src/​content/​docs/​reference/​compilation-process.md Explain boundaries and compatibility.
cmd/​gh-aw/​main.go Update CLI help.
CHANGELOG.md Record breaking behavior.
.github/​aw/​debug-agentic-workflow.md Update hosted-debug safeguards.
.changeset/​major-fail-closed-dry-run-host-effects.md Declare major release and migration guidance.

Review effort: Max

Comment thread pkg/cli/compile_scanner_differential.go
Comment thread pkg/workflow/compiler_development_safety.go Outdated
Comment thread pkg/workflow/compiler_development_safety.go Outdated
Comment thread pkg/workflow/compiler_development_safety.go
Comment thread pkg/workflow/compiler_development_safety.go
Comment thread pkg/workflow/compiler_development_safety.go Outdated
Reject remaining resolved host effects and fail closed on built-in overrides, preserve reusable output bindings and runner policy in inert jobs, separate compiler-owned stack gates from authored steps, and complete the ADR and regression coverage.

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

Copy link
Copy Markdown
Collaborator Author

In response to this review notice:

No code change is needed for this completion notice; the review reported no actionable blocking issues. The actionable inline feedback from the other reviews is addressed separately in this batch.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to this review notice:

Accepted the three architecture suggestions: built-in overrides now use a fail-closed allowlist, and comments explain the reusable-call early return and node-level trigger normalization. Each inline thread has a specific response.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to this review notice:

No code change is needed for this no-action notification, and I am not treating it as validation evidence. The ShellCheck expectations and new guard/placeholder regressions were validated directly in this batch.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to this review notice:

Accepted the ADR follow-up: reviewed and completed ADR-70851 with the decision rationale, alternatives, migration consequences, and remaining sandbox limitations. The PR body now links the ADR.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to this review notice:

No code change is needed for this no-action notification; it does not establish correctness or publication success. The actionable review fixes were validated and published separately in this batch.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to this review notice:

Accepted: finalized ADR-70851 with Context, Decision, Alternatives Considered, and Consequences, and linked it in the PR body. It documents fail-closed admission and inert jobs without claiming a general hosted sandbox.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan merged commit 27cc912 into main Oct 21, 2026
37 checks passed
@pelikhan
pelikhan deleted the pelikhan-silver-winner branch October 21, 2026 12:42
@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.

2 participants