In short

Pin the version first: OSPS Baseline v2026.08.28 is current, with 64 active requirements in eight families, each first required at Level 1 (any project), Level 2 (a code project with at least two maintainers and a small number of consistent users) or Level 3 (a code project with a large number of consistent users). Level 1 on GitHub is mostly settings and files: MFA for everyone with access, a ruleset that blocks direct commits and deletion of the default branch, workflows that don't trust pull-request input, push protection for secrets, an OSI or FSF license, CONTRIBUTING.md, SECURITY.md with contacts, a committed dependency manifest and no binaries in Git. Keep evidence per requirement, publish a Security Insights file, and state the claim with its date, version and level. No tool checks all of it for you.

  • Requirement IDs look like OSPS-AC-03.01: family AC, control 03, requirement 01. Version v2026.08.28 has 41 controls and 64 active requirements: 24 first required at Level 1, 19 at Level 2 and 21 at Level 3.
  • The Baseline is self-attested and point in time. Its FAQ suggests a claim such as: as of a date, this project complies with OSPS Baseline version X, level N.
  • Rulesets are readable by anyone with read access, so ruleset-based controls make public evidence; classic branch protection and secret-scanning settings are visible only to admins.
  • Level 2 adds least-privilege CI tokens, required status checks and tests, signed releases with change logs, a disclosure policy with private reporting, and a security assessment.
  • Level 3 adds non-author approval of every change, blocking SCA and SAST checks, SBOMs with releases, verification instructions that name the signer, a VEX feed and a support policy.
  • Scorecard's own analysis finds its probes fully satisfy only a few Baseline controls; Minder ships a Level 1 profile; the Privateer GitHub scanner, used by LFX Insights, covers every requirement but marks some as needing review.

Verify it

  1. Run the evidence script against the repository and confirm every Level 1 row it reports is pass, with needs-admin rows re-run by an admin.
  2. Push a commit straight to the default branch and try to delete it in a test copy of the repository; both must be refused.
  3. Validate security-insights.yml against the Security Insights 2.2.0 schema and check that every URL in it resolves.
  4. Open a pull request that edits a workflow to echo ${{ github.event.pull_request.title }} in a run: step and confirm review or Scorecard's Dangerous-Workflow check catches it.
  5. Read the published claim and confirm it names the Baseline version, the level and the date it was assessed.
Applies to
  • OpenSSF OSPS Baseline v2026.08.28 (current since 28 August 2026)
  • GitHub.com Rulesets, Actions, secret scanning and private vulnerability reporting as documented in October 2026

Open-source projects hosted on GitHub that want to meet, or claim, an OSPS Baseline level. The settings and API calls are GitHub's; the requirements apply on any forge.

Part of
Workflow: Build an AppSec program
Learning goals and prerequisitesAbout 45 minutes: 20 to read and 25 for the exercise

What you will learn

  • Name the eight OSPS Baseline families and what each maturity level asks of a project.
  • Meet every Level 1 requirement on a GitHub repository and record the evidence for it.
  • Plan the Level 2 and Level 3 controls that change how the project reviews, releases and handles vulnerabilities.
  • Read what Scorecard, Minder and the Privateer scanner do and do not check.

Before you start

Maintainers of open-source projects on GitHub, and the engineers in companies that publish them

  • Admin access to a GitHub repository, or a test repository you can change.
  • Know how branch rulesets and GitHub Actions permissions work.

Refresh first: Protect the source repository: rulesets, reviews and signed commits

Go to the guided practice

Which version should you claim, and what do the levels mean?

The Open Source Project Security Baseline (OSPS Baseline) is a list of MUST statements for open-source projects, maintained by the OpenSSF. Versions are dated: v2025.02.25, v2025.10.10, v2026.02.19 and the current v2026.08.28, released on 28 August 2026. The project says previous versions are for historical reference and only the current one should be used for new compliance efforts, so pin the version in every claim. The v2026.08.28 release added no controls; it let OSPS-LE-03.01 accept a LICENSES/ directory and moved the mappings to other frameworks into machine-readable Gemara documents. The previous release retired OSPS-BR-01.02 and added OSPS-BR-01.03, OSPS-DO-07.01 and OSPS-BR-01.04.

Each requirement belongs to a control and a family: OSPS-AC-03.01 is the first requirement of control 03, Protect the Primary Branch from Accidental Modification, in the Access Control family. Each requirement lists the maturity levels it applies to. Level 1 is for any code or non-code project with any number of maintainers or users; Level 2 is for a code project with at least two maintainers and a small number of consistent users; Level 3 is for a code project with a large number of consistent users. The published checklist files each requirement under the first level it applies to: 24 at Level 1, 19 at Level 2 and 21 at Level 3.

