agentcheckup 0.4.0__tar.gz → 0.4.1__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.
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.github/workflows/publish.yml +2 -4
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/CHANGELOG.md +25 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/PKG-INFO +2 -2
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/README.md +1 -1
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/docs/rules.md +4 -4
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/pyproject.toml +1 -1
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/report.py +1 -1
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/config_rules.py +2 -2
- agentcheckup-0.4.1/tests/test_registry.py +77 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/test_rules.py +1 -1
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.gitattributes +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.github/ISSUE_TEMPLATE/rule_request.yml +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.github/pull_request_template.md +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.github/workflows/action-selftest.yml +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.github/workflows/ci.yml +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/.gitignore +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/CODE_OF_CONDUCT.md +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/CONTRIBUTING.md +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/LICENSE +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/Makefile +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/README.zh-CN.md +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/SECURITY.md +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/action.yml +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/conftest.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/docs/github-action.md +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/scripts/bootstrap.ps1 +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/scripts/bootstrap.sh +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/__init__.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/__main__.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/cli.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/conflicts.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/discovery.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/engine.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/loader.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/models.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/__init__.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/base.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/docs.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/injection.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/naming.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/safety.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/rules/schema.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/src/agentcheckup/tokens.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/fixtures/bad_tools.json +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/fixtures/ci_config.json +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/fixtures/claude_desktop_config.json +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/fixtures/good_tools.json +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/fixtures/mock_server.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/test_cli.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/test_conflicts.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/test_discovery.py +0 -0
- {agentcheckup-0.4.0 → agentcheckup-0.4.1}/tests/test_scoring.py +0 -0
|
@@ -13,10 +13,8 @@ name: Publish
|
|
|
13
13
|
# Environment : pypi
|
|
14
14
|
|
|
15
15
|
on:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# Until then a release would run this and fail with "environment: MISSING",
|
|
19
|
-
# which is noise on the repository home page.
|
|
16
|
+
release:
|
|
17
|
+
types: [published]
|
|
20
18
|
workflow_dispatch:
|
|
21
19
|
|
|
22
20
|
permissions:
|
|
@@ -9,6 +9,31 @@ new ids, and removing a rule is a breaking change.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.4.1] - 2026-11-07
|
|
13
|
+
|
|
14
|
+
Both fixes came out of an external fact-check of the project, not from a bug
|
|
15
|
+
report: the repository has no users yet, so nothing surfaced them in the field.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **`AGT105` was used by two unrelated rules.** "Server did not return a tool
|
|
20
|
+
list" (suite) and "Remote server uses plain HTTP" (config) shared the id, with
|
|
21
|
+
different severities. Rule ids are the tool's public API — people put them in
|
|
22
|
+
suppression lists — so two checks under one name is worse than either check
|
|
23
|
+
being wrong. The config rule is now `AGT108`; `AGT105` keeps the meaning every
|
|
24
|
+
document and the README already gave it.
|
|
25
|
+
- **README pointed the GitHub Action at `@v0.3.0`**, a tag that no longer exists.
|
|
26
|
+
Anyone copying the snippet got a resolution failure. Now `@v0.4.1`.
|
|
27
|
+
- `docs/rules.md` listed `AGT105` twice and described the suite rule as a
|
|
28
|
+
warning when it had been downgraded to info.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `tests/test_registry.py`: ids are unique across all three registries, well
|
|
33
|
+
formed, and — for every rule that constructs its own `Finding` — the severity
|
|
34
|
+
emitted matches the severity registered. That last check is the one that would
|
|
35
|
+
have caught the duplicate before it shipped.
|
|
36
|
+
|
|
12
37
|
## [0.4.0] - 2026-11-07
|
|
13
38
|
|
|
14
39
|
Calibration release, driven by the first run against real servers rather than
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentcheckup
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Diagnose your agent setup: tool conflicts, risky config, context budget.
|
|
5
5
|
Project-URL: Homepage, https://github.com/wanjiaqing/agentcheckup
|
|
6
6
|
Project-URL: Issues, https://github.com/wanjiaqing/agentcheckup/issues
|
|
@@ -149,7 +149,7 @@ The fastest way to keep a manifest from regressing, and the version you pin is
|
|
|
149
149
|
the version you run — the action installs from its own checkout.
|
|
150
150
|
|
|
151
151
|
```yaml
|
|
152
|
-
- uses: wanjiaqing/agentcheckup@v0.
|
|
152
|
+
- uses: wanjiaqing/agentcheckup@v0.4.1
|
|
153
153
|
with:
|
|
154
154
|
config: .mcp.json
|
|
155
155
|
fail-on: warning
|
|
@@ -121,7 +121,7 @@ The fastest way to keep a manifest from regressing, and the version you pin is
|
|
|
121
121
|
the version you run — the action installs from its own checkout.
|
|
122
122
|
|
|
123
123
|
```yaml
|
|
124
|
-
- uses: wanjiaqing/agentcheckup@v0.
|
|
124
|
+
- uses: wanjiaqing/agentcheckup@v0.4.1
|
|
125
125
|
with:
|
|
126
126
|
config: .mcp.json
|
|
127
127
|
fail-on: warning
|
|
@@ -163,9 +163,6 @@ also defeats the escaping the client already did.
|
|
|
163
163
|
Granting the filesystem root gives the agent read-write access to everything. One
|
|
164
164
|
prompt injection is then enough to reach SSH keys and browser profiles.
|
|
165
165
|
|
|
166
|
-
### AGT105 · warning · Remote server uses plain HTTP
|
|
167
|
-
Tool calls carry your data, often your credentials.
|
|
168
|
-
|
|
169
166
|
### AGT106 · error · Config passes a permission-bypass flag
|
|
170
167
|
`--dangerously-skip-permissions`, `--yolo`, `--allow-all`, `--no-sandbox`. These
|
|
171
168
|
disable the only confirmation step between the model and your disk.
|
|
@@ -174,11 +171,14 @@ disable the only confirmation step between the model and your disk.
|
|
|
174
171
|
Config files get committed, screenshotted and pasted into issues. Reference an
|
|
175
172
|
env var instead.
|
|
176
173
|
|
|
174
|
+
### AGT108 · warning · Remote server uses plain HTTP
|
|
175
|
+
Tool calls carry your data, often your credentials, over an unencrypted link.
|
|
176
|
+
|
|
177
177
|
### AGT105 · info · Server did not return a tool list
|
|
178
178
|
The server failed to start, timed out, or is a remote `url` endpoint that cannot
|
|
179
179
|
be introspected over stdio.
|
|
180
180
|
|
|
181
|
-
This is
|
|
181
|
+
This is info rather than an error on purpose: one broken server must not
|
|
182
182
|
stop you auditing the other four. But an unreachable server is also an unaudited
|
|
183
183
|
one — its tools were never checked for conflicts.
|
|
184
184
|
|
|
@@ -132,12 +132,12 @@ def broad_fs_root(server: ServerConfig):
|
|
|
132
132
|
return None
|
|
133
133
|
|
|
134
134
|
|
|
135
|
-
@config_rule("
|
|
135
|
+
@config_rule("AGT108", Severity.WARNING, "Remote server uses plain HTTP")
|
|
136
136
|
def insecure_url(server: ServerConfig):
|
|
137
137
|
"""Tool calls carry your data and often your credentials."""
|
|
138
138
|
if server.url and server.url.lower().startswith("http://"):
|
|
139
139
|
return Finding(
|
|
140
|
-
rule="
|
|
140
|
+
rule="AGT108",
|
|
141
141
|
severity=Severity.WARNING,
|
|
142
142
|
message=f"Server {server.name!r} uses an unencrypted http:// endpoint.",
|
|
143
143
|
tool=server.name,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Guards on the rule registry itself.
|
|
2
|
+
|
|
3
|
+
Rule ids are the public API of this tool: people put them in suppression lists,
|
|
4
|
+
CI thresholds and issue reports. A duplicate id silently gives two unrelated
|
|
5
|
+
checks the same name, which is worse than either check being wrong.
|
|
6
|
+
|
|
7
|
+
This file exists because that happened: `AGT105` was used for both "server did
|
|
8
|
+
not return a tool list" (suite) and "remote server uses plain HTTP" (config).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
from agentcheckup.rules import CONFIG_RULES, SUITE_RULES, TOOL_RULES
|
|
19
|
+
|
|
20
|
+
SRC = Path(__file__).resolve().parents[1] / "src" / "agentcheckup"
|
|
21
|
+
|
|
22
|
+
ALL_RULES = [*TOOL_RULES, *SUITE_RULES, *CONFIG_RULES]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_rule_ids_are_unique():
|
|
26
|
+
ids = [rule.id for rule in ALL_RULES]
|
|
27
|
+
duplicates = {rule_id for rule_id in ids if ids.count(rule_id) > 1}
|
|
28
|
+
assert not duplicates, f"duplicate rule ids: {sorted(duplicates)}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_rule_ids_are_well_formed():
|
|
32
|
+
for rule in ALL_RULES:
|
|
33
|
+
assert re.fullmatch(r"AGT\d{3}", rule.id), rule.id
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_every_rule_has_a_title():
|
|
37
|
+
for rule in ALL_RULES:
|
|
38
|
+
assert rule.title.strip(), f"{rule.id} has no title"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_no_rule_is_registered_twice_across_groups():
|
|
42
|
+
"""A tool rule and a config rule must not share an id either."""
|
|
43
|
+
groups = {
|
|
44
|
+
"tool": {r.id for r in TOOL_RULES},
|
|
45
|
+
"suite": {r.id for r in SUITE_RULES},
|
|
46
|
+
"config": {r.id for r in CONFIG_RULES},
|
|
47
|
+
}
|
|
48
|
+
for a, left in groups.items():
|
|
49
|
+
for b, right in groups.items():
|
|
50
|
+
if a >= b:
|
|
51
|
+
continue
|
|
52
|
+
overlap = left & right
|
|
53
|
+
assert not overlap, f"{a} and {b} share {sorted(overlap)}"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@pytest.mark.parametrize("rule", ALL_RULES, ids=lambda r: r.id)
|
|
57
|
+
def test_emitted_severity_matches_the_registry(rule):
|
|
58
|
+
"""Catch a rule whose Finding says WARNING while the registry says INFO.
|
|
59
|
+
|
|
60
|
+
Without this, a severity change in one place silently disagrees with the
|
|
61
|
+
other, and the report and the score stop matching the docs.
|
|
62
|
+
"""
|
|
63
|
+
pattern = re.compile(
|
|
64
|
+
r'rule="' + re.escape(rule.id) + r'",\s*severity=Severity\.(\w+)'
|
|
65
|
+
)
|
|
66
|
+
emitted: set[str] = set()
|
|
67
|
+
for path in SRC.rglob("*.py"):
|
|
68
|
+
for match in pattern.finditer(path.read_text(encoding="utf-8")):
|
|
69
|
+
emitted.add(match.group(1))
|
|
70
|
+
|
|
71
|
+
if not emitted:
|
|
72
|
+
pytest.skip("severity is passed indirectly for this rule")
|
|
73
|
+
|
|
74
|
+
expected = rule.severity.name
|
|
75
|
+
assert emitted == {expected}, (
|
|
76
|
+
f"{rule.id} is registered as {expected} but emits {sorted(emitted)}"
|
|
77
|
+
)
|
|
@@ -165,7 +165,7 @@ def test_invisible_characters_detected():
|
|
|
165
165
|
def test_config_fixture_flags_expected_rules():
|
|
166
166
|
report = lint_servers(servers_from_config(FIXTURES / "claude_desktop_config.json"))
|
|
167
167
|
ids = rule_ids(report)
|
|
168
|
-
for expected in ("AGT101", "AGT103", "AGT104", "
|
|
168
|
+
for expected in ("AGT101", "AGT103", "AGT104", "AGT106", "AGT107", "AGT108"):
|
|
169
169
|
assert expected in ids, f"{expected} did not fire; got {sorted(ids)}"
|
|
170
170
|
|
|
171
171
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|