Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 77 additions & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: OSSF Scorecard

# Automated security-posture scoring via https://github.com/ossf/scorecard.
# Checks ~20 best practices (branch protection, pinned actions, signed
# commits, dangerous-workflow patterns, vulnerability response time, …)
# and produces a 0-10 score. Complementary to CodeQL / DevSkim, which
# scan the CODE; Scorecard scans the REPO CONFIGURATION.
#
# Results are:
# - Uploaded as SARIF to GitHub Code Scanning (Security tab)
# - Sent to the OpenSSF public scorecard database (only from `main` runs)
# — this powers the README badge and enables consumers to check the
# score at https://api.securityscorecards.dev/projects/github.com/Chris-Wolfgang/Try-Pattern.
#
# Score-floor policy: any PR that drops the score below **7.5** must
# explain the regression in the PR body and — after merge — add a line
# to CHANGELOG.md's [Unreleased] Security section documenting the
# accepted lower score. This is a soft gate (reviewer attention), not a
# hard CI-blocking check, because Scorecard's rules are strict enough
# that a single stale dependency can drop the score below floor while
# a fix is in flight.

on:
# Weekly scan on Monday 06:00 UTC — captures dependency drift + any
# externally-visible config changes over the past week.
schedule:
- cron: '0 6 * * 1'
# Also run on push to main so a change that affects the score shows up
# immediately, not up to a week later.
push:
branches: [main]
# Allow manual re-runs from the Actions tab for on-demand audits.
workflow_dispatch:

permissions: read-all

jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Required to upload SARIF to Code Scanning.
security-events: write
# Required to publish results to the OpenSSF public database via
# keyless signing (OIDC) — this is what makes the score appear
# in the badge + consumer-checkable API.
id-token: write
contents: read
actions: read

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

- name: Run OSSF Scorecard analysis
uses: ossf/scorecard-action@v2.4.2
with:
results_file: results.sarif
results_format: sarif
# Publish to the public OpenSSF database ONLY on main branch
# runs (schedule + push to main). PR runs stay local — we
# don't want in-flight PR scores polluting the badge.
publish_results: ${{ github.event_name != 'pull_request' }}

- name: Upload artifact (JSON copy)
uses: actions/upload-artifact@v7
with:
name: scorecard-results
path: results.sarif
retention-days: 30

- name: Upload results to Code Scanning
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: results.sarif
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[![NuGet downloads](https://img.shields.io/nuget/dt/Wolfgang.TryPattern.svg?logo=nuget&label=downloads)](https://www.nuget.org/packages/Wolfgang.TryPattern)
[![PR build](https://img.shields.io/github/actions/workflow/status/Chris-Wolfgang/Try-Pattern/pr.yaml?branch=main&label=PR%20build&logo=github)](https://github.com/Chris-Wolfgang/Try-Pattern/actions/workflows/pr.yaml)
[![Release](https://img.shields.io/github/actions/workflow/status/Chris-Wolfgang/Try-Pattern/release.yaml?label=release&logo=github)](https://github.com/Chris-Wolfgang/Try-Pattern/actions/workflows/release.yaml)
[![OSSF Scorecard](https://api.scorecard.dev/projects/github.com/Chris-Wolfgang/Try-Pattern/badge)](https://scorecard.dev/viewer/?uri=github.com/Chris-Wolfgang/Try-Pattern)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![.NET](https://img.shields.io/badge/.NET-Multi--Targeted-purple.svg)](https://dotnet.microsoft.com/)
[![GitHub](https://img.shields.io/badge/GitHub-Repository-181717?logo=github)](https://github.com/Chris-Wolfgang/Try-Pattern)
Expand Down
77 changes: 77 additions & 0 deletions docs/SECURITY-SCORECARD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# OSSF Scorecard baseline + policy

[OSSF Scorecard](https://github.com/ossf/scorecard) scores the
`Wolfgang.TryPattern` repo's security posture — branch protection,
signed commits, pinned dependencies, dangerous-workflow patterns, etc.
— on a 0-10 scale. This document captures the baseline score and the
policy for handling regressions.

Workflow: [`.github/workflows/scorecard.yml`](../.github/workflows/scorecard.yml)
Results: [Security → Code scanning](https://github.com/Chris-Wolfgang/Try-Pattern/security/code-scanning)
Public score: <https://scorecard.dev/viewer/?uri=github.com/Chris-Wolfgang/Try-Pattern>

## Score floor

**Target: score ≥ 7.5** on `main`.

Any PR whose merged score drops the repo below 7.5 must:

1. Explain the regression in the PR body — what specifically dropped
and why.
2. After merge, add a line to `CHANGELOG.md`'s `[Unreleased] → Security`
section documenting the accepted lower score, e.g.:

```markdown
- **OSSF Scorecard**: score dropped from 8.2 → 7.6 because the
`github/codeql-action` update introduced a new
`Dangerous-Workflow` finding (accepted; codeql-action is a
first-party GitHub action and the check is a false positive on
the workflow_run trigger).
```

This is a soft gate (reviewer attention), not a hard CI-blocking
check. Scorecard's rules are strict enough that a single stale
dependency or transient false positive can drop the score below floor
while a fix is in flight; hard-gating would produce persistent noise.
If the score drops below **6.0**, treat as an incident: file an
`incident` issue and block further release-prep PRs until back above
floor.

## Baseline (populate after first successful run)

The workflow's first push-to-main run establishes the baseline. Fill
in this table after that run completes.

| Metric | Value |
|---|---|
| First-scan date | *(populate)* |
| First-scan score | *(populate)* /10 |
| Failed checks | *(populate — list each with the score contribution)* |
| Passing checks | *(populate — count only, or list the notable ones)* |

## Known-acceptable findings

Populated over time. Every entry names the check, the finding, and
why we accept it rather than fix it. Review annually.

| Check | Finding | Rationale | Reviewed |
|---|---|---|---|
| *(populate after first run)* | | | |

Example row (for reference — delete once real entries exist):

| Check | Finding | Rationale | Reviewed |
|---|---|---|---|
| `Signed-Releases` | Release artifacts are not signed via cosign / Sigstore | The nupkg + snupkg are signed by nuget.org's own signing pipeline on ingestion, which satisfies consumer verification without an additional sigstore step. Revisit if nuget.org drops that guarantee. | 2026-07-14 |

## Relationship to other security work

- **CodeQL** (`codeql.yaml`) scans the *code* for vulnerabilities.
- **DevSkim** (`Security Scan (DevSkim)` in `pr.yaml`) scans source
patterns for dangerous API usage.
- **Gitleaks** (`Secrets Scan (gitleaks)` in `pr.yaml`) scans for
committed secrets.
- **OSSF Scorecard** (this workflow) scans the *repo configuration*
and *release-engineering practices* — no code inspection.

All four are complementary and none subsumes the others.
Loading