From 22ad0e659493e4634aaaea94c2de0a9a75a1384f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 22 Jan 2026 17:26:45 -0500 Subject: [PATCH] openssf-scorecard: Use merge base as baseline instead of branch tip Previously the action used the base branch tip (github.event.pull_request.base.sha) as the baseline for regression detection. This caused false failures when the base branch improved after a PR was created - the PR would appear to regress even though it didn't change anything security-related. Now we compute the merge base between the base and head SHAs, which represents where the PR branched from the target branch. This ensures we're answering the right question: 'Did this PR introduce regressions?' rather than 'Is this PR as good as current main?' The merge base is stable and won't change when the base branch advances, so PRs will only fail if they actually introduce regressions, not because they're missing improvements that landed on main after the PR was created. Use head -n 1 to handle edge cases where git merge-base returns multiple SHAs (e.g., after criss-cross merges). Assisted-by: OpenCode (claude-sonnet-4-20250514) Signed-off-by: Colin Walters --- openssf-scorecard/action.yml | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/openssf-scorecard/action.yml b/openssf-scorecard/action.yml index 2d8f2a4..282b7fe 100644 --- a/openssf-scorecard/action.yml +++ b/openssf-scorecard/action.yml @@ -1,16 +1,21 @@ # OpenSSF Scorecard Gate # # This action checks each commit in a PR for OpenSSF Scorecard regressions. -# It fails if any commit's score drops below the baseline (base commit). +# It fails if any commit's score drops below the baseline (merge base commit). +# +# The baseline is computed as the merge base between base-sha and head-sha, +# which represents where the PR branched from the target branch. This ensures +# that improvements to the target branch after the PR was created don't cause +# false failures - we only check if the PR itself introduces regressions. # # Testing locally: # 1. Install scorecard: gh release download v5.1.1 --repo ossf/scorecard \ # --pattern 'scorecard_*_linux_amd64.tar.gz' && tar xzf scorecard_*.tar.gz # 2. Create a test commit with an ELF binary: cp /usr/bin/true testbinary && git add testbinary && git commit -m test # 3. Run the check manually: -# INPUT_BASE_SHA=$(git rev-parse HEAD~1) INPUT_HEAD_SHA=$(git rev-parse HEAD) bash -c ' -# baseline=$(scorecard --local=. --format=json | jq -r .score) -# git checkout HEAD~1; baseline_score=$(scorecard --local=. --format=json | jq -r .score) +# MERGE_BASE=$(git merge-base main HEAD) +# INPUT_HEAD_SHA=$(git rev-parse HEAD) bash -c ' +# git checkout $MERGE_BASE; baseline_score=$(scorecard --local=. --format=json | jq -r .score) # git checkout -; current_score=$(scorecard --local=. --format=json | jq -r .score) # echo "Baseline: $baseline_score, Current: $current_score" # [[ $(echo "$current_score < $baseline_score" | bc -l) -eq 1 ]] && echo "REGRESSION DETECTED" @@ -21,10 +26,10 @@ name: 'OpenSSF Scorecard Gate' description: 'Check for OpenSSF Scorecard regressions across commits' inputs: base-sha: - description: 'Base commit SHA to compare against' + description: 'Base branch tip SHA (used to compute merge base with head-sha)' required: true head-sha: - description: 'Head commit SHA' + description: 'Head commit SHA (PR tip)' required: true token: description: 'GitHub token for API access (e.g., downloading scorecard)' @@ -77,6 +82,13 @@ runs: validate_sha "$INPUT_BASE_SHA" "base-sha" validate_sha "$INPUT_HEAD_SHA" "head-sha" + # Compute merge base - the point where the PR branched from the base branch. + # This is the correct baseline because: + # 1. It doesn't change when the base branch advances + # 2. It answers "did this PR introduce regressions?" not "is this PR as good as current main?" + MERGE_BASE=$(git merge-base "$INPUT_BASE_SHA" "$INPUT_HEAD_SHA" | head -n 1) + echo "Merge base: ${MERGE_BASE:0:7} (between base ${INPUT_BASE_SHA:0:7} and head ${INPUT_HEAD_SHA:0:7})" + # Format checks as a markdown table row format_checks_table() { local json="$1" @@ -88,10 +100,10 @@ runs: scorecard --local=. --format=json | jq -r '.score // -1' } - baseline=$(get_score "$INPUT_BASE_SHA") - echo "Baseline score (${INPUT_BASE_SHA:0:7}): $baseline/10" + baseline=$(get_score "$MERGE_BASE") + echo "Baseline score (${MERGE_BASE:0:7}): $baseline/10" - mapfile -t commits < <(git rev-list --reverse "$INPUT_BASE_SHA".."$INPUT_HEAD_SHA") + mapfile -t commits < <(git rev-list --reverse "$MERGE_BASE".."$INPUT_HEAD_SHA") if [[ ${#commits[@]} -eq 0 ]]; then echo "No commits to check" exit 0 @@ -101,7 +113,7 @@ runs: { echo "## OpenSSF Scorecard Results" echo "" - echo "**Baseline (${INPUT_BASE_SHA:0:7}):** $baseline/10" + echo "**Baseline (merge base ${MERGE_BASE:0:7}):** $baseline/10" echo "" } >> "$GITHUB_STEP_SUMMARY"