coding-agent-audit 0.9.0__tar.gz → 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coding_agent_audit-0.9.0/src/coding_agent_audit.egg-info → coding_agent_audit-0.10.0}/PKG-INFO +11 -1
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/README.md +10 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/pyproject.toml +1 -1
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/__init__.py +1 -1
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/configscan.py +90 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0/src/coding_agent_audit.egg-info}/PKG-INFO +11 -1
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/tests/test_configscan.py +59 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/LICENSE +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/setup.cfg +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/checks.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/cli.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/findings.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/iocs.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/mcp_server.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/agentaudit/phantomdeps.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/SOURCES.txt +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/dependency_links.txt +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/entry_points.txt +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/requires.txt +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/top_level.txt +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/tests/test_checks.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/tests/test_cli.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/tests/test_mcp_server.py +0 -0
- {coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/tests/test_phantomdeps.py +0 -0
{coding_agent_audit-0.9.0/src/coding_agent_audit.egg-info → coding_agent_audit-0.10.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coding-agent-audit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Scan your machine for known AI-coding-assistant supply-chain backdoor indicators (Claude Code / Cursor / Windsurf extensions, MCP servers) plus risky hook/permission config patterns. Usable as a CLI or as an MCP server.
|
|
5
5
|
Author-email: StackScribe AI <survivalagent320@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -134,6 +134,16 @@ second look rather than proof of compromise:
|
|
|
134
134
|
`authorized_keys`, use netcat in reverse-shell mode, etc.
|
|
135
135
|
- MCP server definitions that run an unpinned package (`npx -y pkg` with
|
|
136
136
|
no version pin)
|
|
137
|
+
- MCP server definitions explicitly pinned to a package version with a
|
|
138
|
+
disclosed, fixed RCE CVE (added in v0.10.0): `mcp-remote` below v0.1.16
|
|
139
|
+
(**CVE-2025-6514**, CVSS 9.6 — a malicious remote server's OAuth
|
|
140
|
+
`authorization_endpoint` response is passed unsanitized to the OS,
|
|
141
|
+
arbitrary command execution on just opening the client) and
|
|
142
|
+
`mcp-server-git` below v2025.12.18 (**CVE-2025-68143/68144/68145** —
|
|
143
|
+
Anthropic's own reference git MCP server, chainable via indirect prompt
|
|
144
|
+
injection into full RCE). Only an explicit old pin is flagged — an
|
|
145
|
+
unpinned install picks up the current, patched release and is already
|
|
146
|
+
covered by the generic unpinned-package finding above
|
|
137
147
|
- Auto-approved permission rules that are unscoped wildcards (e.g.
|
|
138
148
|
`Bash(*)`)
|
|
139
149
|
- The "lethal trifecta" pattern: an untrusted-content-fetching tool
|
|
@@ -113,6 +113,16 @@ second look rather than proof of compromise:
|
|
|
113
113
|
`authorized_keys`, use netcat in reverse-shell mode, etc.
|
|
114
114
|
- MCP server definitions that run an unpinned package (`npx -y pkg` with
|
|
115
115
|
no version pin)
|
|
116
|
+
- MCP server definitions explicitly pinned to a package version with a
|
|
117
|
+
disclosed, fixed RCE CVE (added in v0.10.0): `mcp-remote` below v0.1.16
|
|
118
|
+
(**CVE-2025-6514**, CVSS 9.6 — a malicious remote server's OAuth
|
|
119
|
+
`authorization_endpoint` response is passed unsanitized to the OS,
|
|
120
|
+
arbitrary command execution on just opening the client) and
|
|
121
|
+
`mcp-server-git` below v2025.12.18 (**CVE-2025-68143/68144/68145** —
|
|
122
|
+
Anthropic's own reference git MCP server, chainable via indirect prompt
|
|
123
|
+
injection into full RCE). Only an explicit old pin is flagged — an
|
|
124
|
+
unpinned install picks up the current, patched release and is already
|
|
125
|
+
covered by the generic unpinned-package finding above
|
|
116
126
|
- Auto-approved permission rules that are unscoped wildcards (e.g.
|
|
117
127
|
`Bash(*)`)
|
|
118
128
|
- The "lethal trifecta" pattern: an untrusted-content-fetching tool
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "coding-agent-audit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.10.0"
|
|
8
8
|
description = "Scan your machine for known AI-coding-assistant supply-chain backdoor indicators (Claude Code / Cursor / Windsurf extensions, MCP servers) plus risky hook/permission config patterns. Usable as a CLI or as an MCP server."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -154,6 +154,95 @@ def scan_hooks(config: dict, source_path: str) -> list[Finding]:
|
|
|
154
154
|
return findings
|
|
155
155
|
|
|
156
156
|
|
|
157
|
+
# Packages with a known, disclosed RCE/command-injection CVE in an old
|
|
158
|
+
# version range that still shows up pinned in copy-pasted configs/tutorials
|
|
159
|
+
# written before the fix shipped. `fixed_version` is the first version where
|
|
160
|
+
# the chain is fully closed; anything strictly older is still exploitable.
|
|
161
|
+
_KNOWN_VULNERABLE_PACKAGES = [
|
|
162
|
+
(
|
|
163
|
+
"mcp-remote",
|
|
164
|
+
(0, 1, 16),
|
|
165
|
+
("CVE-2025-6514",),
|
|
166
|
+
"OS command injection (CVSS 9.6): a malicious/compromised remote MCP "
|
|
167
|
+
"server can return an `authorization_endpoint` URL during the OAuth "
|
|
168
|
+
"handshake that mcp-remote passes unsanitized to the OS, achieving "
|
|
169
|
+
"full remote code execution with no further user interaction beyond "
|
|
170
|
+
"opening the client. Affects v0.0.5-v0.1.15.",
|
|
171
|
+
),
|
|
172
|
+
(
|
|
173
|
+
"mcp-server-git",
|
|
174
|
+
(2025, 12, 18),
|
|
175
|
+
("CVE-2025-68143", "CVE-2025-68144", "CVE-2025-68145"),
|
|
176
|
+
"Chained path-validation bypass, unrestricted git_init, and argument "
|
|
177
|
+
"injection in Anthropic's reference git MCP server, usable via "
|
|
178
|
+
"indirect prompt injection (e.g. a planted README) to reach RCE "
|
|
179
|
+
"together with a filesystem MCP server. Fully fixed in v2025.12.18.",
|
|
180
|
+
),
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _parse_leading_version(text: str) -> tuple[int, ...] | None:
|
|
185
|
+
match = re.match(r"^(\d+(?:\.\d+)*)", text)
|
|
186
|
+
if not match:
|
|
187
|
+
return None
|
|
188
|
+
return tuple(int(part) for part in match.group(1).split("."))
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _version_lt(a: tuple[int, ...], b: tuple[int, ...]) -> bool:
|
|
192
|
+
n = max(len(a), len(b))
|
|
193
|
+
a_padded = a + (0,) * (n - len(a))
|
|
194
|
+
b_padded = b + (0,) * (n - len(b))
|
|
195
|
+
return a_padded < b_padded
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def scan_known_cve_packages(config: dict, source_path: str) -> list[Finding]:
|
|
199
|
+
"""Flag MCP server commands pinned to a package version with a
|
|
200
|
+
disclosed, fixed RCE/command-injection CVE (see
|
|
201
|
+
``_KNOWN_VULNERABLE_PACKAGES``). Unpinned installs are not flagged here -
|
|
202
|
+
they pick up the current (patched) release and are already covered by
|
|
203
|
+
the generic 'unpinned package' info finding in `scan_mcp_servers`.
|
|
204
|
+
"""
|
|
205
|
+
servers = config.get("mcpServers") or config.get("mcp_servers")
|
|
206
|
+
if not isinstance(servers, dict):
|
|
207
|
+
return []
|
|
208
|
+
findings: list[Finding] = []
|
|
209
|
+
for server_name, server_cfg in servers.items():
|
|
210
|
+
if not isinstance(server_cfg, dict):
|
|
211
|
+
continue
|
|
212
|
+
command = server_cfg.get("command", "")
|
|
213
|
+
args = server_cfg.get("args") or []
|
|
214
|
+
full_cmdline = " ".join([command, *[str(a) for a in args]])
|
|
215
|
+
|
|
216
|
+
for pkg_name, fixed_version, cve_ids, description in _KNOWN_VULNERABLE_PACKAGES:
|
|
217
|
+
pin_match = re.search(
|
|
218
|
+
rf"\b{re.escape(pkg_name)}(?:@|==)([0-9][0-9.]*)", full_cmdline
|
|
219
|
+
)
|
|
220
|
+
if not pin_match:
|
|
221
|
+
continue
|
|
222
|
+
pinned_version = _parse_leading_version(pin_match.group(1))
|
|
223
|
+
if pinned_version is None or not _version_lt(pinned_version, fixed_version):
|
|
224
|
+
continue
|
|
225
|
+
findings.append(
|
|
226
|
+
Finding(
|
|
227
|
+
check_id="mcp-known-cve-package",
|
|
228
|
+
severity="critical",
|
|
229
|
+
title=(
|
|
230
|
+
f"MCP server '{server_name}' pins {pkg_name}@"
|
|
231
|
+
f"{pin_match.group(1)}, vulnerable to {', '.join(cve_ids)}"
|
|
232
|
+
),
|
|
233
|
+
detail=(
|
|
234
|
+
f"{description} The config explicitly pins a version "
|
|
235
|
+
f"older than the fixed {'.'.join(map(str, fixed_version))}, "
|
|
236
|
+
f"so upgrading `{pkg_name}` globally will not help "
|
|
237
|
+
f"until this pin is also updated."
|
|
238
|
+
),
|
|
239
|
+
path=source_path,
|
|
240
|
+
references=list(cve_ids),
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
return findings
|
|
244
|
+
|
|
245
|
+
|
|
157
246
|
def scan_mcp_servers(config: dict, source_path: str) -> list[Finding]:
|
|
158
247
|
servers = config.get("mcpServers") or config.get("mcp_servers")
|
|
159
248
|
if not isinstance(servers, dict):
|
|
@@ -293,6 +382,7 @@ def scan_config_dict(config: dict, source_path: str) -> list[Finding]:
|
|
|
293
382
|
findings: list[Finding] = []
|
|
294
383
|
findings += scan_hooks(config, source_path)
|
|
295
384
|
findings += scan_mcp_servers(config, source_path)
|
|
385
|
+
findings += scan_known_cve_packages(config, source_path)
|
|
296
386
|
findings += scan_permissions(config, source_path)
|
|
297
387
|
findings += scan_lethal_trifecta(config, source_path)
|
|
298
388
|
return findings
|
{coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0/src/coding_agent_audit.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coding-agent-audit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Scan your machine for known AI-coding-assistant supply-chain backdoor indicators (Claude Code / Cursor / Windsurf extensions, MCP servers) plus risky hook/permission config patterns. Usable as a CLI or as an MCP server.
|
|
5
5
|
Author-email: StackScribe AI <survivalagent320@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -134,6 +134,16 @@ second look rather than proof of compromise:
|
|
|
134
134
|
`authorized_keys`, use netcat in reverse-shell mode, etc.
|
|
135
135
|
- MCP server definitions that run an unpinned package (`npx -y pkg` with
|
|
136
136
|
no version pin)
|
|
137
|
+
- MCP server definitions explicitly pinned to a package version with a
|
|
138
|
+
disclosed, fixed RCE CVE (added in v0.10.0): `mcp-remote` below v0.1.16
|
|
139
|
+
(**CVE-2025-6514**, CVSS 9.6 — a malicious remote server's OAuth
|
|
140
|
+
`authorization_endpoint` response is passed unsanitized to the OS,
|
|
141
|
+
arbitrary command execution on just opening the client) and
|
|
142
|
+
`mcp-server-git` below v2025.12.18 (**CVE-2025-68143/68144/68145** —
|
|
143
|
+
Anthropic's own reference git MCP server, chainable via indirect prompt
|
|
144
|
+
injection into full RCE). Only an explicit old pin is flagged — an
|
|
145
|
+
unpinned install picks up the current, patched release and is already
|
|
146
|
+
covered by the generic unpinned-package finding above
|
|
137
147
|
- Auto-approved permission rules that are unscoped wildcards (e.g.
|
|
138
148
|
`Bash(*)`)
|
|
139
149
|
- The "lethal trifecta" pattern: an untrusted-content-fetching tool
|
|
@@ -4,6 +4,7 @@ from agentaudit.configscan import (
|
|
|
4
4
|
default_config_paths,
|
|
5
5
|
scan_config_dict,
|
|
6
6
|
scan_hooks,
|
|
7
|
+
scan_known_cve_packages,
|
|
7
8
|
scan_lethal_trifecta,
|
|
8
9
|
scan_mcp_servers,
|
|
9
10
|
scan_paths,
|
|
@@ -96,6 +97,64 @@ def test_scan_mcp_servers_not_a_dict():
|
|
|
96
97
|
assert scan_mcp_servers({"mcpServers": "nope"}, "src") == []
|
|
97
98
|
|
|
98
99
|
|
|
100
|
+
def test_scan_known_cve_packages_vulnerable_mcp_remote_pin():
|
|
101
|
+
config = {
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"remote": {
|
|
104
|
+
"command": "npx",
|
|
105
|
+
"args": ["-y", "mcp-remote@0.1.9", "http://example.com/mcp"],
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
findings = scan_known_cve_packages(config, "src")
|
|
110
|
+
assert len(findings) == 1
|
|
111
|
+
assert findings[0].check_id == "mcp-known-cve-package"
|
|
112
|
+
assert findings[0].severity == "critical"
|
|
113
|
+
assert "CVE-2025-6514" in findings[0].references
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_scan_known_cve_packages_vulnerable_git_server_pin():
|
|
117
|
+
config = {
|
|
118
|
+
"mcpServers": {
|
|
119
|
+
"git": {
|
|
120
|
+
"command": "uvx",
|
|
121
|
+
"args": ["mcp-server-git==2025.9.1", "--repository", "/repo"],
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
findings = scan_known_cve_packages(config, "src")
|
|
126
|
+
assert len(findings) == 1
|
|
127
|
+
assert "CVE-2025-68143" in findings[0].references[0]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_scan_known_cve_packages_fixed_version_ok():
|
|
131
|
+
config = {
|
|
132
|
+
"mcpServers": {
|
|
133
|
+
"remote": {"command": "npx", "args": ["-y", "mcp-remote@0.1.16", "https://example.com"]},
|
|
134
|
+
"git": {"command": "uvx", "args": ["mcp-server-git==2025.12.18", "--repository", "/repo"]},
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
assert scan_known_cve_packages(config, "src") == []
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_scan_known_cve_packages_unpinned_not_flagged():
|
|
141
|
+
config = {
|
|
142
|
+
"mcpServers": {"remote": {"command": "npx", "args": ["-y", "mcp-remote", "https://example.com"]}}
|
|
143
|
+
}
|
|
144
|
+
assert scan_known_cve_packages(config, "src") == []
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_scan_known_cve_packages_unrelated_package_ignored():
|
|
148
|
+
config = {
|
|
149
|
+
"mcpServers": {"fs": {"command": "npx", "args": ["-y", "@scope/pkg@0.0.1"]}}
|
|
150
|
+
}
|
|
151
|
+
assert scan_known_cve_packages(config, "src") == []
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_scan_known_cve_packages_not_a_dict():
|
|
155
|
+
assert scan_known_cve_packages({"mcpServers": "nope"}, "src") == []
|
|
156
|
+
|
|
157
|
+
|
|
99
158
|
def test_scan_permissions_scoped_rule_ok():
|
|
100
159
|
config = {"permissions": {"allow": ["Bash(git diff:*)", "Read(//workspace/**)"]}}
|
|
101
160
|
assert scan_permissions(config, "src") == []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/requires.txt
RENAMED
|
File without changes
|
{coding_agent_audit-0.9.0 → coding_agent_audit-0.10.0}/src/coding_agent_audit.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|