mirror of
https://github.com/anthropics/knowledge-work-plugins.git
synced 2026-09-14 20:16:30 +08:00
6445c15068
* Add SHA bump pipeline, policy scan, and MCP URL liveness check
Brings this marketplace's CI in line with claude-plugins-official:
- bump-plugin-shas.yml: nightly sweep that bumps every external entry's
pinned SHA to upstream HEAD, validates each at the new SHA, opens one
signed PR (createCommitOnBranch satisfies required_signatures), and
dispatches the policy scan on the bump branch.
- scan-plugins.yml: Claude policy review of changed external entries.
Caches verdicts in Actions cache keyed (plugin, sha, policy hash) so
the nightly force-reset doesn't re-burn scan time on already-reviewed
SHAs. Cached pass/fail verdicts are served from disk; only uncached
SHAs are scanned. Fails closed when ANTHROPIC_API_KEY is unset.
Designed to be a required status check on main.
- revert-failed-bumps.yml: drops policy-failing entries from a bump PR
via a follow-up signed commit and re-dispatches the scan, so one bad
upstream can't block the rest. Bounded at 3 passes/night, restricted
to SHA-only diffs and first-party-repo PRs.
- check-mcp-urls.yml: probes every http/sse MCP server URL declared by
vendored plugins. Fails on 404/410/unreachable; passes on auth/method
errors (expected without credentials). Deduplicates shared servers so
cost is bounded by unique URLs, not (plugins x servers). Skips empty
and templated URLs.
- .github/policy/: the policy prompt and structured-output schema the
scan reviews against.
Two operator steps after merge:
1. Set the ANTHROPIC_API_KEY repo secret (the scan refuses to silently
skip a required policy review without it).
2. Add a required_status_checks ruleset on main requiring the `scan`
check from GitHub Actions. Adding it before this merges would block
every PR.
* Fix MCP URL probe: connection failure was reported as PASS
curl writes "000" to -w '%{http_code}' on a connection failure AND exits
nonzero. The previous fallback put the echo inside the command
substitution — both wrote, the captured value was "000000", and the
case statement's 000) arm didn't match, so dead hosts fell through to
PASS. Move the fallback assignment outside the substitution so the
captured value is exactly "000" and connection failures fail.
53 lines
1.9 KiB
JSON
53 lines
1.9 KiB
JSON
{
|
|
"type": "object",
|
|
"required": [
|
|
"passes",
|
|
"summary",
|
|
"violations",
|
|
"may_make_external_network_calls",
|
|
"may_download_additional_software",
|
|
"hooks",
|
|
"has_broad_scope_hooks",
|
|
"has_undisclosed_telemetry",
|
|
"description_matches_behavior"
|
|
],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"passes": {
|
|
"type": "boolean",
|
|
"description": "true only if the plugin is safe AND has no broad-scope hooks AND has no undisclosed telemetry AND its description matches its behavior."
|
|
},
|
|
"summary": {
|
|
"type": "string",
|
|
"description": "Brief description of what the plugin does."
|
|
},
|
|
"violations": {
|
|
"type": "string",
|
|
"description": "Specific files/hooks and issues, or empty string if none. When passes=false this MUST cite the file/hook and state what the user was not told."
|
|
},
|
|
"may_make_external_network_calls": {
|
|
"type": "boolean"
|
|
},
|
|
"may_download_additional_software": {
|
|
"type": "boolean"
|
|
},
|
|
"hooks": {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "One string per registered hook: 'EVENT:path — gated|ungated — network:yes(host)|no'. Empty array if the plugin registers no hooks."
|
|
},
|
|
"has_broad_scope_hooks": {
|
|
"type": "boolean",
|
|
"description": "true if any UserPromptSubmit/PreToolUse/PostToolUse hook runs without a project-relevance gate, or any hook reads user data beyond the plugin's stated scope."
|
|
},
|
|
"has_undisclosed_telemetry": {
|
|
"type": "boolean",
|
|
"description": "true if any hook or shipped code makes an outbound network call to a non-MCP host without explicit disclosure + opt-out in the description/README."
|
|
},
|
|
"description_matches_behavior": {
|
|
"type": "boolean",
|
|
"description": "false if a user reading only the plugin.json description would be surprised by the hooks/telemetry/data-access the plugin actually performs."
|
|
}
|
|
}
|
|
}
|