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
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/VantaInc/grantguard/security/advisories/new
url: https://github.com/OpenVanta/GrantGuard/security/advisories/new
about: Please report security issues privately, not as public issues.
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ jobs:
python-version: ["3.10", "3.14"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
Expand Down
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- Detectors now classify colon-syntax prefix wildcards (`Bash(cmd:*)`, the form
Claude Code itself writes for "always allow") the same as the equivalent
space form (`Bash(cmd *)`), and flag bare tool-wide wildcards such as
`Bash(*)` as overbroad instead of safe. ([#11])
- Dead `VantaInc/grantguard` links now point to `OpenVanta/GrantGuard`: the
CONTRIBUTING clone command, this changelog's link references, the issue
chooser's security-advisory link, and the web UI's share buttons.

[#11]: https://github.com/OpenVanta/GrantGuard/issues/11

## [0.1.0] - 2026-07-07

Initial release.
Expand Down Expand Up @@ -35,5 +46,5 @@ Initial release.
`Origin`, resisting DNS-rebinding and CSRF against the write endpoint, and
caps request body size.

[Unreleased]: https://github.com/VantaInc/grantguard/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/VantaInc/grantguard/releases/tag/v0.1.0
[Unreleased]: https://github.com/OpenVanta/GrantGuard/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/OpenVanta/GrantGuard/releases/tag/v0.1.0
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ coverage are all very welcome.
## Local setup

```bash
git clone https://github.com/VantaInc/grantguard.git
cd grantguard
git clone https://github.com/OpenVanta/GrantGuard.git
cd GrantGuard
uv run grantguard.py audit # CLI, dry-run
uv run grantguard.py # local web UI
```
Expand All @@ -37,7 +37,7 @@ the code looking consistent:
- **[tsgo](https://github.com/microsoft/typescript-go)** (`typescript@rc`) — the
new Go-based TypeScript compiler. We use it in `--checkJs` mode to catch type
errors in the JS without a compile step.
- **[oxfmt](https://github.com/nicolo-ribaudo/oxfmt)** — a fast JS/TS formatter
- **[oxfmt](https://oxc.rs/docs/guide/usage/formatter)** — a fast JS/TS formatter
built on oxc.

```bash
Expand Down
11 changes: 7 additions & 4 deletions grantguard/core/detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ def masked_text(self) -> str:
)
]

# In Claude Code rules, `cmd:*` and `cmd *` are equivalent prefix wildcards,
# and the colon form is what "always allow" writes — so patterns accept both.
DESTRUCTIVE_DETECTORS = [
PatternDetector(
category=RiskCategory.DESTRUCTIVE,
Expand All @@ -180,8 +182,8 @@ def masked_text(self) -> str:
r"\brmdir\s+/s", # Windows recursive rmdir
r"\bdel\s+/[a-z]", # Windows del /q /s
r"Remove-Item\b.*-Recurse", # PowerShell
r"git\s+reset\s+\*",
r"git\s+rebase\s+\*",
r"git\s+reset[\s:]+\*",
r"git\s+rebase[\s:]+\*",
r"\bkill\s+-9\b",
r"\bpkill\b",
r"\bxargs\s+kill",
Expand All @@ -206,8 +208,9 @@ def masked_text(self) -> str:
pattern=re.compile(pattern_source),
)
for pattern_source in (
r"^Bash\((?:sudo\s+)?[\w./\\-]+\s+\*\)$", # `tool *`
r"git\s+(?:clone|add|commit|fetch|merge|pull|checkout|ls-remote|rev-list|ls-tree)\s+\*",
r"^(?:\w{1,64}\(\*\)|\*)$", # `Tool(*)` / bare `*`
r"^Bash\((?:sudo\s+)?[\w./\\-]+[\s:]+\*\)$", # `tool *` / `tool:*`
r"git\s+(?:clone|add|commit|fetch|merge|pull|checkout|ls-remote|rev-list|ls-tree)[\s:]+\*",
r"(?:npm|pip|pip3|npx|gh|cargo|brew|apt|yum|choco)\b.*\*",
r"chmod\s+\+x",
)
Expand Down
4 changes: 2 additions & 2 deletions grantguard/web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const CHEVRON_EL = svgEl(svgIcon(`<path d="M9 6l6 6l-6 6"/>`));
const X_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>`;
const LINKEDIN_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/></svg>`;

const REPO_URL = "https://github.com/VantaInc/grantguard";
const REPO_URL = "https://github.com/OpenVanta/GrantGuard";

// ── Global state ─────────────────────────────────────────────────────────────
const state = {
Expand Down Expand Up @@ -670,7 +670,7 @@ class GgShareModal extends HTMLElement {
ta,
h("div", { class: "modal-link" }, [
"Shares ",
h("code", { text: "github.com/VantaInc/grantguard" }),
h("code", { text: "github.com/OpenVanta/GrantGuard" }),
]),
h("div", { class: "modal-actions" }, [
status,
Expand Down
18 changes: 18 additions & 0 deletions tests/test_detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ def test_buckets(self):
for text, expected in cases.items():
self.assertIs(C(text), expected, text)

def test_bare_tool_wildcards_are_overbroad(self):
for text in ("Bash(*)", "Skill(*)", "*"):
self.assertIs(C(text), RiskCategory.OVERBROAD, text)

def test_colon_and_space_wildcard_syntax_classify_identically(self):
pairs = (
("Bash(nc *)", "Bash(nc:*)"),
("Bash(sudo cp *)", "Bash(sudo cp:*)"),
("Bash(git reset *)", "Bash(git reset:*)"),
("Bash(git rebase *)", "Bash(git rebase:*)"),
("Bash(git add *)", "Bash(git add:*)"),
("Bash(git push *)", "Bash(git push:*)"),
("Bash(npm install *)", "Bash(npm install:*)"),
)
for space_form, colon_form in pairs:
self.assertIsNot(C(space_form), RiskCategory.SAFE, space_form)
self.assertIs(C(colon_form), C(space_form), colon_form)

def test_placeholder_not_flagged_as_secret(self):
self.assertIsNot(C('Bash(curl -H "X-Api-Key: __TRACKED_VAR__" https://x.y)'),
RiskCategory.SECRET)
Expand Down