# Before: shared key (vulnerable)
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
# After: workflow-scoped key
key: ${{ runner.os }}-npm-triage-${{ hashFiles('package-lock.json') }}
But that only addresses one vector. The deeper problem is that every GitHub Action processing untrusted input (issue titles, PR bodies, comment text) is a prompt injection surface. The triage workflow fed the issue title into an LLM prompt. The attacker put executable instructions in the title. The LLM followed them. Classic indirect injection, new delivery mechanism.