Skip to content

[Security 19] security posture: SECURITY.md, CodeQL, secret scan, Scorecard - #246

Merged
willchen96 merged 2 commits into
Open-Legal-Products:mainfrom
amal66:olp-pr/security-posture
Aug 3, 2026
Merged

[Security 19] security posture: SECURITY.md, CodeQL, secret scan, Scorecard#246
willchen96 merged 2 commits into
Open-Legal-Products:mainfrom
amal66:olp-pr/security-posture

Conversation

@amal66

@amal66 amal66 commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

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

  • SECURITY.md — tells a researcher how to reach you privately (GitHub's built-in reporting, no email needed), what to expect (acknowledgment within 7 days — it says plainly you're a solo maintainer), and what's in scope. Since Mike is an LLM legal product, prompt-injection reports are explicitly invited.
  • CodeQL (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.
  • Secret scan (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.
  • OpenSSF Scorecard (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)

  1. Enable private vulnerability reporting: Settings → Code security → "Private vulnerability reporting" → Enable. Without this, the "Report a vulnerability" button SECURITY.md points to doesn't exist.
  2. Enable secret scanning + push protection: Settings → Code security → enable both "Secret scanning" and "Push protection". This is GitHub's server-side layer; the gitleaks workflow complements it, but push protection is the one that stops a secret before it lands in history.
  3. Optional, after the first Scorecard run on main: add the badge to README —
    [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Open-Legal-Products/mike/badge)](https://scorecard.dev/viewer/?uri=github.com/Open-Legal-Products/mike)

Notes

  • CodeQL and Scorecard results go to the Security tab (code scanning); only the gitleaks job blocks PRs directly.
  • codeql-action is pinned to the v3 major; Scorecard to v2.4.3.
  • Merge order vs [Security 11] Server-side hardening pack  #227 doesn't matter — the two touch disjoint files.

🤖 Generated with Claude Code

@CLAassistant

CLAassistant commented Jul 23, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-advanced-security

Copy link
Copy Markdown

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:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@amal66
amal66 force-pushed the olp-pr/security-posture branch from 5e98f07 to 9e2eff9 Compare July 25, 2026 21:31
@amal66
amal66 requested a review from willchen96 July 26, 2026 14:09
@willchen96

Copy link
Copy Markdown
Collaborator

Thanks for adding this security baseline. Private vulnerability reporting is now enabled, so that concern is resolved. I found four remaining issues before merge:

  1. Hosted-service scope: SECURITY.md says there is no hosted service, but the official Mike website offers a cloud version at app.mikeoss.com. The third-party-deployment exclusion should apply only to independent self-hosted installations.

  2. Workflow action pinning: The new workflows reference actions through movable tags such as @v3, @v4, and @v2.4.3. This is particularly concerning in the Scorecard job because it receives id-token: write and security-events: write. GitHub recommends pinning actions to verified full commit SHAs, the only immutable reference format. Please pin all new uses: entries and retain the release version in a comment for Dependabot.

  3. Overbroad gitleaks exclusions: The path allowlists suppress every future secret found in those files, not just the known fake fixtures. A real credential added later to one of the security tests or documentation files would pass the scan. Please allowlist the exact fixture strings or specific historical commits instead of entire paths.

  4. Incorrect release statement: The repository already has tags v0.1.0 through v0.4.0, all predating this PR. If only main receives security fixes, the policy should explicitly mark those tagged versions as unsupported rather than saying no tagged releases exist.

The branch is behind current main, but I confirmed that it merges cleanly. I also verified the gitleaks 8.30.1 checksum and successfully scanned the merged repository with no existing leaks.

amal66 added a commit to amal66/mike that referenced this pull request Aug 3, 2026
…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>
amal66 and others added 2 commits August 2, 2026 18:52
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>
@amal66
amal66 force-pushed the olp-pr/security-posture branch from d3bc19d to a119e3e Compare August 3, 2026 01:57
@willchen96
willchen96 merged commit 3775d53 into Open-Legal-Products:main Aug 3, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants