From a5f87d58325622356967e7983a8056feec9068e3 Mon Sep 17 00:00:00 2001 From: subchatsecure Date: Fri, 26 Jun 2026 03:45:26 +0530 Subject: [PATCH] retest(#3751918): document title injection still present in agent workflows --- RETEST-3751918.md | 100 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 RETEST-3751918.md diff --git a/RETEST-3751918.md b/RETEST-3751918.md new file mode 100644 index 000000000..8f5edc6c1 --- /dev/null +++ b/RETEST-3751918.md @@ -0,0 +1,100 @@ +# Retest — HackerOne Report #3751918 +**Retested by:** @subchatsecure +**Date:** 2026-06-26 +**Status: VULNERABILITY STILL PRESENT** + +--- + +## What was checked + +Both flash-list agent workflows were pulled from `main` and compared against the original report. + +--- + +## agent-triage.yml — Still Vulnerable + +**File:** `.github/workflows/agent-triage.yml` +**Commit fetched from:** `main` branch, 2026-06-26 + +Line 39-40 (current `main`): +```yaml +prompt: | + You are running on CI (GitHub Actions). + Triage issue #${{ github.event.issue.number }}. + Title: ${{ github.event.issue.title }} ← STILL INJECTED VERBATIM +``` + +The only change observed vs the original report is the addition of a proxy content-filter header: +```yaml +env: + ANTHROPIC_BASE_URL: https://proxy.shopify.ai/vendors/anthropic + ANTHROPIC_CUSTOM_HEADERS: |- + Shopify-Security-Scan: paranoid-path-template + Shopify-Security-Scan-Mode: block +``` + +**This is not a fix.** The root cause — attacker-controlled text interpolated directly into the agent prompt at the YAML level before the process starts — is unchanged. A server-side content filter is bypassable and does not address the injection at the source. + +`author_association` check: **still absent** on the `labeled` trigger path. + +--- + +## agent-fix.yml — Still Vulnerable (No Proxy Filter Either) + +**File:** `.github/workflows/agent-fix.yml` +**Commit fetched from:** `main` branch, 2026-06-26 + +Line 84-85 (current `main`): +```yaml +prompt: | + You are running on CI (GitHub Actions). There is no human to confirm with — act autonomously. + ... + YOUR ASSIGNED ISSUE: #${{ github.event.issue.number }} + TITLE: ${{ github.event.issue.title }} ← STILL INJECTED VERBATIM +``` + +This workflow: +- Has `contents: write`, `pull-requests: write`, `issues: write` permissions (unchanged) +- Allows `Bash(git push -u *)`, `Bash(gh pr *)`, `Bash(GH_TOKEN=* gh pr *)` (unchanged) +- Has `AGENT_PR_TOKEN: ${{ secrets.SHOPIFY_GH_ACCESS_TOKEN }}` (unchanged) +- **Does NOT have the `Shopify-Security-Scan` proxy header** — not even the partial mitigation applied to agent-triage.yml +- `author_association` check: **still absent** on the `labeled` trigger path + +--- + +## What a real fix looks like + +The correct fix is to never pass attacker-controlled data as inline prompt text. Use a safe indirection: + +```yaml +# FIXED agent-triage.yml +- name: Triage issue + uses: anthropics/claude-code-action@... + env: + ISSUE_NUMBER: ${{ github.event.issue.number }} + # Do NOT pass title here — Claude reads it via gh issue view + with: + prompt: | + Triage issue #$ISSUE_NUMBER. + Use `gh issue view $ISSUE_NUMBER` to read the title and body. + Read .claude/skills/triage-issue/SKILL.md and follow its instructions. +``` + +And add an `author_association` guard on the `labeled` trigger: +```yaml +if: >- + github.event.label.name == 'agent-triage' && + github.event.sender.type != 'Bot' && + ( + github.event.sender.login == github.repository_owner || + contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.label.author_association) + ) +``` + +--- + +## Conclusion + +The reported vulnerability (#3751918) is **not fixed**. Both workflows continue to interpolate `${{ github.event.issue.title }}` directly into the Claude agent prompt with no escaping or env-var indirection. The only mitigation applied (`Shopify-Security-Scan` proxy header) is limited to `agent-triage.yml` and is server-side only — it does not remove the injection surface. + +`agent-fix.yml` — the highest-severity workflow with full write access — has received zero changes.