Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/setup-gate-skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
a non-configurable rule-of-two trifecta invariant, with a hash-chained audit log.
28 changes: 28 additions & 0 deletions .github/workflows/code-scan.yml
Original file line number Diff line number Diff line change
@@ -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"
38 changes: 38 additions & 0 deletions .github/workflows/dependency-scan.yml
Original file line number Diff line number Diff line change
@@ -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
32 changes: 32 additions & 0 deletions .github/workflows/secret-scan.yml
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -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''',
]
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)).
Apache-2.0 license (see [LICENSE](./LICENSE)).
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -317,4 +343,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.
each other while doing it.
2 changes: 1 addition & 1 deletion docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
regression even if all tests pass. Check `test_m4_corpus.py::TestApprovalBudget`.
Loading