From a89859f71fd5ea649f4701de6b1690e741130142 Mon Sep 17 00:00:00 2001 From: chaksaray Date: Sat, 11 Jul 2026 07:54:15 +0700 Subject: [PATCH 1/3] Add CI security scan pipeline: code scan, dependency scan, secret scan Runs on every PR and push to develop/main: - code-scan: CodeQL Python analysis, results in the repo's code scanning alerts. - dependency-scan: actions/dependency-review-action (fails PRs that introduce high-severity vulnerable or license-incompatible deps) plus pip-audit against the actual installed dependency tree, PR-only since dependency-review compares base vs head. Upgrades pip/setuptools before auditing -- verified locally that a stock venv's bundled setuptools 59.6.0 carries three CVEs of its own, which would fail every run on bootstrap tooling rather than this project's real dependencies. - secret-scan: Gitleaks, full history fetch, push and PR. Also enabled at the repo level (not a file change, so not in this diff): secret scanning, secret scanning push protection, and Dependabot security updates -- all were "disabled", including secret scanning, which is normally free and on by default for public repos. None of these are required status checks yet -- the "Protect develop" ruleset has no required_status_checks rule, so right now these run and report but do not block a merge. That needs a separate ruleset update. --- .claude/skills/setup-gate-skill/SKILL.md | 2 +- .github/workflows/ci.yml | 69 ++++++++++++++++++++++++ CONTRIBUTING.md | 2 +- README.md | 2 +- docs/TESTING.md | 2 +- 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.claude/skills/setup-gate-skill/SKILL.md b/.claude/skills/setup-gate-skill/SKILL.md index 09642a5..786dd87 100644 --- a/.claude/skills/setup-gate-skill/SKILL.md +++ b/.claude/skills/setup-gate-skill/SKILL.md @@ -17,4 +17,4 @@ npx skills@latest add mattpocock/skills/handoff ## Key context this repo is a software of Runtime enforcement for MCP agents: capability manifests, session taint tracking, and -a non-configurable rule-of-two trifecta invariant, with a hash-chained audit log. \ No newline at end of file +a non-configurable rule-of-two trifecta invariant, with a hash-chained audit log. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fedaf49 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,69 @@ +name: CI Security Scans + +on: + pull_request: + branches: ["develop", "main"] + push: + branches: ["develop", "main"] + +permissions: + contents: read + +jobs: + code-scan: + name: Code Scan (CodeQL) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: python + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:python" + + dependency-scan: + name: Dependency Scan + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Audit installed dependencies for known vulnerabilities + run: | + # Upgrade pip/setuptools first: the venv's bootstrap setuptools is + # old enough to carry its own CVEs, which would fail every run on + # build tooling rather than this project's actual dependencies. + pip install --quiet --upgrade pip setuptools + pip install --quiet pip-audit + pip install --quiet -e ".[dev,ops]" + pip-audit + + secret-scan: + name: Secret Leak Scan (Gitleaks) + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run Gitleaks + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7e61d8..84c7758 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,4 +116,4 @@ Do not open a public issue or PR that demonstrates a security bypass. See ## License By contributing, you agree your contribution is licensed under this project's -Apache-2.0 license (see [LICENSE](./LICENSE)). \ No newline at end of file +Apache-2.0 license (see [LICENSE](./LICENSE)). diff --git a/README.md b/README.md index 8c3c884..91e16c0 100644 --- a/README.md +++ b/README.md @@ -317,4 +317,4 @@ touches `policy/`, `taint/`, `audit/`, or `integrity/`: behavior that diverges f the spec needs a DESIGN.md change first, not a larger diff. Commits must carry a DCO sign-off (`git commit -s`); see [GOVERNANCE.md](./GOVERNANCE.md) for who decides what, and [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) for how we treat -each other while doing it. \ No newline at end of file +each other while doing it. diff --git a/docs/TESTING.md b/docs/TESTING.md index 546be2a..c3f896e 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -512,4 +512,4 @@ pytest tests/replay/ # attack replays ``` A change that adds approval prompts on the benign corpus (`tests/corpus/`) is a -regression even if all tests pass. Check `test_m4_corpus.py::TestApprovalBudget`. \ No newline at end of file +regression even if all tests pass. Check `test_m4_corpus.py::TestApprovalBudget`. From 6b7e79806ad875321d73393024ad2b1e2fb338c0 Mon Sep 17 00:00:00 2001 From: chaksaray Date: Sat, 11 Jul 2026 07:59:34 +0700 Subject: [PATCH 2/3] Fix secret-scan and dependency-scan CI failures found on first PR run Both new jobs failed immediately on PR #13's first run: - gitleaks/gitleaks-action requires a paid GITLEAKS_LICENSE for org-owned repos (bawbel is an org; the free tier is personal-account only). Switched to calling the gitleaks CLI directly -- the tool itself is fully open source (Apache-2.0), only the GitHub Action wrapper has the org-licensing gate. Version pinned (8.30.1) for reproducible runs rather than resolved at run time. - dependency-review-action needs the repo's Dependency graph feature enabled; it wasn't. Enabled via the vulnerability-alerts API (repo setting, not a file change). Running gitleaks against this repo's actual history (locally, before wiring it back in) found 6 "leaks" -- all in tests/test_m2_policy.py, which deliberately contains fake JWT/GitLab-PAT strings as fixtures for policy/manifest.py's own secret-scan detector, not real credentials. Added .gitleaks.toml with a path-scoped allowlist for that one file rather than disabling the check; verified locally it suppresses exactly those six and nothing else ("no leaks found"). --- .github/workflows/ci.yml | 11 +++++++++-- .gitleaks.toml | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .gitleaks.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fedaf49..278913d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,13 @@ jobs: with: fetch-depth: 0 - name: Run Gitleaks - uses: gitleaks/gitleaks-action@v2 + # gitleaks/gitleaks-action requires a paid license for org-owned repos + # (bawbel is an org; the free tier is personal-account-only). The + # gitleaks CLI itself is fully open source, so run it directly. + # Version is pinned for reproducible CI runs; bump deliberately. env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_VERSION: "8.30.1" + run: | + curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + | tar -xz -C /usr/local/bin gitleaks + gitleaks detect --source . --log-opts="--all" --redact --config .gitleaks.toml --exit-code 1 diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..3e4ad35 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,10 @@ +title = "bawbel-gate gitleaks config" + +[extend] +useDefault = true + +[allowlist] +description = "Known-fake credentials used as test fixtures for policy/manifest.py's own secret-scan detector, not real leaked secrets" +paths = [ + '''tests/test_m2_policy\.py''', +] From e66dcd6bd1eceee0994f64029611b18ec08470a2 Mon Sep 17 00:00:00 2001 From: chaksaray Date: Sat, 11 Jul 2026 08:11:42 +0700 Subject: [PATCH 3/3] Add CHANGELOG.md; README: security badges + gate.yaml sample Splits ci.yml into three separate workflow files (code-scan.yml, dependency-scan.yml, secret-scan.yml) -- GitHub's native workflow badges are per-workflow-file, not per-job, so a combined workflow can't produce three distinct badges. Same jobs, same triggers, just separated so each has its own README badge. README additions: - Badges for the three new security scans, pointing at develop (where they actually run continuously; main doesn't have them yet). - The gate.yaml sample from DESIGN.md 3.1, since the Quickstart section referenced --config gate.yaml several times without ever showing what one looks like. Verified against the real config loader (bawbel_gate.mux.config.load_config), not just pasted in -- it parses cleanly. CHANGELOG.md: Keep a Changelog format, single [Unreleased] section. No version has been tagged yet, so there's nothing to backdate against; the M0-M7 build stays in README's milestone table rather than being itemized here. Covers everything since that baseline: the multiplexer fix, hub fixes, console dashboard, governance docs, PyPI packaging, and this CI security pipeline. --- .github/workflows/ci.yml | 76 --------------------------- .github/workflows/code-scan.yml | 28 ++++++++++ .github/workflows/dependency-scan.yml | 38 ++++++++++++++ .github/workflows/secret-scan.yml | 32 +++++++++++ CHANGELOG.md | 56 ++++++++++++++++++++ README.md | 26 +++++++++ 6 files changed, 180 insertions(+), 76 deletions(-) delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/code-scan.yml create mode 100644 .github/workflows/dependency-scan.yml create mode 100644 .github/workflows/secret-scan.yml create mode 100644 CHANGELOG.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 278913d..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: CI Security Scans - -on: - pull_request: - branches: ["develop", "main"] - push: - branches: ["develop", "main"] - -permissions: - contents: read - -jobs: - code-scan: - name: Code Scan (CodeQL) - runs-on: ubuntu-latest - permissions: - contents: read - security-events: write - steps: - - uses: actions/checkout@v4 - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: python - - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:python" - - dependency-scan: - name: Dependency Scan - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' - permissions: - contents: read - pull-requests: write - steps: - - uses: actions/checkout@v4 - - name: Dependency Review - uses: actions/dependency-review-action@v4 - with: - fail-on-severity: high - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - name: Audit installed dependencies for known vulnerabilities - run: | - # Upgrade pip/setuptools first: the venv's bootstrap setuptools is - # old enough to carry its own CVEs, which would fail every run on - # build tooling rather than this project's actual dependencies. - pip install --quiet --upgrade pip setuptools - pip install --quiet pip-audit - pip install --quiet -e ".[dev,ops]" - pip-audit - - secret-scan: - name: Secret Leak Scan (Gitleaks) - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Run Gitleaks - # gitleaks/gitleaks-action requires a paid license for org-owned repos - # (bawbel is an org; the free tier is personal-account-only). The - # gitleaks CLI itself is fully open source, so run it directly. - # Version is pinned for reproducible CI runs; bump deliberately. - env: - GITLEAKS_VERSION: "8.30.1" - run: | - curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ - | tar -xz -C /usr/local/bin gitleaks - gitleaks detect --source . --log-opts="--all" --redact --config .gitleaks.toml --exit-code 1 diff --git a/.github/workflows/code-scan.yml b/.github/workflows/code-scan.yml new file mode 100644 index 0000000..e2f41c2 --- /dev/null +++ b/.github/workflows/code-scan.yml @@ -0,0 +1,28 @@ +name: Code Scan + +on: + pull_request: + branches: ["develop", "main"] + push: + branches: ["develop", "main"] + +permissions: + contents: read + +jobs: + code-scan: + name: Code Scan (CodeQL) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: python + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:python" diff --git a/.github/workflows/dependency-scan.yml b/.github/workflows/dependency-scan.yml new file mode 100644 index 0000000..9088b8e --- /dev/null +++ b/.github/workflows/dependency-scan.yml @@ -0,0 +1,38 @@ +name: Dependency Scan + +on: + pull_request: + branches: ["develop", "main"] + push: + branches: ["develop", "main"] + +permissions: + contents: read + +jobs: + dependency-scan: + name: Dependency Scan + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Audit installed dependencies for known vulnerabilities + run: | + # Upgrade pip/setuptools first: the venv's bootstrap setuptools is + # old enough to carry its own CVEs, which would fail every run on + # build tooling rather than this project's actual dependencies. + pip install --quiet --upgrade pip setuptools + pip install --quiet pip-audit + pip install --quiet -e ".[dev,ops]" + pip-audit diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml new file mode 100644 index 0000000..9411b0f --- /dev/null +++ b/.github/workflows/secret-scan.yml @@ -0,0 +1,32 @@ +name: Secret Scan + +on: + pull_request: + branches: ["develop", "main"] + push: + branches: ["develop", "main"] + +permissions: + contents: read + +jobs: + secret-scan: + name: Secret Leak Scan (Gitleaks) + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run Gitleaks + # gitleaks/gitleaks-action requires a paid license for org-owned repos + # (bawbel is an org; the free tier is personal-account-only). The + # gitleaks CLI itself is fully open source, so run it directly. + # Version is pinned for reproducible CI runs; bump deliberately. + env: + GITLEAKS_VERSION: "8.30.1" + run: | + curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + | tar -xz -C /usr/local/bin gitleaks + gitleaks detect --source . --log-opts="--all" --redact --config .gitleaks.toml --exit-code 1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..59dc274 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,56 @@ +# Changelog + +All notable changes to this project are documented here. Format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows +[Semantic Versioning](https://semver.org/). + +No version has been tagged or released yet. The M0-M7 build (project scaffold +through policy engine, integrity pinning, operations layer, embedded console, +and bawbel-hub) is documented as milestones in README's "What is built" table +rather than itemized here; this file picks up from that baseline. + +## [Unreleased] + +### Added + +- MCP multiplexer request loop (`mux/proxy.py`): `bawbel-gate serve` and + `serve --console` previously did nothing -- the enforce/learn loop was a + stub. Now spawns upstream servers, merges/namespaces `tools/list`, routes + every `tools/call` through the policy engine, forwards on allow/approve, + denies without touching upstream, and writes the hash-chained audit trail. +- Real console dashboard (sessions/servers tables, pending-approvals panel, + live audit feed via SSE), replacing a bare JSON dump. +- Minimal browser UI for `bawbel-hub` (`/` route, `?t=` query-token auth), + per DESIGN.md 14.4. +- `GOVERNANCE.md` and `CODE_OF_CONDUCT.md`. +- Full PyPI packaging: complete metadata, trusted-publishing workflow + (auto to TestPyPI, manual-approval gate to production PyPI). +- CI security pipeline on every PR and push to `develop`/`main`: CodeQL + (code-scan.yml), Dependency Review + pip-audit (dependency-scan.yml), + Gitleaks (secret-scan.yml). Repo-level secret scanning, push protection, + and Dependabot security updates enabled. +- OpenSSF Scorecard workflow and badge. + +### Fixed + +- `bawbel-hub`'s `/v1/enroll` required the admin bearer token an unenrolled + gate doesn't have yet -- the single-use enrollment token is itself the + credential (DESIGN.md 14.3). The documented enroll flow was unreachable. +- `ConsoleServer` used a plain `http.server.HTTPServer` (one request at a + time), so a single open `/v1/events` SSE connection starved every other + request, including the page itself. Switched to `ThreadingHTTPServer`. +- `_schemas.py` located `schemas/` via a repo-root-relative path that only + resolves inside an editable/source checkout. A real `pip install + bawbel-gate` wheel would have raised `FileNotFoundError` on every + schema-dependent command. Schemas are now bundled into the wheel and + `_schemas.py` prefers the packaged copy. +- sdist `include` only adds to hatchling's default "everything not + gitignored" file set rather than replacing it, so untracked working-tree + files (`.agents/`, ~150K of unrelated content) were leaking into every + build. Switched to `only-include`, a strict allowlist. + +### Security + +- Documented (`SECURITY.md`, `.gitleaks.toml`) and enforced (branch + ruleset: required signed commits, required PR review) hygiene that was + previously either aspirational or entirely absent. diff --git a/README.md b/README.md index 91e16c0..ba4f356 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ [![Tests](https://img.shields.io/badge/tests-327%20passing-brightgreen)](#install-development) [![Fail-closed](https://img.shields.io/badge/fail--closed-enforced-critical)](./DESIGN.md) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/bawbel/gate/badge)](https://scorecard.dev/viewer/?uri=github.com/bawbel/gate) +[![Code Scan](https://github.com/bawbel/gate/actions/workflows/code-scan.yml/badge.svg?branch=develop)](https://github.com/bawbel/gate/actions/workflows/code-scan.yml) +[![Dependency Scan](https://github.com/bawbel/gate/actions/workflows/dependency-scan.yml/badge.svg?branch=develop)](https://github.com/bawbel/gate/actions/workflows/dependency-scan.yml) +[![Secret Scan](https://github.com/bawbel/gate/actions/workflows/secret-scan.yml/badge.svg?branch=develop)](https://github.com/bawbel/gate/actions/workflows/secret-scan.yml) [![Security Policy](https://img.shields.io/badge/security-policy-blue)](./SECURITY.md) [![Contributor Covenant](https://img.shields.io/badge/code_of_conduct-2.1-blueviolet)](./CODE_OF_CONDUCT.md) [![DCO](https://img.shields.io/badge/DCO-required-lightgrey)](./CONTRIBUTING.md#pull-requests) @@ -176,6 +179,28 @@ Point your MCP client at the gate instead of your servers directly: } ``` +`gate.yaml` holds the upstream server definitions plus one manifest path per server +(see DESIGN.md 3.1 for the full annotated version): + +```yaml +schema: bawbel/gate-config/v1 +audit_log: /var/log/bawbel/gate.audit.jsonl +approval: + channel: terminal # terminal | webhook | slack + webhook_url: null + timeout_seconds: 120 + on_timeout: deny +servers: + - name: github + command: github-mcp + args: [] + manifest: /etc/bawbel/manifests/github-mcp.cap.yaml + - name: filesystem + command: fs-mcp + args: ["--root", "/work"] + manifest: /etc/bawbel/manifests/fs-mcp.cap.yaml +``` + No manifests yet? Run learning mode first: ```bash @@ -291,6 +316,7 @@ Six event classes emitted as structured records regardless of exporter: - [IMPLEMENTATION_PLAN.md](./IMPLEMENTATION_PLAN.md) - milestone sequence. - [BAWBEL_GATE_MITIGATIONS_SPEC.md](./BAWBEL_GATE_MITIGATIONS_SPEC.md) - enforcement policy details. - [docs/LANGUAGE.md](./docs/LANGUAGE.md) - canonical terminology. +- [CHANGELOG.md](./CHANGELOG.md) - notable changes since the M0-M7 baseline. ## Related projects