plugin-scanner 2.0.1__tar.gz → 2.0.2__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.
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/PKG-INFO +62 -5
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/README.md +60 -3
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/guard/architecture.md +11 -2
- plugin_scanner-2.0.2/docs/guard/get-started.md +82 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/guard/testing-matrix.md +10 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/pyproject.toml +12 -2
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/cli.py +5 -1
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/base.py +7 -4
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/claude_code.py +36 -5
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/cli/commands.py +77 -10
- plugin_scanner-2.0.2/src/codex_plugin_scanner/guard/cli/product.py +189 -0
- plugin_scanner-2.0.2/src/codex_plugin_scanner/guard/cli/prompt.py +342 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/cli/render.py +74 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/consumer/service.py +8 -1
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/models.py +2 -1
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/runtime/runner.py +11 -1
- plugin_scanner-2.0.2/src/codex_plugin_scanner/guard/shims.py +95 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/store.py +56 -9
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_guard_cli.py +38 -4
- plugin_scanner-2.0.2/tests/test_guard_launch_env.py +78 -0
- plugin_scanner-2.0.2/tests/test_guard_product_flow.py +272 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_guard_runtime.py +100 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.dockerignore +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/workflows/e2e-test.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/workflows/publish-action-repo.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.gitignore +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/Dockerfile +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/SECURITY.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/action/README.legacy.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/action/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/action/action.yml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/action/cisco-version.txt +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/action/pypi-attestations-version.txt +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/action/scanner-version.txt +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/guard/repo-boundaries.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_action_bundle.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/uv.lock +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plugin-scanner
|
|
3
|
-
Version: 2.0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.0.2
|
|
4
|
+
Summary: Local Guard runtime plus security and publishability scanning for Codex, Claude, Cursor, Gemini, and OpenCode.
|
|
5
5
|
Project-URL: Homepage, https://github.com/hashgraph-online/ai-plugin-scanner
|
|
6
6
|
Project-URL: Repository, https://github.com/hashgraph-online/ai-plugin-scanner
|
|
7
7
|
Project-URL: Issues, https://github.com/hashgraph-online/ai-plugin-scanner/issues
|
|
@@ -47,10 +47,47 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
[](https://github.com/hashgraph-online/ai-plugin-scanner/stargazers)
|
|
48
48
|
[](https://github.com/astral-sh/ruff)
|
|
49
49
|
|
|
50
|
-
|  | **
|
|
50
|
+
|  | **HOL Guard for local harness protection, plus the scanner CI gate for plugin ecosystems**. Protect Codex, Claude Code, Cursor, Gemini, and OpenCode before local tools run, then lint locally, verify in CI, and ship publish-ready bundles for manifests, skills, MCP, and marketplace metadata.<br><br>Use Guard when you want a local safety loop. Use the scanner when you want publishing and CI confidence.<br><br>[PyPI Package (`plugin-scanner`)](https://pypi.org/project/plugin-scanner/)<br>[Legacy Namespace (`codex-plugin-scanner`)](https://pypi.org/project/codex-plugin-scanner/)<br>[HOL Plugin Registry](https://hol.org/registry/plugins)<br>[HOL GitHub Organization](https://github.com/hashgraph-online)<br>[Report an Issue](https://github.com/hashgraph-online/ai-plugin-scanner/issues) |
|
|
51
51
|
| :--- | :--- |
|
|
52
52
|
|
|
53
|
-
## Start In
|
|
53
|
+
## Guard Start In 60 Seconds
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# See what Guard can protect on this machine
|
|
57
|
+
pipx run plugin-guard guard start
|
|
58
|
+
|
|
59
|
+
# Install Guard in front of Codex
|
|
60
|
+
pipx run plugin-guard guard install codex
|
|
61
|
+
|
|
62
|
+
# Review the current tool state before launch
|
|
63
|
+
pipx run plugin-guard guard run codex
|
|
64
|
+
|
|
65
|
+
# Inspect local receipts later
|
|
66
|
+
pipx run plugin-guard guard receipts
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Guard is local-first:
|
|
70
|
+
|
|
71
|
+
1. detect your harnesses
|
|
72
|
+
2. install a Guard launcher
|
|
73
|
+
3. run the harness through Guard
|
|
74
|
+
4. approve or block changes
|
|
75
|
+
5. inspect receipts locally
|
|
76
|
+
6. connect sync only if you want shared history later
|
|
77
|
+
|
|
78
|
+
Guard commands that matter most:
|
|
79
|
+
|
|
80
|
+
- `plugin-scanner guard start` for the first-run path
|
|
81
|
+
- `plugin-scanner guard status` for the current protection state
|
|
82
|
+
- `plugin-scanner guard install <harness>` to create a local Guard launcher
|
|
83
|
+
- `plugin-scanner guard run <harness> --dry-run` to record the current state before launch
|
|
84
|
+
- `plugin-scanner guard run <harness>` to review and approve changed tools before launch
|
|
85
|
+
- `plugin-scanner guard diff <harness>` when Guard says something changed
|
|
86
|
+
- `plugin-scanner guard receipts` for local history
|
|
87
|
+
|
|
88
|
+
See [docs/guard/get-started.md](docs/guard/get-started.md) for the full local flow.
|
|
89
|
+
|
|
90
|
+
## Scanner Start In 30 Seconds
|
|
54
91
|
|
|
55
92
|
```bash
|
|
56
93
|
# Local preflight
|
|
@@ -70,7 +107,27 @@ pipx run plugin-scanner verify .
|
|
|
70
107
|
|
|
71
108
|
If your repository uses a Codex marketplace root like `.agents/plugins/marketplace.json`, keep `plugin_dir: "."`. The scanner will discover local `./plugins/...` entries automatically, scan each local plugin manifest, and skip remote marketplace entries instead of treating the repo root as a single plugin.
|
|
72
109
|
|
|
73
|
-
##
|
|
110
|
+
## Two Product Modes
|
|
111
|
+
|
|
112
|
+
### HOL Guard
|
|
113
|
+
|
|
114
|
+
Use Guard when the problem is local runtime safety inside a harness:
|
|
115
|
+
|
|
116
|
+
- a new MCP server showed up in local config
|
|
117
|
+
- an existing tool changed after you trusted it
|
|
118
|
+
- you want receipts for what was approved or blocked
|
|
119
|
+
- you want to review changes before Codex, Claude Code, Cursor, Gemini, or OpenCode launches
|
|
120
|
+
|
|
121
|
+
### Scanner CI Gate
|
|
122
|
+
|
|
123
|
+
Use the scanner when the problem is authoring, CI, and publish readiness:
|
|
124
|
+
|
|
125
|
+
- lint manifests and metadata
|
|
126
|
+
- verify runtime and install surfaces
|
|
127
|
+
- block PRs with policy gates
|
|
128
|
+
- emit artifacts before submission or publishing
|
|
129
|
+
|
|
130
|
+
## Use Scanner After `$plugin-creator`
|
|
74
131
|
|
|
75
132
|
`plugin-scanner` is designed as the quality gate between plugin creation and distribution:
|
|
76
133
|
|
|
@@ -12,10 +12,47 @@
|
|
|
12
12
|
[](https://github.com/hashgraph-online/ai-plugin-scanner/stargazers)
|
|
13
13
|
[](https://github.com/astral-sh/ruff)
|
|
14
14
|
|
|
15
|
-
|  | **
|
|
15
|
+
|  | **HOL Guard for local harness protection, plus the scanner CI gate for plugin ecosystems**. Protect Codex, Claude Code, Cursor, Gemini, and OpenCode before local tools run, then lint locally, verify in CI, and ship publish-ready bundles for manifests, skills, MCP, and marketplace metadata.<br><br>Use Guard when you want a local safety loop. Use the scanner when you want publishing and CI confidence.<br><br>[PyPI Package (`plugin-scanner`)](https://pypi.org/project/plugin-scanner/)<br>[Legacy Namespace (`codex-plugin-scanner`)](https://pypi.org/project/codex-plugin-scanner/)<br>[HOL Plugin Registry](https://hol.org/registry/plugins)<br>[HOL GitHub Organization](https://github.com/hashgraph-online)<br>[Report an Issue](https://github.com/hashgraph-online/ai-plugin-scanner/issues) |
|
|
16
16
|
| :--- | :--- |
|
|
17
17
|
|
|
18
|
-
## Start In
|
|
18
|
+
## Guard Start In 60 Seconds
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# See what Guard can protect on this machine
|
|
22
|
+
pipx run plugin-guard guard start
|
|
23
|
+
|
|
24
|
+
# Install Guard in front of Codex
|
|
25
|
+
pipx run plugin-guard guard install codex
|
|
26
|
+
|
|
27
|
+
# Review the current tool state before launch
|
|
28
|
+
pipx run plugin-guard guard run codex
|
|
29
|
+
|
|
30
|
+
# Inspect local receipts later
|
|
31
|
+
pipx run plugin-guard guard receipts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Guard is local-first:
|
|
35
|
+
|
|
36
|
+
1. detect your harnesses
|
|
37
|
+
2. install a Guard launcher
|
|
38
|
+
3. run the harness through Guard
|
|
39
|
+
4. approve or block changes
|
|
40
|
+
5. inspect receipts locally
|
|
41
|
+
6. connect sync only if you want shared history later
|
|
42
|
+
|
|
43
|
+
Guard commands that matter most:
|
|
44
|
+
|
|
45
|
+
- `plugin-scanner guard start` for the first-run path
|
|
46
|
+
- `plugin-scanner guard status` for the current protection state
|
|
47
|
+
- `plugin-scanner guard install <harness>` to create a local Guard launcher
|
|
48
|
+
- `plugin-scanner guard run <harness> --dry-run` to record the current state before launch
|
|
49
|
+
- `plugin-scanner guard run <harness>` to review and approve changed tools before launch
|
|
50
|
+
- `plugin-scanner guard diff <harness>` when Guard says something changed
|
|
51
|
+
- `plugin-scanner guard receipts` for local history
|
|
52
|
+
|
|
53
|
+
See [docs/guard/get-started.md](docs/guard/get-started.md) for the full local flow.
|
|
54
|
+
|
|
55
|
+
## Scanner Start In 30 Seconds
|
|
19
56
|
|
|
20
57
|
```bash
|
|
21
58
|
# Local preflight
|
|
@@ -35,7 +72,27 @@ pipx run plugin-scanner verify .
|
|
|
35
72
|
|
|
36
73
|
If your repository uses a Codex marketplace root like `.agents/plugins/marketplace.json`, keep `plugin_dir: "."`. The scanner will discover local `./plugins/...` entries automatically, scan each local plugin manifest, and skip remote marketplace entries instead of treating the repo root as a single plugin.
|
|
37
74
|
|
|
38
|
-
##
|
|
75
|
+
## Two Product Modes
|
|
76
|
+
|
|
77
|
+
### HOL Guard
|
|
78
|
+
|
|
79
|
+
Use Guard when the problem is local runtime safety inside a harness:
|
|
80
|
+
|
|
81
|
+
- a new MCP server showed up in local config
|
|
82
|
+
- an existing tool changed after you trusted it
|
|
83
|
+
- you want receipts for what was approved or blocked
|
|
84
|
+
- you want to review changes before Codex, Claude Code, Cursor, Gemini, or OpenCode launches
|
|
85
|
+
|
|
86
|
+
### Scanner CI Gate
|
|
87
|
+
|
|
88
|
+
Use the scanner when the problem is authoring, CI, and publish readiness:
|
|
89
|
+
|
|
90
|
+
- lint manifests and metadata
|
|
91
|
+
- verify runtime and install surfaces
|
|
92
|
+
- block PRs with policy gates
|
|
93
|
+
- emit artifacts before submission or publishing
|
|
94
|
+
|
|
95
|
+
## Use Scanner After `$plugin-creator`
|
|
39
96
|
|
|
40
97
|
`plugin-scanner` is designed as the quality gate between plugin creation and distribution:
|
|
41
98
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Guard Architecture
|
|
2
2
|
|
|
3
|
-
Guard lives inside `codex_plugin_scanner` and
|
|
3
|
+
Guard lives inside `codex_plugin_scanner` and is the local product surface for harness protection. The existing scan engine remains the trust and evidence core, but the user workflow starts with local harness installs and launch interception rather than CI.
|
|
4
4
|
|
|
5
5
|
The runtime is split into:
|
|
6
6
|
|
|
7
7
|
- `guard/adapters`: harness discovery for Codex, Claude Code, Cursor, Gemini, and OpenCode
|
|
8
|
+
- `guard/shims`: local launcher shims that route harness launches through Guard
|
|
8
9
|
- `guard/consumer`: orchestration for detection, policy evaluation, and consumer-mode scan output
|
|
9
10
|
- `guard/policy`: local action resolution for allow, review, warn, and block decisions
|
|
10
11
|
- `guard/receipts`: receipt creation for first use and changed-artifact events
|
|
@@ -21,4 +22,12 @@ Guard evaluates local artifacts in this order:
|
|
|
21
22
|
5. Record a receipt and optional diff
|
|
22
23
|
6. Launch the harness only if the effective action is not `block`
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
The local product loop is:
|
|
26
|
+
|
|
27
|
+
1. `guard start` detects supported harnesses and suggests the next step
|
|
28
|
+
2. `guard install <harness>` creates a local launcher shim
|
|
29
|
+
3. `guard run <harness>` evaluates changes before the harness launches
|
|
30
|
+
4. `guard receipts` and `guard status` let users inspect local decisions
|
|
31
|
+
5. `guard login` and `guard sync` stay optional
|
|
32
|
+
|
|
33
|
+
Wrapper mode is still the core execution strategy in this phase. Config mutation is limited to the Claude Code hook helper, where Guard can add and remove its own hook entry in workspace-local settings.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Guard Get Started
|
|
2
|
+
|
|
3
|
+
Guard is the local product inside `plugin-scanner`.
|
|
4
|
+
If you want the shortest entrypoint, install and run the dedicated `plugin-guard` console script.
|
|
5
|
+
|
|
6
|
+
Use it when you want to protect a harness before local MCP servers, skills, hooks, or plugin surfaces run.
|
|
7
|
+
|
|
8
|
+
## The local loop
|
|
9
|
+
|
|
10
|
+
1. Detect your harnesses:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
plugin-guard guard start
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
2. Install Guard in front of the harness you use most:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
plugin-guard guard install codex
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
3. Run one dry pass so Guard records the current state:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
plugin-guard guard run codex --dry-run
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
4. Launch through Guard after that. Guard will prompt you if a tool is new or changed:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
plugin-guard guard run codex
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
5. Review changes when Guard blocks or asks for another look:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
plugin-scanner guard diff codex
|
|
38
|
+
plugin-scanner guard allow codex --scope artifact --artifact-id codex:project:workspace_skill
|
|
39
|
+
plugin-scanner guard deny codex --scope artifact --artifact-id codex:project:workspace_skill
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
6. Inspect receipts:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
plugin-guard guard receipts
|
|
46
|
+
plugin-guard guard status
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
7. Connect sync only if you want shared history later:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
plugin-scanner guard login --sync-url <url> --token <token>
|
|
53
|
+
plugin-scanner guard sync
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What `install` does
|
|
57
|
+
|
|
58
|
+
`guard install <harness>` creates a local launcher shim under Guard’s home directory:
|
|
59
|
+
|
|
60
|
+
- macOS/Linux: `~/.config/.ai-plugin-scanner-guard/bin/guard-<harness>`
|
|
61
|
+
- Windows: `~/.config/.ai-plugin-scanner-guard/bin/guard-<harness>.cmd`
|
|
62
|
+
|
|
63
|
+
Claude Code also gets Guard hook entries in `.claude/settings.local.json` when you install from a workspace.
|
|
64
|
+
|
|
65
|
+
## First-party canaries
|
|
66
|
+
|
|
67
|
+
Use these local repos to prove Guard against real first-party surfaces:
|
|
68
|
+
|
|
69
|
+
- `hashnet-mcp-js` for a real MCP server harness target
|
|
70
|
+
- `registry-broker-skills` for a real skills registry fixture during scan and trust checks
|
|
71
|
+
|
|
72
|
+
Suggested local validation:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
plugin-scanner guard detect codex --json
|
|
76
|
+
plugin-scanner guard install codex
|
|
77
|
+
plugin-scanner guard status
|
|
78
|
+
plugin-scanner guard run codex --dry-run
|
|
79
|
+
plugin-scanner guard receipts
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For a real Codex canary, point `~/.codex/config.toml` or `<workspace>/.codex/config.toml` at a local `hashnet-mcp` command, then repeat the Guard loop above.
|
|
@@ -3,20 +3,30 @@
|
|
|
3
3
|
Automated coverage in this phase includes:
|
|
4
4
|
|
|
5
5
|
- Guard CLI behavior tests for detect, scan, run, diff, receipts, install, uninstall, login, and sync
|
|
6
|
+
- Guard product-flow tests for `guard start`, `guard status`, and launcher shim creation
|
|
6
7
|
- SQLite persistence through real command execution in temporary homes and workspaces
|
|
7
8
|
- consumer-mode JSON contract generation against scanner fixtures
|
|
8
9
|
- local HTTP sync against a live in-process server instead of mocked transport
|
|
9
10
|
|
|
10
11
|
Manual verification should include:
|
|
11
12
|
|
|
13
|
+
- `guard start`
|
|
14
|
+
- `guard status`
|
|
12
15
|
- `guard detect codex --json`
|
|
13
16
|
- `guard detect cursor --json`
|
|
14
17
|
- `guard detect gemini --json`
|
|
15
18
|
- `guard detect opencode --json`
|
|
19
|
+
- `guard install codex`
|
|
16
20
|
- `guard run codex --dry-run --default-action allow --json`
|
|
21
|
+
- `guard receipts`
|
|
17
22
|
- `codex mcp list`
|
|
18
23
|
- `cursor-agent mcp list`
|
|
19
24
|
- `gemini --help`
|
|
20
25
|
- `opencode --help`
|
|
21
26
|
|
|
27
|
+
First-party canaries for local manual validation:
|
|
28
|
+
|
|
29
|
+
- a local `hashnet-mcp-js` checkout wired into Codex, Cursor, or Claude Code config
|
|
30
|
+
- a local `registry-broker-skills` checkout for scanner fixtures and trust review
|
|
31
|
+
|
|
22
32
|
Claude Code smoke tests remain conditional on the local `claude` binary being available.
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "plugin-scanner"
|
|
7
|
-
version = "2.0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "2.0.2"
|
|
8
|
+
description = "Local Guard runtime plus security and publishability scanning for Codex, Claude, Cursor, Gemini, and OpenCode."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -50,6 +50,7 @@ publish = [
|
|
|
50
50
|
|
|
51
51
|
[project.scripts]
|
|
52
52
|
plugin-scanner = "codex_plugin_scanner.cli:main"
|
|
53
|
+
plugin-guard = "codex_plugin_scanner.cli:main"
|
|
53
54
|
codex-plugin-scanner = "codex_plugin_scanner.cli:main"
|
|
54
55
|
plugin-ecosystem-scanner = "codex_plugin_scanner.cli:main"
|
|
55
56
|
|
|
@@ -66,6 +67,15 @@ extend-exclude = ["tests/test-trust-scoring.py", "tests/test-trust-specs.py"]
|
|
|
66
67
|
[tool.hatch.build.targets.wheel]
|
|
67
68
|
packages = ["src/codex_plugin_scanner"]
|
|
68
69
|
|
|
70
|
+
[tool.hatch.build]
|
|
71
|
+
exclude = [
|
|
72
|
+
".guard-e2e/**",
|
|
73
|
+
".guard-e2e-prod/**",
|
|
74
|
+
".guard-e2e-prod2/**",
|
|
75
|
+
".guard-prod-venv/**",
|
|
76
|
+
".guard-prod-venv-fresh/**",
|
|
77
|
+
]
|
|
78
|
+
|
|
69
79
|
[tool.ruff.lint]
|
|
70
80
|
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "SIM", "RUF"]
|
|
71
81
|
|
|
@@ -137,7 +137,11 @@ def _add_common_policy_args(parser: argparse.ArgumentParser) -> None:
|
|
|
137
137
|
|
|
138
138
|
|
|
139
139
|
def _build_parser() -> argparse.ArgumentParser:
|
|
140
|
-
|
|
140
|
+
program_name = Path(sys.argv[0]).name or "plugin-scanner"
|
|
141
|
+
parser = argparse.ArgumentParser(
|
|
142
|
+
prog=program_name,
|
|
143
|
+
description="Run HOL Guard locally or scan plugin ecosystems for CI and publish readiness.",
|
|
144
|
+
)
|
|
141
145
|
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
142
146
|
parser.add_argument("--list-ecosystems", action="store_true", help="List supported plugin ecosystems and exit")
|
|
143
147
|
subparsers = parser.add_subparsers(dest="command")
|
{plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/base.py
RENAMED
|
@@ -9,6 +9,7 @@ from dataclasses import dataclass
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
11
|
from ..models import GuardArtifact, HarnessDetection
|
|
12
|
+
from ..shims import install_guard_shim, remove_guard_shim
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
@dataclass(frozen=True, slots=True)
|
|
@@ -77,19 +78,21 @@ class HarnessAdapter:
|
|
|
77
78
|
raise NotImplementedError
|
|
78
79
|
|
|
79
80
|
def install(self, context: HarnessContext) -> dict[str, object]:
|
|
81
|
+
shim_manifest = install_guard_shim(self.harness, context)
|
|
80
82
|
return {
|
|
81
83
|
"harness": self.harness,
|
|
82
84
|
"active": True,
|
|
83
|
-
"config_path":
|
|
84
|
-
|
|
85
|
+
"config_path": shim_manifest["shim_path"],
|
|
86
|
+
**shim_manifest,
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
def uninstall(self, context: HarnessContext) -> dict[str, object]:
|
|
90
|
+
shim_manifest = remove_guard_shim(self.harness, context)
|
|
88
91
|
return {
|
|
89
92
|
"harness": self.harness,
|
|
90
93
|
"active": False,
|
|
91
|
-
"config_path":
|
|
92
|
-
|
|
94
|
+
"config_path": shim_manifest["shim_path"],
|
|
95
|
+
**shim_manifest,
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
def launch_command(self, context: HarnessContext, passthrough_args: list[str]) -> list[str]:
|
{plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/adapters/claude_code.py
RENAMED
|
@@ -8,6 +8,7 @@ import sys
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
10
|
from ..models import GuardArtifact, HarnessDetection
|
|
11
|
+
from ..shims import install_guard_shim, remove_guard_shim
|
|
11
12
|
from .base import HarnessAdapter, HarnessContext, _command_available, _json_payload
|
|
12
13
|
|
|
13
14
|
|
|
@@ -119,14 +120,30 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
119
120
|
|
|
120
121
|
@staticmethod
|
|
121
122
|
def _hook_command(context: HarnessContext) -> str:
|
|
122
|
-
command = [
|
|
123
|
+
command = [
|
|
124
|
+
sys.executable,
|
|
125
|
+
"-m",
|
|
126
|
+
"codex_plugin_scanner.cli",
|
|
127
|
+
"guard",
|
|
128
|
+
"hook",
|
|
129
|
+
"--guard-home",
|
|
130
|
+
str(context.guard_home),
|
|
131
|
+
]
|
|
132
|
+
if context.home_dir.resolve() != Path.home().resolve():
|
|
133
|
+
command.extend(["--home", str(context.home_dir)])
|
|
123
134
|
if context.workspace_dir is not None:
|
|
124
135
|
command.extend(["--workspace", str(context.workspace_dir)])
|
|
125
136
|
return subprocess.list2cmdline(command)
|
|
126
137
|
|
|
127
138
|
def install(self, context: HarnessContext) -> dict[str, object]:
|
|
139
|
+
shim_manifest = install_guard_shim(self.harness, context)
|
|
128
140
|
if context.workspace_dir is None:
|
|
129
|
-
return
|
|
141
|
+
return {
|
|
142
|
+
"harness": self.harness,
|
|
143
|
+
"active": True,
|
|
144
|
+
"config_path": shim_manifest["shim_path"],
|
|
145
|
+
**shim_manifest,
|
|
146
|
+
}
|
|
130
147
|
settings_path = context.workspace_dir / ".claude" / "settings.local.json"
|
|
131
148
|
payload = _json_payload(settings_path)
|
|
132
149
|
hook_command = self._hook_command(context)
|
|
@@ -144,12 +161,22 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
144
161
|
"harness": self.harness,
|
|
145
162
|
"active": True,
|
|
146
163
|
"config_path": str(settings_path),
|
|
147
|
-
|
|
164
|
+
**shim_manifest,
|
|
165
|
+
"notes": [
|
|
166
|
+
"Guard hook entries added to .claude/settings.local.json",
|
|
167
|
+
*[str(note) for note in shim_manifest.get("notes", [])],
|
|
168
|
+
],
|
|
148
169
|
}
|
|
149
170
|
|
|
150
171
|
def uninstall(self, context: HarnessContext) -> dict[str, object]:
|
|
172
|
+
shim_manifest = remove_guard_shim(self.harness, context)
|
|
151
173
|
if context.workspace_dir is None:
|
|
152
|
-
return
|
|
174
|
+
return {
|
|
175
|
+
"harness": self.harness,
|
|
176
|
+
"active": False,
|
|
177
|
+
"config_path": shim_manifest["shim_path"],
|
|
178
|
+
**shim_manifest,
|
|
179
|
+
}
|
|
153
180
|
settings_path = context.workspace_dir / ".claude" / "settings.local.json"
|
|
154
181
|
payload = _json_payload(settings_path)
|
|
155
182
|
hook_command = self._hook_command(context)
|
|
@@ -164,5 +191,9 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
164
191
|
"harness": self.harness,
|
|
165
192
|
"active": False,
|
|
166
193
|
"config_path": str(settings_path),
|
|
167
|
-
|
|
194
|
+
**shim_manifest,
|
|
195
|
+
"notes": [
|
|
196
|
+
"Guard hook entries removed from .claude/settings.local.json",
|
|
197
|
+
*[str(note) for note in shim_manifest.get("notes", [])],
|
|
198
|
+
],
|
|
168
199
|
}
|
{plugin_scanner-2.0.1 → plugin_scanner-2.0.2}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -16,6 +16,8 @@ from ..policy.engine import SAFE_CHANGED_HASH_ACTION, VALID_GUARD_ACTIONS
|
|
|
16
16
|
from ..receipts import build_receipt
|
|
17
17
|
from ..runtime import guard_run, sync_receipts
|
|
18
18
|
from ..store import GuardStore
|
|
19
|
+
from .product import build_guard_start_payload, build_guard_status_payload
|
|
20
|
+
from .prompt import build_prompt_artifacts, resolve_interactive_decisions
|
|
19
21
|
from .render import emit_guard_payload
|
|
20
22
|
|
|
21
23
|
|
|
@@ -26,6 +28,7 @@ def _now() -> str:
|
|
|
26
28
|
def add_guard_parser(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
27
29
|
"""Register the Guard command family."""
|
|
28
30
|
|
|
31
|
+
program_name = Path(sys.argv[0]).name or "plugin-scanner"
|
|
29
32
|
guard_parser = subparsers.add_parser(
|
|
30
33
|
"guard",
|
|
31
34
|
help="Run local harness protection workflows",
|
|
@@ -35,19 +38,27 @@ def add_guard_parser(subparsers: argparse._SubParsersAction[argparse.ArgumentPar
|
|
|
35
38
|
),
|
|
36
39
|
epilog=(
|
|
37
40
|
"Examples:\n"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
41
|
+
f" {program_name} guard detect\n"
|
|
42
|
+
f" {program_name} guard doctor cursor\n"
|
|
43
|
+
f" {program_name} guard run codex --dry-run\n"
|
|
44
|
+
f" {program_name} guard install claude-code --workspace ."
|
|
42
45
|
),
|
|
43
46
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
44
47
|
)
|
|
45
48
|
guard_subparsers = guard_parser.add_subparsers(
|
|
46
49
|
dest="guard_command",
|
|
47
|
-
metavar="{detect,install,uninstall,run,scan,diff,receipts,explain,allow,deny,doctor,login,sync}",
|
|
50
|
+
metavar="{start,status,detect,install,uninstall,run,scan,diff,receipts,explain,allow,deny,doctor,login,sync}",
|
|
48
51
|
)
|
|
49
52
|
guard_subparsers.required = True
|
|
50
53
|
|
|
54
|
+
start_parser = guard_subparsers.add_parser("start", help="Show the first Guard steps for a local harness")
|
|
55
|
+
_add_guard_common_args(start_parser)
|
|
56
|
+
start_parser.add_argument("--json", action="store_true")
|
|
57
|
+
|
|
58
|
+
status_parser = guard_subparsers.add_parser("status", help="Show current Guard protection status")
|
|
59
|
+
_add_guard_common_args(status_parser)
|
|
60
|
+
status_parser.add_argument("--json", action="store_true")
|
|
61
|
+
|
|
51
62
|
detect_parser = guard_subparsers.add_parser("detect", help="Discover supported harnesses and local artifacts")
|
|
52
63
|
detect_parser.add_argument("harness", nargs="?")
|
|
53
64
|
_add_guard_common_args(detect_parser)
|
|
@@ -95,10 +106,11 @@ def add_guard_parser(subparsers: argparse._SubParsersAction[argparse.ArgumentPar
|
|
|
95
106
|
policy_parser.add_argument("--artifact-id")
|
|
96
107
|
policy_parser.add_argument(
|
|
97
108
|
"--scope",
|
|
98
|
-
choices=("global", "harness", "workspace", "artifact"),
|
|
109
|
+
choices=("global", "harness", "workspace", "artifact", "publisher"),
|
|
99
110
|
default="harness",
|
|
100
111
|
)
|
|
101
112
|
policy_parser.add_argument("--reason")
|
|
113
|
+
policy_parser.add_argument("--publisher")
|
|
102
114
|
_add_guard_common_args(policy_parser)
|
|
103
115
|
policy_parser.add_argument("--json", action="store_true")
|
|
104
116
|
policy_parser.set_defaults(policy_action=action)
|
|
@@ -112,10 +124,12 @@ def add_guard_parser(subparsers: argparse._SubParsersAction[argparse.ArgumentPar
|
|
|
112
124
|
login_parser.add_argument("--sync-url", required=True)
|
|
113
125
|
login_parser.add_argument("--token", required=True)
|
|
114
126
|
login_parser.add_argument("--home")
|
|
127
|
+
login_parser.add_argument("--guard-home")
|
|
115
128
|
login_parser.add_argument("--json", action="store_true")
|
|
116
129
|
|
|
117
130
|
sync_parser = guard_subparsers.add_parser("sync", help="Sync receipts to the configured Guard endpoint")
|
|
118
131
|
sync_parser.add_argument("--home")
|
|
132
|
+
sync_parser.add_argument("--guard-home")
|
|
119
133
|
sync_parser.add_argument("--json", action="store_true")
|
|
120
134
|
|
|
121
135
|
hook_parser = guard_subparsers.add_parser("hook", help=argparse.SUPPRESS)
|
|
@@ -133,6 +147,7 @@ def add_guard_parser(subparsers: argparse._SubParsersAction[argparse.ArgumentPar
|
|
|
133
147
|
|
|
134
148
|
def _add_guard_common_args(parser: argparse.ArgumentParser) -> None:
|
|
135
149
|
parser.add_argument("--home")
|
|
150
|
+
parser.add_argument("--guard-home")
|
|
136
151
|
parser.add_argument("--workspace")
|
|
137
152
|
|
|
138
153
|
|
|
@@ -144,16 +159,27 @@ def run_guard_command(args: argparse.Namespace) -> int:
|
|
|
144
159
|
_emit("scan", payload, args.json or args.consumer_mode)
|
|
145
160
|
return 0
|
|
146
161
|
|
|
147
|
-
|
|
162
|
+
home_override = getattr(args, "home", None)
|
|
163
|
+
guard_home = resolve_guard_home(getattr(args, "guard_home", None) or home_override)
|
|
148
164
|
workspace = Path(args.workspace).resolve() if getattr(args, "workspace", None) else None
|
|
149
165
|
context = HarnessContext(
|
|
150
|
-
home_dir=Path(
|
|
166
|
+
home_dir=Path(home_override).resolve() if home_override else Path.home().resolve(),
|
|
151
167
|
workspace_dir=workspace,
|
|
152
168
|
guard_home=guard_home,
|
|
153
169
|
)
|
|
154
170
|
config = load_guard_config(guard_home, workspace=workspace)
|
|
155
171
|
store = GuardStore(guard_home)
|
|
156
172
|
|
|
173
|
+
if args.guard_command == "start":
|
|
174
|
+
payload = build_guard_start_payload(context, store, config)
|
|
175
|
+
_emit("start", payload, getattr(args, "json", False))
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
if args.guard_command == "status":
|
|
179
|
+
payload = build_guard_status_payload(context, store, config)
|
|
180
|
+
_emit("status", payload, getattr(args, "json", False))
|
|
181
|
+
return 0
|
|
182
|
+
|
|
157
183
|
if args.guard_command == "detect":
|
|
158
184
|
detections = [detect_harness(args.harness, context)] if args.harness else detect_all(context)
|
|
159
185
|
payload = {
|
|
@@ -182,6 +208,27 @@ def run_guard_command(args: argparse.Namespace) -> int:
|
|
|
182
208
|
return 0
|
|
183
209
|
|
|
184
210
|
if args.guard_command == "run":
|
|
211
|
+
interactive_resolver = None
|
|
212
|
+
if (
|
|
213
|
+
not getattr(args, "json", False)
|
|
214
|
+
and not bool(args.dry_run)
|
|
215
|
+
and config.mode == "prompt"
|
|
216
|
+
and sys.stdin.isatty()
|
|
217
|
+
):
|
|
218
|
+
|
|
219
|
+
def interactive_resolver(detection, payload):
|
|
220
|
+
return resolve_interactive_decisions(
|
|
221
|
+
store=store,
|
|
222
|
+
evaluation=payload,
|
|
223
|
+
prompt_artifacts=build_prompt_artifacts(
|
|
224
|
+
harness=detection.harness,
|
|
225
|
+
artifacts=list(detection.artifacts),
|
|
226
|
+
evaluation_artifacts=[item for item in payload.get("artifacts", []) if isinstance(item, dict)],
|
|
227
|
+
),
|
|
228
|
+
workspace=str(workspace) if workspace else None,
|
|
229
|
+
now=_now(),
|
|
230
|
+
)
|
|
231
|
+
|
|
185
232
|
payload = guard_run(
|
|
186
233
|
args.harness,
|
|
187
234
|
context=context,
|
|
@@ -190,7 +237,18 @@ def run_guard_command(args: argparse.Namespace) -> int:
|
|
|
190
237
|
dry_run=bool(args.dry_run),
|
|
191
238
|
passthrough_args=list(args.passthrough_args),
|
|
192
239
|
default_action=args.default_action,
|
|
240
|
+
interactive_resolver=interactive_resolver,
|
|
193
241
|
)
|
|
242
|
+
if (
|
|
243
|
+
payload.get("blocked")
|
|
244
|
+
and config.mode == "prompt"
|
|
245
|
+
and not getattr(args, "json", False)
|
|
246
|
+
and not sys.stdin.isatty()
|
|
247
|
+
):
|
|
248
|
+
payload["review_hint"] = (
|
|
249
|
+
f"Guard blocked {args.harness} because this shell is non-interactive. "
|
|
250
|
+
f"Run `plugin-guard guard diff {args.harness}` and allow or deny the changed artifacts first."
|
|
251
|
+
)
|
|
194
252
|
_emit("run", payload, getattr(args, "json", False))
|
|
195
253
|
if payload.get("blocked"):
|
|
196
254
|
return 1
|
|
@@ -216,7 +274,7 @@ def run_guard_command(args: argparse.Namespace) -> int:
|
|
|
216
274
|
return 0
|
|
217
275
|
|
|
218
276
|
if args.guard_command in {"allow", "deny"}:
|
|
219
|
-
_validate_policy_scope(args.scope, args.artifact_id, workspace)
|
|
277
|
+
_validate_policy_scope(args.scope, args.artifact_id, workspace, getattr(args, "publisher", None))
|
|
220
278
|
payload = record_policy(
|
|
221
279
|
store=store,
|
|
222
280
|
harness=args.harness,
|
|
@@ -224,6 +282,7 @@ def run_guard_command(args: argparse.Namespace) -> int:
|
|
|
224
282
|
scope=args.scope,
|
|
225
283
|
artifact_id=args.artifact_id,
|
|
226
284
|
workspace=str(workspace) if workspace else None,
|
|
285
|
+
publisher=getattr(args, "publisher", None),
|
|
227
286
|
reason=args.reason,
|
|
228
287
|
)
|
|
229
288
|
_emit(args.guard_command, {"decision": payload}, getattr(args, "json", False))
|
|
@@ -349,10 +408,18 @@ def _string_list(value: object | None) -> list[str]:
|
|
|
349
408
|
return [str(item) for item in value if isinstance(item, str) and item.strip()]
|
|
350
409
|
|
|
351
410
|
|
|
352
|
-
def _validate_policy_scope(
|
|
411
|
+
def _validate_policy_scope(
|
|
412
|
+
scope: str,
|
|
413
|
+
artifact_id: str | None,
|
|
414
|
+
workspace: Path | None,
|
|
415
|
+
publisher: str | None,
|
|
416
|
+
) -> None:
|
|
353
417
|
if scope == "artifact" and not artifact_id:
|
|
354
418
|
print("--artifact-id is required when --scope artifact", file=sys.stderr)
|
|
355
419
|
raise SystemExit(2)
|
|
356
420
|
if scope == "workspace" and workspace is None:
|
|
357
421
|
print("--workspace is required when --scope workspace", file=sys.stderr)
|
|
358
422
|
raise SystemExit(2)
|
|
423
|
+
if scope == "publisher" and not publisher:
|
|
424
|
+
print("--publisher is required when --scope publisher", file=sys.stderr)
|
|
425
|
+
raise SystemExit(2)
|