Security Review — May 2026¶
This document records the security review performed at the v1.0.0
public release. It satisfies the OpenSSF Best Practices Gold criterion
security_review (a documented security review within the last 5 years).
- Project:
legal-text-mcp-dev1.0.0 - Commit reviewed:
mainat 2026-05-16 (tagv1.0.0) - Reviewer: Martin Klein (
flitzrrr, sole maintainer at v1.0.0) - Review window: 2026-05-14 to 2026-05-16
- Next scheduled review: 2026-11 (six-month cadence) or before any release that introduces auth, persistence, or multi-tenant features
Scope¶
The review covers everything published in the v1.0.0 release surface:
- Python package
legal-text-mcp-deon PyPI (1.0.0) — wheel + sdist - Container image
ghcr.io/klein-business/legal-text-mcp-de:1.0.0forlinux/amd64andlinux/arm64 - Source tree on
mainat the v1.0.0 tag - GitHub Actions release pipeline (
.github/workflows/release.yml) including PyPI Trusted Publisher, SLSA-3 provenance, cosign keyless, Syft + CycloneDX SBOM - Documentation site at
https://klein-business.github.io/legal-text-mcp-de/
Out of scope (no implementation in v1.0.0):
- User authentication or session management
- Multi-tenant data isolation
- Persistent storage of user-provided data
- Third-party integrations beyond data sources
(
gesetze-im-internet.de, EUR-Lex / Cellar)
Threat model summary¶
See the project SECURITY.md for the canonical threat model.
Key points carried forward into this review:
- The runtime is local or server-side infrastructure with no SaaS, no accounts, no PII processing, no auth.
- The MCP server and HTTP API expose read-only access to a pre-validated legal-text corpus. No write paths to user-controlled storage.
- Data sources are trusted publishers (German federal government
via
gesetze-im-internet.de, EU institutions via Cellar/EUR-Lex) consumed over HTTPS with content-type verification. - The release pipeline is the security boundary: any compromise must go through Trusted-Publisher OIDC, sigstore TUF chain, or branch protection.
Security requirements considered¶
-
Confidentiality: no user-supplied data is persisted; corpus data is public-record law text. No confidentiality concerns at the application layer.
-
Integrity: every release artifact carries cryptographic provenance (PEP 740 attestations for PyPI, cosign keyless for container, SLSA-3 build provenance for both). The corpus loader validates fixtures against a checksum manifest before serving.
-
Availability: rate limiting and request validation are delegated to the deployment layer (operator's reverse proxy); the application does not implement DoS protection itself. This is documented in
SECURITY.md. -
Supply-chain integrity: covered by hardened CI workflows (15 third-party actions pinned by SHA), Dependabot security updates, Scorecard, CodeQL, Trivy, Gitleaks.
-
Disclosure / response: Private Vulnerability Reporting is enabled at the org level; SECURITY.md commits to a 14-day initial response and a 60-day fix SLA.
Review activities performed¶
Manual code audit¶
The following modules were read line-by-line for security-relevant patterns (path traversal, deserialization, command injection, SSRF, time-of-check / time-of-use):
src/legal_text_mcp_de/server.py— MCP transport entry pointsrc/legal_text_mcp_de/http_api.py— FastAPI surfacesrc/legal_text_mcp_de/config.py— pydantic-settings env loadingsrc/legal_text_mcp_de/legal_texts/sources.py— upstream source loadingsrc/legal_text_mcp_de/legal_texts/parser.py— XML / HTML parsingsrc/legal_text_mcp_de/legal_texts/normalizer.py— corpus normalisationscripts/verify_pre_flip.py— pre-release safety gatescripts/verify_uv_runtime_docker.py— container image gate
Findings: none with severity ≥ medium. Two low-severity observations were filed for the next maintenance window:
- The HTTP API does not currently enforce a request body size limit
at the FastAPI layer. The operator's reverse proxy is expected to
do so. Tracked in [#TBD] —
good first issue. - The MCP transport does not authenticate the client. This is by
design for the local-or-server-side deployment model; documented
in
SECURITY.mdas an operator responsibility. No code change needed; documentation already covers it.
Automated analysis (run on main at v1.0.0)¶
- CodeQL extended-suite (
.github/workflows/codeql.yml): 0 findings. - Trivy container scan: 0 high/critical CVEs against
ghcr.io/klein-business/legal-text-mcp-de:1.0.0. One transitiverustls-webpkiadvisory (GHSA-82j2-j2ch-gfr8) was fixed by bumpinguvfrom 0.10.12 to 0.11.14 in PR #40. - Gitleaks: 0 leaked secrets on
main.detect-secretsbaseline in.secrets.baselineaccounts for known acceptable patterns (hashed test fixtures). - OpenSSF Scorecard: monthly run; baseline score ≥ 6 across all checks.
- GitHub Dependency Review: 0 blocked dependencies on PR #16 (Phase 3+4 atomic rename) and all subsequent PRs.
Architecture review¶
- The Dockerfile runs as non-root UID 10001, uses a pinned base
image digest, sets
UV_CACHE_DIR=/tmp/uv-cache, and defines aHEALTHCHECKagainst/health. - Branch protection on
main: 14 required status checks,required_signatures=true,required_linear_history=true,enforce_admins=true,required_pull_request_reviews=1. The sole bypass-allowlist entry is the maintainerflitzrrr. - The release pipeline (
release.yml) uses GitHub OIDC for PyPI Trusted Publisher and cosign keyless signing. No long-lived credentials are stored in repository secrets.
Supply-chain review¶
- All 15 third-party GitHub Actions used in CI are SHA-pinned. The
pin set was re-verified during the v1.0.0 release (PR #59 corrected
two fake SHAs that had slipped into
release.yml). - Python dependencies are loosely pinned in
pyproject.tomland exactly resolved inuv.lock. Dependabot is configured for weekly version updates and immediate security updates via theuvecosystem. - The OCI base image (
python:3.12-slim) is pinned by digest, with a documented refresh cadence.
Mitigations and follow-ups¶
| # | Item | Action | Owner | Target |
|---|---|---|---|---|
| 1 | HTTP body-size limit | File good first issue to add request_max_body_size configuration |
maintainer | Next minor |
| 2 | Branch-coverage gate | Lift coverage measurement to include branches (branch=true) and gate at 80% |
maintainer | v1.1.0 |
| 3 | Co-maintainer onboarding | Recruit a second maintainer to enable two_person_review |
maintainer | 2026 H2 |
| 4 | Six-month review | Re-run this template at 2026-11 with diff against this report | maintainer | 2026-11-16 |
Conclusion¶
legal-text-mcp-de v1.0.0 is fit for public, server-side deployment.
The threat model is narrow (no PII, no auth, read-only public law
text). Automated analysis (CodeQL, Trivy, Gitleaks, dependency
review) produces zero blocking findings. Release artefacts are
cryptographically attested end-to-end (PEP 740, cosign keyless,
SLSA-3). The two observed gaps are documentation/configuration
matters, not exploitable vulnerabilities.
The project is approved for public release at v1.0.0.
— Martin Klein (flitzrrr), 2026-05-16