تخطَّ إلى المحتوى

OpenSSF Scorecard Cheat Sheet

Overview

OpenSSF Scorecard is an automated tool from the Open Source Security Foundation that assesses the security posture of open-source projects hosted on GitHub. It runs a series of checks that evaluate security practices including code review enforcement, branch protection, dependency management, fuzzing, SAST usage, vulnerability disclosure processes, and CI/CD security. Each check produces a score from 0 to 10, and the aggregate score helps organizations make informed decisions about the security risk of their dependencies.

Scorecard is critical for software supply chain security, helping organizations identify which open-source dependencies follow security best practices and which may pose risks. It can be integrated into CI/CD pipelines to continuously monitor your own repositories against security standards, and the results can be published to the OpenSSF Scorecard API for public visibility. The tool supports GitHub Actions integration for automated weekly scanning and issue creation.

Installation

CLI Installation

# macOS
brew install scorecard

# Linux
curl -fsSL https://github.com/ossf/scorecard/releases/latest/download/scorecard-linux-amd64 \
  -o /usr/local/bin/scorecard
chmod +x /usr/local/bin/scorecard

# Go install
go install github.com/ossf/scorecard/v5/cmd/scorecard@latest

# Docker
docker pull gcr.io/openssf/scorecard:latest

# Verify installation
scorecard version

Authentication Setup

# Scorecard requires a GitHub token for API access
export GITHUB_AUTH_TOKEN="ghp_your_github_token"

# For higher rate limits, use a GitHub App
export GITHUB_APP_ID="app-id"
export GITHUB_APP_INSTALLATION_ID="installation-id"
export GITHUB_APP_PRIVATE_KEY_PATH="/path/to/private-key.pem"

# Verify authentication
scorecard --repo github.com/ossf/scorecard --checks Token-Permissions

Core Commands

Scanning Repositories

# Scan a repository (all checks)
scorecard --repo github.com/kubernetes/kubernetes

# Scan with specific output format
scorecard --repo github.com/kubernetes/kubernetes --format json > results.json
scorecard --repo github.com/kubernetes/kubernetes --format sarif > results.sarif
scorecard --repo github.com/kubernetes/kubernetes --format default

# Scan specific checks only
scorecard --repo github.com/kubernetes/kubernetes \
  --checks Code-Review,Branch-Protection,Signed-Releases

# Scan a local repository
scorecard --local .

# Scan with verbose output
scorecard --repo github.com/kubernetes/kubernetes --show-details

# Scan and output to file
scorecard --repo github.com/kubernetes/kubernetes \
  --format json \
  --output results.json

# Docker scan
docker run -e GITHUB_AUTH_TOKEN="$GITHUB_AUTH_TOKEN" \
  gcr.io/openssf/scorecard:latest \
  --repo github.com/kubernetes/kubernetes --format json

Available Checks

CheckDescriptionScore Range
Binary-ArtifactsNo binary artifacts in source0-10
Branch-ProtectionBranch protection rules enabled0-10
CI-TestsCI tests run on commits0-10
CII-Best-PracticesCII Best Practices badge0-10
Code-ReviewCode changes are reviewed0-10
ContributorsProject has multiple contributors0-10
Dangerous-WorkflowNo dangerous patterns in CI0-10
Dependency-Update-ToolUses dependency update tools0-10
FuzzingProject uses fuzzing0-10
LicenseHas an OSS license0-10
MaintainedProject is actively maintained0-10
PackagingPublished as a package0-10
Pinned-DependenciesDependencies pinned by hash0-10
SASTUses static analysis tools0-10
Security-PolicyHas SECURITY.md0-10
Signed-ReleasesReleases are signed0-10
Token-PermissionsCI tokens use least privilege0-10
VulnerabilitiesNo known vulnerabilities0-10
WebhooksWebhooks use secrets0-10
# Run specific checks
scorecard --repo github.com/org/repo --checks Branch-Protection
scorecard --repo github.com/org/repo --checks Code-Review,CI-Tests,SAST
scorecard --repo github.com/org/repo --checks Vulnerabilities,Dependency-Update-Tool

# List all available checks
scorecard --repo github.com/org/repo --checks "" 2>&1 | grep "check"

Querying the Public API