The Baseline is not a score and not a comparison between projects; its FAQ says so directly. Nothing requires you to meet it unless a sponsoring organization such as a foundation does, but every project is encouraged to meet Level 1. Compliance is self-attested and point in time, so write the claim the way the FAQ suggests: as of 11 October 2026, this project complies with OSPS Baseline version 2026.08.28, Level 1. Some controls depend on admin-only settings that a consumer can't see, which is why the evidence you keep matters more than a badge.

What do the eight control families cover?

The table counts the active requirements in each family by the level at which they first apply. Most Level 1 requirements also list Levels 2 and 3; if you script against the catalog YAML, note that in v2026.08.28 OSPS-BR-07.01 and OSPS-VM-02.01 list only Level 1, although the published checklist and the requirements' purpose apply them at every level.

What do the eight control families cover?
FamilyWhat it coversLevel 1Level 2Level 3
AC: Access ControlMFA, collaborator permissions, primary branch protection, CI/CD token privileges411
BR: Build and ReleaseUntrusted input in pipelines, version identifiers, encrypted channels, change logs, dependency tooling, signed releases, secrets543
DO: DocumentationUser guides, defect reporting, verification instructions, support and security-update scope, dependency policy, build instructions224
GV: GovernanceRoles and members with sensitive access, public discussion, contribution guide, review of permission grants231
LE: LegalRight to commit (DCO or CLA), OSI or FSF licenses, license location410
QA: QualityPublic source and history, dependency list and SBOM, status checks, subprojects, no binaries, automated tests, merge approvals625
SA: Security AssessmentDesign documentation, external interfaces, security assessment and threat model031
VM: Vulnerability ManagementDisclosure policy, security contacts, private reporting, published vulnerabilities and VEX, SCA and SAST policies136

How do you meet each Level 1 requirement on GitHub, and what proves it?

Level 1 is the floor the Baseline asks every project to reach. On GitHub most of it is a setting or a file. The evidence column says what to keep: an API export, a file at a path, or a scan result, dated. /domains/supply-chain/ci-cd/source-repository-protection/ has the ruleset JSON and the commands to apply it, and /domains/supply-chain/ci-cd/github-actions-hardening/ covers the workflow patterns behind OSPS-BR-01.01 and OSPS-BR-01.03.

How do you meet each Level 1 requirement on GitHub, and what proves it?
RequirementWhat it asksHow to meet it on GitHubEvidence to keep
OSPS-AC-01.01MFA to read or change sensitive resources in the authoritative repositoryOrganization settings, Authentication security: require two-factor authentication; prefer passkeys or security keys. For a personal-account repository, every maintainer enables 2FA.two_factor_requirement_enabled: true from GET /orgs/{org}, read with an owner token
OSPS-AC-02.01New collaborators get manual or lowest-privilege permissionsSet the organization's base permission to Read or No permission and add collaborators with the lowest role that works.default_repository_permission from GET /orgs/{org}; the collaborator list with roles
OSPS-AC-03.01Direct commits to the primary branch are preventedA branch ruleset on the default branch with Require a pull request before merging, active, with an empty bypass list.Rules from GET /repos/{owner}/{repo}/rules/branches/{branch}, which anyone with read access can call
OSPS-AC-03.02Deleting the primary branch needs explicit confirmationThe same ruleset with Restrict deletions; add Block force pushes so history can't be rewritten.The same export, showing deletion and non_fast_forward
OSPS-BR-01.01Untrusted metadata is sanitized before a pipeline uses itNever expand ${{ github.event.* }} text such as titles, branch names or commit messages inside run:; pass it through env: and quote it.The workflow files and Scorecard's Dangerous-Workflow result
OSPS-BR-01.03Untrusted code snapshots can't reach privileged credentialsBuild pull-request code in pull_request jobs with read-only tokens; never check out the pull request head in pull_request_target or workflow_run jobs that hold secrets.Workflow review record and Dangerous-Workflow result
OSPS-BR-03.01Official project channels use encrypted transport onlyList only https:// URLs for the website, documentation and chat; on GitHub Pages turn on Enforce HTTPS.The list of official channels, such as Security Insights project.homepage and documentation links
OSPS-BR-03.02Distribution channels resist adversary-in-the-middle attacksPublish through GitHub Releases or the ecosystem's registry over HTTPS, and sign releases when you can.Security Insights repository.release.distribution-points
OSPS-BR-07.01Unencrypted secrets can't be stored by accident in version controlTurn on secret scanning and push protection (free on public repositories), and ignore files such as .env in .gitignore.security_and_analysis from GET /repos/{owner}/{repo}, readable only by admins; the push protection bypass log
OSPS-DO-01.01User guides for all basic functionality, once there is a releaseA README or documentation site covering install, configuration and use, with warnings on destructive actions.The documentation link at the release tag
OSPS-DO-02.01A guide for reporting defectsEnable Issues and add a bug report template, or a Reporting bugs section in CONTRIBUTING.md.The template path and has_issues
OSPS-GV-02.01A public place to discuss changes and usage problemsIssues, Discussions or a public mailing list.has_issues and has_discussions from the repository API
OSPS-GV-03.01An explanation of the contribution processCONTRIBUTING.md at the root, in .github/ or in docs/, or a clear statement that contributions aren't accepted.files.contributing from GET /repos/{owner}/{repo}/community/profile
OSPS-LE-02.01The source license meets the OSI Open Source Definition or the FSF Free Software DefinitionChoose an OSI-approved or FSF-free license.The SPDX ID from GET /repos/{owner}/{repo}/license, checked against the SPDX License List's isOsiApproved and isFsfLibre
OSPS-LE-02.02The license for released assets meets the same definitionsRelease under the same license, or state the release license if it differs.The license inside the release archive; Security Insights repository.release.license when it differs
OSPS-LE-03.01The source license is in a LICENSE or COPYING file or a LICENSES/ or LICENSE/ directoryKeep the license file at the repository root; an extension such as .md is allowed.The path from the license API
OSPS-LE-03.02The license ships with the released assetsInclude the license in source archives, or publish it next to each release asset.A listing of the release archive showing the license file
OSPS-QA-01.01The source repository is publicly readable at a static URLA public repository; if you mirror it, say which copy is primary.visibility: public
OSPS-QA-01.02A public record of every change, who made it and whenKeep history public and don't rewrite it; the force-push block above enforces that.The commit history and the non_fast_forward rule
OSPS-QA-02.01A list of direct dependencies where the package manager supports oneCommit the manifest, such as package.json, go.mod, pyproject.toml or Cargo.toml.The manifest path in the dependency graph
OSPS-QA-04.01Projects with several repositories list their codebasesList every repository that is part of the project.Security Insights project.repositories
OSPS-QA-05.01No generated executable artifacts in version controlBuild binaries in CI instead of committing them.Scorecard's Binary-Artifacts result
OSPS-QA-05.02No unreviewable binary artifacts in version controlKeep libraries and executables out of Git; images and audio are allowed.Scorecard's Binary-Artifacts result
OSPS-VM-02.01Security contacts in the project documentationSECURITY.md at the root, in .github/ or in docs/, naming how to reach the security contacts.The file path and Security Insights project.vulnerability-reporting.contact

