From f0827e4bb15fa8bdfb476e1c6c10773d15fbfef1 Mon Sep 17 00:00:00 2001 From: jdalton Date: Sat, 4 Apr 2026 00:09:11 -0400 Subject: [PATCH 1/5] =?UTF-8?q?fix(ci):=20harden=20weekly-update=20?= =?UTF-8?q?=E2=80=94=20allowedTools,=20two-phase=20update,=20diff=20valida?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/weekly-update.yml | 151 ++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 11 deletions(-) diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index 9f9dc4f8c..470bde3a1 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -94,13 +94,20 @@ jobs: with: gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} - - name: Run updating skill with Claude Code - id: claude - timeout-minutes: 30 + - name: Update dependencies (haiku — fast, cheap) + id: update + timeout-minutes: 10 env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_ACTIONS: 'true' run: | + if [ -n "$SFW_BIN" ]; then + mkdir -p /tmp/sfw-bin + printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm + chmod +x /tmp/sfw-bin/pnpm + export PATH="/tmp/sfw-bin:$PATH" + fi + if [ -z "$ANTHROPIC_API_KEY" ]; then echo "ANTHROPIC_API_KEY not set - skipping automated update" echo "success=false" >> $GITHUB_OUTPUT @@ -108,8 +115,10 @@ jobs: fi set +e - pnpm exec claude --print --dangerously-skip-permissions \ - --model sonnet \ + pnpm exec claude --print \ + --allowedTools "Bash(pnpm:*)" "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" "Read" "Write" "Edit" "Glob" "Grep" \ + --model haiku \ + --max-turns 15 \ "$(cat <<'PROMPT' /updating @@ -122,7 +131,7 @@ jobs: Update all dependencies to their latest versions. Create one atomic commit per dependency update with a conventional commit message. Leave all changes local — the workflow handles pushing and PR creation. - Skip running builds, tests, and type checks — CI runs those separately. + Do not run builds or tests — the next step handles that. @@ -132,7 +141,91 @@ jobs: PROMPT )" \ - 2>&1 | tee claude-output.log + 2>&1 | tee claude-update.log + CLAUDE_EXIT=${PIPESTATUS[0]} + set -e + + if [ "$CLAUDE_EXIT" -eq 0 ]; then + echo "success=true" >> $GITHUB_OUTPUT + else + echo "success=false" >> $GITHUB_OUTPUT + fi + + - name: Run tests + id: tests + if: steps.update.outputs.success == 'true' + run: | + if [ -n "$SFW_BIN" ]; then + mkdir -p /tmp/sfw-bin + printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm + chmod +x /tmp/sfw-bin/pnpm + export PATH="/tmp/sfw-bin:$PATH" + fi + + set +e + pnpm build 2>&1 | tee build.log + BUILD_EXIT=${PIPESTATUS[0]} + + pnpm test 2>&1 | tee test.log + TEST_EXIT=${PIPESTATUS[0]} + set -e + + if [ "$BUILD_EXIT" -eq 0 ] && [ "$TEST_EXIT" -eq 0 ]; then + echo "tests-passed=true" >> $GITHUB_OUTPUT + else + echo "tests-passed=false" >> $GITHUB_OUTPUT + fi + + - name: Fix test failures (sonnet — smarter, escalated) + id: claude + if: steps.update.outputs.success == 'true' && steps.tests.outputs.tests-passed == 'false' + timeout-minutes: 15 + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_ACTIONS: 'true' + run: | + if [ -n "$SFW_BIN" ]; then + mkdir -p /tmp/sfw-bin + printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm + chmod +x /tmp/sfw-bin/pnpm + export PATH="/tmp/sfw-bin:$PATH" + fi + + FAILURE_LOG="$(cat build.log test.log 2>/dev/null)" + + set +e + pnpm exec claude --print \ + --allowedTools "Bash(pnpm:*)" "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" "Read" "Write" "Edit" "Glob" "Grep" \ + --model sonnet \ + --max-turns 25 \ + "$(cat < + You are an automated CI agent in a weekly dependency update workflow. + Git is configured with GPG signing. A branch has been created for you. + Dependencies were updated in the previous step but build/tests failed. + + + + $FAILURE_LOG + + + + The dependency updates above caused build or test failures. + Diagnose the failures from the logs and fix the code so it builds and tests pass. + Create one atomic commit per fix with a conventional commit message. + Run pnpm build && pnpm test to verify your fixes. + Leave all changes local — the workflow handles pushing and PR creation. + + + + pnpm build succeeds. + pnpm test succeeds. + Each fix has its own commit. + No uncommitted changes remain in the working tree. + + PROMPT + )" \ + 2>&1 | tee claude-fix.log CLAUDE_EXIT=${PIPESTATUS[0]} set -e @@ -142,6 +235,38 @@ jobs: echo "success=false" >> $GITHUB_OUTPUT fi + - name: Set final status + id: final + if: always() + run: | + if [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.tests.outputs.tests-passed }}" = "true" ]; then + echo "success=true" >> $GITHUB_OUTPUT + elif [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.claude.outputs.success }}" = "true" ]; then + echo "success=true" >> $GITHUB_OUTPUT + else + echo "success=false" >> $GITHUB_OUTPUT + fi + + - name: Validate changes + id: validate + if: steps.final.outputs.success == 'true' + run: | + UNEXPECTED="" + for file in $(git diff --name-only origin/main..HEAD); do + case "$file" in + package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml|.npmrc|pnpm-workspace.yaml) ;; + src/*|test/*) ;; + *.ts|*.mts|*.js|*.mjs) ;; + *) UNEXPECTED="$UNEXPECTED $file" ;; + esac + done + if [ -n "$UNEXPECTED" ]; then + echo "::error::Unexpected files modified by Claude:$UNEXPECTED" + echo "valid=false" >> $GITHUB_OUTPUT + else + echo "valid=true" >> $GITHUB_OUTPUT + fi + - name: Check for changes id: changes run: | @@ -152,13 +277,13 @@ jobs: fi - name: Push branch - if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true' + if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true' env: BRANCH_NAME: ${{ steps.branch.outputs.branch }} run: git push origin "$BRANCH_NAME" - name: Create Pull Request - if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true' + if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true' env: GH_TOKEN: ${{ github.token }} BRANCH_NAME: ${{ steps.branch.outputs.branch }} @@ -187,7 +312,7 @@ jobs: --base main - name: Add job summary - if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true' + if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true' env: BRANCH_NAME: ${{ steps.branch.outputs.branch }} run: | @@ -202,7 +327,11 @@ jobs: uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: claude-output-${{ github.run_id }} - path: claude-output.log + path: | + claude-update.log + claude-fix.log + build.log + test.log retention-days: 7 - uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main From 604dbcf25663efb2cd947b145df30221f756c15a Mon Sep 17 00:00:00 2001 From: jdalton Date: Sat, 4 Apr 2026 00:16:09 -0400 Subject: [PATCH 2/5] fix(ci): use curl+tar for npm upgrade (no npm/npx self-install) --- .github/workflows/provenance.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/provenance.yml b/.github/workflows/provenance.yml index 35828c2db..d1ece10c7 100644 --- a/.github/workflows/provenance.yml +++ b/.github/workflows/provenance.yml @@ -166,7 +166,12 @@ jobs: - uses: SocketDev/socket-registry/.github/actions/install@6096b06b1790f411714c89c40f72aade2eeaab7c # main - - run: npm install -g npm@11.12.1 + - name: Upgrade npm for trusted publishing + run: | + # Avoid npm self-upgrade (corrupts deps mid-install on Node 22). + NPM_PREFIX="$(node -p 'process.config.variables.node_prefix')" + curl -sL https://registry.npmjs.org/npm/-/npm-11.12.1.tgz | tar xz -C "$NPM_PREFIX/lib/node_modules/npm" --strip-components=1 + echo "npm version: $(npm --version)" # Get versions for lock-stepped and independent packages. - name: Get versions From 87101418bdd6203916e6e8637d636a926b38eaf4 Mon Sep 17 00:00:00 2001 From: jdalton Date: Sat, 4 Apr 2026 00:19:45 -0400 Subject: [PATCH 3/5] fix(ci): use npm config get prefix for reliable npm dir detection --- .github/workflows/provenance.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/provenance.yml b/.github/workflows/provenance.yml index d1ece10c7..77af369b1 100644 --- a/.github/workflows/provenance.yml +++ b/.github/workflows/provenance.yml @@ -169,8 +169,8 @@ jobs: - name: Upgrade npm for trusted publishing run: | # Avoid npm self-upgrade (corrupts deps mid-install on Node 22). - NPM_PREFIX="$(node -p 'process.config.variables.node_prefix')" - curl -sL https://registry.npmjs.org/npm/-/npm-11.12.1.tgz | tar xz -C "$NPM_PREFIX/lib/node_modules/npm" --strip-components=1 + NPM_DIR="$(npm config get prefix)/lib/node_modules/npm" + curl -sL https://registry.npmjs.org/npm/-/npm-11.12.1.tgz | tar xz -C "$NPM_DIR" --strip-components=1 echo "npm version: $(npm --version)" # Get versions for lock-stepped and independent packages. From b70ac8df7e39d7f4a71f96232af41e6a2e7d04e1 Mon Sep 17 00:00:00 2001 From: jdalton Date: Sat, 4 Apr 2026 08:55:46 -0400 Subject: [PATCH 4/5] fix(ci): use env vars instead of template expressions in run blocks (zizmor) --- .github/workflows/weekly-update.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index 470bde3a1..a2b42e3c6 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -238,10 +238,14 @@ jobs: - name: Set final status id: final if: always() + env: + UPDATE_SUCCESS: ${{ steps.update.outputs.success }} + TESTS_PASSED: ${{ steps.tests.outputs.tests-passed }} + FIX_SUCCESS: ${{ steps.claude.outputs.success }} run: | - if [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.tests.outputs.tests-passed }}" = "true" ]; then + if [ "$UPDATE_SUCCESS" = "true" ] && [ "$TESTS_PASSED" = "true" ]; then echo "success=true" >> $GITHUB_OUTPUT - elif [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.claude.outputs.success }}" = "true" ]; then + elif [ "$UPDATE_SUCCESS" = "true" ] && [ "$FIX_SUCCESS" = "true" ]; then echo "success=true" >> $GITHUB_OUTPUT else echo "success=false" >> $GITHUB_OUTPUT From d2cb3682e3dae8081c4e32cd9a72af7a77962e83 Mon Sep 17 00:00:00 2001 From: jdalton Date: Sat, 4 Apr 2026 09:21:39 -0400 Subject: [PATCH 5/5] fix(deps): override defu >=6.1.5 (prototype pollution CVE) --- package.json | 1 + pnpm-lock.yaml | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 66682ddad..61bab4ef6 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,7 @@ "aggregate-error": "catalog:", "ansi-regex": "catalog:", "brace-expansion": "catalog:", + "defu": ">=6.1.5", "emoji-regex": "catalog:", "es-define-property": "catalog:", "es-set-tostringtag": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f04aa4446..788abd552 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -261,6 +261,7 @@ overrides: aggregate-error: npm:@socketregistry/aggregate-error@^1.0.15 ansi-regex: 6.2.2 brace-expansion: 5.0.5 + defu: '>=6.1.5' emoji-regex: 10.6.0 es-define-property: npm:@socketregistry/es-define-property@^1.0.7 es-set-tostringtag: npm:@socketregistry/es-set-tostringtag@^1.0.10 @@ -2687,8 +2688,8 @@ packages: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + defu@6.1.6: + resolution: {integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==} del-cli@6.0.0: resolution: {integrity: sha512-9nitGV2W6KLFyya4qYt4+9AKQFL+c0Ehj5K7V7IwlxTc6RMCfQUGY9E9pLG6e8TQjtwXpuiWIGGZb3mfVxyZkw==} @@ -6340,7 +6341,7 @@ snapshots: define-lazy-prop@3.0.0: {} - defu@6.1.4: {} + defu@6.1.6: {} del-cli@6.0.0: dependencies: @@ -8018,7 +8019,7 @@ snapshots: unconfig@7.4.1: dependencies: '@quansync/fs': 0.1.5 - defu: 6.1.4 + defu: 6.1.6 jiti: 2.6.1 quansync: 0.2.11 unconfig-core: 7.4.1 @@ -8041,7 +8042,7 @@ snapshots: unplugin-purge-polyfills@0.1.0: dependencies: - defu: 6.1.4 + defu: 6.1.6 magic-string: 0.30.19 mlly: 1.8.0 unplugin: 2.3.10