The >256KB rejection case fails on Linux CI because Linux's
MAX_ARG_STRLEN (128 KiB per argv element on most kernels) trips
before the helper's own size cap fires. spawnSync returns
status=null with error.code='E2BIG' instead of status=1.
Local macOS development passes because Mach's per-arg limit is
~1 MiB so the helper's cap is what fires. The smoke now accepts
either path — the assertion is "rejected before gh is invoked,"
and E2BIG satisfies that even more strongly (the body cannot
reach exec at all).
Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com>
github-safe.js (both copies — dogfood + init-template)
Adds GITHUB_SAFE_VERSION='1.0.0' constant for smoke assertions.
Adds explicit 256KB body cap (MAX_BODY_BYTES): rejects oversized bodies
with exit 1 before any temp-file write, matching the GitHub API body
field limit documented in ADR-127. Adds strict error handling: all
execSync calls wrapped in try/catch, cleanup in finally. Adds
GITHUB_SAFE_DRY_RUN=1 env-var for smoke tests to skip gh exec.
github-setup.sh (both copies)
Prepends set -euo pipefail. Replaces bare `gh auth status &>/dev/null`
with a check that parses "Logged in" and "repo" scope from the output.
Adds security rationale comment explaining why each flag is needed.
Agent frontmatter WebFetch removal (dogfood copies only — init-template
commands don't have frontmatter; the relevant change there is the injection
fix below):
release-manager.md — removes WebFetch (keeps Write for CHANGELOG)
release-swarm.md — removes WebFetch (keeps Write for CHANGELOG)
repo-architect.md — removes WebFetch
All 13 agents already have explicit tools: lines; only the 3 above had
WebFetch. No agent in scope now has WebFetch in its tools: list.
swarm-pr.md + swarm-issue.md (BOTH dogfood .claude/agents/github/ AND
init-template v3/@claude-flow/cli/.claude/commands/github/):
Replaces unquoted ${{ github.event.comment.body }} interpolated into
--comment "..." with mktemp + printf + grep + --comment-file pattern.
Replaces unquoted ${{ github.event.label.name }} == "swarm-ready" with
mktemp + printf + grep -qx pattern. Closes prompt-injection path where
a PR comment with shell metacharacters could execute attacker-controlled
shell in a pull_request-triggered workflow.
smoke-github-safe-injection.mjs updated:
Upgrades the >256KB test case from "note" (Phase 1 red) to "ok" (Phase 2
green) now that the 256KB cap is enforced.
Acceptance checks:
node scripts/smoke-github-safe-injection.mjs → all 5 cases pass
node scripts/smoke-github-actions-pins.mjs → ok (no regressions)
grep -r 'github.event.comment.body.*--comment' .claude/agents/ v3/@claude-flow/cli/.claude/
→ exit 1 (no matches)
Co-Authored-By: RuFlo <ruv@ruv.net>
Creates two new CI smoke scripts and wires them into v3-ci.yml.
smoke-github-safe-injection.mjs
Generalizes the smoke-pre-bash-hook.mjs pattern (#2017) to the GitHub
helper surface. Drives adversarial PR/issue bodies (backticks, $(),
semicolons, empty) through github-safe.js via a fake-gh shim and
asserts the body lands verbatim in a temp file with --body-file, not
inline as --body. The >256KB cap case is documented as a red→green
transition for Phase 2 (no cap yet enforced). Runs against both
.claude/helpers/github-safe.js (dogfood) and the init-template copy.
smoke-github-actions-pins.mjs
Static scan of .claude/agents/github/*.md, .claude/skills/github-*/
SKILL.md, and v3/@claude-flow/cli/.claude/commands/github/*.md. Every
uses: ref must be SHA-pinned (40-hex) OR present in the new
.github/supply-chain/allowed-deps.json "actions" block. Exits 0 now
that allowed-deps.json covers the full set of refs in-use. Phase 3
upgrades @v3→@v4 across all files; this smoke is the regression gate
that prevents them drifting back.
.github/supply-chain/allowed-deps.json
Adds "actions" key with allowed[], minimumVersion, and an explanatory
comment per ADR-127 §Phase 1. Lists all GitHub Actions currently
referenced in scope: checkout, setup-node, pnpm/action-setup, cache,
upload-artifact, ruvnet/swarm-action, aws-actions/configure-aws-credentials.
.github/workflows/v3-ci.yml
Adds path-filter entries under both push and pull_request triggers
covering the 6 ADR-127-specified paths:
.claude/agents/github/**
.claude/skills/github-*/**
v3/@claude-flow/cli/.claude/commands/github/**
.claude/helpers/github-safe.js
v3/@claude-flow/cli/.claude/helpers/github-safe.js
scripts/smoke-github-*.mjs
.github/supply-chain/allowed-deps.json
Adds two independent smoke jobs: github-safe-injection-smoke and
github-actions-pins-smoke, following the ruvllm-wasm-auto-init-smoke
pattern.
Co-Authored-By: RuFlo <ruv@ruv.net>