How do you publish the facts a scanner can't see?

Several requirements depend on things an API can't infer: which channels are official, which repositories belong to the project, where the support policy and the self-assessment are. The Baseline's maintainer guidance points to the OpenSSF Security Insights specification for this, and the Privateer scanner reads the file for many controls. Put security-insights.yml at the repository root or in .github/. The example below uses schema 2.2.0 (January 2026) and records the evidence for OSPS-BR-03.02, OSPS-QA-04.01, OSPS-VM-02.01, OSPS-DO-05.01 and the self-assessment behind OSPS-SA-03.01.

Validate it before you publish it. With CUE 0.17.1 and the specification's schema.cue from the v2.2.0 tag, cue vet -c -d '#SecurityInsights' schema.cue security-insights.yml exits 0 for this file and fails with the field name when a required value such as reports-accepted is missing or status is not one of the allowed values. Update last-reviewed whenever you review it, because a stale file is evidence of nothing.

Configurationsecurity-insights.ymlYAML
header:
  schema-version: 2.2.0
  last-updated: '2026-10-11'
  last-reviewed: '2026-10-11'
  url: https://github.com/example-org/tidepool/raw/main/security-insights.yml
project:
  name: tidepool
  administrators:
    - name: Ana Ruiz
      affiliation: Example Org
      email: ana@example.org
      primary: true
  repositories:
    - name: tidepool
      url: https://github.com/example-org/tidepool
      comment: The library and its command-line tool; the only codebase in this project.
  vulnerability-reporting:
    reports-accepted: true
    bug-bounty-available: false
    policy: https://github.com/example-org/tidepool/blob/main/SECURITY.md
    contact:
      name: tidepool security team
      email: security@example.org
      primary: true
  documentation:
    quickstart-guide: https://github.com/example-org/tidepool/blob/main/README.md
    support-policy: https://github.com/example-org/tidepool/blob/main/SUPPORT.md
    signature-verification: https://github.com/example-org/tidepool/blob/main/docs/verify.md
