From 9e2eff9659340df5cf5dca75a25f50c8e19b2ff5 Mon Sep 17 00:00:00 2001 From: Amalanand Muthukumaran Date: Sat, 25 Jul 2026 14:30:37 -0700 Subject: [PATCH] ci(security): SECURITY.md, CodeQL, gitleaks, OpenSSF Scorecard Public-repo security posture on top of PR #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 --- .github/workflows/codeql.yml | 41 ++++++++++++++++++++++++++++++ .github/workflows/gitleaks.yml | 45 +++++++++++++++++++++++++++++++++ .github/workflows/scorecard.yml | 43 +++++++++++++++++++++++++++++++ .gitleaks.toml | 34 +++++++++++++++++++++++++ SECURITY.md | 32 +++++++++++++++++++++++ 5 files changed, 195 insertions(+) create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/gitleaks.yml create mode 100644 .github/workflows/scorecard.yml create mode 100644 .gitleaks.toml create mode 100644 SECURITY.md diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..32b4bf4b9 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +# CodeQL: static analysis for the whole tree (backend/ + frontend/). +# +# Both halves are interpreted TypeScript, so CodeQL's `build-mode: none` +# extracts them straight from source — no npm ci, no build step, and no +# autobuild guessing at the two-package layout. One analyze job covers +# backend and frontend together. The weekly cron re-runs analysis on an +# otherwise-quiet tree so newly shipped CodeQL queries still get applied. +# Findings land in the Security tab (code scanning), not as CI noise. +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Mondays 04:30 UTC. + - cron: "30 4 * * 1" + +permissions: + contents: read + +jobs: + analyze: + name: Analyze (javascript-typescript) + runs-on: ubuntu-latest + permissions: + contents: read + # Required to upload results to code scanning. + security-events: write + steps: + - uses: actions/checkout@v4 + + - uses: github/codeql-action/init@v3 + with: + languages: javascript-typescript + build-mode: none + + - uses: github/codeql-action/analyze@v3 + with: + category: "/language:javascript-typescript" diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml new file mode 100644 index 000000000..65666d2aa --- /dev/null +++ b/.github/workflows/gitleaks.yml @@ -0,0 +1,45 @@ +# Secret scan: gitleaks over the full git history. +# +# Deliberately NOT gitleaks/gitleaks-action — that action requires a paid +# GITLEAKS_LICENSE for organization-owned repos. The scanner itself is a +# single MIT-licensed Go binary, so this downloads a pinned release and +# verifies its sha256 (from the upstream release checksums file) before +# running it. fetch-depth: 0 so every commit is scanned, not just the PR +# head — a secret that was committed and later deleted is still leaked. +# Known fake secrets (test fixtures, docs placeholders) are allowlisted +# in .gitleaks.toml at the repo root; anything new fails the run. +name: Secret scan + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + gitleaks: + name: gitleaks (full history) + runs-on: ubuntu-latest + env: + GITLEAKS_VERSION: 8.30.1 + # sha256 of gitleaks_8.30.1_linux_x64.tar.gz, from + # gitleaks_8.30.1_checksums.txt on the upstream release. + GITLEAKS_SHA256: 551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb + steps: + - uses: actions/checkout@v4 + with: + # Full history — gitleaks scans commits, not the working tree. + fetch-depth: 0 + + - name: Download and verify gitleaks + run: | + curl -sSfL -o gitleaks.tar.gz \ + "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" + echo "${GITLEAKS_SHA256} gitleaks.tar.gz" | sha256sum -c - + tar -xzf gitleaks.tar.gz gitleaks + + - name: Scan full history + run: ./gitleaks detect --no-banner --redact diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..f9d16de8e --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,43 @@ +# OpenSSF Scorecard: automated checks of this repo's own security posture +# (branch protection, token permissions, pinned dependencies, ...). +# +# Runs on pushes to main plus a weekly cron — Scorecard rates the repo, not +# a PR's diff, so there is no pull_request trigger. publish_results: true +# sends the score to the OpenSSF public API (which enables the README badge +# and is what other tools query); that upload is signed via OIDC, hence +# id-token: write. Results also land in the Security tab as SARIF. +name: Scorecard + +on: + push: + branches: [main] + schedule: + # Mondays 05:30 UTC. + - cron: "30 5 * * 1" + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to sign the published results (OIDC). + id-token: write + # Needed to upload SARIF to code scanning. + security-events: write + contents: read + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - uses: ossf/scorecard-action@v2.4.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + + - uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 000000000..2643f6def --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,34 @@ +# gitleaks config for the Secret scan workflow (.github/workflows/gitleaks.yml). +# +# Default rules apply everywhere; the allowlist below covers paths whose +# "secrets" are deliberate fakes, verified by hand before listing: +# test fixtures (sk-proj-abc123..., minted throwaway JWTs, the public +# supabase-demo anon key) and docs examples (mike_sk_... placeholders). +# Several of the paths only exist in old commits from before the repo was +# restructured (apps/, sdks/, word-addin/) — the scan covers full history, +# so history-only paths still need allowlisting. Keep entries narrow: +# allowlist a specific file or test directory, never a whole source tree. + +[extend] +useDefault = true + +[[allowlists]] +description = "Unit-test fixtures: fake API keys and locally minted JWTs" +paths = [ + '''(^|/)src/lib/__tests__/safeError\.test\.ts$''', + '''(^|/)src/lib/__tests__/secretGuard\.test\.ts$''', + '''(^|/)src/lib/__tests__/webhookSignature\.test\.ts$''', +] + +[[allowlists]] +description = "E2E fixtures: mocked login tokens (history includes pre-restructure apps/word-addin/ path)" +paths = [ + '''(^|/)word-addin/e2e/auth\.spec\.ts$''', +] + +[[allowlists]] +description = "History-only paths: Python SDK tests and developer-platform docs with mike_sk_... placeholders" +paths = [ + '''^sdks/python/tests/''', + '''^docs/developer-platform\.md$''', +] diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..c8a495197 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,32 @@ +# Security Policy + +## Supported versions + +There are no tagged releases yet; the only supported version is the tip of +`main`. If you are self-hosting, please update to the latest `main` before +reporting — the issue may already be fixed. + +## Reporting a vulnerability + +Please report vulnerabilities privately through GitHub: + +**Security tab → Report a vulnerability** on this repository +(GitHub's private vulnerability reporting). + +Please do not open a public issue for anything security-sensitive. + +This project has a solo maintainer. You can expect an acknowledgment within +7 days; fixes are prioritized by severity after that. + +## Scope + +- Mike is **self-hosted** — there is no hosted service operated by this + repository to test against. Reports about the code, default configuration, + and deployment guidance in this repo are all in scope. Findings that only + apply to a specific third-party deployment should go to whoever operates it. +- Mike is an **LLM legal product**, so LLM-specific reports are explicitly + welcome: prompt injection (including via uploaded documents), getting the + model to ignore its guardrails, leaking another user's data or system + prompts through model output, and similar. +- Secrets accidentally committed to this repository's history are also worth + a private report, even though CI runs a secret scanner.