Skip to content
Open
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
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ repos:
# already corrected and staged-ready instead of as an instruction.
- repo: local
hooks:
- id: check-forbidden-terms-sync
name: check-forbidden-terms-sync (canonical scrub terms vs publication.py)
language: system
entry: python3 tools/dev/sync-forbidden-terms.py --check
files: ^(tools/vcs/src/magpie_vcs/publication\.py|tools/dev/sync-forbidden-terms\.py|plugins/magpie-security/hooks/forbidden-terms\.(json|ts))$
pass_filenames: false
- id: check-family-plugins
name: check-family-plugins (marketplace plugins vs frontmatter)
language: system
Expand Down
58 changes: 58 additions & 0 deletions docs/setup/secure-agent-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
- [Extensible — any skill can contribute a guard](#extensible--any-skill-can-contribute-a-guard)
- [Install (user-scope)](#install-user-scope-1)
- [Verify](#verify-1)
- [Public-surface scrub mod (Claude Code plugin / hook)](#public-surface-scrub-mod-claude-code-plugin--hook)
- [Term canonicalization](#term-canonicalization)
- [Scoping and fail-closed gates](#scoping-and-fail-closed-gates)
- [Trade-offs and runtime characteristics](#trade-offs-and-runtime-characteristics)
- [Sandbox-error hint hook](#sandbox-error-hint-hook)
- [Why install it](#why-install-it)
- [Why install it user-scope, not project-scope](#why-install-it-user-scope-not-project-scope-1)
Expand Down Expand Up @@ -1778,6 +1782,60 @@ Expected: a JSON object with `permissionDecision: "deny"` and a
reason mentioning the `mention` guard. A plain command
(`{"tool_input":{"command":"ls"}}`) produces no output and `exit=0`.

## Public-surface scrub mod (Claude Code plugin / hook)

For Claude Code harnesses using plugins (such as `magpie-security`),
a deterministic TypeScript mod hook
([`plugins/magpie-security/hooks/public-surface-scrub.ts`](../../plugins/magpie-security/hooks/public-surface-scrub.ts))
intercepts `Bash` tool calls (`git commit`, `git tag`, `gh pr create|edit|review|comment`, `gh api`)
before execution to prevent accidental early disclosure of security vulnerabilities on public surfaces.

### Term canonicalization

The mod does not maintain a private list of forbidden terms. The forbidden
disclosure terms and regex patterns are derived directly from the canonical
single source of truth: `magpie_vcs.publication.DEFAULT_TERMS`
in `tools/vcs/src/magpie_vcs/publication.py` (via `tools/dev/sync-forbidden-terms.py`
and tracked by the pre-commit sync check).

### Scoping and fail-closed gates

- **Private tracker exemption**: Subcommands explicitly targeting the private security
tracker repository (`tracker_repo` in `project.md`, e.g. `-R airflow-s/airflow-s`)
are exempt.
- **Upstream repository protection**: Subcommands targeting the public codebase
(`upstream_repo` in `project.md`) or operating within an upstream working tree
are scrubbed. Commands targeting unrelated third-party or adopter documentation
repositories pass unchecked.
- **`cd <dir>` transition tracking**: Tracks working directory transitions across
composite subcommands (e.g. `cd ../upstream && git commit -m "..."`).
- **Deterministic git remote verification**: Repository scope is determined by
reading `.git/config` remotes rather than directory-name substring heuristics.
- **Fail-closed on indeterminate scope**: If the target repository scope cannot
be verified deterministically, commands containing embargo terms fail closed.
- **Fail-closed on unreadable files**: Invocations referencing message files
(`-F <file>` / `--file` / `--body-file`) fail closed if the referenced file cannot
be read.
- **Fail-closed on dynamic input**: Commands constructing commit or PR content
dynamically via variables (`$VAR`), command substitution (`$(...)`), stdin pipes
(`-`), or automatic reuse (`git commit --amend` without an explicit message,
`gh pr create --fill`) cannot be verified statically and fail closed. The block
explanation instructs providing text explicitly or via an inspectable message file
(`-F <file>`).

### Trade-offs and runtime characteristics

- **Unsandboxed execution**: Claude Code mods and hooks execute directly in the
local Node runtime outside the container sandbox.
- **File inspection**: The hook inspects message files specified by `-F <file>`
via the Claude Code plugin filesystem capability (`$.fs.read()`).
- **Human-only escape hatch**: When legitimate post-coordinated-disclosure
maintenance requires mentioning CVE IDs or vulnerability terms on public surfaces,
a human maintainer sets `MAGPIE_ALLOW_EMBARGO_TERMS=1` in the environment
before starting the session. The escape hatch serves as a zero-token deterministic
guard against accidental leaks; inline environment variables passed to the command
line (e.g. `MAGPIE_ALLOW_EMBARGO_TERMS=1 git commit ...`) are ignored by the hook.

## Sandbox-error hint hook

Companion to the *Sandbox-bypass visibility hook* above — a
Expand Down
97 changes: 97 additions & 0 deletions plugins/magpie-security/hooks/forbidden-terms.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
[
{
"name": "CVE id",
"pattern": "\\bCVE-\\d{4}-\\d{4,}\\b",
"flags": "i"
},
{
"name": "CVE",
"pattern": "\\bCVEs?\\b",
"flags": "i"
},
{
"name": "vulnerability",
"pattern": "\\bvulnerab(?:le|ility|ilities)\\b",
"flags": "i"
},
{
"name": "security fix",
"pattern": "\\bsecurity[\\s_-]+fix(?:es|ed)?\\b",
"flags": "i"
},
{
"name": "advisory",
"pattern": "\\badvisor(?:y|ies)\\b",
"flags": "i"
},
{
"name": "security@",
"pattern": "\\bsecurity@",
"flags": "i"
},
{
"name": "CVE-tool URL",
"pattern": "cveprocess\\.apache\\.org",
"flags": "i"
},
{
"name": "exploit",
"pattern": "\\bexploit(?:s|able|ed)?\\b",
"flags": "i"
},
{
"name": "SSRF",
"pattern": "\\bSSRF\\b",
"flags": "i"
},
{
"name": "XSS",
"pattern": "\\bXSS\\b",
"flags": "i"
},
{
"name": "CSRF",
"pattern": "\\bCSRF\\b",
"flags": "i"
},
{
"name": "RCE",
"pattern": "\\bRCE\\b",
"flags": "i"
},
{
"name": "SQL injection",
"pattern": "\\bSQL[\\s_-]+injection\\b",
"flags": "i"
},
{
"name": "path traversal",
"pattern": "\\b(?:path|directory)[\\s_-]+traversal\\b",
"flags": "i"
},
{
"name": "remote code execution",
"pattern": "\\b(?:remote|arbitrary)[\\s_-]+code[\\s_-]+execution\\b",
"flags": "i"
},
{
"name": "privilege escalation",
"pattern": "\\bprivilege[\\s_-]+escalation\\b",
"flags": "i"
},
{
"name": "auth bypass",
"pattern": "\\b(?:auth|authentication|authorization)[\\s_-]+bypass\\b",
"flags": "i"
},
{
"name": "buffer overflow",
"pattern": "\\b(?:buffer|heap|stack)[\\s_-]+overflow\\b",
"flags": "i"
},
{
"name": "use-after-free",
"pattern": "\\buse[\\s_-]+after[\\s_-]+free\\b",
"flags": "i"
}
]
49 changes: 49 additions & 0 deletions plugins/magpie-security/hooks/forbidden-terms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.0
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// Generated from tools/vcs/src/magpie_vcs/publication.py:DEFAULT_TERMS. Do not edit.

export interface CanonicalForbiddenTerm {
name: string;
pattern: string;
flags: string;
}

export const CANONICAL_FORBIDDEN_TERMS: ReadonlyArray<CanonicalForbiddenTerm> = [

{ name: "CVE id", pattern: "\\bCVE-\\d{4}-\\d{4,}\\b", flags: "i" },
{ name: "CVE", pattern: "\\bCVEs?\\b", flags: "i" },
{ name: "vulnerability", pattern: "\\bvulnerab(?:le|ility|ilities)\\b", flags: "i" },
{ name: "security fix", pattern: "\\bsecurity[\\s_-]+fix(?:es|ed)?\\b", flags: "i" },
{ name: "advisory", pattern: "\\badvisor(?:y|ies)\\b", flags: "i" },
{ name: "security@", pattern: "\\bsecurity@", flags: "i" },
{ name: "CVE-tool URL", pattern: "cveprocess\\.apache\\.org", flags: "i" },
{ name: "exploit", pattern: "\\bexploit(?:s|able|ed)?\\b", flags: "i" },
{ name: "SSRF", pattern: "\\bSSRF\\b", flags: "i" },
{ name: "XSS", pattern: "\\bXSS\\b", flags: "i" },
{ name: "CSRF", pattern: "\\bCSRF\\b", flags: "i" },
{ name: "RCE", pattern: "\\bRCE\\b", flags: "i" },
{ name: "SQL injection", pattern: "\\bSQL[\\s_-]+injection\\b", flags: "i" },
{ name: "path traversal", pattern: "\\b(?:path|directory)[\\s_-]+traversal\\b", flags: "i" },
{ name: "remote code execution", pattern: "\\b(?:remote|arbitrary)[\\s_-]+code[\\s_-]+execution\\b", flags: "i" },
{ name: "privilege escalation", pattern: "\\bprivilege[\\s_-]+escalation\\b", flags: "i" },
{ name: "auth bypass", pattern: "\\b(?:auth|authentication|authorization)[\\s_-]+bypass\\b", flags: "i" },
{ name: "buffer overflow", pattern: "\\b(?:buffer|heap|stack)[\\s_-]+overflow\\b", flags: "i" },
{ name: "use-after-free", pattern: "\\buse[\\s_-]+after[\\s_-]+free\\b", flags: "i" },
];
7 changes: 7 additions & 0 deletions plugins/magpie-security/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://code.claude.com/schemas/hooks.json",
"description": "Apache Magpie — Claude Code mod for deterministic public-surface scrub against unredacted security embargo leaks.",
"modules": [
"./public-surface-scrub.ts"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the first mod in magpie-security and the PR touches no docs. docs/setup/secure-agent-setup.md should cover its trade-offs (it runs unsandboxed and reads any -F path via $.fs.read) and how the escape hatch is set, and the issue-fix skill should mention it next to the scrub step so a maintainer knows where a block came from.

]
}
Loading
Loading