repository:
  url: https://github.com/example-org/tidepool
  status: active
  accepts-change-request: true
  accepts-automated-change-request: true
  core-team:
    - name: Ana Ruiz
      primary: true
    - name: Ben Okafor
      primary: false
  license:
    url: https://github.com/example-org/tidepool/blob/main/LICENSE
    expression: Apache-2.0
  documentation:
    contributing-guide: https://github.com/example-org/tidepool/blob/main/CONTRIBUTING.md
    dependency-management-policy: https://github.com/example-org/tidepool/blob/main/docs/dependencies.md
    governance: https://github.com/example-org/tidepool/blob/main/GOVERNANCE.md
    security-policy: https://github.com/example-org/tidepool/blob/main/SECURITY.md
  release:
    automated-pipeline: true
    distribution-points:
      - uri: https://github.com/example-org/tidepool/releases
        comment: Signed release archives with SLSA provenance and an SBOM.
    changelog: https://github.com/example-org/tidepool/releases/tag/v{version}
  security:
    assessments:
      self:
        comment: Threat model and security self-assessment, reviewed at each minor release.
        evidence: https://github.com/example-org/tidepool/blob/main/docs/security-assessment.md
        date: '2026-09-30'
    tools:
      - name: osv-scanner
        type: SCA
        rulesets: [default]
        integration: {adhoc: false, ci: true, release: true}
        results: {}

Which Level 2 controls matter most?

Level 2 assumes at least two maintainers and regular users, so it adds the controls that keep a release trustworthy when more than one person can ship. The table covers the ones that change settings or pipelines. The rest are documents: OSPS-DO-06.01 (how you select, obtain and track dependencies), OSPS-DO-07.01 (build instructions), OSPS-GV-01.01 and OSPS-GV-01.02 (members with access to sensitive resources, and roles), OSPS-GV-03.02 (requirements for acceptable contributions), OSPS-SA-01.01 and OSPS-SA-02.01 (design documentation of actors and actions, and external interfaces), OSPS-BR-02.01 (a unique version for each release) and OSPS-BR-05.01 (standard dependency tooling).

Which Level 2 controls matter most?
RequirementHow to meet it on GitHubEvidence to keep
OSPS-AC-04.01: tasks default to the lowest permissionsSettings, Actions, General, Workflow permissions: Read repository contents and packages permissions, set at the organization when you have one.default_workflow_permissions: read from GET /repos/{owner}/{repo}/actions/permissions/workflow
OSPS-QA-03.01: status checks pass before a commit reaches the primary branchAdd Require status checks to pass to the default-branch ruleset, with the check pinned to the GitHub Actions app.The required_status_checks rule in the ruleset export
OSPS-QA-06.01: CI runs at least one automated test suite before a change is acceptedA pull_request workflow that runs the tests, required by the ruleset.The workflow and a sample of merged pull requests with passing runs
OSPS-BR-06.01: releases are signed or listed in a signed manifest with hashesAttest release artifacts with actions/attest, which signs SLSA provenance with a short-lived Sigstore certificate, or sign a checksums file.The attestation for each release, verifiable with gh attestation verify
OSPS-BR-04.01: each release has a log of functional and security changesRelease notes that say what changed and why it matters, not only commit titles.The release notes at each tag
OSPS-LE-01.01: contributors assert the right to contribute on every commitRequire a Signed-off-by: trailer (the DCO) and enforce it with a status check such as the DCO GitHub App, or use a CLA. GitHub's sign-off policy covers only commits made in the web interface.The DCO check on merged pull requests
OSPS-VM-01.01: a coordinated disclosure policy with a response timeframeSECURITY.md that says how to report, what happens next and how quickly you respond.SECURITY.md at the release tag
OSPS-VM-03.01: private reporting directly to the security contactsTurn on private vulnerability reporting, or publish a monitored security address.enabled: true from GET /repos/{owner}/{repo}/private-vulnerability-reporting
OSPS-VM-04.01: discovered vulnerabilities are publishedPublish GitHub security advisories, and request CVE IDs for them, with affected and fixed versions.The advisory list
OSPS-SA-03.01: a security assessment of the most likely and impactful problemsA short self-assessment kept in the repository and reviewed for new features, such as a threat model of the parser or the network surface.The assessment with its date, linked from Security Insights repository.security.assessments.self

What do least-privilege pipelines and signed releases look like?

These workflows meet OSPS-AC-04.02 (every job asks for only the permissions it needs, under an empty top-level default), OSPS-QA-06.01, OSPS-BR-06.01 and OSPS-QA-02.02. The pull-request workflow runs the tests and the dependency review action, which fails on a newly added dependency with a high or critical advisory or a license outside the allowlist; it runs on public repositories, and on private ones with GitHub Code Security. The release workflow attests the archives twice with actions/attest: once for SLSA build provenance and once for the CycloneDX SBOM. Actions are pinned to full commit SHAs with the version in a comment. Both files pass actionlint 1.7.12.

