A Rust-based WASM security guard implementing DIFC (Decentralized Information Flow Control) for GitHub MCP servers.
The GitHub Guard labels all GitHub operations and data for the MCP Gateway reference monitor. The guard runs as a sandboxed WebAssembly module loaded by the MCP Gateway.
The MCP Gateway's Decentralized Information Flow Control (DIFC) relies on two types of labels to enforce security policies:
- Rule: Data can only be consumed by agents with equal or higher secrecy clearance
- Effect: Prevents private data from leaking to unauthorized agents
- Example: A response labeled
private:org/repocan only be read by agents withprivate:org/repoclearance in their secrecy label
- Rule: Agents can only trust data with the same or greater endorsement than their own
- Effect: Prevents untrusted data from influencing high-integrity operations
- Example: An agent with
unapproved:org/repointegrity will reject data with only ∅ (empty) integrity
| Agent Labels | Data/Resource Labels | Can Read? | Can Write? | Reason |
|---|---|---|---|---|
Secrecy: [], Integrity: [approved:X, unapproved:X] |
Secrecy: [], Integrity: [approved:X, unapproved:X] |
✅ Yes | ✅ Yes | Labels match |
Secrecy: [private:X], Integrity: [approved:X, unapproved:X] |
Secrecy: [private:X], Integrity: [approved:X, unapproved:X] |
✅ Yes | ✅ Yes | Agent has full clearance |
Secrecy: [], Integrity: [approved:X, unapproved:X] |
Secrecy: [private:X], Integrity: [approved:X, unapproved:X] |
❌ No | ✅ Yes | Read blocked (no clearance), write OK (no secrets to leak) |
Secrecy: [private:X], Integrity: [approved:X, unapproved:X] |
Secrecy: [], Integrity: [] |
❌ No | ❌ No | Read blocked (low integrity), write blocked (would leak secrets) |
Secrecy: [], Integrity: [] |
Secrecy: [], Integrity: [approved:X, unapproved:X] |
✅ Yes | ❌ No | Read OK, write blocked (agent integrity too low) |
Secrecy: [private:X], Integrity: [unapproved:X] |
Secrecy: [], Integrity: [approved:X, unapproved:X] |
✅ Yes | ❌ No | Read OK, write blocked (would leak private data AND integrity too low) |
Read rule: Agent secrecy ⊇ data secrecy AND agent integrity ⊆ data integrity
Write rule: Agent secrecy ⊆ resource secrecy AND agent integrity ⊇ resource required integrity
The guard exposes three WASM entrypoints used by the gateway:
-
label_agent- Purpose: initialize the agent/session labels from policy input.
- Input: session policy (
allow-onlypayload). - Output: agent secrecy/integrity labels,
difc_mode, and normalized policy metadata.
-
label_resource- Purpose: label a tool invocation before execution.
- Input:
tool_name+tool_args. - Output: resource labels (
description,secrecy,integrity) and operation type (read,write,read-write). - Enforcement role: the operation type determines which DIFC flow rule is evaluated between agent labels and resource labels before write-capable execution.
- Assumption:
readoperations are treated as side-effect-free, so they are safe to execute and then forward tolabel_response.
-
label_response- Purpose: label tool results after backend execution.
- Input:
tool_name,tool_args,tool_result. - Output: fine-grained labels (preferred path-based format), or legacy item-labeled output for singleton/fallback cases.
For a guarded request, the effective flow is:
- Session init: call
label_agentonce to establish session labels/policy context. - Before tool execution: call
label_resourcefor the requested tool. - Operation-aware DIFC evaluation (from
label_resource.operation):read: execute the backend call (read-only calls are side-effect-free).write: evaluate write flow rule using agent + resource labels before backend execution.read-write: evaluate required rules for write-capable operations using agent + resource labels.
- Apply evaluation result:
- if write-capable DIFC rule evaluation fails, backend execution is skipped and
label_responseis not called. - otherwise, gateway executes the backend tool call.
- if write-capable DIFC rule evaluation fails, backend execution is skipped and
- After tool execution: call
label_responseto attach result labels for the returned payload. - Final enforcement/filtering: gateway applies DIFC checks/filters using session + resource/response labels.
sequenceDiagram
participant Client
participant Gateway
participant Guard as WASM Guard
participant GitHub as GitHub Backend
Client->>Gateway: New guarded session
Gateway->>Guard: label_agent(policy)
Guard-->>Gateway: agent labels + difc_mode + normalized policy
Client->>Gateway: Tool request (tool_name, tool_args)
Gateway->>Guard: label_resource(tool_name, tool_args)
Guard-->>Gateway: resource labels + operation
alt DIFC evaluation fails for write-capable call
Gateway-->>Client: Block write-capable operation
else Resource check allowed
Gateway->>GitHub: Execute tool call
GitHub-->>Gateway: tool_result
Gateway->>Guard: label_response(tool_name, tool_args, tool_result)
alt label_response returns 0
Guard-->>Gateway: Skip fine-grained response labels
else Path/legacy labels available
Guard-->>Gateway: Labeled response payload
end
Gateway-->>Client: Enforced/filtered result
end
- Operation-driven evaluation:
label_resource.operationdetermines which DIFC rule(s) are evaluated from agent + resource labels. - Write-capable enforcement: if DIFC evaluation fails for
write/read-write, backend invocation is skipped andlabel_responseis not called. - Read-only execution:
readcalls execute and their results are forwarded tolabel_response. - Response-stage short-circuit: when
label_responsereturns0, the guard is signaling “skip fine-grained labeling” (for example, parse/serialization/buffer issues), and the gateway continues without path/item-level response labels. - Guard fallback inside
label_response: when path-based labeling is not available, the guard falls back to legacy item labeling and can create a single-item fallback label so singleton responses are still labeled.
The guard's behavior in this repository is controlled by AllowOnly policy payloads passed via MCP_GATEWAY_GUARD_POLICY_JSON.
| Mode | AllowOnly Policy | Effect |
|---|---|---|
| YOLO | N/A (no guard) | No protection - all data flows freely |
| Public-Only | {"allow-only":{"repos":"public","min-integrity": "none"}} |
Public-safe filtering with lowest integrity floor |
| Owner-Only | {"allow-only":{"repos":["<owner>/*"],"min-integrity": "none"}} |
Owner-scoped filtering |
| Repo-Only | {"allow-only":{"repos":["<owner>/<repo>"],"min-integrity": "none"}} |
Repo-scoped filtering pinned to one repo |
YOLO Mode: The guard is not loaded. No labels are checked.
Public-Only Mode:
- ✅ Public data remains accessible
- ❌ Private data is filtered/blocked
- ✅ Integrity floor is
None
Owner-Only Mode:
- ✅ Data in owner scope can be accessed when labels satisfy policy
- ❌ Data outside owner scope is blocked/filtered
- ✅ Integrity floor is
none(runner default)
Repo-Only Mode:
- ✅ Data in scoped repo can be accessed when labels satisfy policy
- ❌ Data outside scoped repo is blocked/filtered
- ✅ Scope is pinned by
DIFC_SCOPE(defaultlpcox/github-guard)
label_agent returns difc_mode, which tells the gateway how to enforce labels:
filter: enforce by filtering/removing disallowed data while preserving allowed portions of the response.strict: enforce as hard deny when checks fail (no partial filtered success).propagate: preserve and forward labels for downstream DIFC handling rather than applying this guard's filtering behavior.
In this repository's operating modes, AllowOnly policies use filter mode.
- ✅ Complete GitHub MCP Coverage: Handles all GitHub MCP server tools
- ✅ DIFC Enforcement: Implements integrity and secrecy flow control
- ✅ Fine-grained Labeling: Labels individual items in collection responses
- ✅ Backend Integration: Calls GitHub APIs to verify contributor status
- ✅ Sensitive Content Detection: Identifies secrets and security issues
- ✅ WASM Sandboxing: Runs as an isolated WebAssembly module (~170KB)
The guard applies security labels to all GitHub data:
| Repository Type | Secrecy Label |
|---|---|
| Public repo | [] (empty - anyone can read) |
| Private repo | ["private:<owner>/<repo>"] |
| Job logs, secret scanning alerts, sensitive files, artifact downloads | ["private:<owner>/<repo>"] (always, even for public repos) |
Integrity labels are hierarchical: merged > approved > unapproved > none
| Object Type | Condition | Integrity |
|---|---|---|
| Repository metadata | Always | approved + unapproved |
| Commits | On default branch | merged + approved + unapproved |
| Commits | On feature branch | unapproved (or elevated by policy evidence) |
| Pull Requests | Merged | merged + approved + unapproved |
| Pull Requests | Open/unmerged (human) | unapproved (or elevated by policy evidence) |
| Pull Requests | From bots | approved + unapproved |
| Issues/Comments | From repo owner | approved + unapproved |
| Issues/Comments | From bots | approved + unapproved |
| Issues/Comments | From verified contributor | unapproved |
| Issues/Comments | From unknown author | [] (untrusted) |
| Security alerts | Code/Dependabot scanning | approved + unapproved |
| Secret scanning | Alerts reference secrets | approved + unapproved, secrecy: secret |
Note: A "verified contributor" is a user with at least one merged PR in the repository. The guard queries the GitHub API to check this at runtime.
For detailed labeling rules, see docs/LABELING.md.
See How AllowOnly Policies Create Operating Modes above for how these work.
| Mode | Use Case | Recommended For |
|---|---|---|
| YOLO | Development without security overhead | Local development, debugging |
| Public-Only | Prevent private data leakage | Open source work, public data analysis |
| Owner-Only | Owner-scoped filtering | Owner-wide protected workflows |
| Repo-Only | Repo-scoped filtering pinned to one repository | Scoped private repo testing and guarded automation |
Configuration: Set DIFC_SCOPE environment variable to specify the scoped repository (default: lpcox/github-guard). This applies to repo-only mode.
-
Rust (latest stable)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
WASI target
rustup target add wasm32-wasip1
# Build the WASM module
make build
# Run default test pipeline
make test
# See all available targets
make helpThis produces github-guard-rust.wasm (~170KB), ready to be loaded by the MCP Gateway.
- Main Documentation - Complete overview, features, and usage guide
- Labeling Specification - How DIFC labels are applied
- Quick Start Guide - Get started quickly
- Testing Guide - Information about tests and coverage
- Implementation Details - Technical implementation details
- Security Summary - Security analysis and findings
| Target | Description |
|---|---|
make or make all |
Build WASM module and run tests |
make build |
Build the Rust WASM module |
make test |
Run default pipeline (build, unit, wasm, integration, integrity) |
make test-all |
Run default pipeline plus Copilot test |
make test-unit |
Run Rust unit tests only |
make test-wasm |
Verify WASM build |
make test-integration |
Run integration tests (requires Docker + .env) |
make test-integrity-harness |
Run corpus-driven WASM integrity harness tests |
make capture-integrity-corpus |
Refresh integrity corpus fixture from live OSS repos |
make test-copilot |
Run Copilot CLI in yolo mode (default) |
make test-copilot-yolo |
Run Copilot CLI with no guard (development) |
make test-copilot-public-only |
Run Copilot CLI filtering private data (public repos only) |
make test-copilot-owner-only |
Run Copilot CLI in owner-scoped mode (ALLOW_OWNER, default lpcox) |
make test-copilot-repo-only |
Run Copilot CLI in repo-scoped mode (DIFC_SCOPE) |
make clean |
Remove build artifacts |
| `make release patch | minor |
make help |
Show help message |
Integration tests use Docker containers to test the guard with the MCP Gateway:
# Create .env file with your GitHub token (never commit this!)
echo 'GITHUB_TOKEN=ghp_your_token_here' > .env
# Run integration tests
make test-integrationgithub-guard/
├── rust-guard/ # Rust WASM implementation
│ ├── src/
│ │ ├── lib.rs # Main entry point, exports
│ │ ├── labels/ # DIFC label generation (refactored module)
│ │ │ ├── mod.rs # Module exports
│ │ │ ├── constants.rs # Label constants and limits
│ │ │ ├── helpers.rs # Label creation helpers
│ │ │ ├── backend.rs # Backend API calls
│ │ │ ├── tool_rules.rs # Tool-specific labeling rules
│ │ │ ├── response_paths.rs # Path-based response labeling
│ │ │ ├── response_items.rs # Item-based response labeling
│ │ │ └── README.md # Module documentation
│ │ ├── tools.rs # Tool classification
│ │ └── permissions.rs # Permission helpers
│ ├── Cargo.toml
│ └── build.sh
├── docs/ # Documentation
│ ├── README.md # Main documentation
│ ├── LABELING.md # Labeling specification
│ ├── QUICKSTART.md # Quick start guide
│ └── ...
├── scripts/ # Helper scripts
├── Makefile # Build and test automation
└── config.example.json # Configuration example
See LICENSE file for details.