From 9e41aaee9e37f269db1d12186ad95a621c859c82 Mon Sep 17 00:00:00 2001
From: Dishanth C A <93441414+Dishanth234@users.noreply.github.com>
Date: Mon, 31 Aug 2026 12:16:30 -0400
Subject: [PATCH] Fix wildcard detection (#11), dead repo URLs, and duplicate
CI steps
---
.github/ISSUE_TEMPLATE/config.yml | 2 +-
.github/workflows/ci.yml | 2 --
CHANGELOG.md | 15 +++++++++++++--
CONTRIBUTING.md | 6 +++---
grantguard/core/detectors.py | 11 +++++++----
grantguard/web/app.js | 4 ++--
tests/test_detectors.py | 18 ++++++++++++++++++
7 files changed, 44 insertions(+), 14 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index e7e6176..a8b28b0 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -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.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2a240e6..68ea892 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -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 }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cb0444..a7ce4f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
@@ -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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d764c85..2a7989b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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
```
@@ -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
diff --git a/grantguard/core/detectors.py b/grantguard/core/detectors.py
index 6e90e15..f4faee3 100644
--- a/grantguard/core/detectors.py
+++ b/grantguard/core/detectors.py
@@ -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,
@@ -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",
@@ -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",
)
diff --git a/grantguard/web/app.js b/grantguard/web/app.js
index 34e3667..7a665a6 100644
--- a/grantguard/web/app.js
+++ b/grantguard/web/app.js
@@ -52,7 +52,7 @@ const CHEVRON_EL = svgEl(svgIcon(``));
const X_SVG = ``;
const LINKEDIN_SVG = ``;
-const REPO_URL = "https://github.com/VantaInc/grantguard";
+const REPO_URL = "https://github.com/OpenVanta/GrantGuard";
// ── Global state ─────────────────────────────────────────────────────────────
const state = {
@@ -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,
diff --git a/tests/test_detectors.py b/tests/test_detectors.py
index 9761edf..345482a 100644
--- a/tests/test_detectors.py
+++ b/tests/test_detectors.py
@@ -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)