plugin-scanner 2.0.60__tar.gz → 2.0.61__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.60 → plugin_scanner-2.0.61}/PKG-INFO +1 -1
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/pyproject.toml +1 -1
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/claude_code.py +25 -1
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/commands.py +333 -12
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_claude_adapter.py +13 -2
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_cli.py +4 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_runtime.py +233 -8
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_surface_server.py +9 -2
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.dockerignore +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.gitignore +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/Dockerfile +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/requirements.txt +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plugin-scanner
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.61
|
|
4
4
|
Summary: Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows.
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "plugin-scanner"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.61"
|
|
8
8
|
description = "Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hol-guard"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.61"
|
|
8
8
|
description = "Protect local AI harnesses with HOL Guard and run scanner checks for Codex, Claude, Cursor, Gemini, and OpenCode."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -25,6 +25,7 @@ CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS = 30
|
|
|
25
25
|
CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS = 20
|
|
26
26
|
CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS = 10
|
|
27
27
|
CLAUDE_GUARD_SESSION_START_TIMEOUT_SECONDS = 10
|
|
28
|
+
CLAUDE_GUARD_STOP_TIMEOUT_SECONDS = 10
|
|
28
29
|
CLAUDE_SETTINGS_FILES = ("settings.json", "settings.local.json")
|
|
29
30
|
CLAUDE_GUARD_DAEMON_HOOK_MARKER = "HOL_GUARD_CLAUDE_DAEMON_HOOK"
|
|
30
31
|
|
|
@@ -46,6 +47,7 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
|
|
|
46
47
|
("PostToolUse", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
|
|
47
48
|
("UserPromptSubmit", None, CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS),
|
|
48
49
|
("Notification", CLAUDE_GUARD_NOTIFICATION_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
|
|
50
|
+
("Stop", None, CLAUDE_GUARD_STOP_TIMEOUT_SECONDS),
|
|
49
51
|
):
|
|
50
52
|
existing_entries = hooks.get(key)
|
|
51
53
|
hooks[key] = _merge_hook_group(
|
|
@@ -55,6 +57,18 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
|
|
|
55
57
|
)
|
|
56
58
|
|
|
57
59
|
|
|
60
|
+
def _remove_unsupported_guard_hook_groups(hooks: dict[str, object]) -> None:
|
|
61
|
+
for key in ("PermissionDenied",):
|
|
62
|
+
entries = hooks.get(key)
|
|
63
|
+
if not isinstance(entries, list):
|
|
64
|
+
continue
|
|
65
|
+
remaining = _prune_guard_hook_entries(entries)
|
|
66
|
+
if remaining:
|
|
67
|
+
hooks[key] = remaining
|
|
68
|
+
else:
|
|
69
|
+
hooks.pop(key, None)
|
|
70
|
+
|
|
71
|
+
|
|
58
72
|
def _guard_hook_group(matcher: str | None, handler: dict[str, object]) -> dict[str, object]:
|
|
59
73
|
payload: dict[str, object] = {"hooks": [handler]}
|
|
60
74
|
if isinstance(matcher, str) and matcher.strip():
|
|
@@ -577,6 +591,7 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
577
591
|
if not isinstance(hooks, dict):
|
|
578
592
|
return
|
|
579
593
|
_sync_runtime_hook_groups(hooks, self._daemon_hook_command(context))
|
|
594
|
+
_remove_unsupported_guard_hook_groups(hooks)
|
|
580
595
|
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
581
596
|
settings_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
582
597
|
|
|
@@ -620,6 +635,7 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
620
635
|
session_start_entries = _merge_hook_group(session_start_entries, matcher, session_start_handler)
|
|
621
636
|
hooks["SessionStart"] = session_start_entries
|
|
622
637
|
_sync_runtime_hook_groups(hooks, hook_command)
|
|
638
|
+
_remove_unsupported_guard_hook_groups(hooks)
|
|
623
639
|
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
624
640
|
settings_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
625
641
|
return {
|
|
@@ -653,9 +669,17 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
653
669
|
payload = _json_payload(settings_path)
|
|
654
670
|
hooks = payload.get("hooks")
|
|
655
671
|
if isinstance(hooks, dict):
|
|
656
|
-
for key in (
|
|
672
|
+
for key in (
|
|
673
|
+
"SessionStart",
|
|
674
|
+
"PreToolUse",
|
|
675
|
+
"PostToolUse",
|
|
676
|
+
"UserPromptSubmit",
|
|
677
|
+
"Notification",
|
|
678
|
+
"Stop",
|
|
679
|
+
):
|
|
657
680
|
entries = hooks.get(key)
|
|
658
681
|
hooks[key] = _prune_guard_hook_entries(entries if isinstance(entries, list) else [])
|
|
682
|
+
_remove_unsupported_guard_hook_groups(hooks)
|
|
659
683
|
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
660
684
|
settings_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
661
685
|
return {
|
{plugin_scanner-2.0.60 → plugin_scanner-2.0.61}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -14,6 +14,7 @@ import sys
|
|
|
14
14
|
import urllib.error
|
|
15
15
|
import urllib.parse
|
|
16
16
|
import webbrowser
|
|
17
|
+
from dataclasses import replace
|
|
17
18
|
from datetime import datetime, timedelta, timezone
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
from typing import TextIO
|
|
@@ -55,7 +56,7 @@ from ..mcp_tool_calls import (
|
|
|
55
56
|
build_tool_call_hash,
|
|
56
57
|
evaluate_tool_call,
|
|
57
58
|
)
|
|
58
|
-
from ..models import GuardArtifact, HarnessDetection
|
|
59
|
+
from ..models import GuardArtifact, HarnessDetection, PolicyDecision
|
|
59
60
|
from ..policy.engine import SAFE_CHANGED_HASH_ACTION, VALID_GUARD_ACTIONS
|
|
60
61
|
from ..protect import build_protect_payload
|
|
61
62
|
from ..proxy import (
|
|
@@ -1215,17 +1216,40 @@ def run_guard_command(
|
|
|
1215
1216
|
output_stream=output_stream,
|
|
1216
1217
|
)
|
|
1217
1218
|
return 0
|
|
1219
|
+
if _canonical_harness_name(args.harness) == "claude-code" and _hook_event_name(payload) == "Stop":
|
|
1220
|
+
denied = _persist_claude_pending_permission_denials(store, payload)
|
|
1221
|
+
store.add_event(
|
|
1222
|
+
"claude/turn_stop",
|
|
1223
|
+
{"session_id": payload.get("session_id"), "saved_denials": denied},
|
|
1224
|
+
_now(),
|
|
1225
|
+
)
|
|
1226
|
+
return 0
|
|
1218
1227
|
if runtime_artifact is not None:
|
|
1219
1228
|
event_name = _hook_event_name(payload) or "PreToolUse"
|
|
1220
1229
|
runtime_artifact_hash = artifact_hash(runtime_artifact)
|
|
1221
1230
|
artifact_id = runtime_artifact.artifact_id
|
|
1222
1231
|
artifact_name = runtime_artifact.name
|
|
1232
|
+
policy_harness = _canonical_harness_name(args.harness)
|
|
1223
1233
|
stored_policy_action = store.resolve_policy(
|
|
1224
|
-
|
|
1234
|
+
policy_harness,
|
|
1225
1235
|
artifact_id,
|
|
1226
1236
|
runtime_artifact_hash,
|
|
1227
1237
|
str(runtime_workspace) if runtime_workspace else None,
|
|
1228
1238
|
)
|
|
1239
|
+
if stored_policy_action is None:
|
|
1240
|
+
legacy_artifact = _legacy_claude_alias_runtime_artifact(
|
|
1241
|
+
artifact=runtime_artifact,
|
|
1242
|
+
requested_harness=args.harness,
|
|
1243
|
+
home_dir=context.home_dir,
|
|
1244
|
+
workspace=runtime_workspace,
|
|
1245
|
+
)
|
|
1246
|
+
if legacy_artifact is not None:
|
|
1247
|
+
stored_policy_action = store.resolve_policy(
|
|
1248
|
+
args.harness,
|
|
1249
|
+
legacy_artifact.artifact_id,
|
|
1250
|
+
artifact_hash(legacy_artifact),
|
|
1251
|
+
str(runtime_workspace) if runtime_workspace else None,
|
|
1252
|
+
)
|
|
1229
1253
|
policy_action = _coalesce_string(
|
|
1230
1254
|
getattr(args, "policy_action", None),
|
|
1231
1255
|
stored_policy_action,
|
|
@@ -1233,6 +1257,33 @@ def run_guard_command(
|
|
|
1233
1257
|
)
|
|
1234
1258
|
if policy_action not in VALID_GUARD_ACTIONS:
|
|
1235
1259
|
policy_action = SAFE_CHANGED_HASH_ACTION
|
|
1260
|
+
if _canonical_harness_name(args.harness) == "claude-code" and event_name in {
|
|
1261
|
+
"PostToolUse",
|
|
1262
|
+
"PostToolUseFailure",
|
|
1263
|
+
}:
|
|
1264
|
+
saved = _persist_claude_native_permission_for_runtime_artifact(
|
|
1265
|
+
store=store,
|
|
1266
|
+
payload=payload,
|
|
1267
|
+
artifact=runtime_artifact,
|
|
1268
|
+
artifact_hash=runtime_artifact_hash,
|
|
1269
|
+
action="allow",
|
|
1270
|
+
reason="Approved in Claude native approval prompt.",
|
|
1271
|
+
)
|
|
1272
|
+
if saved:
|
|
1273
|
+
receipt = build_receipt(
|
|
1274
|
+
harness=policy_harness,
|
|
1275
|
+
artifact_id=artifact_id,
|
|
1276
|
+
artifact_hash=runtime_artifact_hash,
|
|
1277
|
+
policy_decision="allow",
|
|
1278
|
+
capabilities_summary=_runtime_capabilities_summary(runtime_artifact),
|
|
1279
|
+
changed_capabilities=[runtime_artifact.artifact_type, "claude-native-approved"],
|
|
1280
|
+
provenance_summary=f"runtime tool request approved from {runtime_artifact.config_path}",
|
|
1281
|
+
artifact_name=artifact_name,
|
|
1282
|
+
source_scope=runtime_artifact.source_scope,
|
|
1283
|
+
user_override="claude-native-approve",
|
|
1284
|
+
)
|
|
1285
|
+
store.add_receipt(receipt)
|
|
1286
|
+
return 0
|
|
1236
1287
|
changed_capabilities = [runtime_artifact.artifact_type]
|
|
1237
1288
|
risk_signals = list(artifact_risk_signals(runtime_artifact))
|
|
1238
1289
|
risk_summary = artifact_risk_summary(runtime_artifact)
|
|
@@ -1288,16 +1339,6 @@ def run_guard_command(
|
|
|
1288
1339
|
artifact=runtime_artifact,
|
|
1289
1340
|
native_reason=native_reason,
|
|
1290
1341
|
)
|
|
1291
|
-
if (
|
|
1292
|
-
_canonical_harness_name(args.harness) == "claude-code"
|
|
1293
|
-
and event_name == "UserPromptSubmit"
|
|
1294
|
-
and policy_action == "require-reapproval"
|
|
1295
|
-
and not _prompt_requires_hard_block(runtime_artifact)
|
|
1296
|
-
and (output_stream is not None or not getattr(args, "json", False))
|
|
1297
|
-
):
|
|
1298
|
-
if getattr(args, "json", False) and output_stream is not None:
|
|
1299
|
-
_write_json_line({}, output_stream=output_stream)
|
|
1300
|
-
return 0
|
|
1301
1342
|
if (
|
|
1302
1343
|
_canonical_harness_name(args.harness) == "claude-code"
|
|
1303
1344
|
and event_name == "PreToolUse"
|
|
@@ -1308,6 +1349,7 @@ def run_guard_command(
|
|
|
1308
1349
|
payload=payload,
|
|
1309
1350
|
reason=native_reason,
|
|
1310
1351
|
artifact=runtime_artifact,
|
|
1352
|
+
artifact_hash=runtime_artifact_hash,
|
|
1311
1353
|
)
|
|
1312
1354
|
if _should_emit_copilot_hook_response(args):
|
|
1313
1355
|
_emit_copilot_hook_response(
|
|
@@ -1668,12 +1710,62 @@ def _claude_permission_notice_state_key(session_id: str, tool_name: str | None =
|
|
|
1668
1710
|
return f"claude_permission_notice:{session_id}"
|
|
1669
1711
|
|
|
1670
1712
|
|
|
1713
|
+
def _claude_pending_permission_index_key(session_id: str) -> str:
|
|
1714
|
+
return f"claude_pending_permissions:{session_id}"
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
def _claude_pending_permission_state_key(session_id: str, artifact_id: str) -> str:
|
|
1718
|
+
fingerprint = hashlib.sha256(artifact_id.encode("utf-8")).hexdigest()[:24]
|
|
1719
|
+
return f"claude_pending_permission:{session_id}:{fingerprint}"
|
|
1720
|
+
|
|
1721
|
+
|
|
1722
|
+
def _sync_payload_list_from_row(row: sqlite3.Row | None) -> list[str]:
|
|
1723
|
+
if row is None:
|
|
1724
|
+
return []
|
|
1725
|
+
try:
|
|
1726
|
+
payload = json.loads(str(row["payload_json"]))
|
|
1727
|
+
except json.JSONDecodeError:
|
|
1728
|
+
return []
|
|
1729
|
+
return [str(item) for item in payload] if isinstance(payload, list) else []
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
def _append_claude_pending_permission_key(
|
|
1733
|
+
store: GuardStore,
|
|
1734
|
+
*,
|
|
1735
|
+
session_id: str,
|
|
1736
|
+
pending_key: str,
|
|
1737
|
+
now: str,
|
|
1738
|
+
) -> None:
|
|
1739
|
+
index_key = _claude_pending_permission_index_key(session_id)
|
|
1740
|
+
with store._connect() as connection:
|
|
1741
|
+
connection.execute("begin immediate")
|
|
1742
|
+
row = connection.execute(
|
|
1743
|
+
"select payload_json from sync_state where state_key = ?",
|
|
1744
|
+
(index_key,),
|
|
1745
|
+
).fetchone()
|
|
1746
|
+
pending_keys = _sync_payload_list_from_row(row)
|
|
1747
|
+
if pending_key in pending_keys:
|
|
1748
|
+
return
|
|
1749
|
+
pending_keys.append(pending_key)
|
|
1750
|
+
connection.execute(
|
|
1751
|
+
"""
|
|
1752
|
+
insert into sync_state (state_key, payload_json, updated_at)
|
|
1753
|
+
values (?, ?, ?)
|
|
1754
|
+
on conflict(state_key) do update set
|
|
1755
|
+
payload_json = excluded.payload_json,
|
|
1756
|
+
updated_at = excluded.updated_at
|
|
1757
|
+
""",
|
|
1758
|
+
(index_key, json.dumps(pending_keys), now),
|
|
1759
|
+
)
|
|
1760
|
+
|
|
1761
|
+
|
|
1671
1762
|
def _record_claude_permission_notice(
|
|
1672
1763
|
*,
|
|
1673
1764
|
store: GuardStore,
|
|
1674
1765
|
payload: dict[str, object],
|
|
1675
1766
|
reason: str,
|
|
1676
1767
|
artifact: GuardArtifact,
|
|
1768
|
+
artifact_hash: str,
|
|
1677
1769
|
) -> None:
|
|
1678
1770
|
session_id = _optional_string(payload.get("session_id"))
|
|
1679
1771
|
if session_id is None:
|
|
@@ -1683,12 +1775,19 @@ def _record_claude_permission_notice(
|
|
|
1683
1775
|
"saved_at": _now(),
|
|
1684
1776
|
"reason": reason,
|
|
1685
1777
|
"artifact_id": artifact.artifact_id,
|
|
1778
|
+
"artifact_hash": artifact_hash,
|
|
1686
1779
|
"artifact_name": artifact.name,
|
|
1780
|
+
"artifact_type": artifact.artifact_type,
|
|
1781
|
+
"config_path": artifact.config_path,
|
|
1782
|
+
"source_scope": artifact.source_scope,
|
|
1687
1783
|
}
|
|
1688
1784
|
if tool_name is not None:
|
|
1689
1785
|
notice_payload["tool_name"] = tool_name
|
|
1690
1786
|
try:
|
|
1691
1787
|
store.set_sync_payload(_claude_permission_notice_state_key(session_id, tool_name), notice_payload, _now())
|
|
1788
|
+
pending_key = _claude_pending_permission_state_key(session_id, artifact.artifact_id)
|
|
1789
|
+
store.set_sync_payload(pending_key, notice_payload, _now())
|
|
1790
|
+
_append_claude_pending_permission_key(store, session_id=session_id, pending_key=pending_key, now=_now())
|
|
1692
1791
|
except (OSError, sqlite3.Error):
|
|
1693
1792
|
return
|
|
1694
1793
|
|
|
@@ -1713,6 +1812,205 @@ def _load_claude_permission_notice(store: GuardStore, payload: dict[str, object]
|
|
|
1713
1812
|
return None
|
|
1714
1813
|
|
|
1715
1814
|
|
|
1815
|
+
def _load_claude_pending_permission(
|
|
1816
|
+
store: GuardStore,
|
|
1817
|
+
payload: dict[str, object],
|
|
1818
|
+
artifact: GuardArtifact,
|
|
1819
|
+
) -> dict[str, object] | None:
|
|
1820
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
1821
|
+
if session_id is None:
|
|
1822
|
+
return None
|
|
1823
|
+
pending_key = _claude_pending_permission_state_key(session_id, artifact.artifact_id)
|
|
1824
|
+
try:
|
|
1825
|
+
persisted = store.get_sync_payload(pending_key)
|
|
1826
|
+
except (OSError, sqlite3.Error):
|
|
1827
|
+
return None
|
|
1828
|
+
return persisted if isinstance(persisted, dict) else None
|
|
1829
|
+
|
|
1830
|
+
|
|
1831
|
+
def _remove_claude_pending_permission(
|
|
1832
|
+
store: GuardStore,
|
|
1833
|
+
*,
|
|
1834
|
+
session_id: str,
|
|
1835
|
+
pending_key: str,
|
|
1836
|
+
) -> None:
|
|
1837
|
+
try:
|
|
1838
|
+
index_key = _claude_pending_permission_index_key(session_id)
|
|
1839
|
+
with store._connect() as connection:
|
|
1840
|
+
connection.execute("begin immediate")
|
|
1841
|
+
connection.execute("delete from sync_state where state_key = ?", (pending_key,))
|
|
1842
|
+
row = connection.execute(
|
|
1843
|
+
"select payload_json from sync_state where state_key = ?",
|
|
1844
|
+
(index_key,),
|
|
1845
|
+
).fetchone()
|
|
1846
|
+
remaining = [key for key in _sync_payload_list_from_row(row) if key != pending_key]
|
|
1847
|
+
if remaining:
|
|
1848
|
+
connection.execute(
|
|
1849
|
+
"""
|
|
1850
|
+
insert into sync_state (state_key, payload_json, updated_at)
|
|
1851
|
+
values (?, ?, ?)
|
|
1852
|
+
on conflict(state_key) do update set
|
|
1853
|
+
payload_json = excluded.payload_json,
|
|
1854
|
+
updated_at = excluded.updated_at
|
|
1855
|
+
""",
|
|
1856
|
+
(index_key, json.dumps(remaining), _now()),
|
|
1857
|
+
)
|
|
1858
|
+
else:
|
|
1859
|
+
connection.execute("delete from sync_state where state_key = ?", (index_key,))
|
|
1860
|
+
except (OSError, sqlite3.Error):
|
|
1861
|
+
return
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
def _persist_claude_native_permission_policy(
|
|
1865
|
+
*,
|
|
1866
|
+
store: GuardStore,
|
|
1867
|
+
artifact_id: str,
|
|
1868
|
+
artifact_hash: str,
|
|
1869
|
+
action: str,
|
|
1870
|
+
reason: str,
|
|
1871
|
+
now: str,
|
|
1872
|
+
) -> bool:
|
|
1873
|
+
try:
|
|
1874
|
+
store.upsert_policy(
|
|
1875
|
+
PolicyDecision(
|
|
1876
|
+
harness="claude-code",
|
|
1877
|
+
scope="artifact",
|
|
1878
|
+
action="allow" if action == "allow" else "block",
|
|
1879
|
+
artifact_id=artifact_id,
|
|
1880
|
+
artifact_hash=artifact_hash,
|
|
1881
|
+
reason=reason,
|
|
1882
|
+
source="claude-native-approval",
|
|
1883
|
+
),
|
|
1884
|
+
now,
|
|
1885
|
+
)
|
|
1886
|
+
store.add_event(
|
|
1887
|
+
"claude/native_permission_saved",
|
|
1888
|
+
{
|
|
1889
|
+
"artifact_id": artifact_id,
|
|
1890
|
+
"artifact_hash": artifact_hash,
|
|
1891
|
+
"action": action,
|
|
1892
|
+
"reason": reason,
|
|
1893
|
+
},
|
|
1894
|
+
now,
|
|
1895
|
+
)
|
|
1896
|
+
except (OSError, sqlite3.Error):
|
|
1897
|
+
return False
|
|
1898
|
+
return True
|
|
1899
|
+
|
|
1900
|
+
|
|
1901
|
+
def _persist_claude_native_permission_for_runtime_artifact(
|
|
1902
|
+
*,
|
|
1903
|
+
store: GuardStore,
|
|
1904
|
+
payload: dict[str, object],
|
|
1905
|
+
artifact: GuardArtifact,
|
|
1906
|
+
artifact_hash: str,
|
|
1907
|
+
action: str,
|
|
1908
|
+
reason: str,
|
|
1909
|
+
) -> bool:
|
|
1910
|
+
pending = _load_claude_pending_permission(store, payload, artifact)
|
|
1911
|
+
if pending is None:
|
|
1912
|
+
return False
|
|
1913
|
+
now = _now()
|
|
1914
|
+
saved_policy = _persist_claude_native_permission_policy(
|
|
1915
|
+
store=store,
|
|
1916
|
+
artifact_id=artifact.artifact_id,
|
|
1917
|
+
artifact_hash=artifact_hash,
|
|
1918
|
+
action=action,
|
|
1919
|
+
reason=reason,
|
|
1920
|
+
now=now,
|
|
1921
|
+
)
|
|
1922
|
+
if not saved_policy:
|
|
1923
|
+
return False
|
|
1924
|
+
try:
|
|
1925
|
+
store.record_inventory_artifact(
|
|
1926
|
+
artifact=artifact,
|
|
1927
|
+
artifact_hash=artifact_hash,
|
|
1928
|
+
policy_action="allow" if action == "allow" else "block",
|
|
1929
|
+
changed=False,
|
|
1930
|
+
now=now,
|
|
1931
|
+
approved=action == "allow",
|
|
1932
|
+
)
|
|
1933
|
+
except (OSError, sqlite3.Error):
|
|
1934
|
+
return False
|
|
1935
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
1936
|
+
if session_id is not None:
|
|
1937
|
+
_remove_claude_pending_permission(
|
|
1938
|
+
store,
|
|
1939
|
+
session_id=session_id,
|
|
1940
|
+
pending_key=_claude_pending_permission_state_key(session_id, artifact.artifact_id),
|
|
1941
|
+
)
|
|
1942
|
+
return True
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[str, object]) -> int:
|
|
1946
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
1947
|
+
if session_id is None:
|
|
1948
|
+
return 0
|
|
1949
|
+
index_key = _claude_pending_permission_index_key(session_id)
|
|
1950
|
+
try:
|
|
1951
|
+
index_payload = store.get_sync_payload(index_key)
|
|
1952
|
+
except (OSError, sqlite3.Error):
|
|
1953
|
+
return 0
|
|
1954
|
+
if not isinstance(index_payload, list):
|
|
1955
|
+
return 0
|
|
1956
|
+
pending_keys = [str(item) for item in index_payload]
|
|
1957
|
+
processed_keys: list[str] = []
|
|
1958
|
+
denied = 0
|
|
1959
|
+
for pending_key in pending_keys:
|
|
1960
|
+
try:
|
|
1961
|
+
pending = store.get_sync_payload(pending_key)
|
|
1962
|
+
except (OSError, sqlite3.Error):
|
|
1963
|
+
continue
|
|
1964
|
+
if not isinstance(pending, dict):
|
|
1965
|
+
continue
|
|
1966
|
+
artifact_id = _optional_string(pending.get("artifact_id"))
|
|
1967
|
+
artifact_hash_value = _optional_string(pending.get("artifact_hash"))
|
|
1968
|
+
if artifact_id is None or artifact_hash_value is None:
|
|
1969
|
+
continue
|
|
1970
|
+
reason = _optional_string(pending.get("reason")) or "Denied in Claude's native approval prompt."
|
|
1971
|
+
saved_policy = _persist_claude_native_permission_policy(
|
|
1972
|
+
store=store,
|
|
1973
|
+
artifact_id=artifact_id,
|
|
1974
|
+
artifact_hash=artifact_hash_value,
|
|
1975
|
+
action="block",
|
|
1976
|
+
reason=f"Denied in Claude native approval prompt. {reason}",
|
|
1977
|
+
now=_now(),
|
|
1978
|
+
)
|
|
1979
|
+
if not saved_policy:
|
|
1980
|
+
continue
|
|
1981
|
+
processed_keys.append(pending_key)
|
|
1982
|
+
denied += 1
|
|
1983
|
+
if processed_keys:
|
|
1984
|
+
processed_set = set(processed_keys)
|
|
1985
|
+
try:
|
|
1986
|
+
with store._connect() as connection:
|
|
1987
|
+
connection.execute("begin immediate")
|
|
1988
|
+
for pending_key in processed_keys:
|
|
1989
|
+
connection.execute("delete from sync_state where state_key = ?", (pending_key,))
|
|
1990
|
+
row = connection.execute(
|
|
1991
|
+
"select payload_json from sync_state where state_key = ?",
|
|
1992
|
+
(index_key,),
|
|
1993
|
+
).fetchone()
|
|
1994
|
+
current_keys = _sync_payload_list_from_row(row)
|
|
1995
|
+
remaining_keys = [pending_key for pending_key in current_keys if pending_key not in processed_set]
|
|
1996
|
+
if remaining_keys:
|
|
1997
|
+
connection.execute(
|
|
1998
|
+
"""
|
|
1999
|
+
insert into sync_state (state_key, payload_json, updated_at)
|
|
2000
|
+
values (?, ?, ?)
|
|
2001
|
+
on conflict(state_key) do update set
|
|
2002
|
+
payload_json = excluded.payload_json,
|
|
2003
|
+
updated_at = excluded.updated_at
|
|
2004
|
+
""",
|
|
2005
|
+
(index_key, json.dumps(remaining_keys), _now()),
|
|
2006
|
+
)
|
|
2007
|
+
else:
|
|
2008
|
+
connection.execute("delete from sync_state where state_key = ?", (index_key,))
|
|
2009
|
+
except (OSError, sqlite3.Error):
|
|
2010
|
+
return denied
|
|
2011
|
+
return denied
|
|
2012
|
+
|
|
2013
|
+
|
|
1716
2014
|
def _is_claude_permission_prompt_notification(args: argparse.Namespace, payload: dict[str, object]) -> bool:
|
|
1717
2015
|
return (
|
|
1718
2016
|
_canonical_harness_name(args.harness) == "claude-code"
|
|
@@ -2484,6 +2782,7 @@ def _hook_runtime_artifact(
|
|
|
2484
2782
|
guard_home: Path,
|
|
2485
2783
|
workspace: Path | None,
|
|
2486
2784
|
) -> GuardArtifact | None:
|
|
2785
|
+
harness = _canonical_harness_name(harness)
|
|
2487
2786
|
event_name = _hook_event_name(payload)
|
|
2488
2787
|
if event_name == "UserPromptSubmit":
|
|
2489
2788
|
prompt_text = payload.get("prompt")
|
|
@@ -2541,6 +2840,28 @@ def _hook_runtime_artifact(
|
|
|
2541
2840
|
)
|
|
2542
2841
|
|
|
2543
2842
|
|
|
2843
|
+
def _legacy_claude_alias_runtime_artifact(
|
|
2844
|
+
*,
|
|
2845
|
+
artifact: GuardArtifact,
|
|
2846
|
+
requested_harness: str,
|
|
2847
|
+
home_dir: Path,
|
|
2848
|
+
workspace: Path | None,
|
|
2849
|
+
) -> GuardArtifact | None:
|
|
2850
|
+
if requested_harness == artifact.harness:
|
|
2851
|
+
return None
|
|
2852
|
+
if requested_harness != "claude" or artifact.harness != "claude-code":
|
|
2853
|
+
return None
|
|
2854
|
+
legacy_prefix = "claude-code:"
|
|
2855
|
+
if not artifact.artifact_id.startswith(legacy_prefix):
|
|
2856
|
+
return None
|
|
2857
|
+
return replace(
|
|
2858
|
+
artifact,
|
|
2859
|
+
artifact_id=f"claude:{artifact.artifact_id[len(legacy_prefix) :]}",
|
|
2860
|
+
harness="claude",
|
|
2861
|
+
config_path=str(_runtime_policy_path("claude", home_dir, workspace)),
|
|
2862
|
+
)
|
|
2863
|
+
|
|
2864
|
+
|
|
2544
2865
|
def _is_copilot_permission_request(payload: dict[str, object]) -> bool:
|
|
2545
2866
|
for key in ("hook_name", "hook_event_name", "hookEventName"):
|
|
2546
2867
|
hook_name = payload.get(key)
|
|
@@ -47,7 +47,7 @@ def _runtime_hook_handlers(payload: dict[str, object]) -> list[dict[str, object]
|
|
|
47
47
|
hooks = payload["hooks"]
|
|
48
48
|
assert isinstance(hooks, dict)
|
|
49
49
|
handlers: list[dict[str, object]] = []
|
|
50
|
-
for key in ("PreToolUse", "PostToolUse", "UserPromptSubmit", "Notification"):
|
|
50
|
+
for key in ("PreToolUse", "PostToolUse", "UserPromptSubmit", "Notification", "Stop"):
|
|
51
51
|
entries = hooks[key]
|
|
52
52
|
assert isinstance(entries, list)
|
|
53
53
|
for entry in entries:
|
|
@@ -147,6 +147,7 @@ def test_claude_install_writes_session_start_and_command_hook_schema_and_is_idem
|
|
|
147
147
|
post_tool_use = payload["hooks"]["PostToolUse"]
|
|
148
148
|
prompt_submit = payload["hooks"]["UserPromptSubmit"]
|
|
149
149
|
notification = payload["hooks"]["Notification"]
|
|
150
|
+
stop = payload["hooks"]["Stop"]
|
|
150
151
|
assert len(session_start) == 4
|
|
151
152
|
assert {entry["matcher"] for entry in session_start} == {"startup", "resume", "clear", "compact"}
|
|
152
153
|
assert all(entry["hooks"][0]["type"] == "command" for entry in session_start)
|
|
@@ -165,6 +166,10 @@ def test_claude_install_writes_session_start_and_command_hook_schema_and_is_idem
|
|
|
165
166
|
assert notification[0]["matcher"] == "permission_prompt"
|
|
166
167
|
assert notification[0]["hooks"][0]["type"] == "command"
|
|
167
168
|
assert notification[0]["hooks"][0]["timeout"] == 10
|
|
169
|
+
assert len(stop) == 1
|
|
170
|
+
assert "matcher" not in stop[0]
|
|
171
|
+
assert stop[0]["hooks"][0]["type"] == "command"
|
|
172
|
+
assert stop[0]["hooks"][0]["timeout"] == 10
|
|
168
173
|
assert all("url" not in handler for handler in _runtime_hook_handlers(payload))
|
|
169
174
|
|
|
170
175
|
|
|
@@ -238,7 +243,13 @@ def test_claude_install_replaces_legacy_http_guard_hooks(tmp_path):
|
|
|
238
243
|
payload = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
239
244
|
installed_handlers = _runtime_hook_handlers(payload)
|
|
240
245
|
|
|
241
|
-
assert [handler["type"] for handler in installed_handlers] == [
|
|
246
|
+
assert [handler["type"] for handler in installed_handlers] == [
|
|
247
|
+
"command",
|
|
248
|
+
"command",
|
|
249
|
+
"command",
|
|
250
|
+
"command",
|
|
251
|
+
"command",
|
|
252
|
+
]
|
|
242
253
|
assert all(CLAUDE_GUARD_DAEMON_HOOK_MARKER in str(handler.get("command", "")) for handler in installed_handlers)
|
|
243
254
|
assert all("url" not in handler for handler in installed_handlers)
|
|
244
255
|
|
|
@@ -2129,11 +2129,13 @@ args = ["workspace-skill.js", "--changed"]
|
|
|
2129
2129
|
assert install_settings_payload["hooks"]["Notification"][0]["hooks"][0]["type"] == "command"
|
|
2130
2130
|
assert install_settings_payload["hooks"]["Notification"][0]["hooks"][0]["command"] == expected_hook_command
|
|
2131
2131
|
assert "url" not in install_settings_payload["hooks"]["Notification"][0]["hooks"][0]
|
|
2132
|
+
assert install_settings_payload["hooks"]["Stop"][0]["hooks"][0]["command"] == expected_hook_command
|
|
2132
2133
|
assert uninstall_rc == 0
|
|
2133
2134
|
assert uninstall_output["managed_install"]["active"] is False
|
|
2134
2135
|
assert settings_payload["hooks"]["SessionStart"] == []
|
|
2135
2136
|
assert settings_payload["hooks"]["PreToolUse"] == []
|
|
2136
2137
|
assert settings_payload["hooks"]["Notification"] == []
|
|
2138
|
+
assert settings_payload["hooks"]["Stop"] == []
|
|
2137
2139
|
|
|
2138
2140
|
def test_guard_uninstall_handles_non_dict_claude_hook_entries(self, tmp_path, capsys):
|
|
2139
2141
|
home_dir = tmp_path / "home"
|
|
@@ -2222,6 +2224,7 @@ args = ["workspace-skill.js", "--changed"]
|
|
|
2222
2224
|
assert payload["hooks"]["PreToolUse"] == []
|
|
2223
2225
|
assert payload["hooks"]["UserPromptSubmit"] == []
|
|
2224
2226
|
assert payload["hooks"]["Notification"] == []
|
|
2227
|
+
assert payload["hooks"]["Stop"] == []
|
|
2225
2228
|
|
|
2226
2229
|
def test_guard_install_claude_alias_persists_canonical_managed_install(self, tmp_path, capsys):
|
|
2227
2230
|
home_dir = tmp_path / "home"
|
|
@@ -2352,6 +2355,7 @@ args = ["workspace-skill.js", "--changed"]
|
|
|
2352
2355
|
assert len(payload["hooks"]["PostToolUse"]) == 1
|
|
2353
2356
|
assert len(payload["hooks"]["UserPromptSubmit"]) == 1
|
|
2354
2357
|
assert len(payload["hooks"]["Notification"]) == 1
|
|
2358
|
+
assert len(payload["hooks"]["Stop"]) == 1
|
|
2355
2359
|
pretool_hook_commands = [
|
|
2356
2360
|
hook["command"]
|
|
2357
2361
|
for hook in payload["hooks"]["PreToolUse"][0]["hooks"]
|