Quarterly GitHub Actions Security Audit — 2026 Q3
Audit date: 2026-07-01
Methodology: docs/github-actions-security-methodology.md
Prior audit: 2026-04-29 Q2 auto — dev-env#23
Repos scanned: 31 (20 smartwatermelon + 11 nightowlstudiollc) — up from 29 in Q2
Repo changes since Q2: smartwatermelon/lamatic added; nightowlstudiollc/reliquarist added; nightowlstudiollc/juliet-cleaning renamed → nightowlstudiollc/tnjcleaning
Findings Table
| Pattern |
Description |
Affected Repos |
Severity |
Status vs Q2 |
| P7 |
issue_comment trigger without author_association gate |
swm/archive-resolver, swm/lock-sync, swm/ralph-burndown, swm/scripts, swm/claude-config, swm/spokane-snow, swm/.github, nos/.github |
Medium |
NEW (Q2 audit gap) |
| P2/P8 |
Lamatic/sync-flows-to-lamatic@v1 (mutable tag) in new repo |
swm/lamatic |
Medium |
NEW (new repo) |
| P2/P8 |
Cyb3r-Jak3/html5validator-action@v8.0.0 (mutable tag) in new repo |
nos/tnjcleaning |
Medium |
NEW (new repo) |
| P2/P8 |
dependabot/fetch-metadata@v3 (mutable tag) |
26 repos (all with dependabot-auto-merge.yml) |
Low |
UNCHANGED — tracked in dev-env#19 |
Total new findings: 3 (all Medium). No new Critical or High.
| Category |
Count |
Details |
| NEW findings |
3 |
P7 (8 repos, Medium); P2/P8 lamatic (Medium); P2/P8 tnjcleaning (Medium) |
| RESOLVED findings |
0 |
— |
| UNCHANGED findings |
1 |
P2/P8 dependabot/fetch-metadata@v3 — Low, Tier-3 |
Pattern-by-Pattern Results
Pattern 1 — pull_request_target + untrusted checkout
CLEAN. All pull_request_target hits are dependabot-auto-merge.yml at the same canonical SHA c53c7e3e300fd4fd2e5eddda56c928b1652aba31 in 26 repos. No checkout of PR-controlled code. Identical to Q2.
Pattern 2/8 — Mutable action references
UNCHANGED FINDING: dependabot/fetch-metadata@v3 in dependabot-auto-merge.yml across 26 repos. Severity: Low. Tracked in dev-env#19.
NEW FINDING — lamatic: smartwatermelon/lamatic (new repo) has .github/workflows/lamatic-update.yml using:
Lamatic/sync-flows-to-lamatic@v1 — third-party action, mutable semver tag. Medium.
actions/checkout@v4 — GitHub-owned, mutable tag. Low.
NEW FINDING — tnjcleaning: nightowlstudiollc/tnjcleaning (was juliet-cleaning) has .github/workflows/html-validation.yml using:
Cyb3r-Jak3/html5validator-action@v8.0.0 — third-party action, mutable semver tag. Medium.
actions/checkout@v7 — GitHub-owned, mutable tag. Low.
Additional consistent (Low) — not newly flagged: Multiple repos use GitHub-owned actions at mutable tags (actions/checkout@v7, actions/setup-node@v6, actions/setup-python@v6, pnpm/action-setup@v6) and anthropics/claude-code-action@v1 at a mutable tag. These are Low severity (first-party or well-maintained publishers) and consistent with the inaugural audit's tier-3 residual.
Pattern 3 — Cache poisoning
CLEAN. No actions/cache steps in any pull_request_target workflow.
Pattern 4 — Imposter commits from forks
NOT VERIFIED. Requires git reachability analysis not available in this environment. Same constraint as Q2.
Pattern 5 — Template injection in run: blocks
CLEAN. All ${{ github.event.* }} expressions in run: contexts use the safe env: isolation pattern (confirmed via code search across all 31 repos). Invariant holds.
Pattern 6 — Overpermissive default GITHUB_TOKEN
PARTIAL VERIFICATION — TOOL LIMITATION. The /repos/{owner}/{repo}/actions/permissions/workflow API endpoint is not reachable via MCP code search. Same constraint as Q2. The two repos flipped to read in the inaugural audit (ralph-burndown, mac-server-setup) are not expected to have regressed. Verify locally per methodology doc.
Pattern 7 — issue_comment without author_association gate
NEW FINDING. Eight repos have standalone claude.yml files that trigger on issue_comment WITHOUT the author_association sender-identity gate. Any GitHub user can @-mention Claude and trigger the workflow.
Affected repos (confirmed — author_association search returned 0 results in all 8):
| Repo |
claude.yml SHA |
Notes |
smartwatermelon/archive-resolver |
5087689860eb |
Inline implementation, no reusable workflow |
smartwatermelon/lock-sync |
be1946a4d197 |
Inline implementation |
smartwatermelon/ralph-burndown |
cb5cde7c20dd |
Inline implementation |
smartwatermelon/scripts |
5087689860eb |
Inline implementation |
smartwatermelon/claude-config |
5087689860eb |
Inline implementation |
smartwatermelon/spokane-snow |
5087689860eb |
Inline implementation |
smartwatermelon/.github |
e9838ad71150 |
Org-level default workflow — applies to all swm repos without their own claude.yml |
nightowlstudiollc/.github |
edaee438b740 |
Org-level default workflow — applies to all nos repos without their own claude.yml |
Why Q2 missed this: The Q2 audit incorrectly assumed all repos used the canonical reusable workflow (smartwatermelon/github-workflows/.github/workflows/claude-assistant.yml) and inherited its gate. These repos use an older inline implementation that predates the canonical pattern. This is likely a Q2 audit gap, not a post-Q2 regression.
Remediation — Option A (quick fix): Add the author_association check to the existing if: block:
# In .github/workflows/claude.yml — current (INSECURE):
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
...
# Remediated:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association))
Remediation — Option B (preferred, aligns with canonical pattern): Migrate to the reusable workflow call (same as how swift-progress-indicator, mac-server-setup, slack-mcp, and others are set up):
# .github/workflows/claude.yml
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association))
uses: smartwatermelon/github-workflows/.github/workflows/claude-assistant.yml@v3
secrets:
claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
Apply this to all 8 affected repos (or at minimum the 6 non-.github repos; the .github defaults are lower priority if most repos have their own files).
Pattern 9 — Missing workflow permissions: block
CLEAN for canonical workflows and new repos. nightowlstudiollc/reliquarist/claude.yml has explicit permissions: { contents: read, pull-requests: read, issues: read, id-token: write }. The inline claude.yml files in the 8 Pattern-7 repos were not fully verified (MCP file read is scoped to smartwatermelon/dev-env), but resolving Pattern 7 via Option B remediation would also bring permissions into the canonical state.
Draft PRs
0 draft PRs created. MCP GitHub write access is scoped to smartwatermelon/dev-env only in this session; cross-repo PR creation fails with access-denied.
- PR creation skipped for smartwatermelon/archive-resolver: MCP write scope limited to dev-env
- PR creation skipped for smartwatermelon/lock-sync: MCP write scope limited to dev-env
- PR creation skipped for smartwatermelon/ralph-burndown: MCP write scope limited to dev-env
- PR creation skipped for smartwatermelon/scripts: MCP write scope limited to dev-env
- PR creation skipped for smartwatermelon/claude-config: MCP write scope limited to dev-env
- PR creation skipped for smartwatermelon/spokane-snow: MCP write scope limited to dev-env
- PR creation skipped for smartwatermelon/.github: MCP write scope limited to dev-env
- PR creation skipped for nightowlstudiollc/.github: MCP write scope limited to dev-env
- PR creation skipped for smartwatermelon/lamatic: MCP write scope limited to dev-env
- PR creation skipped for nightowlstudiollc/tnjcleaning: MCP write scope limited to dev-env
Use the inline remediation snippets above to apply fixes manually (branch + PR in each affected repo).
Audit Infrastructure Notes
Same constraints as Q2:
gh CLI unavailable — Pattern 6 (default workflow permissions) cannot be verified via API. Mark as PARTIAL pending local run.
- MCP write scope — Limited to
smartwatermelon/dev-env; prevents automated draft PRs in other repos.
The 3 new Medium findings this quarter (vs 0 new in Q2) warrant tracking the resolution timeline. If manually applying remediations for the P7 finding across 8 repos proves slow, consider upgrading the routine environment to one with broader MCP write scope or gh CLI access.
Audit History Update
| Date |
Repos |
Findings (severity) |
PRs opened |
Issues filed |
| 2026-07-01 (Q3, auto) |
31 |
0 critical, 0 high, 3 medium (new), 1 low (unchanged) |
0 |
1 (this) |
| 2026-04-29 (Q2, auto) |
29 |
0 new (1 unchanged Low) |
0 |
1 (dev-env#23) |
| 2026-04-29 (inaugural, manual) |
29 |
0 critical, 4 high, 10 medium |
9 |
5 |
Quarterly GitHub Actions Security Audit — 2026 Q3
Audit date: 2026-07-01
Methodology: docs/github-actions-security-methodology.md
Prior audit: 2026-04-29 Q2 auto — dev-env#23
Repos scanned: 31 (20 smartwatermelon + 11 nightowlstudiollc) — up from 29 in Q2
Repo changes since Q2:
smartwatermelon/lamaticadded;nightowlstudiollc/reliquaristadded;nightowlstudiollc/juliet-cleaningrenamed →nightowlstudiollc/tnjcleaningFindings Table
issue_commenttrigger withoutauthor_associationgateLamatic/sync-flows-to-lamatic@v1(mutable tag) in new repoCyb3r-Jak3/html5validator-action@v8.0.0(mutable tag) in new repodependabot/fetch-metadata@v3(mutable tag)Total new findings: 3 (all Medium). No new Critical or High.
Delta vs Q2 Audit (dev-env#23)
dependabot/fetch-metadata@v3— Low, Tier-3Pattern-by-Pattern Results
Pattern 1 —
pull_request_target+ untrusted checkoutCLEAN. All
pull_request_targethits aredependabot-auto-merge.ymlat the same canonical SHAc53c7e3e300fd4fd2e5eddda56c928b1652aba31in 26 repos. No checkout of PR-controlled code. Identical to Q2.Pattern 2/8 — Mutable action references
UNCHANGED FINDING:
dependabot/fetch-metadata@v3independabot-auto-merge.ymlacross 26 repos. Severity: Low. Tracked in dev-env#19.NEW FINDING — lamatic:
smartwatermelon/lamatic(new repo) has.github/workflows/lamatic-update.ymlusing:Lamatic/sync-flows-to-lamatic@v1— third-party action, mutable semver tag. Medium.actions/checkout@v4— GitHub-owned, mutable tag. Low.NEW FINDING — tnjcleaning:
nightowlstudiollc/tnjcleaning(wasjuliet-cleaning) has.github/workflows/html-validation.ymlusing:Cyb3r-Jak3/html5validator-action@v8.0.0— third-party action, mutable semver tag. Medium.actions/checkout@v7— GitHub-owned, mutable tag. Low.Additional consistent (Low) — not newly flagged: Multiple repos use GitHub-owned actions at mutable tags (
actions/checkout@v7,actions/setup-node@v6,actions/setup-python@v6,pnpm/action-setup@v6) andanthropics/claude-code-action@v1at a mutable tag. These are Low severity (first-party or well-maintained publishers) and consistent with the inaugural audit's tier-3 residual.Pattern 3 — Cache poisoning
CLEAN. No
actions/cachesteps in anypull_request_targetworkflow.Pattern 4 — Imposter commits from forks
NOT VERIFIED. Requires git reachability analysis not available in this environment. Same constraint as Q2.
Pattern 5 — Template injection in
run:blocksCLEAN. All
${{ github.event.* }}expressions inrun:contexts use the safeenv:isolation pattern (confirmed via code search across all 31 repos). Invariant holds.Pattern 6 — Overpermissive default
GITHUB_TOKENPARTIAL VERIFICATION — TOOL LIMITATION. The
/repos/{owner}/{repo}/actions/permissions/workflowAPI endpoint is not reachable via MCP code search. Same constraint as Q2. The two repos flipped toreadin the inaugural audit (ralph-burndown,mac-server-setup) are not expected to have regressed. Verify locally per methodology doc.Pattern 7 —
issue_commentwithoutauthor_associationgateNEW FINDING. Eight repos have standalone
claude.ymlfiles that trigger onissue_commentWITHOUT theauthor_associationsender-identity gate. Any GitHub user can @-mention Claude and trigger the workflow.Affected repos (confirmed —
author_associationsearch returned 0 results in all 8):smartwatermelon/archive-resolver5087689860ebsmartwatermelon/lock-syncbe1946a4d197smartwatermelon/ralph-burndowncb5cde7c20ddsmartwatermelon/scripts5087689860ebsmartwatermelon/claude-config5087689860ebsmartwatermelon/spokane-snow5087689860ebsmartwatermelon/.githube9838ad71150nightowlstudiollc/.githubedaee438b740Why Q2 missed this: The Q2 audit incorrectly assumed all repos used the canonical reusable workflow (
smartwatermelon/github-workflows/.github/workflows/claude-assistant.yml) and inherited its gate. These repos use an older inline implementation that predates the canonical pattern. This is likely a Q2 audit gap, not a post-Q2 regression.Remediation — Option A (quick fix): Add the
author_associationcheck to the existingif:block:Remediation — Option B (preferred, aligns with canonical pattern): Migrate to the reusable workflow call (same as how
swift-progress-indicator,mac-server-setup,slack-mcp, and others are set up):Apply this to all 8 affected repos (or at minimum the 6 non-
.githubrepos; the.githubdefaults are lower priority if most repos have their own files).Pattern 9 — Missing workflow
permissions:blockCLEAN for canonical workflows and new repos.
nightowlstudiollc/reliquarist/claude.ymlhas explicitpermissions: { contents: read, pull-requests: read, issues: read, id-token: write }. The inlineclaude.ymlfiles in the 8 Pattern-7 repos were not fully verified (MCP file read is scoped tosmartwatermelon/dev-env), but resolving Pattern 7 via Option B remediation would also bring permissions into the canonical state.Draft PRs
0 draft PRs created. MCP GitHub write access is scoped to
smartwatermelon/dev-envonly in this session; cross-repo PR creation fails with access-denied.Use the inline remediation snippets above to apply fixes manually (branch + PR in each affected repo).
Audit Infrastructure Notes
Same constraints as Q2:
ghCLI unavailable — Pattern 6 (default workflow permissions) cannot be verified via API. Mark as PARTIAL pending local run.smartwatermelon/dev-env; prevents automated draft PRs in other repos.The 3 new Medium findings this quarter (vs 0 new in Q2) warrant tracking the resolution timeline. If manually applying remediations for the P7 finding across 8 repos proves slow, consider upgrading the routine environment to one with broader MCP write scope or
ghCLI access.Audit History Update