# Check score for popular projects via the API
curl -s "https://api.securityscorecards.dev/projects/github.com/kubernetes/kubernetes" | jq '{
  repo: .repo.name,
  score: .score,
  date: .date,
  checks: [.checks[] | {name, score, reason}]
}'

# Get specific check details
curl -s "https://api.securityscorecards.dev/projects/github.com/kubernetes/kubernetes" \
  | jq '.checks[] | select(.name == "Branch-Protection") | {name, score, reason, details}'

# List projects with scores above threshold
curl -s "https://api.securityscorecards.dev/projects/github.com/golang/go" \
  | jq '{score, checks: [.checks[] | select(.score >= 8) | {name, score}]}'

Configuration

GitHub Actions Integration

# .github/workflows/scorecard.yml
name: OpenSSF Scorecard
on:
  # Run on any branch protection rule change
  branch_protection_rule:
  # Run weekly
  schedule:
    - cron: '0 6 * * 1'
  push:
    branches: [main]

permissions: read-all

jobs:
  analysis:
    name: Scorecard analysis
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      id-token: write
      contents: read
      actions: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Run Scorecard
        uses: ossf/scorecard-action@v2
        with:
          results_file: results.sarif
          results_format: sarif
          publish_results: true
          # Optionally filter checks
          # checks: 'Code-Review,Branch-Protection,Signed-Releases'

      - name: Upload to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: scorecard-results
          path: results.sarif

SARIF Output for Security Tools

# Generate SARIF for GitHub Code Scanning
scorecard --repo github.com/org/repo \
  --format sarif \
  --output scorecard-results.sarif

# Upload to GitHub Code Scanning via API
curl -X POST \
  "https://api.github.com/repos/ORG/REPO/code-scanning/sarifs" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"commit_sha\": \"$(git rev-parse HEAD)\",
    \"ref\": \"refs/heads/main\",
    \"sarif\": \"$(gzip -c scorecard-results.sarif | base64 -w0)\"
  }"

Policy Enforcement

#!/bin/bash
# scorecard-gate.sh — CI quality gate based on Scorecard
set -euo pipefail

REPO="${1:-github.com/org/repo}"
MIN_SCORE="${2:-7}"

echo "Running Scorecard for $REPO (minimum score: $MIN_SCORE)..."

RESULTS=$(scorecard --repo "$REPO" --format json 2>/dev/null)
OVERALL_SCORE=$(echo "$RESULTS" | jq '.aggregate_score')

echo "Overall Score: $OVERALL_SCORE / 10"

# Check critical checks
CRITICAL_CHECKS=("Branch-Protection" "Code-Review" "Vulnerabilities" "Token-Permissions")
FAILED=false

for check in "${CRITICAL_CHECKS[@]}"; do
  SCORE=$(echo "$RESULTS" | jq -r ".checks[] | select(.name == \"$check\") | .score")
  STATUS="PASS"
  if [ "$SCORE" -lt "$MIN_SCORE" ]; then
    STATUS="FAIL"
    FAILED=true
  fi
  printf "  %-30s Score: %2s  [%s]\n" "$check" "$SCORE" "$STATUS"
done

echo ""
if [ "$FAILED" = true ]; then
  echo "FAILED: One or more critical checks below minimum score of $MIN_SCORE"
  exit 1
fi

if (( $(echo "$OVERALL_SCORE < $MIN_SCORE" | bc -l) )); then
  echo "FAILED: Overall score $OVERALL_SCORE below minimum $MIN_SCORE"
  exit 1
fi

echo "PASSED: All security checks meet minimum requirements"

Advanced Usage

Batch Scanning Multiple Repos

#!/bin/bash
# batch-scan.sh — Scan all repos in an organization
ORG="your-org"
OUTPUT_DIR="scorecard-results"
mkdir -p "$OUTPUT_DIR"

# Get all repos via GitHub API
REPOS=$(curl -s -H "Authorization: Bearer $GITHUB_AUTH_TOKEN" \
  "https://api.github.com/orgs/$ORG/repos?per_page=100&type=sources" \
  | jq -r '.[].full_name')

echo "Scanning $(echo "$REPOS" | wc -l) repositories..."

