Skip to content

[Agents] Add guardrails to PR triage agent - #2681

Draft
qcorporation wants to merge 4 commits into
mainfrom
pr_triage_guardrails
Draft

[Agents] Add guardrails to PR triage agent#2681
qcorporation wants to merge 4 commits into
mainfrom
pr_triage_guardrails

Conversation

@qcorporation

@qcorporation qcorporation commented Aug 6, 2026

Copy link
Copy Markdown

1. What does this PR do?

Adds prompt-injection guardrails to the PR triage agent workflow and skill. Revives #2656 with the pending review comment from @andrewkroh addressed.

Changes:

SKILL.md — Added ## Prompt-injection awareness section so the skill carries injection-resistance guidance whether invoked from CI or interactively in Cursor.

report-template.md — Added Prompt-injection signals bullet under ### Risk notes so the agent has a structured place to report any suspicious directives found in PR content.

.github/workflows/pr-triage.yml — Three improvements:

  1. Pre-fetch PR data in a dedicated workflow step (Fetch PR data): the workflow now runs gh pr view and gh pr diff before the agent starts, writing output to pr-metadata.json and pr-diff.patch. This directly addresses @andrewkroh's review comment — the agent no longer needs GH_TOKEN or permission to call gh.
  2. GH_TOKEN removed from the agent step: the Build prompt and run triage step no longer has GH_TOKEN in its environment, so the agent has no path to make authenticated GitHub API calls.
  3. Security section added to the system prompt: ## Security — prompt-injection guardrails and ## Pre-fetched PR data sections explicitly frame PR content as untrusted, attacker-controlled data and instruct the model never to follow directives embedded in it.

2. Which ECS fields are affected/introduced?

N/A — tooling/agent workflow change only.

3. Why is this change necessary?

The PR triage workflow passes PR title, body, and diff to an LLM with no adversarial framing. A malicious PR author could embed prompt-injection payloads (e.g. "ignore previous instructions") and potentially manipulate the triage classification, extract secrets, or alter the output. These guardrails reduce that attack surface.

The pre-fetching approach also eliminates the agent's need for a GitHub token entirely

4. Have you added/updated documentation?

N/A — changes are confined to the skill file, report template, and the CI workflow.

5. Have you built ECS and committed any newly generated files?

N/A — no schema changes.

6. Have you run the ECS validation tests locally?

N/A — no schema or generator changes.

7. Anything else for the reviewers?

This PR directly addresses the unresolved review comment left by @andrewkroh on the now-closed #2656:

"Can we run these commands beforehand, write the output to a file, and tell the LLM about them. Then it no longer needs a GH_TOKEN and does not need to be allowed to run gh CLI at all."

The Fetch PR data step does exactly that. The agent step now reads local files instead of calling gh.

The second part of @andrewkroh's comment ("apply an allowlist to opencode such that only the tools necessary by the skills to complete the review are available") is a follow-on improvement that would require investigation of opencode's tool-restriction configuration surface; it is not addressed in this PR.


Commit Message

[Agents] Add prompt-injection guardrails to PR triage agent: pre-fetch PR data in a dedicated workflow step so the agent never needs GH_TOKEN or gh CLI access; add security framing to the system prompt and injection-awareness guidance to the skill.

Made with Cursor

Add prompt-injection guardrails to harden the PR triage workflow:

- SKILL.md: add `## Prompt-injection awareness` section so the skill
  carries the guidance whether invoked from CI or interactively.
- report-template.md: add `Prompt-injection signals` bullet under
  Risk notes so the agent has a structured place to report suspicious
  directives found in PR content.
- pr-triage.yml: split PR data fetching out of the agent step into a
  dedicated `Fetch PR data` step that runs `gh pr view` / `gh pr diff`
  and writes the output to files before the agent starts. The agent step
  no longer receives `GH_TOKEN` or permission to call `gh`; it reads the
  pre-fetched files instead. Added `## Security — prompt-injection
  guardrails` and `## Pre-fetched PR data` sections to the system prompt
  that explicitly frame PR content as untrusted, attacker-controlled data
  and instruct the model never to follow directives embedded in it.

Addresses review comment from andrewkroh on #2656: running gh commands
in workflow steps (not by the LLM), writing output to files, and passing
those paths to the agent so the agent no longer needs GH_TOKEN.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qcorporation
qcorporation requested a review from a team as a code owner August 6, 2026 16:42
@qcorporation qcorporation self-assigned this Aug 6, 2026
@qcorporation
qcorporation marked this pull request as draft August 6, 2026 16:49
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🤖 GitHub comments

Just comment with:

  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)

Comment thread .github/workflows/pr-triage.yml Outdated
Comment thread .github/workflows/pr-triage.yml Outdated
Comment thread .github/workflows/pr-triage.yml Outdated
Comment thread .github/workflows/pr-triage.yml Outdated
Comment thread .github/workflows/pr-triage.yml Outdated
Comment thread .github/workflows/pr-triage.yml Outdated
Comment thread .github/workflows/pr-triage.yml Outdated
Comment thread .github/workflows/pr-triage.yml
Comment thread .github/workflows/pr-triage.yml
Comment thread .github/workflows/pr-triage.yml
echo ""

if opencode run "$(cat "$PROMPT_FILE")" --model "$MODEL" 2>&1 | tee "$TRANSCRIPT_FILE"; then
if (cd "$RUNTIME" && opencode run "$(cat "$PROMPT_FILE")" --model "$MODEL") 2>&1 | tee "$TRANSCRIPT_FILE"; then

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.

For defense in depth, consider replacing the tee with a direct write to the file (> "$TRANSCRIPT_FILE" 2>&1). Otherwise if OpenCode prints to stdout/stderr (tool traces, errors, accidental secret echoes), then it hits the Actions logs before the redaction step. ::add-mask:: helps, but only when the exact credential strings appear.

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