Without --bare, claude -p launches MCP servers defined in the project-level
.mcp.json before the model loads. A PR replacing .mcp.json with a malicious
entry gets an attacker-controlled subprocess launched on the CI runner —
same RCE surface as a SessionStart hook.
This repo already has .mcp.json committed (nx-mcp). Removing it before the
social copy generator runs is safe: the workflow's --allowedTools already
restricts Claude to specific read/diff tools and MCP servers are not needed
for social copy generation.
Call-site enumeration: rm -f is self-contained, no callers.
Extends the security fix to also cover .claude/settings.local.json, which
Claude Code reads with equal authority to settings.json. A PR author can
force-commit a gitignored file, so the .gitignore entry does not protect
against this vector.
Also moves the removal step to immediately after Checkout (before any other
step runs in the checked-out workspace), rather than after Install Claude Code.
Claude is not invoked during the intermediate steps, but this ordering is
strictly more defensive and prevents any future step insertions from reopening
the window.
Call-site enumeration: rm -f has no callers; step is self-contained YAML.
Comprehensive CI/CD security hardening pass over all 33 workflows.
Action pinning
- Every `uses:` is now pinned to a 40-char commit SHA with a `# vX.Y.Z`
comment alongside (167 occurrences resolved). Tag-style refs like `@v4`
are mutable and have been used in past supply-chain attacks (e.g.
tj-actions/changed-files in March 2025) to repoint widely-used actions
to malicious commits.
- Removed redundant `version: "10.13.1"` hardcodes from `pnpm/action-setup`
call sites so the action inherits from package.json `packageManager`
(one source of truth).
Automated maintenance
- Added `.github/dependabot.yml` for the `github-actions` ecosystem so
SHA pins stay current. Without this, pins go stale fast and new
upstream advisories never reach us. Minor/patch bumps are grouped;
major bumps stay separate so they get a real review.
Static analysis
- Added `.github/zizmor.yml` configuration and
`.github/workflows/security_zizmor.yml` (blocking on PR, runs on push
to main, weekly schedule for advisory drift). zizmor catches the
well-known classes of Actions footguns: template injection from
untrusted input, dangerous triggers, unpinned uses, excessive token
scopes, secret exfil patterns.
- All 28 high-severity and 54 medium-severity findings from the baseline
scan are remediated. Each suppression in zizmor.yml carries a
per-finding justification comment so future maintainers can audit the
trust assumption.
Workflow hardening (from zizmor + manual audit)
- Added `persist-credentials: false` to every `actions/checkout` except
the 7 workflows that legitimately push back to the repo via the
workflow token (release tagging, auto-formatting, docs-sync, registry
updates). Each retained credential persistence carries a
`persist-credentials required: ...` comment explaining the call site.
- Routed every attacker-controllable expansion (`github.head_ref`,
`github.event.pull_request.head.repo.full_name`, `inputs.*`,
step outputs) through `env:` and referenced as quoted shell variables.
Eliminates 17 template-injection vectors in fork-PR-reachable
workflows.
- Added per-job `permissions:` blocks across 14 workflows; demoted
broad workflow-level `id-token: write` to the specific Depot-runner
jobs that need it; narrowed `pull-requests: write` /
`actions: write` to the jobs that actually call those APIs.
Audit-driven fixes
- `publish-release.yml` build job: dropped `token:` and added
`persist-credentials: false`. The subsequent `Upload workspace` step
was packing `.git/config` (with the persisted GITHUB_TOKEN) into a
1-day-retention artifact downloadable by anyone with `actions:read`.
- `auto_merge_showcases.yml`: team-membership check now authorizes on
the PR AUTHOR (`pull_request.user.login`), never `context.actor` —
the actor is whoever triggered the latest event, so a team member
synchronizing or reopening an outsider's PR would otherwise
green-light auto-merge of code they didn't author.
- `static_quality.yml`: pinned ruff to a specific version so a
compromised release can't land on the next PR run with the
persisted-credentials write token in the format job.
- `showcase_capture-previews.yml`: switched the args-string construction
to a bash array so a slug or demo value containing whitespace or shell
metacharacters stays a single argument rather than being re-tokenized
by the shell.
Replace anthropics/claude-code-base-action@beta with the Claude Code
CLI (`claude -p` with `--output-format json`). This gives direct
control over the output format — a single JSON object with a `result`
field instead of ambiguous JSONL. The extraction script parses the
JSON, strips preamble, and writes clean markdown to pr-social-copy.md
which is uploaded as the artifact and posted to the PR comment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The claude-execution-output.json is JSONL (one JSON object per line),
not a single JSON object. JSON.parse() on the whole file silently
failed, producing empty output. Now parses line by line, finding the
"type": "result" entry or falling back to the last assistant message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract result text and save as pr-social-copy.md (not raw JSON)
- Strip preamble/analysis before first ### header
- Upload the markdown file as the artifact (90-day retention)
- Comment reads from the same markdown file
- Remove character limit from blog post and LinkedIn (only Twitter
keeps 280 char limit)
- Instruct Claude to skip preamble and start directly with content
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add disallowed_tools to prevent Claude from wasting turns on
TodoWrite, Edit, Write, and other non-essential tools that cause
error_during_execution crashes
- Make extract/upload/update steps run with always() so they still
execute even if Claude's step exits with error_during_execution
- Add JSONL log fallback: if result field is empty, scan assistant
messages for the generated content (contains ###)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a direct "Download full output" link in the comment footer that
points to the uploaded artifact, alongside the existing workflow run
link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code sandboxes file access to the working directory. Files in
/tmp are inaccessible regardless of allowed_tools config. Move diff
files and extracted result to .claude-tmp/ inside the repo.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add `Read` to allowed_tools so Claude can read the diff files
- Read Claude output from JSON file instead of step output (fixes
empty result caused by multiline content breaking GH Actions outputs)
- Upload claude-execution-output.json as a downloadable artifact
- Add link to workflow run in the success comment footer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Skip posting the initial comment on PRs from forks (job-level guard
+ runtime check for workflow_dispatch)
- Verify the checkbox clicker has write/admin permission before
running generation (prevents external collaborators from triggering
Claude API calls)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The claude-code-base-action expects anthropic_api_key as a `with:`
input parameter, not an `env:` variable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add workflow_dispatch trigger with pr_number input so the social copy
generator can be used on any existing PR (open or merged). Also adds
duplicate comment detection and a GitHub API diff fallback for merged
PRs where the branch may have been deleted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a GitHub Actions workflow that posts a comment on every new PR with a
checkbox to generate social media copies (Twitter/X, LinkedIn, Blog Post)
using Claude Code. Checking the checkbox triggers Claude to analyze the PR
diff and generate content. After generation, the checkbox resets to
"Regenerate" so users can re-trigger after pushing new changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>