for repo in $REPOS; do
  echo "Scanning: $repo"
  scorecard --repo "github.com/$repo" --format json \
    > "$OUTPUT_DIR/$(echo "$repo" | tr '/' '-').json" 2>/dev/null || true
  sleep 2  # Rate limiting
done

# Generate summary report
echo "Repository,Score,Branch-Protection,Code-Review,Vulnerabilities" > "$OUTPUT_DIR/summary.csv"
for file in "$OUTPUT_DIR"/*.json; do
  REPO=$(jq -r '.repo.name' "$file")
  SCORE=$(jq -r '.aggregate_score' "$file")
  BP=$(jq -r '.checks[] | select(.name=="Branch-Protection") | .score' "$file")
  CR=$(jq -r '.checks[] | select(.name=="Code-Review") | .score' "$file")
  VU=$(jq -r '.checks[] | select(.name=="Vulnerabilities") | .score' "$file")
  echo "$REPO,$SCORE,$BP,$CR,$VU" >> "$OUTPUT_DIR/summary.csv"
done

echo "Results saved to $OUTPUT_DIR/summary.csv"

Dependency Scorecard Assessment

#!/bin/bash
# dep-scores.sh — Score all direct dependencies
# Requires: go (for Go projects) or package.json analysis

# For Go projects
go list -m -json all | jq -r 'select(.Main != true) | .Path' | while read dep; do
  # Convert Go module path to GitHub repo
  repo=$(echo "$dep" | sed 's|^golang.org/x/|github.com/golang/|;s|^google.golang.org/|github.com/googleapis/|')
  
  if [[ "$repo" == github.com/* ]]; then
    SCORE=$(scorecard --repo "$repo" --format json 2>/dev/null | jq '.aggregate_score // "N/A"')
    echo "$dep: $SCORE"
  fi
done

# For npm projects
jq -r '.dependencies // {} | keys[]' package.json | while read pkg; do
  # Look up repo from npm registry
  REPO=$(curl -s "https://registry.npmjs.org/$pkg" | jq -r '.repository.url // ""' | sed 's|.*github.com/||;s|\.git$||;s|^git\+https://||')
  
  if [ -n "$REPO" ]; then
    SCORE=$(scorecard --repo "github.com/$REPO" --format json 2>/dev/null | jq '.aggregate_score // "N/A"')
    echo "$pkg ($REPO): $SCORE"
  fi
done

Custom Scorecard with Probes

# Scorecard v5 introduced granular probes
# Run specific probes for more targeted checks

# Check if branch protection requires code review
scorecard --repo github.com/org/repo \
  --probes branchProtectionAppliesToAdmins,branchProtectionRequiresCodeOwnerReviews \
  --format json | jq '.checks'

# Check CI/CD security probes
scorecard --repo github.com/org/repo \
  --probes hasDangerousWorkflowScriptInjection,hasDangerousWorkflowUntrustedCheckout \
  --format json

Troubleshooting

IssueCauseSolution
Rate limited (403)GitHub API rate limit exceededUse a GitHub App for higher limits (5000/hr vs 60/hr)
Check returns -1Check not applicable or erroredReview check details for specific error message
Branch-Protection score 0No branch protection rulesEnable branch protection in GitHub repo settings
Vulnerabilities check failingKnown CVEs in dependenciesUpdate vulnerable dependencies
Token-Permissions low scoreOverly permissive CI tokensUse permissions: block in GitHub Actions
Pinned-Dependencies lowUsing tags instead of hashesPin actions/images to SHA hashes
SARIF upload failingMissing security-events: writeAdd permission to GitHub Actions workflow
Local scan incompleteMissing git historyClone with --depth=0 for full history
# Debug: run with verbose output
scorecard --repo github.com/org/repo --show-details --verbosity debug

# Debug: check specific failing check
scorecard --repo github.com/org/repo --checks Branch-Protection --show-details

# Verify GitHub token scope
curl -s -H "Authorization: Bearer $GITHUB_AUTH_TOKEN" \
  https://api.github.com/rate_limit | jq '.rate'

# Check if repo is in the public scorecard database
curl -s "https://api.securityscorecards.dev/projects/github.com/org/repo" | jq '.score'

# Validate SARIF output
cat results.sarif | jq '.runs[0].results | length'