From 7e8c773a5b22ec00700f567fc78777b0d302504c Mon Sep 17 00:00:00 2001 From: Jeff Otterson Date: Wed, 17 Jun 2026 17:47:07 -0600 Subject: [PATCH] ci: add CodeQL + Dependabot + coverage gate + SECURITY; pin actions to SHAs Uniform reliability/security standard (template proven on agent-gate): - codeql.yml: security-extended SAST on push/PR/weekly - dependabot.yml: weekly pip + github-actions updates - ci.yml: coverage-gated matrix (--cov=rag_guard --cov-fail-under=90; 99% covered) + self-contained coverage badge/PR-comment job; least-priv perms; SHA-pinned actions - pyproject: [tool.coverage.run] source + relative_files - SECURITY.md: private vuln disclosure policy - README: CodeQL + coverage badges + Reliability & security section Co-Authored-By: Claude Opus 4.8 --- .github/dependabot.yml | 19 ++++++++++++++++++ .github/workflows/ci.yml | 33 ++++++++++++++++++++++++++++---- .github/workflows/codeql.yml | 35 ++++++++++++++++++++++++++++++++++ .gitignore | 5 +++++ README.md | 12 ++++++++++++ SECURITY.md | 37 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 5 +++++ 7 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 SECURITY.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..08f7ddc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + groups: + python: + patterns: ["*"] + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + groups: + actions: + patterns: ["*"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19c356f..fa4da80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: branches: [main] pull_request: +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest @@ -13,9 +16,31 @@ jobs: matrix: python-version: ["3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: ${{ matrix.python-version }} - - run: pip install -e ".[dev]" - - run: python -m pytest -q + - run: pip install -e ".[dev]" pytest-cov + - name: Run tests + coverage gate + run: python -m pytest -q --cov=rag_guard --cov-report=term-missing --cov-fail-under=90 + + coverage: + # Coverage badge + per-PR comment (self-contained, no third-party service). + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: "3.12" + - run: pip install -e ".[dev]" coverage + - name: Measure coverage + run: | + coverage run -m pytest + coverage report + - name: Coverage badge + PR comment + uses: py-cov-action/python-coverage-comment-action@cc6cde2639ed103404ce0da9e75114ae9561e691 # v3 + with: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..10afb38 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,35 @@ +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: "27 3 * * 1" # weekly, Monday 03:27 UTC + +permissions: + contents: read + +jobs: + analyze: + name: Analyze (python) + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3 + with: + languages: python + queries: security-extended + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3 + with: + category: "/language:python" diff --git a/.gitignore b/.gitignore index fe28229..8ac90d3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ __pycache__/ *.egg-info/ build/ dist/ + +# coverage artifacts +.coverage +coverage.xml +htmlcov/ diff --git a/README.md b/README.md index e9b66fd..06d4a2f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # rag-guard [![ci](https://github.com/Jott2121/rag-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/Jott2121/rag-guard/actions/workflows/ci.yml) +[![CodeQL](https://github.com/Jott2121/rag-guard/actions/workflows/codeql.yml/badge.svg)](https://github.com/Jott2121/rag-guard/actions/workflows/codeql.yml) +[![Coverage](https://raw.githubusercontent.com/Jott2121/rag-guard/python-coverage-comment-action-data/badge.svg)](https://github.com/Jott2121/rag-guard/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Jott2121/rag-guard/blob/main/LICENSE) [![Python](https://img.shields.io/badge/Python-3.11+-3776AB?logo=python&logoColor=white)](https://www.python.org/) @@ -120,6 +122,16 @@ python bin/demo.py # see grounded answer, refus CI (badge above) runs the same suite across Python 3.11, 3.12, and 3.13 on every push. +## Reliability & security + +A guardrail library has to hold itself to its own bar, so the repo is gated: + +- **Coverage-gated test matrix** — pytest on Python 3.11–3.13, build fails below the coverage floor (currently 99% covered). +- **CodeQL** — `security-extended` static analysis on every push, PR, and weekly; findings surface in the Security tab. +- **Pinned supply chain** — GitHub Actions pinned to commit SHAs, kept current by **Dependabot**. +- **Branch protection** — `main` requires CI + CodeQL to pass before a merge. +- **Disclosure policy** — see [SECURITY.md](SECURITY.md); private reporting is enabled. + ## About Built by **Jeff Otterson** ([Jott2121](https://github.com/Jott2121)). Companion to [**agent-gate**](https://github.com/Jott2121/agent-gate) (an MCP gate for agent work), [**bow**](https://github.com/Jott2121/bow), [**fleet-mode**](https://github.com/Jott2121/fleet-mode), and [**agent-cost-attribution**](https://github.com/Jott2121/agent-cost-attribution). This one's job is simple: if the context can't back the answer, the answer doesn't ship. MIT. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..fa2bb39 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,37 @@ +# Security Policy + +## Supported versions + +`rag-guard` is actively maintained. Security fixes target the latest released +version on the `main` branch. + +| Version | Supported | +| ---------------- | --------- | +| latest (`main`) | ✅ | +| older tags | ❌ | + +## Reporting a vulnerability + +Please **do not** open a public issue for security vulnerabilities. + +Report privately through GitHub's +[**Report a vulnerability**](https://github.com/Jott2121/rag-guard/security/advisories/new) +flow (the repository's **Security → Advisories** tab). I aim to acknowledge +reports within 72 hours and to ship a fix or mitigation for confirmed issues as +quickly as is practical. + +When reporting, please include: + +- a description of the issue and its impact, +- steps to reproduce (a minimal proof-of-concept if possible), and +- any suggested remediation. + +## Scope + +`rag-guard` is a dependency-light retrieval guardrail library. Findings of +particular interest: prompt-injection or grounding bypass that defeats the +refusal logic, PII leakage past the redaction layer, unsafe deserialization, +and supply-chain risks in CI (this repository pins its GitHub Actions to commit +SHAs and runs CodeQL + Dependabot to reduce that surface). + +Thanks for helping keep it solid. diff --git a/pyproject.toml b/pyproject.toml index ed8ef42..af4de5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,3 +35,8 @@ include = ["rag_guard*"] [tool.pytest.ini_options] testpaths = ["tests"] + +[tool.coverage.run] +source = ["rag_guard"] +# relative_files lets the coverage-comment action resolve paths from a checkout +relative_files = true