Repository navigation
v0.2.0-rc1
Pre-release⚠ BREAKING: schema_version is now 2.0.0, and repo_score means something different
Superseded by stable v0.2.0, which also posts the PR findings comment after a policy failure.
If you consume SCRAM's JSON, read this before upgrading. Your code will not
fail — it will be quietly wrong.
summary.repo_score changed meaning and kept its name. It is now a
presented score out of 65. It used to be a raw total out of 100.
A consumer written against schema_version: 1.0.0 parses the new document
successfully, because the field names are unchanged. It then renders
25/100 for what is now 25/65 — wrong by 35%, with nothing in the
document to contradict it. That is why this is a major version bump rather
than a minor one: the failure mode is silence.
What to change
- fmt.Printf("score %d/100\n", summary.repo_score)
+ fmt.Printf("score %d/%d\n", summary.repo_score, summary.repo_score_max)Read summary.repo_score_max. Do not hardcode either 65 or 100. If your
consumer compares scores across versions of SCRAM, note that a score from
1.x and a score from 2.x are on different scales and are not comparable —
this is the one case where a trend line built from old history needs to be
discarded rather than continued.
What did not change
- The ordering is identical. A consumer that only sorts, ranks, or picks
the maximum is unaffected; the severity ordering underneath is the same. scram badgereads a scan document, so it picked the change up
automatically and now emits25/65.- SARIF is unaffected: it never carried a repository score, and its
securitySeverityis GitHub's own 0–10 CVSS field. - The formula is untouched. Severity (40), exploitability (25),
maintenance (20) and freshness (15) are computed exactly as before, in the
same units with the same weights.--explainprints all four plus the
0–100 subtotal they sum to, so the arithmetic stays checkable. Only the
total the score is reported against changed.
Bucket boundaries moved with the scale
| Bucket | 2.x (of 65) | was (of 100) |
|---|---|---|
| Critical | 58–65 | 90–100 |
| High | 45–57 | 70–89 |
| Medium | 26–44 | 40–69 |
| Low | 1–25 | 1–39 |
| Clean | 0 | 0 |
The rescale is monotonic but not bucket-preserving. Checked across all 101
possible inputs, exactly two change bucket, both upward: a raw total of 69
becomes high (it was medium), and 89 becomes critical (it was high).
No input is demoted. If you alert on bucket transitions, expect those two.
Why 65 and not 100
scripts/measure_score_terms.sh runs every ecosystem and prints which terms
carry data. Severity is the only term that scores without an opt-in flag.
Exploitability needs EPSS, which is a per-CVE fetch that returns nothing when
FIRST is unreachable, and freshness needs latest-version data this project
does not collect — so it is structurally zero everywhere.
Dividing by 100 advertised 35 points the tool usually cannot earn, and a
moderately-scored repository read as "39/100" when 39 was all the evidence
there was. See ROADMAP.md
for what a 100-point scale would require.
Changelog
Security
- 4bf37c4 fix(diff): the vulnerability differential was comparing raw id strings
- af8e58f fix(resolve): every go.mod-only project reported zero vulnerabilities
Features
- fe2ff76 feat(resolve): yarn, pnpm and Cargo, and a go.mod fallback
- 343cb32 feat(score): the maintenance term reads a real OpenSSF Scorecard
- c93bc05 feat(score): the presented score is out of 65, because 65 is what it measures
Fixes
- 202752f fix(ci): pass the script path on stdin, not in the program text
- e452879 fix(ci): resolve the test interpreter per platform
- 32569c0 fix(report): the JSON said 25 and never said out of what
- 9f4ae85 fix(resolve): pnpm 6.0 lost 76% of its dependency tree
- aeefc1d fix: reject malformed Go module paths before v0.2.0 release
Other
- 0d53163 test(corpus): real yarn, pnpm and cargo lockfiles, 29 files, 13.8 MB
- f7232df test(resolve): the silent-zero invariant now follows the registry
Verifying this release
Every binary is checksummed and signed keyless. The signature is
bound to this repository's workflow identity and recorded in the
public Rekor transparency log, so it can be verified by anyone
without trusting anything GitHub says.
Download the binary for your platform, plus its .pem and .sig,
and the checksum file. Then, from that directory:
# 1. Checksums. Verifies every binary present in this directory
# against scram_0.2.0-rc1_checksums.txt. If you downloaded
# only one platform, the lines for the others report "No such
# file" -- that is expected, and is not a verification failure.
shasum -a 256 -c scram_0.2.0-rc1_checksums.txt
# (Linux: sha256sum -c scram_0.2.0-rc1_checksums.txt)
# 2. Signature. Replace the platform suffix with yours:
# darwin-arm64, darwin-amd64, linux-amd64, linux-arm64, or
# windows-amd64.exe
cosign verify-blob \
--certificate scram-0.2.0-rc1-linux-amd64.pem \
--signature scram-0.2.0-rc1-linux-amd64.sig \
--certificate-identity-regexp 'https://github.com/0xsan7/SCRAM/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
scram-0.2.0-rc1-linux-amd64Success prints Verified OK. A signature from a different
repository is rejected with none of the expected identities matched what was in the certificate.
The certificate's identity is
https://github.com/0xsan7/SCRAM/.github/workflows/ci.yml@refs/tags/v0.2.0-rc1,
which is what ties the binary to this specific tag.
Built from commit aeefc1d.
What is in this binary
The CycloneDX and SPDX SBOMs ship as release assets alongside these
binaries, generated by this project's own code from this
repository. They list every module in the released binary's own
dependency graph, so you can enumerate exactly what shipped without
rebuilding or re-running anything.