Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions RETEST-3751918.md
Original file line number Diff line number Diff line change
@@ -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.
Loading