Require the test and dependency-review checks in the default-branch ruleset, and add the ruleset rule Require code scanning results with CodeQL so a new high-severity finding blocks the merge. That pair is the automated enforcement OSPS-VM-05.03 and OSPS-VM-06.02 ask for at Level 3, alongside the written thresholds in OSPS-VM-05.01, OSPS-VM-05.02 and OSPS-VM-06.01.

Configuration.github/workflows/pull-request.yml and release.ymlYAML
name: pull-request
on:
  pull_request:
    branches: [main]
permissions: {}
jobs:
  test:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - run: make test
  dependency-review:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    steps:
      - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
        with:
          fail-on-severity: high
          allow-licenses: Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, MIT
---
name: release
on:
  push:
    tags: ['v*']
permissions: {}
jobs:
  release:
    runs-on: ubuntu-24.04
    permissions:
      contents: write
      id-token: write
      attestations: write
      artifact-metadata: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - run: make dist
      - uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
        with:
          subject-path: dist/*.tar.gz
      - uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
        with:
          subject-path: dist/*.tar.gz
          sbom-path: dist/sbom.cdx.json
      - run: gh release create "$GITHUB_REF_NAME" dist/* --verify-tag --notes-file dist/release-notes.md
        env:
          GH_TOKEN: ${{ github.token }}

Which Level 3 controls change how the project works?

Level 3 is for projects with a large number of consistent users, where a compromise reaches many people. Its requirements mostly turn Level 2 practices into enforced rules and published commitments.

  • OSPS-QA-07.01: at least one non-author human approval before merging to the primary branch. Require one approval in the ruleset, with Require approval of the most recent reviewable push, so the approved revision is the merged one; a single-maintainer project can't meet this.
  • OSPS-VM-05.03 and OSPS-VM-06.02: every change is evaluated against a documented policy for vulnerable and malicious dependencies and for code weaknesses, and blocked on violation unless declared non-exploitable. The required checks above do this; the policy documents are OSPS-VM-05.01, OSPS-VM-05.02 and OSPS-VM-06.01.
  • OSPS-QA-02.02: every compiled release asset ships with an SBOM. /domains/supply-chain/sbom/generate-release-inventory/ covers generating one per release.
  • OSPS-DO-03.01 and OSPS-DO-03.02: documentation explains how to verify release integrity and authenticity, and the expected identity of the signer. Name the repository and the signing workflow, not only the tool, and keep the instructions outside the release pipeline so one breach can't change both.
  • OSPS-VM-04.02: vulnerabilities in components that don't affect the project are recorded in a VEX document. /domains/supply-chain/vex/producer-and-consumer-decisions/ covers writing the statements.
  • OSPS-DO-04.01 and OSPS-DO-05.01: a statement of the scope and duration of support for each release, and when releases stop getting security updates, in SUPPORT.md or SECURITY.md.
  • OSPS-SA-03.02: threat modeling and attack surface analysis of critical code paths, updated for new features.
  • OSPS-GV-04.01, OSPS-BR-07.02 and OSPS-BR-01.04: a policy to review collaborators before granting escalated permissions, a policy for storing, accessing and rotating secrets, and sanitizing even trusted collaborators' manual workflow inputs such as workflow_dispatch values.
  • OSPS-AC-04.02, OSPS-BR-02.02, OSPS-QA-04.02, OSPS-QA-06.02 and OSPS-QA-06.03: job-level least privilege, every release asset tied to the release identifier, subprojects held to the same rules, and documented testing practice and policy.

How do you write verification instructions that name the signer?

For OSPS-DO-03.02, the instructions must tell users who is expected to have produced the release. With GitHub artifact attestations that identity is the repository and the workflow file that signed the provenance. The commands below are the shape to publish; the same commands verify the GitHub CLI's own release: with gh 2.94.0, gh attestation verify gh_2.94.0_linux_arm64.tar.gz --repo cli/cli --signer-workflow cli/cli/.github/workflows/deployment.yml exits 0, and naming any other workflow file makes it fail. Add --source-ref when you know which branch or tag the release workflow runs from.

docs/verify.mdShell
# Verify a tidepool release (example-org/tidepool) before you use it.
# 1. The archive is the one the release workflow built and attested:
gh attestation verify tidepool-1.4.0.tar.gz \
  --repo example-org/tidepool \
  --signer-workflow example-org/tidepool/.github/workflows/release.yml \
  --deny-self-hosted-runners
# 2. The SBOM attestation for the same archive:
gh attestation verify tidepool-1.4.0.tar.gz \
  --repo example-org/tidepool \
  --predicate-type https://cyclonedx.org/bom

What evidence should you keep, and how do you collect it?

Keep one dated record per requirement: its ID, its status, the evidence, and the date. The script below collects what GitHub's API shows for the Level 1 and Level 2 settings it can read, as JSON lines, and marks admin-only settings needs-admin instead of guessing. Run it with an authenticated gh and jq. On 11 October 2026 it reported pass for all ten readable rows on ossf/scorecard, whose default branch has an active ruleset with pull_request, deletion, non_fast_forward and required_status_checks rules, and needs-admin for its workflow permissions and push protection. On ossf/security-baseline, whose branch uses classic protection, the three branch rows were needs-admin; on octocat/Hello-World they failed, as did the license, CONTRIBUTING and SECURITY.md rows.

Keep the rest by hand: the organization MFA and base-permission exports, the workflow review behind OSPS-BR-01.01, links to documentation at the release tag, and the scanner output. Store the records with each release, and repeat the collection on a schedule so a quietly weakened setting shows up.

osps-evidence.shShell
#!/usr/bin/env bash
# Record the OSPS Baseline v2026.08.28 evidence GitHub's API shows for one repository.
# Usage: ./osps-evidence.sh owner/repo > evidence.jsonl   (needs an authenticated gh and jq)
# "needs-admin" means the setting is only visible to a repository or organization admin.
set -euo pipefail
repo="$1"
emit() { jq -cn --arg id "$1" --arg status "$2" --arg evidence "$3" \
  '{baseline: "v2026.08.28", id: $id, status: $status, evidence: $evidence, checked: (now | todate)}'; }

meta=$(gh api "repos/$repo")
branch=$(jq -r .default_branch <<<"$meta")
rules=$(gh api "repos/$repo/rules/branches/$branch" --jq '[.[].type] | unique')
has_rule() { jq -e --arg t "$1" 'index($t) != null' <<<"$rules" >/dev/null && echo pass || echo fail; }
if [ "$rules" = "[]" ] && [ "$(gh api "repos/$repo/branches/$branch" --jq .protected)" = "true" ]; then
  # Classic branch protection is only readable with admin rights; rulesets are public.
  for id in OSPS-AC-03.01 OSPS-AC-03.02 OSPS-QA-03.01; do
    emit "$id" needs-admin "$branch is protected by a classic rule; export it with an admin token"
  done
else
  emit OSPS-AC-03.01 "$(has_rule pull_request)" "rules on $branch: $rules"
  emit OSPS-AC-03.02 "$(has_rule deletion)" "rules on $branch: $rules"
  emit OSPS-QA-03.01 "$(has_rule required_status_checks)" "rules on $branch: $rules"
fi

visibility=$(jq -r .visibility <<<"$meta")
emit OSPS-QA-01.01 "$([ "$visibility" = public ] && echo pass || echo fail)" "visibility: $visibility"
emit OSPS-GV-02.01 "$(jq -r 'if .has_issues or .has_discussions then "pass" else "fail" end' <<<"$meta")" \
  "issues: $(jq .has_issues <<<"$meta"), discussions: $(jq .has_discussions <<<"$meta")"

if license=$(gh api "repos/$repo/license" 2>/dev/null); then
  spdx=$(jq -r '.license.spdx_id // "NOASSERTION"' <<<"$license")
  emit OSPS-LE-03.01 pass "license file: $(jq -r .path <<<"$license")"
  free=$(curl -fsS https://spdx.org/licenses/licenses.json | jq -r --arg id "$spdx" \
    '[.licenses[] | select(.licenseId == $id and (.isOsiApproved or .isFsfLibre))] | length')
  emit OSPS-LE-02.01 "$([ "$free" = 1 ] && echo pass || echo manual)" "SPDX ID $spdx; OSI or FSF approved: $free"
else
  emit OSPS-LE-03.01 fail "no license file GitHub can find"
  emit OSPS-LE-02.01 fail "no license file GitHub can find"
fi

contributing=$(gh api "repos/$repo/community/profile" --jq '.files.contributing.html_url // empty')
emit OSPS-GV-03.01 "$([ -n "$contributing" ] && echo pass || echo fail)" "contributing guide: ${contributing:-none}"

policy=none
for path in SECURITY.md .github/SECURITY.md docs/SECURITY.md; do
  if gh api "repos/$repo/contents/$path" --silent 2>/dev/null; then policy=$path; break; fi
done
emit OSPS-VM-02.01 "$([ "$policy" != none ] && echo pass || echo fail)" "security policy: $policy"

pvr=$(gh api "repos/$repo/private-vulnerability-reporting" --jq .enabled)
emit OSPS-VM-03.01 "$([ "$pvr" = true ] && echo pass || echo manual)" \
  "private vulnerability reporting: $pvr (another private channel in SECURITY.md also counts)"

perm=$(gh api "repos/$repo/actions/permissions/workflow" --jq .default_workflow_permissions 2>/dev/null) || perm=unknown
case "$perm" in
  read) emit OSPS-AC-04.01 pass "default GITHUB_TOKEN permissions: read" ;;
  write) emit OSPS-AC-04.01 fail "default GITHUB_TOKEN permissions: write" ;;
  *) emit OSPS-AC-04.01 needs-admin "workflow permissions are not readable with this token" ;;
esac

push=$(jq -r '.security_and_analysis.secret_scanning_push_protection.status // "unknown"' <<<"$meta")
case "$push" in
  enabled) emit OSPS-BR-07.01 pass "secret scanning push protection: enabled" ;;
  disabled) emit OSPS-BR-07.01 fail "secret scanning push protection: disabled" ;;
  *) emit OSPS-BR-07.01 needs-admin "security_and_analysis is only returned to admins" ;;
esac

What do Scorecard, Minder and the Privateer scanner actually check?

Automated tools help, but none of them can see every requirement, and the Baseline FAQ warns against treating tool output as the true security posture. Know what each one reads before you cite it as evidence.

What do Scorecard, Minder and the Privateer scanner actually check?
ToolWhat it checks against the BaselineLimits
Privateer plugin for GitHub repositories (ossf/pvtr-github-repo-scanner, v0.32.0), also as the OSPS Baseline Scanner actionHas a step for every requirement in the bundled catalogs, including osps-baseline-2026-08, and powers LFX Insights' security results. The action v1.5.2 runs scanner v0.31.0 and can upload failures as SARIF.Many steps rely on the Security Insights file; some return Needs Review; Level 2 and 3 steps are less tested than Level 1. It needs a personal access token, and the MFA check runs only with admin:org.
Minder (mindersec/minder-rules-and-profiles, security-baseline directory)A security-baseline-level-1 profile for GitHub with rule types for 22 Level 1 requirements, written for v2026.08.28; several have remediations that open a pull request or create a ruleset.Level 1 only, rules marked alpha, no MFA or subproject check, and GitLab coverage is partial. Its osps-ac-03-01 rule checks force pushes rather than direct commits.
OpenSSF Scorecard v5.5.0Scores checks from 0 to 10. The Baseline maps Branch-Protection, Dangerous-Workflow, Signed-Releases, Pinned-Dependencies, License, Binary-Artifacts, CI-Tests, Code-Review, Security-Policy, Vulnerabilities and SAST to Baseline controls as related, not equivalent.Scorecard's own coverage analysis, written against v2026.02.19, rates most controls partial or uncovered and MFA and collaborator permissions not observable. A pass or fail per control is proposed for Scorecard v6, not shipped.

How will consumers read your claim?

The Baseline is written for the people who run a project, but its readers include the companies that depend on it. Its FAQ says a consumer can accept the project's self-attestation or make another arrangement, because some controls depend on settings only maintainers see. Make that easy: publish the claim with its version, level and date in the README or SECURITY.md, link the Security Insights file and the scanner output, and keep the per-requirement records available on request.

On the consumer side, the OpenSSF's Secure Supply Chain Consumption Framework describes how organizations take in open source, and /domains/supply-chain/dependencies/s2c2f-adoption/ walks through it. /domains/supply-chain/dependencies/project-health-and-intake/ shows how a consumer weighs signals such as Scorecard results when deciding to adopt a package.

Guided practice

tidepool (fictional) is a parsing library with three maintainers and a few hundred regular users. The maintainers want to claim OSPS Baseline v2026.08.28 Level 1 and plan Level 2. Use the facts below to find the gaps, fix the risky workflow and write the claim.

Supplied scenario material
Plain text
Repository: github.com/example-org/tidepool, public, default branch main.
Organization: two-factor authentication required; base permission Write.
Ruleset on main: Require a pull request before merging, active, bypass list: org admins.
No rule restricting deletions or force pushes.
Workflow permissions setting: Read and write.
Secret scanning: on. Push protection: off.
Files: README.md (install and usage), LICENSE (Apache-2.0), SECURITY.md ("email security@example.org"), CONTRIBUTING.md.
Issues: enabled. Discussions: disabled.
Committed: go.mod, go.sum, testdata/fixtures.bin (a crafted input file), tools/protoc (a compiled binary).
Releases: tags v1.0.0 to v1.4.0 with GitHub release archives, not signed.
Workflow .github/workflows/label.yml:
  on: pull_request_target
  steps:
    - uses: actions/checkout@<sha>  # with: ref: ${{ github.event.pull_request.head.sha }}
    - run: ./scripts/label.sh "${{ github.event.pull_request.title }}"
  secrets used: LABEL_BOT_TOKEN
  1. 1. Find the Level 1 gaps

    Go through the Level 1 table and list each requirement tidepool doesn't meet yet, with the evidence that shows it.

    Expected result and reasoning

    OSPS-AC-02.01: base permission Write means a new member gets write access to every repository; set it to Read. OSPS-AC-03.02: no deletion rule, so deleting main isn't blocked by the ruleset; add Restrict deletions and Block force pushes. OSPS-AC-03.01 holds only partly while org admins can bypass the pull-request rule; empty the bypass list. OSPS-BR-01.01 and OSPS-BR-01.03 fail on label.yml (next step). OSPS-BR-07.01 is weak without push protection; turn it on. OSPS-QA-05.01 fails on tools/protoc, a generated executable; build or download it in CI instead. testdata/fixtures.bin is not an executable, but OSPS-QA-05.02 still asks whether it can be reviewed: the xz-utils backdoor hid in binary test files, so generate it from a script in the repository or document exactly how it was made. The other Level 1 rows are met.

  2. 2. Fix the workflow

    Explain what is wrong with label.yml and rewrite the risky part.

    Expected result and reasoning

    It runs on pull_request_target, so it has secrets and a write token, yet it checks out and runs the pull request's own code (scripts/label.sh at the head SHA), which breaks OSPS-BR-01.03. It also expands the pull request title straight into a shell command, which breaks OSPS-BR-01.01: GitHub substitutes the title before the shell runs, so a title containing a double quote and a semicolon ends the argument and starts a new command. Fix: don't check out the head SHA in this job, run only the base branch's script, pass the title through env: TITLE: ${{ github.event.pull_request.title }} and use "$TITLE" quoted, and give the job only pull-requests: write.

  3. 3. Plan the first Level 2 changes

    Pick the four Level 2 changes that matter most for tidepool's releases and say what evidence each produces.

    Expected result and reasoning

    OSPS-AC-04.01: set workflow permissions to read, evidence from the workflow permissions API. OSPS-QA-03.01 and OSPS-QA-06.01: a test workflow required by the ruleset, evidence in the rule export and merged pull requests. OSPS-BR-06.01: attest release archives with actions/attest, evidence verifiable with gh attestation verify. OSPS-VM-01.01 and OSPS-VM-03.01: expand SECURITY.md with a response timeframe and turn on private vulnerability reporting. The documentation controls (DO-06.01, DO-07.01, GV-01.01, GV-01.02, GV-03.02, SA-01.01, SA-02.01, SA-03.01) follow as files.

  4. 4. Write the claim

    Write the sentence tidepool can publish once the Level 1 gaps are closed, and list what backs it.

    Expected result and reasoning

    As of <date of the evidence run>, tidepool complies with OSPS Baseline version 2026.08.28, Level 1. Backing: the dated evidence records for all 24 Level 1 requirements, the org MFA and base-permission exports read by an owner, the ruleset export, the workflow review, and security-insights.yml validated against schema 2.2.0. It is a self-attestation for that date, not a score or a certification.

Your deliverable: A list of tidepool's Level 1 gaps with fixes, a corrected label workflow, a four-item Level 2 plan with evidence, and a dated Baseline claim.

tidepool, its settings and its people are fictional. Checking these settings on a real repository needs the admin access the evidence script marks as needs-admin.

Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.

No draft saved on this device yet.

Sources & further reading

The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.

  1. OpenSSF OSPS Baseline v2026.08.28 (controls, levels and recommendations). v2026.08.28 (28 August 2026).
  2. OSPS Baseline v2026.08.28 checklist by level. v2026.08.28.
  3. OSPS Baseline: versions and guiding principles
  4. OSPS Baseline release notes. 2026-08-28 and 2026-02-19 entries.
  5. OSPS Baseline FAQ (self-attestation, point-in-time claims, consumers)
  6. OSPS Baseline: implementation guidance for maintainers
  7. ossf/security-baseline: control catalog YAML at tag v2026.08.28. v2026.08.28.
  8. OpenSSF Security Insights specification: schema.cue. v2.2.0 (31 January 2026).
  9. Privateer plugin for GitHub repositories (OSPS Baseline scanner). v0.32.0 (9 October 2026).
  10. OSPS Baseline Scanner GitHub Action. v1.5.2 (1 October 2026).
  11. Minder rules and profiles: OpenSSF Security Baseline
  12. OpenSSF Scorecard: OSPS Baseline coverage analysis. Written against Baseline v2026.02.19.
  13. OpenSSF Scorecard v6 proposal: OSPS Baseline conformance
  14. GitHub Docs: Available rules for rulesets
  15. GitHub Docs: Managing GitHub Actions settings for a repository (workflow permissions)
  16. GitHub Docs: Configuring private vulnerability reporting for a repository
  17. GitHub blog: Push protection is generally available, and free for all public repositories. May 2023.
  18. GitHub Docs: Managing the commit signoff policy for your organization
  19. actions/attest: generate signed attestations (provenance and SBOM). v4.2.2.
  20. GitHub Docs: About dependency review
  21. SPDX License List (isOsiApproved and isFsfLibre). 3.29.0 (16 September 2026).

Reference information

Download this guide as Markdown