[Security 19] security posture: SECURITY.md, CodeQL, secret scan, Scorecard - #246
Conversation
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
5e98f07 to
9e2eff9
Compare
|
Thanks for adding this security baseline. Private vulnerability reporting is now enabled, so that concern is resolved. I found four remaining issues before merge:
The branch is behind current |
…correct policy scope Addresses all four points from review on PR Open-Legal-Products#246. WHY THIS MATTERS A security-posture PR has to hold itself to the standard it introduces. Each of the four fixes below closes a gap where the original version either overstated its guarantees (policy text) or quietly weakened them (scanner allowlists, mutable action refs). 1. SECURITY.MD SCOPE — THE HOSTED SERVICE EXISTS The policy claimed "there is no hosted service", but the official Mike website offers a cloud version at app.mikeoss.com. A researcher reading the old text would conclude the hosted service was out of scope and either not report or disclose publicly. The policy now explicitly takes hosted-service reports through the same private channel (with the usual safe-harbor expectations: own accounts only, non-destructive testing) and narrows the exclusion to what it was always meant to cover: independent third-party self-hosted deployments. 2. SUPPORTED VERSIONS — TAGS EXIST AND ARE UNSUPPORTED The repository has tags v0.1.0 through v0.4.0, so "there are no tagged releases" was simply wrong, and wrong in the dangerous direction: a user pinned to v0.4.0 might assume they were covered. The policy now lists the tags explicitly as unsupported historical snapshots — only the tip of main receives security fixes. 3. WORKFLOW ACTION PINNING — TAGS ARE MUTABLE, SHAS ARE NOT `uses: some/action@v4` re-resolves on every run: whoever controls that tag controls code that executes inside this repo's CI, which is exactly how the 2025 tj-actions/changed-files compromise spread. The risk is sharpest in the Scorecard job, which holds id-token: write (OIDC signing) and security-events: write. Every `uses:` now references a full commit SHA — the only immutable ref format GitHub offers — with the release version kept in a trailing comment, which is the convention Dependabot parses to keep proposing pinned updates: actions/checkout@11d5960a… # v4.4.0 github/codeql-action/*@a2983b8b… # v3.37.4 ossf/scorecard-action@4eaacf05… # v2.4.3 4. GITLEAKS — ALLOWLIST EXACT SECRETS, NEVER PATHS The old config allowlisted entire files ("this file contains a fake secret, ignore it"). gitleaks applies a global allowlist's `paths` as a whole-file skip, so a REAL credential later committed to any of those files would sail through the scan — the allowlist grew a permanent blind spot with every entry. Reproduced before fixing: planting a high-entropy secret in an allowlisted fixture file and scanning full history reported zero leaks under the old config. The new config allowlists exact secret VALUES instead — one anchored regex per hand-verified fake (the safeError test fixtures, the add-in e2e mock token, and Supabase's published local-dev demo JWTs, which are public by construction). A real secret can never equal a known fake, so the blind spot is gone; the same planted-secret experiment now fails the scan, and the full history of both this branch and current main still passes clean. This also surfaced two findings the old path list missed entirely (the demo JWTs in .env.example / docker-compose.yml history) — they are now consciously allowlisted by value rather than accidentally green. VERIFICATION - All three workflows parse as valid YAML. - gitleaks 8.30.1 (checksum-verified binary): full-history scan clean on this branch and on current origin/main with the new config. - Planted-secret experiment: old config 0 leaks (blind), new config 1 leak (caught) on an identical history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Public-repo security posture on top of PR Open-Legal-Products#227's audit/eslint/dependabot gates: - SECURITY.md: private vulnerability reporting via the Security tab, 7-day acknowledgment (solo maintainer), self-hosted + LLM prompt-injection scope notes. main-only support (no release tags yet). - codeql.yml: javascript-typescript analysis with build-mode: none (interpreted TS, no build needed) on PRs, main, and a weekly cron. - gitleaks.yml: full-history secret scan using a sha256-verified pinned release binary instead of gitleaks-action (which needs a paid license for org repos). .gitleaks.toml allowlists hand-verified fake secrets (test fixtures, docs placeholders, the public supabase-demo anon key); a local run over all 551 commits is clean with this config. - scorecard.yml: OpenSSF Scorecard on main + weekly cron with publish_results: true. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…correct policy scope Addresses all four points from review on PR Open-Legal-Products#246. WHY THIS MATTERS A security-posture PR has to hold itself to the standard it introduces. Each of the four fixes below closes a gap where the original version either overstated its guarantees (policy text) or quietly weakened them (scanner allowlists, mutable action refs). 1. SECURITY.MD SCOPE — THE HOSTED SERVICE EXISTS The policy claimed "there is no hosted service", but the official Mike website offers a cloud version at app.mikeoss.com. A researcher reading the old text would conclude the hosted service was out of scope and either not report or disclose publicly. The policy now explicitly takes hosted-service reports through the same private channel (with the usual safe-harbor expectations: own accounts only, non-destructive testing) and narrows the exclusion to what it was always meant to cover: independent third-party self-hosted deployments. 2. SUPPORTED VERSIONS — TAGS EXIST AND ARE UNSUPPORTED The repository has tags v0.1.0 through v0.4.0, so "there are no tagged releases" was simply wrong, and wrong in the dangerous direction: a user pinned to v0.4.0 might assume they were covered. The policy now lists the tags explicitly as unsupported historical snapshots — only the tip of main receives security fixes. 3. WORKFLOW ACTION PINNING — TAGS ARE MUTABLE, SHAS ARE NOT `uses: some/action@v4` re-resolves on every run: whoever controls that tag controls code that executes inside this repo's CI, which is exactly how the 2025 tj-actions/changed-files compromise spread. The risk is sharpest in the Scorecard job, which holds id-token: write (OIDC signing) and security-events: write. Every `uses:` now references a full commit SHA — the only immutable ref format GitHub offers — with the release version kept in a trailing comment, which is the convention Dependabot parses to keep proposing pinned updates: actions/checkout@11d5960a… # v4.4.0 github/codeql-action/*@a2983b8b… # v3.37.4 ossf/scorecard-action@4eaacf05… # v2.4.3 4. GITLEAKS — ALLOWLIST EXACT SECRETS, NEVER PATHS The old config allowlisted entire files ("this file contains a fake secret, ignore it"). gitleaks applies a global allowlist's `paths` as a whole-file skip, so a REAL credential later committed to any of those files would sail through the scan — the allowlist grew a permanent blind spot with every entry. Reproduced before fixing: planting a high-entropy secret in an allowlisted fixture file and scanning full history reported zero leaks under the old config. The new config allowlists exact secret VALUES instead — one anchored regex per hand-verified fake (the safeError test fixtures, the add-in e2e mock token, and Supabase's published local-dev demo JWTs, which are public by construction). A real secret can never equal a known fake, so the blind spot is gone; the same planted-secret experiment now fails the scan, and the full history of both this branch and current main still passes clean. This also surfaced two findings the old path list missed entirely (the demo JWTs in .env.example / docker-compose.yml history) — they are now consciously allowlisted by value rather than accidentally green. VERIFICATION - All three workflows parse as valid YAML. - gitleaks 8.30.1 (checksum-verified binary): full-history scan clean on this branch and on current origin/main with the new config. - Planted-secret experiment: old config 0 leaks (blind), new config 1 leak (caught) on an identical history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d3bc19d to
a119e3e
Compare
This picks up the security-posture pieces that #227 (npm audit gate, security ESLint, dependabot) doesn't cover. No overlap: #227 gates dependencies and code patterns on each PR; this PR adds a disclosure policy and three scanners.
What's in it
codeql.yml) — GitHub's own static analyzer, on every PR, push to main, and weekly. Both backend/ and frontend/ are interpreted TypeScript, so it needs no build step. Findings appear under the Security tab, not as PR noise.gitleaks.yml+.gitleaks.toml) — scans the entire git history for committed credentials on every PR and push. It deliberately does not use the official gitleaks-action, which requires a paid license key for organization repos — instead it downloads the pinned 8.30.1 binary and verifies its sha256 before running. I ran it over all 551 commits locally: the only hits were fake keys in tests and docs examples (verified by hand, each one allowlisted with a comment in.gitleaks.toml), so the check starts green and any new secret fails CI.scorecard.yml) — a weekly automated report card on the repo's own security hygiene (branch protection, workflow permissions, etc.), published to the public OpenSSF database.Maintainer action required (repo Settings, ~2 minutes)
[](https://scorecard.dev/viewer/?uri=github.com/Open-Legal-Products/mike)Notes
🤖 Generated with Claude Code