* ci: use GitHub API to produce verified commits in socket-fix workflow Replace git commit/push with the GitHub Git Data API (blobs, trees, commits) so the resulting PR commit is signed and verified by GitHub, matching the approach used in update-dep.yml. * ci: disable socket fix CI mode to prevent auto-PR creation * ci: address code review feedback on open-pr step - Add set -eEo pipefail / shopt -s inherit_errexit - Use git status --porcelain to catch new and deleted files - Read file mode from git index instead of hardcoding 100644 - Use base64 -w 0 to avoid line-wrap corruption - Handle existing branch on workflow rerun - Handle existing PR on workflow rerun - Support comma-separated issue_link for multiple GHSAs - Include WDP/Leo PR links in body when refs are provided * ci: address second round of code review feedback - Fetch HEAD_SHA from master ref via API to guard against workflow dispatch on a non-master ref - Switch to git status --porcelain=v1 -z (NUL-delimited) to handle filenames with spaces and rename/copy entries correctly - Use @<(...) process substitution for blob content to avoid hitting shell argument length limits on large files like package-lock.json * ci: fix gh pr create missing --head and rename tree deletion - Add --head "$BRANCH" to gh pr create so the PR is opened from the API-created branch rather than the currently checked-out branch - Emit a null-sha tree entry for the source path of rename operations so the old path is removed from the resulting commit
180 lines
7.5 KiB
YAML
180 lines
7.5 KiB
YAML
name: Socket Fix by GHSA
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
ghsa_ids:
|
|
description: 'Comma-separated list of GHSA IDs (e.g. GHSA-442j-39wm-28r2,GHSA-7rx3-28cr-v5wh)'
|
|
required: true
|
|
type: string
|
|
issue_link:
|
|
description: 'Comma-separated links to related security issues (e.g. https://github.com/brave/brave-browser/issues/54048,https://github.com/brave/brave-browser/issues/54049)'
|
|
required: true
|
|
type: string
|
|
wdp_ref:
|
|
description: 'Commit hash for web-discovery-project DEPS update (optional)'
|
|
required: false
|
|
type: string
|
|
leo_ref:
|
|
description: 'Commit hash for @brave/leo package.json update (optional)'
|
|
required: false
|
|
type: string
|
|
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
|
|
jobs:
|
|
socket-fix:
|
|
runs-on: ubuntu-slim
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
with:
|
|
node-version: '24'
|
|
|
|
- name: Update WDP in DEPS
|
|
if: inputs.wdp_ref != ''
|
|
env:
|
|
WDP_REF: ${{ inputs.wdp_ref }}
|
|
run: |
|
|
[[ "$WDP_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error::Invalid wdp_ref: must be a 40-char hex SHA"; exit 1; }
|
|
sed -i -E "s|(\"vendor/web-discovery-project\"[^@]*@)[0-9a-f]{40}|\1${WDP_REF}|" DEPS
|
|
grep -q "$WDP_REF" DEPS || { echo "::error::Failed to update WDP hash in DEPS"; exit 1; }
|
|
|
|
- name: Update Leo in package.json
|
|
if: inputs.leo_ref != ''
|
|
env:
|
|
LEO_REF: ${{ inputs.leo_ref }}
|
|
run: |
|
|
[[ "$LEO_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error::Invalid leo_ref: must be a 40-char hex SHA"; exit 1; }
|
|
sed -i -E "s|(\"@brave/leo\"[^#]*#)[0-9a-f]{40}|\1${LEO_REF}|" package.json
|
|
grep -q "$LEO_REF" package.json || { echo "::error::Failed to update Leo hash in package.json"; exit 1; }
|
|
|
|
- name: Install Socket CLI
|
|
run: npm install -g @socketsecurity/cli
|
|
|
|
- name: Run Socket Fix
|
|
env:
|
|
GHSA_IDS: ${{ inputs.ghsa_ids }}
|
|
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_SECURITY_API_KEY }}
|
|
SOCKET_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
CI: ''
|
|
run: |
|
|
if ! [[ "$GHSA_IDS" =~ ^GHSA-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}(,[[:space:]]?GHSA-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4})*$ ]]; then
|
|
echo "::error::GHSA_IDS contains invalid entries. Expected format: GHSA-xxxx-xxxx-xxxx (comma or comma-space separated)"
|
|
exit 1
|
|
fi
|
|
IFS=',' read -ra IDS <<< "$GHSA_IDS"
|
|
for id in "${IDS[@]}"; do FLAGS+=(--id "${id// /}"); done
|
|
socket config set defaultOrg "brave"
|
|
socket fix . "${FLAGS[@]}"
|
|
|
|
- name: Open Pull Request
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GHSA_IDS: ${{ inputs.ghsa_ids }}
|
|
ISSUE_LINK: ${{ inputs.issue_link }}
|
|
WDP_REF: ${{ inputs.wdp_ref }}
|
|
LEO_REF: ${{ inputs.leo_ref }}
|
|
run: |
|
|
shopt -s inherit_errexit
|
|
set -eEo pipefail
|
|
|
|
if [ -z "$(git status --porcelain)" ]; then
|
|
echo "::error::No changes produced by socket fix."
|
|
exit 1
|
|
fi
|
|
|
|
BRANCH="socket-fix/$(echo "$GHSA_IDS" | tr ', ' '-' | tr -s '-' | cut -c1-60)"
|
|
HEAD_SHA=$(gh api "/repos/${GITHUB_REPOSITORY}/git/refs/heads/master" -q '.object.sha')
|
|
|
|
if gh api "repos/${GITHUB_REPOSITORY}/git/refs/heads/$BRANCH" -q .ref > /dev/null 2>&1; then
|
|
gh api --method PATCH "/repos/${GITHUB_REPOSITORY}/git/refs/heads/$BRANCH" \
|
|
--field sha="$HEAD_SHA" --field force=true
|
|
else
|
|
gh api --method POST /repos/${GITHUB_REPOSITORY}/git/refs \
|
|
--field ref="refs/heads/$BRANCH" --field sha="$HEAD_SHA"
|
|
fi
|
|
|
|
TREE_JSON="[]"
|
|
while IFS= read -r -d $'\0' line; do
|
|
XY="${line:0:2}"
|
|
FILE="${line:3}"
|
|
# For rename/copy entries the old path follows as the next NUL entry;
|
|
# emit a deletion for it so the source path is removed from the tree.
|
|
if [[ "${XY:0:1}" =~ [RC] ]]; then
|
|
IFS= read -r -d $'\0' _old_path
|
|
if [[ "${XY:0:1}" == "R" ]]; then
|
|
TREE_JSON=$(jq --arg path "$_old_path" \
|
|
'. += [{"path": $path, "mode": "100644", "type": "blob", "sha": null}]' \
|
|
<<< "$TREE_JSON")
|
|
fi
|
|
fi
|
|
if [[ "${XY:1:1}" == "D" ]]; then
|
|
TREE_JSON=$(jq --arg path "$FILE" \
|
|
'. += [{"path": $path, "mode": "100644", "type": "blob", "sha": null}]' \
|
|
<<< "$TREE_JSON")
|
|
else
|
|
MODE=$(git ls-files -s -- "$FILE" | awk 'NR==1 { print $1 }')
|
|
[[ -z "$MODE" ]] && MODE="100644"
|
|
if [[ "$MODE" == "120000" ]]; then
|
|
BLOB_SHA=$(gh api --method POST /repos/${GITHUB_REPOSITORY}/git/blobs \
|
|
--field content=@<(printf '%s' "$(readlink -- "$FILE")" | base64 -w 0) \
|
|
--field encoding=base64 \
|
|
-q .sha)
|
|
else
|
|
BLOB_SHA=$(gh api --method POST /repos/${GITHUB_REPOSITORY}/git/blobs \
|
|
--field content=@<(base64 -w 0 -- "$FILE") \
|
|
--field encoding=base64 \
|
|
-q .sha)
|
|
fi
|
|
TREE_JSON=$(jq --arg path "$FILE" --arg mode "$MODE" --arg sha "$BLOB_SHA" \
|
|
'. += [{"path": $path, "mode": $mode, "type": "blob", "sha": $sha}]' \
|
|
<<< "$TREE_JSON")
|
|
fi
|
|
done < <(git status --porcelain=v1 -z)
|
|
|
|
BASE_TREE=$(gh api /repos/${GITHUB_REPOSITORY}/git/commits/"$HEAD_SHA" -q .tree.sha)
|
|
|
|
NEW_TREE_SHA=$(jq -n \
|
|
--arg base_tree "$BASE_TREE" \
|
|
--argjson tree "$TREE_JSON" \
|
|
'{"base_tree": $base_tree, "tree": $tree}' | \
|
|
gh api --method POST /repos/${GITHUB_REPOSITORY}/git/trees --input - -q .sha)
|
|
|
|
NEW_COMMIT_SHA=$(jq -n \
|
|
--arg tree "$NEW_TREE_SHA" \
|
|
--arg parent "$HEAD_SHA" \
|
|
'{"message": "fix: address security advisories", "tree": $tree, "parents": [$parent]}' | \
|
|
gh api --method POST /repos/${GITHUB_REPOSITORY}/git/commits --input - -q .sha)
|
|
|
|
gh api --method PATCH /repos/${GITHUB_REPOSITORY}/git/refs/heads/"$BRANCH" \
|
|
--field sha="$NEW_COMMIT_SHA"
|
|
|
|
BODY="Addresses: $GHSA_IDS"
|
|
IFS=',' read -ra LINKS <<< "$ISSUE_LINK"
|
|
for link in "${LINKS[@]}"; do
|
|
BODY="$BODY"$'\n\n'"closes ${link// /}"
|
|
done
|
|
if [[ -n "$WDP_REF" ]]; then
|
|
WDP_PR=$(gh api "repos/brave/web-discovery-project/commits/$WDP_REF/pulls" -q '.[0].html_url' 2>/dev/null || true)
|
|
[[ -n "$WDP_PR" ]] && BODY="$BODY"$'\n\n'"WDP: $WDP_PR"
|
|
fi
|
|
if [[ -n "$LEO_REF" ]]; then
|
|
LEO_PR=$(gh api "repos/brave/leo/commits/$LEO_REF/pulls" -q '.[0].html_url' 2>/dev/null || true)
|
|
[[ -n "$LEO_PR" ]] && BODY="$BODY"$'\n\n'"Leo: $LEO_PR"
|
|
fi
|
|
if ! gh pr list --repo "${GITHUB_REPOSITORY}" --head "$BRANCH" --json number -q '.[].number' | grep -q .; then
|
|
gh pr create \
|
|
--repo "${GITHUB_REPOSITORY}" \
|
|
--title "fix: address security advisories" \
|
|
--body "$BODY" \
|
|
--head "$BRANCH" \
|
|
--base master \
|
|
--label "CI/skip" \
|
|
--label "security"
|
|
fi
|