Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/workflows/scorecard-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
persist-credentials: false

- name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ jobs:
fi
echo "SECURITY_CHECKOUT scanner=scorecard revision=head repository=${EXPECTED_CHECKOUT_REPOSITORY} expected_sha=${EXPECTED_CHECKOUT_SHA} actual_sha=${actual_sha}"
- name: Run Scorecard
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,9 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Aligned every central OpenSSF Scorecard Action use to the official v2.4.4
commit so pull-request, scheduled, and combined security scans execute one
immutable, reviewed release.
- Prefer the job-scoped `github.token` when the central OpenCode dispatch
publishes a commit status back to the same `.github` repository. The job's
declared `statuses: write` permission now reaches the endpoint instead of an
Expand Down
33 changes: 33 additions & 0 deletions docs/doctoring/scorecard-action-single-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Scorecard Action single-version boundary

## Incident boundary

The central pull-request, scheduled, and combined security workflows all use
OpenSSF Scorecard, but dependency automation updates workflow references
independently. A partial bump can leave posture evidence produced by different
action releases even though the jobs appear to provide one control.

## Decision

Pin every central `ossf/scorecard-action` use to
`2d1146689b8cda280b9bc96326124645441f03bc`, the commit referenced by the
official signed v2.4.4 tag. The current release updates Scorecard to v5.5.0 and
records POST failures without failing the entire action (Open Source Security
Foundation, 2026).

GitHub documents that a full commit SHA is unique and immutable and should be
verified against the action repository (GitHub, n.d.). A repository-wide
contract therefore parses every central workflow occurrence, rejects malformed
pins, and admits only the reviewed v2.4.4 SHA and tag. Workflow permissions,
events, arguments, SARIF semantics, thresholds, and fail-closed gates are
unchanged.

## References

GitHub. (n.d.). *Using pre-written building blocks in your workflow*.
Retrieved August 24, 2026, from
https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/find-and-customize-actions

Open Source Security Foundation. (2026, July 23). *Scorecard Action v2.4.4*
[Software release].
https://github.com/ossf/scorecard-action/releases/tag/v2.4.4
2 changes: 1 addition & 1 deletion tests/test_reusable_default_branch_scorecard_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def test_scorecard_analysis_keeps_authoritative_sarif_boundaries() -> None:

analysis_path = _step_path_by_name(workflow_contract, "Run analysis")
assert workflow_contract[analysis_path + ("uses",)] == (
"ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a"
"ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc"
)
assert _mapping_contract(
workflow_contract,
Expand Down
31 changes: 31 additions & 0 deletions tests/test_scorecard_action_pin_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Keep every central OpenSSF Scorecard Action use on one reviewed release."""

from __future__ import annotations

import re
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[1]
SCORECARD_SHA = "2d1146689b8cda280b9bc96326124645441f03bc"
SCORECARD_TAG = "v2.4.4"
_PIN = re.compile(
r"ossf/scorecard-action@(?P<sha>[^\s]+)\s+#\s+(?P<tag>v[^\s]+)"
)


def test_all_scorecard_actions_share_the_reviewed_current_release() -> None:
"""Reject partial bumps, malformed refs, and stale Scorecard releases."""
observed: set[tuple[str, str]] = set()

for path in sorted((REPO_ROOT / ".github/workflows").glob("*.y*ml")):
for line_number, line in enumerate(
path.read_text(encoding="utf-8").splitlines(), start=1
):
if "uses:" not in line or "ossf/scorecard-action@" not in line:
continue
match = _PIN.search(line)
assert match is not None, f"malformed Scorecard pin: {path}:{line_number}"
observed.add((match.group("sha"), match.group("tag")))

assert observed == {(SCORECARD_SHA, SCORECARD_TAG)}
Comment thread
seonghobae marked this conversation as resolved.
Loading