Skip to content

Preserve Runner Guard suppressions for model inventory requests - #52064

Closed
pelikhan with Copilot wants to merge 8 commits into
mainfrom
copilot/rgs-012-fix-secret-exfiltration-again
Closed

Preserve Runner Guard suppressions for model inventory requests#52064
pelikhan with Copilot wants to merge 8 commits into
mainfrom
copilot/rgs-012-fix-secret-exfiltration-again

Conversation

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Runner Guard flagged four intentional, read-only model inventory requests as potential secret exfiltration. Existing source-level suppressions were discarded during workflow compilation.

  • Workflow

    • Add scoped RGS-012 suppressions with endpoint-specific justifications.
    • Regenerate daily-model-inventory.lock.yml.
  • Compiler

    • Preserve step-level Runner Guard directives in generated workflows.
    • Skip ambiguous duplicate step names and misplaced script comments.
# runner-guard:ignore RGS-012 -- public read-only GET; no secrets are sent.
- name: Predownload models.dev API index

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.1 AIC · ⌖ 5.7 AIC · ⊞ 8.5K ·
Comment /souschef to run again

Copilot AI and others added 2 commits August 11, 2026 13:49
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix secret exfiltration via outbound HTTP request in daily-model-inventory.lock.yml Preserve Runner Guard suppressions for model inventory requests Aug 11, 2026
Copilot AI requested a review from pelikhan August 11, 2026 13:59
@pelikhan
pelikhan marked this pull request as ready for review August 11, 2026 14:01
Copilot AI balanced review requested due to automatic review settings August 11, 2026 14:01
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Aug 11, 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 Aug 11, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • api.individual.githubcopilot.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.individual.githubcopilot.com"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #52064

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

The implementation is clean and correct. preserveRunnerGuardStepSuppressions correctly injects suppression comments only when step names are unique in both frontmatter and generated YAML, preventing ambiguous matches. Indentation is derived from the generated line, and inline script comments that happen to contain the prefix are correctly ignored. Tests cover the positive case, duplicate-name guard, and misplaced inline comment. No actionable issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 18.8 AIC · ⌖ 6.13 AIC · ⊞ 5.4K

@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 /diagnosing-bugs and /codebase-design — approving with two minor suggestions.

📋 Key Themes & Highlights

Positive Highlights

  • ✅ Compiler integration is a single, well-placed call in compiler_yaml.go
  • ✅ Duplicate-name and misplaced-script-comment guards are present and tested
  • ✅ Tests cover happy path, script-comment false positive, and duplicate-name skipping
  • ✅ Suppression justifications are endpoint-specific and accurate

Minor Issues (non-blocking)

  1. Silent map overwrite — when two different directives precede two identically-named steps in the frontmatter, the second overwrites the first, then the duplicate-name guard drops it. The net result is correct (no injection) but the overwrite path is undetected and untested.
  2. Per-line workflowStepName in the injection loopcountWorkflowStepNames already iterates all lines; the injection loop iterates them again calling workflowStepName per line. Minor inefficiency, but worth noting for very large workflows.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 19.6 AIC · ⌖ 6.7 AIC · ⊞ 7.1K
Comment /matt to run again

