Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
# CodeRabbit catch (CWE-250, task_1788457898992): the default
# persisted credential would stay live through npm ci/build/test
# below, so a compromised dependency's lifecycle script could
# misuse it to push. Each git network call downstream instead
# authenticates individually via an inline `-c http.extraheader`
# (never written to .git/config — see the second CodeRabbit catch,
# CWE-522, at "Determine mode" below).
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
Expand All @@ -89,14 +97,34 @@ jobs:
# steady state, and never retries the artifacts that actually failed).
# This makes a rerun after a partial failure resume exactly the
# missing steps instead of silently skipping them.
#
# Git auth note (CodeRabbit, CWE-250 then CWE-522, task_1788457898992):
# persist-credentials is false on checkout above, and this step's own
# `git fetch --tags` is the first git network call after the untrusted
# npm lifecycle. A first pass re-authenticated via `git remote
# set-url`, but that WRITES the token into .git/config where any later
# process in the job could read it back off disk. Using a `-c
# http.extraheader` on the git invocation itself instead scopes the
# credential to that one command's process environment -- nothing
# persists to a file. Every git network call in this workflow uses
# this same inline pattern; none set the remote URL.
#
# Also (CodeRabbit, CWE-319): every such call targets an explicit
# https://github.com/... URL rather than the `origin` remote name --
# if something upstream of this point ever rewrote origin's URL to an
# http:// scheme, using the remote name would silently send this
# Basic-auth header in cleartext. An explicit https:// URL can't be
# redirected that way.
- name: Determine mode (publish vs prepare)
id: mode
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
git fetch --tags
AUTH_HEADER="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n')"
echo "::add-mask::$AUTH_HEADER"
git -c http.extraheader="$AUTH_HEADER" fetch "https://github.com/${{ github.repository }}.git" --tags
VERSION=$(jq -r '.version' package.json)
PKG_NAME=$(jq -r '.name' package.json)

Expand Down Expand Up @@ -125,11 +153,15 @@ jobs:

- name: "Publish: tag"
if: steps.mode.outputs.mode == 'publish' && steps.mode.outputs.tag_exists == 'false'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION="${{ steps.mode.outputs.version }}"
AUTH_HEADER="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n')"
echo "::add-mask::$AUTH_HEADER"
git tag "v${VERSION}"
git push origin "v${VERSION}"
git -c http.extraheader="$AUTH_HEADER" push "https://github.com/${{ github.repository }}.git" "v${VERSION}"

- name: "Publish: npm publish"
if: steps.mode.outputs.mode == 'publish' && steps.mode.outputs.npm_published == 'false'
Expand Down Expand Up @@ -173,8 +205,9 @@ jobs:
# subject to that restriction (verified live the same day: an
# App-token `gh pr create` against this exact repo succeeded, PR
# authored by app/wyre-agent-fleet). Used only for the `gh pr` calls
# below — `git push` keeps using actions/checkout's default credential,
# which was never the blocked operation.
# below — `git push` authenticates separately with the default
# GITHUB_TOKEN via an inline http.extraheader (see PUSH_TOKEN below),
# since push was never the blocked operation.
- name: "Prepare: mint App token for PR creation"
if: steps.mode.outputs.mode == 'prepare' && steps.prepare.outputs.release_needed == 'true'
id: app-token
Expand Down Expand Up @@ -205,6 +238,7 @@ jobs:
if: steps.mode.outputs.mode == 'prepare' && steps.prepare.outputs.release_needed == 'true'
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
PUSH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.prepare.outputs.version }}
run: |
set -euo pipefail
Expand All @@ -220,7 +254,9 @@ jobs:
merge method) triggers this workflow's PUBLISH mode, which tags,
publishes to npm, and creates the GitHub release — nothing
publishes until this merges."
git push --force origin release/next
AUTH_HEADER="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 | tr -d '\n')"
echo "::add-mask::$AUTH_HEADER"
git -c http.extraheader="$AUTH_HEADER" push --force "https://github.com/${{ github.repository }}.git" release/next

if gh pr view release/next --json state --jq .state 2>/dev/null | grep -q OPEN; then
gh pr edit release/next --title "chore(release): ${VERSION}"
Expand Down