fix(ci): harden self-hosted workflow trust boundaries

Keep active CCS workflows on self-hosted runners, gate self-hosted PR execution to trusted authors, and scope privileged release credentials to the exact git operations that need them.
This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-05-12 12:34:15 -04:00
1 parent d86032ed0a
commit 2273757c83
17 files changed
+210 -29

No files matched your search

+5 -1
View File
@@ -74,10 +74,14 @@ jobs:
if: >-
github.event_name != 'workflow_dispatch' &&
(
github.event_name == 'pull_request_target' ||
(
github.event_name == 'pull_request_target' &&
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)
) ||
(
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.issue.author_association) &&
github.event.comment.body == '/review' &&
(
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.comment.author_association) ||
+6
View File
@@ -16,6 +16,8 @@ concurrency:
jobs:
validate:
if: >-
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)
runs-on: [self-hosted, linux, x64]
env:
# Keep Bun cache isolated per job workspace so parallel self-hosted runs
@@ -58,6 +60,8 @@ jobs:
run: ${{ matrix.check.cmd }}
build:
if: >-
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)
runs-on: [self-hosted, linux, x64]
name: build
env:
@@ -98,6 +102,8 @@ jobs:
if-no-files-found: error
test:
if: >-
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)
runs-on: [self-hosted, linux, x64]
name: test
needs: [build]
+4 -1
View File
@@ -21,7 +21,10 @@ on:
jobs:
deploy:
runs-on: ubuntu-latest
if: >-
github.event_name != 'pull_request' ||
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)
runs-on: [self-hosted, linux, x64]
name: Deploy ccs-installer to CloudFlare
steps:
+11 -5
View File
@@ -50,9 +50,7 @@ jobs:
with:
fetch-depth: 0
ref: dev
# PAT_TOKEN required: dev-release pushes version bump commits to dev,
# and protected branches reject github.token writes once status checks are required.
token: ${{ secrets.PAT_TOKEN }}
persist-credentials: false
- name: Setup Bun
uses: oven-sh/setup-bun@v2
@@ -93,8 +91,16 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
# Use custom dev release script instead of semantic-release
# This ensures dev versions follow {stable}-dev.{N} pattern
auth_header=$(printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n')
echo "::add-mask::${auth_header}"
export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0=http.https://github.com/kaitranntt/ccs.extraheader
export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${auth_header}"
export GIT_CONFIG_KEY_1=http.https://github.com/kaitranntt/ccs.git.extraheader
export GIT_CONFIG_VALUE_1="AUTHORIZATION: basic ${auth_header}"
# Use custom dev release script instead of semantic-release.
# This ensures dev versions follow {stable}-dev.{N} pattern.
chmod +x scripts/dev-release.sh
./scripts/dev-release.sh
+1 -1
View File
@@ -18,7 +18,7 @@ concurrency:
jobs:
publish:
if: ${{ github.event_name != 'release' || !github.event.release.prerelease }}
runs-on: ubuntu-latest
runs-on: [self-hosted, linux, x64]
permissions:
contents: read
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
label:
runs-on: ubuntu-latest
runs-on: [self-hosted, linux, x64]
permissions:
issues: write
pull-requests: read
+2 -2
View File
@@ -7,7 +7,7 @@ on:
jobs:
publish:
runs-on: ubuntu-latest
runs-on: [self-hosted, linux, x64]
permissions:
contents: write # For creating GitHub releases
@@ -66,4 +66,4 @@ jobs:
## What's Changed
See [CHANGELOG.md](https://github.com/kaitranntt/ccs/blob/main/CHANGELOG.md)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+10 -4
View File
@@ -21,9 +21,7 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
# PAT_TOKEN required: semantic-release pushes version bump commits to main,
# which needs branch protection bypass. github.token cannot push to protected branches.
token: ${{ secrets.PAT_TOKEN }}
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -58,12 +56,20 @@ jobs:
env:
HUSKY: 0
# PAT_TOKEN bypasses branch protection so semantic-release can push
# version bump + CHANGELOG commits directly to main
# version bump + CHANGELOG commits directly to main.
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
GH_TOKEN: ${{ secrets.PAT_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
auth_header=$(printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n')
echo "::add-mask::${auth_header}"
export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0=http.https://github.com/kaitranntt/ccs.extraheader
export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${auth_header}"
export GIT_CONFIG_KEY_1=http.https://github.com/kaitranntt/ccs.git.extraheader
export GIT_CONFIG_VALUE_1="AUTHORIZATION: basic ${auth_header}"
set +e
OUTPUT=$(bun x semantic-release 2>&1)
STATUS=$?
@@ -22,7 +22,7 @@ permissions:
jobs:
sync-project:
runs-on: ubuntu-latest
runs-on: [self-hosted, linux, x64]
steps:
- name: Checkout
uses: actions/checkout@v4
+19 -5
View File
@@ -10,16 +10,20 @@ jobs:
runs-on: [self-hosted, linux, x64]
permissions:
contents: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# PAT_TOKEN is required because this pushes to protected dev and must
# trigger follow-up Push CI / Dev Release workflows.
token: ${{ secrets.PAT_TOKEN }}
persist-credentials: false
- name: Harden Git workspace
run: |
rm -rf .git/hooks
mkdir -p .git/hooks
git config --local core.hooksPath /dev/null
- name: Configure Git
run: |
@@ -47,4 +51,14 @@ jobs:
git commit --no-edit
fi
git push origin dev
# PAT_TOKEN is required because this pushes to protected dev and must
# trigger follow-up Push CI / Dev Release workflows. Keep it scoped to
# the final push rather than persisting it in the checkout config.
auth_header=$(printf 'x-access-token:%s' "$PAT_TOKEN" | base64 | tr -d '\n')
echo "::add-mask::${auth_header}"
git -c core.hooksPath=/dev/null \
-c "http.https://github.com/kaitranntt/ccs.extraheader=AUTHORIZATION: basic ${auth_header}" \
-c "http.https://github.com/kaitranntt/ccs.git.extraheader=AUTHORIZATION: basic ${auth_header}" \
push origin dev
env:
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}