if !strings.HasPrefix(directive, runnerGuardIgnorePrefix) {
continue
}
if stepName := workflowStepName(frontmatterLines[i+1]); stepName != "" {

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.

[/diagnosing-bugs] If two different runner-guard:ignore directives precede two identically-named steps in the frontmatter, the map silently overwrites the first — then the duplicate-name guard on line 31 drops both. A directive for an ambiguous step name disappears with no warning.

💡 Suggested fix

Detect the collision and skip the duplicate explicitly:

if _, ok := suppressions[stepName]; ok {
    delete(suppressions, stepName) // ambiguous: skip both
    continue
}
suppressions[stepName] = directive

Or at minimum add a test documenting this silent-overwrite behaviour.

@copilot please address this.

lines := strings.Split(workflowYAML, "\n")
output := make([]string, 0, len(lines)+len(suppressions))
for _, line := range lines {
stepName := workflowStepName(line)

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.

[/codebase-design] workflowStepName is called on every line of both the frontmatter and the generated YAML. For large workflows this is O(n2) in the number of lines × steps. Consider building the step-name index once per string (already done by countWorkflowStepNames) and reusing it when injecting, instead of calling workflowStepName again in the injection loop.

@copilot please address this.

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.

Pull request overview

Preserves scoped Runner Guard suppressions when compiling workflow frontmatter into generated GitHub Actions workflows.

Changes:

  • Adds suppression extraction and step-name matching.
  • Adds unit coverage for preservation, duplicates, and script comments.
  • Adds four RGS-012 suppressions and regenerates the model inventory workflow.
Show a summary per file
File Description
pkg/workflow/runner_guard_suppressions.go Implements suppression preservation.
pkg/workflow/runner_guard_suppressions_test.go Tests suppression handling.
pkg/workflow/compiler_yaml.go Integrates preservation into compilation.
.github/workflows/daily-model-inventory.md Adds scoped RGS-012 directives.
.github/workflows/daily-model-inventory.lock.yml Regenerates compiled workflow output.

Review details

Tip

Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment on lines +30 to +31
stepName := workflowStepName(line)
if directive := suppressions[stepName]; directive != "" && frontmatterNames[stepName] == 1 && generatedNames[stepName] == 1 {
if !strings.HasPrefix(directive, runnerGuardIgnorePrefix) {
continue
}
if stepName := workflowStepName(frontmatterLines[i+1]); stepName != "" {
…compilation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Design Decision Gate — ADR Required

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

Draft ADR committed: docs/adr/52064-preserve-runner-guard-step-suppressions.md — 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 committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI could not 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-52064: Preserve Runner Guard Step Suppressions Through Workflow Compilation

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

Why ADRs Matter

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 (e.g., 0042-use-postgresql.md for PR #42).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 57.3 AIC · ⌖ 23.1 AIC · ⊞ 8.7K ·
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100 — Excellent

Analyzed 3 test(s): 3 design, 0 implementation, 0 violation(s).

📊 Metrics (3 tests)
Metric Value
Analyzed 3 (Go: 3, JS: 0)
✅ Design 3 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (67%)
Duplicate clusters 0
Inflation No
🚨 Violations 0
Test File Classification Issues
TestPreserveRunnerGuardStepSuppressions runner_guard_suppressions_test.go behavioral_contract · design_test · high_value Assertions lack descriptive failure messages (minor)
TestPreserveRunnerGuardStepSuppressionsIgnoresScriptComments runner_guard_suppressions_test.go behavioral_contract · design_test · high_value
TestPreserveRunnerGuardStepSuppressionsIgnoresDuplicateNames runner_guard_suppressions_test.go behavioral_contract · design_test · high_value
⚠️ Flagged Tests (1)

TestPreserveRunnerGuardStepSuppressions (runner_guard_suppressions_test.go) — assertions use bare assert.Contains / assert.Equal / assert.True without descriptive failure messages. Consider adding a message argument (e.g. assert.Contains(t, result, "...", "expected suppression comment before step")) to make failures self-explanatory in CI output. This is a minor style note, not a violation.

Verdict

Passed. 0% implementation tests (threshold: 30%). All 3 tests verify behavioral contracts of the new preserveRunnerGuardStepSuppressions function — happy-path injection, script-comment exclusion, and duplicate-step-name safety. No violations.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 · 32.9 AIC · ⌖ 6.76 AIC · ⊞ 7.6K ·
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.

✅ Test Quality Sentinel: 90/100. 0% implementation tests (threshold: 30%).

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the open review feedback, refresh the branch if needed, and use the pr-finisher skill before handing back.

Open review threads (newest reviewer feedback first):

No failed checks were reported in the compact candidate set for this run.
Branch update was requested by sous-chef in run https://github.com/github/gh-aw/actions/runs/31500985584.

Generated by 👨🍳 PR Sous Chef
Comment /souschef to run again

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.1 AIC · ⌖ 5.7 AIC · ⊞ 8.5K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please address the open review feedback, refresh the branch if needed, and use the pr-finisher skill before handing back.

Open review threads (newest reviewer feedback first):...

Addressed all four open review threads in e4d15cf:

  • Block scalar payloads — collection, counting and insertion now run off a single structural index built with yamlBlockScalarState, so run: |/run: > payloads are never treated as steps in either document. Script content can no longer be mutated.
  • Directive forms — directives are matched to step sequence entries rather than requiring a following - name: line. Inline trailing directives (- uses: action@ref # runner-guard:ignore RGS-007) and standalone directives before unnamed - uses:/- run: steps are now preserved; unnamed steps are keyed by their first line's content.
  • Ambiguous duplicates — a second directive for the same step identity marks the key ambiguous and both are dropped instead of silently overwriting. Duplicate step identities are additionally excluded by the existing uniqueness guard.
  • Repeated scanning — the step index (line, key) is built once per document and reused during injection, so workflowStepKey runs once per line instead of once per line per pass.

Added five tests covering inline directives, standalone directives on unnamed steps, ambiguous directives, and block-scalar payloads on both the frontmatter and generated sides. daily-model-inventory.lock.yml recompiles unchanged with all four RGS-012 suppressions intact.

Copilot AI requested a review from gh-aw-bot August 11, 2026 14:40
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the open review feedback, resolve any addressed threads that remain open, refresh the branch if needed, and use the pr-finisher skill before handing back.

Open review threads (newest reviewer feedback first):

No failed checks were reported in the compact candidate set for this run.
Branch update was requested by sous-chef in run https://github.com/github/gh-aw/actions/runs/31505952002.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 6.3 AIC · ⌖ 7.9 AIC · ⊞ 8.5K ·
Comment /souschef to run again

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot resolve the merge conflicts on this branch.

PR #52083 landed the RGS-012 suppressions on main using the repo's
established convention: the directive is placed inside the run script
directly above the offending curl, where the compiler emits it verbatim.
That makes the step-level comment preservation added here unnecessary,
so the compiler post-processing, its tests, the ADR and the duplicate
step-level suppressions are removed in favour of main's version.

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please address the open review feedback, resolve any addressed threads that remain open, refresh the branch if needed, and use the pr-finisher skill before handing back.

Open review threads ...

This PR is now superseded and should be closed rather than merged.

While working the open threads I found that #52083 landed on main (1dd49c6) and already fixes #51943 — using the repo's established convention rather than a compiler change:

# runner-guard:ignore RGS-012 -- unauthenticated GET from a public read-only model index; no secrets are sent.
curl -fsS https://models.dev/api.json -o "$OUT/api.json"

The directive goes inside the run: script, directly above the offending curl — which is where Runner Guard attributes the finding. Because the compiler copies script payloads verbatim, the suppression survives compilation with no machinery at all. This is the same pattern already used for the gVisor install step in pkg/workflow/copilot_engine_installation.go (#51322).

That invalidates the premise of this PR. My step-level comment preservation solved a problem that doesn't exist, and it did so at the wrong layer: the two reviewer threads about block-scalar payloads and unrecognised directive forms were both symptoms of trying to reconstruct, via raw-text post-processing, an association that the native in-script form never loses.

Actions taken in 7695c0f:

  • Merged origin/main and resolved conflicts in daily-model-inventory.md / .lock.yml by taking main's version. The auto-merge had produced duplicate suppressions (mine at step level, main's in-script); main's are the effective ones.
  • Removed runner_guard_suppressions.go, its tests, the call site in compiler_yaml.go, and ADR-52064.

The branch is now byte-identical to origin/maingit diff origin/main is empty and the pre-PR gate reports "No changes relative to origin/main". The four outdated review threads are moot since the code they referenced no longer exists.

I did not close the PR, as that is outside what I should decide unilaterally.

…ecret-exfiltration-again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts on this branch.

Done in 7a6c63a.

The conflicts were in daily-model-inventory.md / .lock.yml and I resolved them in 7695c0f last session by taking main's version. 7a6c63a merges the latest main (48e5fa3) on top — that one merged cleanly with no conflicts. GitHub had already moved the PR from dirty to blocked.

Worth flagging before you spend review time on this: the branch is now byte-identical to origin/maingit diff origin/main is empty and the pre-PR gate reports "No changes relative to origin/main". Merging it would be a no-op.

That's because #52083 landed on main (1dd49c6) and already fixes #51943, using the convention this repo already follows — the directive goes inside the run: script directly above the offending curl, where Runner Guard attributes the finding and where the compiler emits it verbatim (same as the gVisor install step in copilot_engine_installation.go from #51322). My compiler post-processing was solving a problem that doesn't exist, so I removed it rather than keep polishing it.

Recommend closing this as superseded by #52083. I'll leave that call to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[static-analysis] RGS-012: Secret Exfiltration via Outbound HTTP Request in daily-model-inventory.lock.yml

4 participants