Security
OpenSSF Scorecard
This page records the current OpenSSF Scorecard posture for this repository, the known gaps, and the remediation roadmap. The badge in the README links to the live result; this page is the maintained narrative behind it.
Live score
Aggregate score 7.6 / 10 at 2026-05-17T08:11:51Z.
| Check | Score | Reason |
|---|---|---|
| Binary-Artifacts | 10 | no binaries found in the repo |
| Branch-Protection | -1 | internal error: error during branchesHandler.setup: internal error: some github tokens can't read cl |
| CI-Tests | 10 | 15 out of 15 merged PRs checked by a CI test -- score normalized to 10 |
| CII-Best-Practices | 2 | badge detected: InProgress |
| Code-Review | 0 | Found 0/30 approved changesets -- score normalized to 0 |
| Contributors | 0 | project has 0 contributing companies or organizations -- score normalized to 0 |
| Dangerous-Workflow | 10 | no dangerous workflow patterns detected |
| Dependency-Update-Tool | 10 | update tool detected |
| Fuzzing | 0 | project is not fuzzed |
| License | 9 | license file detected |
| Maintained | 10 | 30 commit(s) and 30 issue activity found in the last 90 days -- score normalized to 10 |
| Packaging | 10 | packaging workflow detected |
| Pinned-Dependencies | 9 | dependency not pinned by hash detected -- score normalized to 9 |
| SAST | 10 | SAST tool is run on all commits |
| Security-Policy | 10 | security policy file detected |
| Signed-Releases | 3 | 1 out of the last 3 releases have a total of 2 signed artifacts. |
| Token-Permissions | 10 | GitHub workflow tokens follow principle of least privilege |
| Vulnerabilities | 10 | 0 existing vulnerabilities detected |
Refresh: scripts/qa/scorecard-snapshot.sh · Monitoring: .github/workflows/scorecard.yml.
The badge above is regenerated every Monday at 06:00 UTC and on every push to main by .github/workflows/scorecard.yml. The SARIF result is uploaded to GitHub Code Scanning (Security tab) and retained as a 30-day workflow artifact.
Threshold policy
| Score | Action |
|---|---|
| ≥ 8.0 | Healthy. Keep doing what we're doing. |
| 7.0 – 7.9 | Acceptable. Tracked. |
| < 7.0 | Regression — the workflow auto-opens a tracking issue labelled type:security + priority:high. |
Regressions are triaged within one Monday cycle: identify the check that dropped, either remediate or file an exception entry below with a rationale and an expiry date.
Per-check posture (snapshot, main HEAD)
The checks below come from OpenSSF Scorecard's published rubric. Tick marks reflect this repo's posture at the time of writing — the live badge is authoritative if these diverge.
| Check | Status | Notes |
|---|---|---|
| Branch-Protection | ⚠ | main requires signed commits, linear history, required checks (.github/rulesets/main.json). Scorecard can't read classic protection rules without a fine-grained PAT, so this check reports as -1 ("internal error"). |
| Code-Review | ⚠ | Single-maintainer repo; PRs are self-merged after CI. Scorecard scores 0/30 here by design — see Open findings below. |
| Signed-Commits | ✓ | Enforced by pre-push hook (scripts/git-hooks/pre-push) and at-push by branch protection (#853 + #857). |
| Dependency-Update-Tool | ✓ | Dependabot configured for github-actions / npm / docker / devcontainers / uv. |
| Fuzzing | ⚠ | install.sh fuzz harness lives under fuzz/install/ (closes #881), but it's shell-based and Scorecard's heuristic only recognizes OSS-Fuzz / ClusterFuzzLite / native Go fuzz / libFuzzer / Atheris. None support shell. Property tests under tests/unit/functions/test_property_*.sh cover the closest equivalent surface. |
| License | ✓ | MIT at repo root (LICENSE). |
| Maintained | ✓ | Active commit cadence; the README lists the current dotfiles_version. |
| Pinned-Dependencies | ⚠ | Closed 8 of 14 findings this cycle (every Dockerfile base + every workflow action + 2 curl | sh installers + the npm install -g npm upgrade step). 5 residual findings stay open by design — see Open findings. |
| SAST | ✓ | CodeQL (.github/workflows/codeql.yml) + Checkov + Grype. |
| SBOM | ✓ | Generated per PR by sbom-diff.yml and per release by security-release.yml. |
| Security-Policy | ✓ | SECURITY.md + this page + docs/security/THREAT_MODEL.md. |
| Token-Permissions | ✓ | Top-level permissions: blocks restricted to contents: read. write scopes scoped to the jobs that need them (#886). |
| Vulnerabilities | ✓ | Grype gate hard-fails on high / critical on main (#852). |
| Webhooks | n/a | No external webhooks configured. |
| CI-Tests | ✓ | Linux + macOS Intel + Apple Silicon matrix; optional Windows. |
| CII-Best-Practices | ☐ | Not applied yet — see Open work below. |
| Dangerous-Workflow | ✓ | No pull_request_target with checkout-PR-code anti-pattern. |
| Packaging | ✓ | npm publish --provenance via OIDC (.github/workflows/npm-publish.yml); policy-bundle-release.yml for the policy artifact. |
| Signed-Releases | ⚠ | SLSA provenance + minisig attached via security-release.yml; Cosign keyless signing tracked at #876. |
Open findings
The eight items below are surfaced as alerts at https://github.com/sebastienrousseau/dotfiles/security/code-scanning. Each row gives the dismissal reason and the exact comment text to paste when triaging.
Bucket 1 — Dismiss in the UI (5 items)
Scorecard flags every npm install, pip install, go install, and curl | sh invocation under the Pinned-Dependencies check. The five below either belong to user-facing "update everything" aliases or are false positives in Scorecard's regex.
| Path | Reason | Comment to paste |
|---|---|---|
.chezmoitemplates/aliases/legal/legal.aliases.sh:64 | Won't fix | User-facing convenience alias — go install <fortune-style joke command>@latest. Pinning by SHA would require users to update the alias manually on every upstream change. Intentional. |
.chezmoitemplates/aliases/update/update.aliases.sh:173 | Won't fix | User-facing convenience alias — bulk-update wrapper around npm. The alias's purpose is to update everything; pinning defeats it. Intentional. |
dot_local/bin/executable_update:99 | Won't fix | dot_local/bin/executable_update is the user-invoked "update everything" command. The literal point of npm update -g is to update to whatever's latest. Pinning defeats it. Intentional. |
scripts/dot/commands/tools.sh:121 | False positive | npm install --package-lock-only --ignore-scripts --silent generates a lockfile and does NOT fetch packages. Scorecard's regex matches npm install blindly; this invocation has no supply-chain surface. |
install/provision/run_onchange_10-linux-packages.sh.tmpl:369 | Won't fix | Already exact-version-pins aider-chat==0.86.2. Scorecard wants sha256-hash pinning, which would require a ~2,700-line lockfile covering all 108 transitive deps and re-generated on every aider release — disproportionate maintenance for a single optional provisioning install. |
Bucket 2 — Architectural (1 item)
| Alert | Why architectural |
|---|---|
CodeReviewID (high) — score 0 | Scorecard requires every merged PR to record an approval review by a different GitHub user than the author. This repo is single-maintainer; PRs are self-merged after CI. The fix path is either (a) onboard a co-maintainer who reviews PRs before merge, or (b) accept the finding. Dismiss as Won't fix with comment: "Single-maintainer project — no second reviewer available. CI gates (47 required checks per PR) substitute for human review." |
Bucket 3 — External action (2 items)
| Alert | Action |
|---|---|
FuzzingID (medium) | Dismiss as Won't fix with comment: "Repo is bash + Go-template + Lua. ClusterFuzzLite / OSS-Fuzz / native Go fuzz / libFuzzer / Atheris (the frameworks Scorecard recognises) all target compiled languages, none support shell. The fuzz/install/fuzz_install.sh harness + property tests under tests/unit/functions/test_property_*.sh cover the equivalent surface." |
CIIBestPracticesID (low) | Apply at https://www.bestpractices.dev/projects/new. ~67 self-attested questions, free, 1-2 hours. After silver-tier approval, paste the badge into README.md next to the existing Scorecard badge. Most criteria already met (signed commits, CI, security policy, license). |
Closed this cycle
| Date | Score | Alerts open | Change |
|---|---|---|---|
| 2026-05-14 | 6.5 | 28 | First clean publish after fixing the scorecard.yml uses:-only restriction (#885). Findings had been hidden by publish-step 400s until then. |
| 2026-05-14 | 6.5 | 11 | Closed 17 of 28: 10× TokenPermissions (#886), 6× Dockerfile bases (#886), 1× gitleaks fixture (#884). |
| 2026-05-14 | 6.5 | 9 | Closed 2× curl | sh installers (#888). |
| 2026-05-14 | 6.5 | 8 | Closed 1× npm install -g npm@… via Node 24 bump (#889). |
| 2026-05-17 | 7.6 | 7 | v0.2.502 released; Cosign keyless signing live (#876 implementation landed in security-release.yml sbom job). |
| 2026-05-17 | 7.6 | 6 | SLSA Release Attestation pipeline unblocked (PR #894 — 6 prior releases had failed identically). v0.2.502 backfilled with .intoto.jsonl + .sig + .pem triplet. Signed-Releases should bump from 3 to ~10 on next Scorecard re-scrape. |
| 2026-05-17 | 7.6 | — | Added MAINTAINERS.md + GOVERNANCE.md at repo root. Provides the formal context for the Code-Review 0/30 score (solo maintainer) and unblocks the CII Best Practices badge application. |
Open work
- Apply for the OpenSSF Best Practices Badge — see Bucket 3 above. All passing-tier criteria are now met (signed commits, CI, security policy, MIT license, MAINTAINERS.md, GOVERNANCE.md, RFC process documented).
- Re-trigger Scorecard after SLSA backfill propagates — expected
Signed-Releases3 → 10. harden-runnerblock-mode adoption (#878) — should tighten theToken-Permissionscheck further.- Investigate
License9/10 false-positive — repo is MIT (SPDX-compliant), Scorecard's penalty should not apply. - Investigate
Branch-Protection-1— scanner internal error; manually verify withgh api repos/:owner/:repo/branches/main/protectionand document.
Enabling Branch-Protection + Webhooks checks (one-time PAT setup)
Scorecard's Branch-Protection and Webhooks checks require a fine-grained Personal Access Token with Administration: read scope. Without it both checks return -1 ("internal error"). The workflow already wires this up — only the secret needs to be created.
Steps (one-time, repo owner):
- Go to https://github.com/settings/personal-access-tokens/new
- Resource owner:
sebastienrousseau· Repository access: onlysebastienrousseau/dotfiles - Repository permissions →
Administration: read-onlyandMetadata: read-only - Expiration: 1 year (set a calendar reminder to rotate; document the rotation in this file when done)
- Generate the token, copy it once
- Add as a secret named
SCORECARD_TOKENat https://github.com/sebastienrousseau/dotfiles/settings/secrets/actions/new - Next Scorecard run (manual via
gh workflow run scorecard.ymlor weekly cron) picks it up automatically - Expected effect:
Branch-Protection-1→ 10/10 if all rules are correctly configured;Webhooks-1→ 10/10 (no webhooks present).
The PAT secret is optional. When absent, the workflow falls back to ${{ github.token }} and Scorecard continues to run, only with those two checks at -1. Adding the PAT cannot reduce any other check's score — it's a strict improvement.
Exceptions
| Check | Expiry | Rationale |
|---|---|---|
| (none currently) | — | — |
If you add an exception, include the check name, an expiry date (don't allow indefinite), and the rationale. Re-evaluate every quarter.
Refreshing this document
# Re-count open alerts:
# Trigger Scorecard manually:
If a new category appears, add a row to the matching bucket. If a remediation closes one of the rows above, delete the row.
References
.github/workflows/scorecard.yml— the scanner workflow (split intoanalysis+track-regressionjobs per #885).- Scorecard project.
- Scorecard checks reference.
- Tracking issue #869.