plugin-scanner 2.0.62__tar.gz → 2.0.63__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.62 → plugin_scanner-2.0.63}/PKG-INFO +1 -1
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/pyproject.toml +1 -1
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/claude_code.py +8 -6
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/commands.py +198 -46
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_claude_adapter.py +10 -2
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_runtime.py +347 -31
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_surface_server.py +9 -3
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.dockerignore +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.gitignore +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/Dockerfile +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/requirements.txt +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/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.63
|
|
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.63"
|
|
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.63"
|
|
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"
|
|
@@ -19,6 +19,7 @@ from ..shims import install_guard_shim, remove_guard_shim
|
|
|
19
19
|
from .base import HarnessAdapter, HarnessContext, _ensure_path_within_root, _json_payload, _run_command_probe
|
|
20
20
|
|
|
21
21
|
CLAUDE_GUARD_TOOL_MATCHER = "Bash|Read|Write|Edit|MultiEdit|WebFetch|WebSearch|mcp__.*"
|
|
22
|
+
CLAUDE_GUARD_POST_TOOL_MATCHER = f"{CLAUDE_GUARD_TOOL_MATCHER}|AskUserQuestion"
|
|
22
23
|
CLAUDE_GUARD_NOTIFICATION_MATCHER = "permission_prompt"
|
|
23
24
|
CLAUDE_GUARD_SESSION_START_MATCHERS = ("startup", "resume", "clear", "compact")
|
|
24
25
|
CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS = 30
|
|
@@ -45,7 +46,7 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
|
|
|
45
46
|
for key, matcher, timeout in (
|
|
46
47
|
("PreToolUse", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
|
|
47
48
|
("PermissionRequest", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
|
|
48
|
-
("PostToolUse",
|
|
49
|
+
("PostToolUse", CLAUDE_GUARD_POST_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
|
|
49
50
|
("UserPromptSubmit", None, CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS),
|
|
50
51
|
("Notification", CLAUDE_GUARD_NOTIFICATION_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
|
|
51
52
|
("Stop", None, CLAUDE_GUARD_STOP_TIMEOUT_SECONDS),
|
|
@@ -519,12 +520,13 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
519
520
|
"function degraded(reason,data){"
|
|
520
521
|
"const event=eventName(data);"
|
|
521
522
|
"const message=`HOL Guard could not reach the local daemon (${reason}), so it is using Claude's native "
|
|
522
|
-
"approval prompt as a safety fallback.`;"
|
|
523
|
+
"approval prompt as a temporary safety fallback.`;"
|
|
523
524
|
"if(event==='UserPromptSubmit'){return '';}"
|
|
524
|
-
"if(event==='PreToolUse'){return JSON.stringify({systemMessage:'HOL Guard
|
|
525
|
-
"
|
|
526
|
-
"permissionDecision:'ask',permissionDecisionReason:`${message}
|
|
527
|
-
"
|
|
525
|
+
"if(event==='PreToolUse'){return JSON.stringify({systemMessage:'HOL Guard could not reach the local "
|
|
526
|
+
"daemon, so it cannot render the full HOL Guard approval flow.',hookSpecificOutput:{"
|
|
527
|
+
"hookEventName:'PreToolUse',permissionDecision:'ask',permissionDecisionReason:`${message} Keep this "
|
|
528
|
+
"action blocked unless you intentionally trust it. Restart Guard to restore the branded Allow once / "
|
|
529
|
+
"Allow during this session / Keep blocked flow.`}});}"
|
|
528
530
|
"return '{}';"
|
|
529
531
|
"}"
|
|
530
532
|
"function shouldSuppressOutput(data,responseBody){"
|
{plugin_scanner-2.0.62 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -1195,11 +1195,11 @@ def run_guard_command(
|
|
|
1195
1195
|
_mark_claude_pending_permission_prompt_seen(store=store, payload=payload, notice=notice)
|
|
1196
1196
|
_emit_native_hook_response(
|
|
1197
1197
|
harness=args.harness,
|
|
1198
|
-
policy_action="
|
|
1198
|
+
policy_action="block",
|
|
1199
1199
|
event_name="PermissionRequest",
|
|
1200
|
-
reason="HOL Guard is
|
|
1200
|
+
reason="HOL Guard is routing this approval through AskUserQuestion.",
|
|
1201
1201
|
system_message=_claude_permission_prompt_system_message(payload=payload, notice=notice),
|
|
1202
|
-
additional_context=
|
|
1202
|
+
additional_context=_claude_guard_approval_question_message(notice),
|
|
1203
1203
|
output_stream=output_stream,
|
|
1204
1204
|
)
|
|
1205
1205
|
return 0
|
|
@@ -1226,7 +1226,7 @@ def run_guard_command(
|
|
|
1226
1226
|
harness=args.harness,
|
|
1227
1227
|
policy_action="allow",
|
|
1228
1228
|
event_name="Notification",
|
|
1229
|
-
reason="HOL Guard intercepted the tool request and
|
|
1229
|
+
reason="HOL Guard intercepted the tool request and is routing it through a HOL Guard approval prompt.",
|
|
1230
1230
|
system_message=system_message,
|
|
1231
1231
|
additional_context=additional_context,
|
|
1232
1232
|
output_stream=output_stream,
|
|
@@ -1240,6 +1240,10 @@ def run_guard_command(
|
|
|
1240
1240
|
_now(),
|
|
1241
1241
|
)
|
|
1242
1242
|
return 0
|
|
1243
|
+
if _canonical_harness_name(args.harness) == "claude-code" and _persist_claude_guard_question_decision(
|
|
1244
|
+
store, payload
|
|
1245
|
+
):
|
|
1246
|
+
return 0
|
|
1243
1247
|
if runtime_artifact is not None:
|
|
1244
1248
|
event_name = _hook_event_name(payload) or "PreToolUse"
|
|
1245
1249
|
runtime_artifact_hash = artifact_hash(runtime_artifact)
|
|
@@ -1346,14 +1350,6 @@ def run_guard_command(
|
|
|
1346
1350
|
"risk_headline": incident["risk_headline"],
|
|
1347
1351
|
"path_summary": _runtime_requested_path(runtime_artifact),
|
|
1348
1352
|
}
|
|
1349
|
-
if (
|
|
1350
|
-
_canonical_harness_name(args.harness) == "claude-code"
|
|
1351
|
-
and event_name == "UserPromptSubmit"
|
|
1352
|
-
and policy_action == "require-reapproval"
|
|
1353
|
-
and not _prompt_requires_hard_block(runtime_artifact)
|
|
1354
|
-
and (not getattr(args, "json", False) or output_stream is not None)
|
|
1355
|
-
):
|
|
1356
|
-
return 0
|
|
1357
1353
|
if policy_action in {"block", "sandbox-required", "require-reapproval"}:
|
|
1358
1354
|
native_reason = _runtime_artifact_native_reason(runtime_artifact, response_payload)
|
|
1359
1355
|
additional_context = _claude_prompt_additional_context(
|
|
@@ -1881,6 +1877,42 @@ def _mark_claude_pending_permission_prompt_seen(
|
|
|
1881
1877
|
return
|
|
1882
1878
|
|
|
1883
1879
|
|
|
1880
|
+
def _load_single_claude_pending_permission(
|
|
1881
|
+
store: GuardStore,
|
|
1882
|
+
payload: dict[str, object],
|
|
1883
|
+
) -> tuple[str, dict[str, object]] | None:
|
|
1884
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
1885
|
+
if session_id is None:
|
|
1886
|
+
return None
|
|
1887
|
+
try:
|
|
1888
|
+
index_payload = store.get_sync_payload(_claude_pending_permission_index_key(session_id))
|
|
1889
|
+
except (OSError, sqlite3.Error):
|
|
1890
|
+
return None
|
|
1891
|
+
if not isinstance(index_payload, list):
|
|
1892
|
+
return None
|
|
1893
|
+
pending_keys = [str(item) for item in index_payload]
|
|
1894
|
+
pending_items: list[tuple[str, dict[str, object]]] = []
|
|
1895
|
+
for pending_key in pending_keys:
|
|
1896
|
+
try:
|
|
1897
|
+
pending = store.get_sync_payload(pending_key)
|
|
1898
|
+
except (OSError, sqlite3.Error):
|
|
1899
|
+
continue
|
|
1900
|
+
if isinstance(pending, dict):
|
|
1901
|
+
pending_items.append((pending_key, pending))
|
|
1902
|
+
prompt_seen_items = [item for item in pending_items if item[1].get("permission_prompt_seen") is True]
|
|
1903
|
+
if len(prompt_seen_items) == 1:
|
|
1904
|
+
return prompt_seen_items[0]
|
|
1905
|
+
if len(pending_items) != 1:
|
|
1906
|
+
return None
|
|
1907
|
+
try:
|
|
1908
|
+
pending = store.get_sync_payload(pending_items[0][0])
|
|
1909
|
+
except (OSError, sqlite3.Error):
|
|
1910
|
+
return None
|
|
1911
|
+
if not isinstance(pending, dict):
|
|
1912
|
+
return None
|
|
1913
|
+
return pending_items[0][0], pending
|
|
1914
|
+
|
|
1915
|
+
|
|
1884
1916
|
def _load_claude_pending_permission(
|
|
1885
1917
|
store: GuardStore,
|
|
1886
1918
|
payload: dict[str, object],
|
|
@@ -1938,6 +1970,7 @@ def _persist_claude_native_permission_policy(
|
|
|
1938
1970
|
action: str,
|
|
1939
1971
|
reason: str,
|
|
1940
1972
|
now: str,
|
|
1973
|
+
source: str = "claude-native-approval",
|
|
1941
1974
|
) -> bool:
|
|
1942
1975
|
try:
|
|
1943
1976
|
store.upsert_policy(
|
|
@@ -1948,7 +1981,7 @@ def _persist_claude_native_permission_policy(
|
|
|
1948
1981
|
artifact_id=artifact_id,
|
|
1949
1982
|
artifact_hash=artifact_hash,
|
|
1950
1983
|
reason=reason,
|
|
1951
|
-
source=
|
|
1984
|
+
source=source,
|
|
1952
1985
|
),
|
|
1953
1986
|
now,
|
|
1954
1987
|
)
|
|
@@ -2082,6 +2115,106 @@ def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[
|
|
|
2082
2115
|
return denied
|
|
2083
2116
|
|
|
2084
2117
|
|
|
2118
|
+
def _claude_guard_approval_question_message(notice: dict[str, object] | None) -> str:
|
|
2119
|
+
tool_name = _optional_string((notice or {}).get("tool_name")) or "this tool"
|
|
2120
|
+
reason = _optional_string((notice or {}).get("reason"))
|
|
2121
|
+
reason_text = f" HOL Guard reason: {_ensure_terminal_punctuation(reason)}" if reason is not None else ""
|
|
2122
|
+
return (
|
|
2123
|
+
f"HOL Guard needs the user's explicit decision before {tool_name} can run.{reason_text} "
|
|
2124
|
+
"The native Claude permission prompt is not the final decision surface for this request. Call "
|
|
2125
|
+
"AskUserQuestion now with one HOL Guard approval question before retrying the tool. Use header "
|
|
2126
|
+
"'HOL Guard', question 'HOL Guard intercepted this sensitive action. What should Claude do?', and exactly "
|
|
2127
|
+
"these options: 'Allow once', 'Allow during this session', and 'Keep blocked'. If the user chooses an "
|
|
2128
|
+
"allow option, retry the same tool once. If the user chooses Keep blocked, do not retry the sensitive "
|
|
2129
|
+
"action."
|
|
2130
|
+
)
|
|
2131
|
+
|
|
2132
|
+
|
|
2133
|
+
def _is_claude_guard_approval_question(payload: dict[str, object]) -> bool:
|
|
2134
|
+
if _hook_event_name(payload) != "PostToolUse":
|
|
2135
|
+
return False
|
|
2136
|
+
tool_name = _optional_string(payload.get("tool_name"))
|
|
2137
|
+
if tool_name is None or tool_name.lower() != "askuserquestion":
|
|
2138
|
+
return False
|
|
2139
|
+
combined = json.dumps(
|
|
2140
|
+
{
|
|
2141
|
+
"tool_input": payload.get("tool_input"),
|
|
2142
|
+
"tool_response": payload.get("tool_response"),
|
|
2143
|
+
},
|
|
2144
|
+
sort_keys=True,
|
|
2145
|
+
default=str,
|
|
2146
|
+
).lower()
|
|
2147
|
+
return "hol guard" in combined and (
|
|
2148
|
+
"allow once" in combined or "allow during this session" in combined or "keep blocked" in combined
|
|
2149
|
+
)
|
|
2150
|
+
|
|
2151
|
+
|
|
2152
|
+
def _claude_guard_approval_answer(payload: dict[str, object]) -> str | None:
|
|
2153
|
+
response = payload.get("tool_response")
|
|
2154
|
+
answer_text: str | None = None
|
|
2155
|
+
if isinstance(response, dict):
|
|
2156
|
+
answers = response.get("answers")
|
|
2157
|
+
if isinstance(answers, dict):
|
|
2158
|
+
joined_answers = " ".join(str(answer) for answer in answers.values() if str(answer).strip())
|
|
2159
|
+
if joined_answers:
|
|
2160
|
+
answer_text = joined_answers
|
|
2161
|
+
if answer_text is None:
|
|
2162
|
+
for key in ("answer", "selected_answer", "selected", "choice", "value", "label"):
|
|
2163
|
+
value = response.get(key)
|
|
2164
|
+
if isinstance(value, str) and value.strip():
|
|
2165
|
+
answer_text = value
|
|
2166
|
+
break
|
|
2167
|
+
if answer_text is None and "questions" not in response and "options" not in response:
|
|
2168
|
+
content = response.get("content")
|
|
2169
|
+
if isinstance(content, str) and content.strip():
|
|
2170
|
+
answer_text = content
|
|
2171
|
+
elif isinstance(response, str) and response.strip():
|
|
2172
|
+
answer_text = response
|
|
2173
|
+
if answer_text is None:
|
|
2174
|
+
return None
|
|
2175
|
+
normalized_answer = answer_text.lower()
|
|
2176
|
+
if "keep blocked" in normalized_answer:
|
|
2177
|
+
return "block"
|
|
2178
|
+
if "allow during this session" in normalized_answer or "allow once" in normalized_answer:
|
|
2179
|
+
return "allow"
|
|
2180
|
+
return None
|
|
2181
|
+
|
|
2182
|
+
|
|
2183
|
+
def _persist_claude_guard_question_decision(store: GuardStore, payload: dict[str, object]) -> bool:
|
|
2184
|
+
if not _is_claude_guard_approval_question(payload):
|
|
2185
|
+
return False
|
|
2186
|
+
action = _claude_guard_approval_answer(payload)
|
|
2187
|
+
if action is None:
|
|
2188
|
+
return False
|
|
2189
|
+
pending_pair = _load_single_claude_pending_permission(store, payload)
|
|
2190
|
+
if pending_pair is None:
|
|
2191
|
+
return False
|
|
2192
|
+
pending_key, pending = pending_pair
|
|
2193
|
+
artifact_id = _optional_string(pending.get("artifact_id"))
|
|
2194
|
+
artifact_hash_value = _optional_string(pending.get("artifact_hash"))
|
|
2195
|
+
if artifact_id is None or artifact_hash_value is None:
|
|
2196
|
+
return False
|
|
2197
|
+
saved = _persist_claude_native_permission_policy(
|
|
2198
|
+
store=store,
|
|
2199
|
+
artifact_id=artifact_id,
|
|
2200
|
+
artifact_hash=artifact_hash_value,
|
|
2201
|
+
action=action,
|
|
2202
|
+
reason=(
|
|
2203
|
+
"Allowed through HOL Guard AskUserQuestion approval."
|
|
2204
|
+
if action == "allow"
|
|
2205
|
+
else "Blocked through HOL Guard AskUserQuestion approval."
|
|
2206
|
+
),
|
|
2207
|
+
now=_now(),
|
|
2208
|
+
source="claude-ask-user-question",
|
|
2209
|
+
)
|
|
2210
|
+
if not saved:
|
|
2211
|
+
return False
|
|
2212
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
2213
|
+
if session_id is not None:
|
|
2214
|
+
_remove_claude_pending_permission(store, session_id=session_id, pending_key=pending_key)
|
|
2215
|
+
return True
|
|
2216
|
+
|
|
2217
|
+
|
|
2085
2218
|
def _is_claude_permission_prompt_notification(args: argparse.Namespace, payload: dict[str, object]) -> bool:
|
|
2086
2219
|
return (
|
|
2087
2220
|
_canonical_harness_name(args.harness) == "claude-code"
|
|
@@ -2103,20 +2236,23 @@ def _claude_permission_prompt_system_message(
|
|
|
2103
2236
|
if tool_name is None and notice is not None:
|
|
2104
2237
|
tool_name = _optional_string(notice.get("tool_name"))
|
|
2105
2238
|
reason = _optional_string(notice.get("reason")) if notice is not None else None
|
|
2106
|
-
intro = "HOL Guard intercepted a sensitive request and
|
|
2239
|
+
intro = "HOL Guard intercepted a sensitive request and is routing it to a HOL Guard approval question."
|
|
2107
2240
|
if tool_name is not None:
|
|
2108
|
-
intro =
|
|
2241
|
+
intro = (
|
|
2242
|
+
f"HOL Guard intercepted Claude's attempt to use {tool_name} and is routing it to a HOL Guard approval "
|
|
2243
|
+
"question."
|
|
2244
|
+
)
|
|
2109
2245
|
if reason is not None:
|
|
2110
2246
|
return (
|
|
2111
|
-
f"{intro} This approval
|
|
2247
|
+
f"{intro} This approval flow came from HOL Guard, not from Claude alone. "
|
|
2112
2248
|
f"{_ensure_terminal_punctuation(reason)} "
|
|
2113
|
-
"
|
|
2114
|
-
"
|
|
2249
|
+
"HOL Guard will ask the user to choose Allow once, Allow during this session, or Keep blocked before "
|
|
2250
|
+
"Claude retries the action."
|
|
2115
2251
|
)
|
|
2116
2252
|
return (
|
|
2117
|
-
f"{intro} This approval
|
|
2118
|
-
"
|
|
2119
|
-
"
|
|
2253
|
+
f"{intro} This approval flow came from HOL Guard, not from Claude alone. "
|
|
2254
|
+
"HOL Guard will ask the user to choose Allow once, Allow during this session, or Keep blocked before Claude "
|
|
2255
|
+
"retries the action."
|
|
2120
2256
|
)
|
|
2121
2257
|
|
|
2122
2258
|
|
|
@@ -2124,17 +2260,17 @@ def _claude_permission_prompt_additional_context(notice: dict[str, object] | Non
|
|
|
2124
2260
|
reason = _optional_string(notice.get("reason")) if notice is not None else None
|
|
2125
2261
|
if reason is not None:
|
|
2126
2262
|
return (
|
|
2127
|
-
"HOL Guard intercepted the sensitive request and
|
|
2128
|
-
"
|
|
2129
|
-
"
|
|
2130
|
-
|
|
2131
|
-
"
|
|
2263
|
+
"HOL Guard intercepted the sensitive request and is routing it into a HOL Guard approval question. "
|
|
2264
|
+
"This approval flow came from HOL Guard, not from Claude alone. "
|
|
2265
|
+
f"{_ensure_terminal_punctuation(reason)} Ask the user with AskUserQuestion and the options Allow once, "
|
|
2266
|
+
"Allow during this session, and Keep blocked. If the user chooses Keep blocked, do not retry the same "
|
|
2267
|
+
"sensitive access."
|
|
2132
2268
|
)
|
|
2133
2269
|
return (
|
|
2134
|
-
"HOL Guard intercepted the sensitive request and
|
|
2135
|
-
"This approval
|
|
2136
|
-
"
|
|
2137
|
-
"
|
|
2270
|
+
"HOL Guard intercepted the sensitive request and is routing it into a HOL Guard approval question. "
|
|
2271
|
+
"This approval flow came from HOL Guard, not from Claude alone. Ask the user with AskUserQuestion and the "
|
|
2272
|
+
"options Allow once, Allow during this session, and Keep blocked. If the user chooses Keep blocked, do not "
|
|
2273
|
+
"retry the same action."
|
|
2138
2274
|
)
|
|
2139
2275
|
|
|
2140
2276
|
|
|
@@ -2147,28 +2283,32 @@ def _claude_permission_prompt_terminal_notice(
|
|
|
2147
2283
|
reason = _optional_string(notice.get("reason")) if notice is not None else None
|
|
2148
2284
|
if tool_name is not None and reason is not None:
|
|
2149
2285
|
return (
|
|
2150
|
-
f"HOL Guard
|
|
2286
|
+
f"HOL Guard is routing this Claude approval request for {tool_name} into a HOL Guard decision prompt. "
|
|
2151
2287
|
f"{_ensure_terminal_punctuation(reason)} "
|
|
2152
|
-
"
|
|
2288
|
+
"Choose Allow once, Allow during this session, or Keep blocked in the HOL Guard prompt."
|
|
2153
2289
|
)
|
|
2154
2290
|
if tool_name is not None:
|
|
2155
2291
|
return (
|
|
2156
|
-
f"HOL Guard
|
|
2157
|
-
"
|
|
2292
|
+
f"HOL Guard is routing this Claude approval request for {tool_name} into a HOL Guard decision prompt. "
|
|
2293
|
+
"Choose Allow once, Allow during this session, or Keep blocked in the HOL Guard prompt."
|
|
2158
2294
|
)
|
|
2159
2295
|
return (
|
|
2160
|
-
"HOL Guard
|
|
2161
|
-
"
|
|
2296
|
+
"HOL Guard is routing this Claude approval request into a HOL Guard decision prompt to protect a sensitive "
|
|
2297
|
+
"action. Choose Allow once, Allow during this session, or Keep blocked in the HOL Guard prompt."
|
|
2162
2298
|
)
|
|
2163
2299
|
|
|
2164
2300
|
|
|
2165
2301
|
def _claude_native_pretooluse_terminal_notice(*, payload: dict[str, object], reason: str) -> str:
|
|
2166
2302
|
tool_name = _claude_notification_tool_name(payload)
|
|
2167
2303
|
if tool_name is not None:
|
|
2168
|
-
return
|
|
2304
|
+
return (
|
|
2305
|
+
f"HOL Guard intercepted Claude's attempt to use {tool_name}. {_ensure_terminal_punctuation(reason)} "
|
|
2306
|
+
"Guard will route the next approval through a HOL Guard prompt if Claude asks to continue."
|
|
2307
|
+
)
|
|
2169
2308
|
return (
|
|
2170
|
-
"HOL Guard
|
|
2171
|
-
f"{_ensure_terminal_punctuation(reason)}"
|
|
2309
|
+
"HOL Guard intercepted a sensitive Claude action. "
|
|
2310
|
+
f"{_ensure_terminal_punctuation(reason)} Guard will route the next approval through a HOL Guard prompt if "
|
|
2311
|
+
"Claude asks to continue."
|
|
2172
2312
|
)
|
|
2173
2313
|
|
|
2174
2314
|
|
|
@@ -2266,9 +2406,8 @@ def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: d
|
|
|
2266
2406
|
if harness == "claude-code" and policy_action == "require-reapproval":
|
|
2267
2407
|
return (
|
|
2268
2408
|
f"HOL Guard intercepted Claude's attempt to use {tool_name} for {path_class} to protect your local "
|
|
2269
|
-
"secrets.
|
|
2270
|
-
"
|
|
2271
|
-
"session, or No to keep the secret private."
|
|
2409
|
+
"secrets. The approval flow came from HOL Guard, not from Claude alone. HOL Guard will ask you to "
|
|
2410
|
+
"choose Allow once, Allow during this session, or Keep blocked before Claude retries this action."
|
|
2272
2411
|
)
|
|
2273
2412
|
return (
|
|
2274
2413
|
f"HOL Guard blocked Claude's attempt to use {tool_name} for {path_class} to protect your local secrets. "
|
|
@@ -2311,7 +2450,10 @@ def _claude_prompt_additional_context(
|
|
|
2311
2450
|
)
|
|
2312
2451
|
return (
|
|
2313
2452
|
f"{_ensure_terminal_punctuation(native_reason)} "
|
|
2314
|
-
"Before you use the first sensitive tool for this request,
|
|
2453
|
+
"Before you use the first sensitive tool for this request, call AskUserQuestion with header 'HOL Guard', "
|
|
2454
|
+
"question 'HOL Guard intercepted this sensitive action. What should Claude do?', and exactly these options: "
|
|
2455
|
+
"'Allow once', 'Allow during this session', and 'Keep blocked'. If the user chooses Keep blocked, do not "
|
|
2456
|
+
"retry the sensitive action. Then tell the user exactly: "
|
|
2315
2457
|
f"'{briefing_sentence}' "
|
|
2316
2458
|
"Attempt that sensitive tool at most once. If HOL Guard or Claude denies it, do not retry the same sensitive "
|
|
2317
2459
|
"action automatically. Instead, tell the user approval is required in Claude to continue."
|
|
@@ -2330,12 +2472,11 @@ def _claude_prompt_system_message(
|
|
|
2330
2472
|
if "secret_read" in _prompt_request_classes(artifact):
|
|
2331
2473
|
return (
|
|
2332
2474
|
"HOL Guard intercepted this prompt because it asks Claude to access local secrets. "
|
|
2333
|
-
"If Claude
|
|
2334
|
-
"Guard."
|
|
2475
|
+
"If Claude asks to continue, HOL Guard will route the decision through a branded approval prompt."
|
|
2335
2476
|
)
|
|
2336
2477
|
return (
|
|
2337
2478
|
"HOL Guard intercepted this prompt because it leads to a sensitive action. "
|
|
2338
|
-
"If Claude
|
|
2479
|
+
"If Claude asks to continue, HOL Guard will route the decision through a branded approval prompt."
|
|
2339
2480
|
)
|
|
2340
2481
|
if policy_action in {"block", "sandbox-required"}:
|
|
2341
2482
|
return _ensure_terminal_punctuation(native_reason)
|
|
@@ -2453,6 +2594,17 @@ def _emit_native_hook_response(
|
|
|
2453
2594
|
_write_json_line(payload, output_stream=output_stream)
|
|
2454
2595
|
return
|
|
2455
2596
|
if event_name in {"Notification", "PermissionRequest"}:
|
|
2597
|
+
if event_name == "PermissionRequest" and policy_action in {"block", "sandbox-required"}:
|
|
2598
|
+
payload["hookSpecificOutput"] = {
|
|
2599
|
+
"hookEventName": event_name,
|
|
2600
|
+
"decision": {
|
|
2601
|
+
"behavior": "deny",
|
|
2602
|
+
"message": additional_context or reason,
|
|
2603
|
+
"interrupt": False,
|
|
2604
|
+
},
|
|
2605
|
+
}
|
|
2606
|
+
_write_json_line(payload, output_stream=output_stream)
|
|
2607
|
+
return
|
|
2456
2608
|
if additional_context:
|
|
2457
2609
|
payload["hookSpecificOutput"] = {
|
|
2458
2610
|
"hookEventName": event_name,
|
|
@@ -163,6 +163,7 @@ def test_claude_install_writes_session_start_and_command_hook_schema_and_is_idem
|
|
|
163
163
|
assert permission_request[0]["hooks"][0]["type"] == "command"
|
|
164
164
|
assert permission_request[0]["hooks"][0]["timeout"] == 10
|
|
165
165
|
assert len(post_tool_use) == 1
|
|
166
|
+
assert post_tool_use[0]["matcher"] == "Bash|Read|Write|Edit|MultiEdit|WebFetch|WebSearch|mcp__.*|AskUserQuestion"
|
|
166
167
|
assert post_tool_use[0]["hooks"][0]["type"] == "command"
|
|
167
168
|
assert len(prompt_submit) == 1
|
|
168
169
|
assert "matcher" not in prompt_submit[0]
|
|
@@ -396,7 +397,10 @@ def test_claude_daemon_hook_command_falls_back_without_blocking_prompt_on_daemon
|
|
|
396
397
|
)
|
|
397
398
|
assert result.returncode == 0
|
|
398
399
|
assert result.stderr == ""
|
|
399
|
-
|
|
400
|
+
payload = json.loads(result.stdout)
|
|
401
|
+
assert payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
|
|
402
|
+
assert "AskUserQuestion" in payload["hookSpecificOutput"]["additionalContext"]
|
|
403
|
+
assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
|
|
400
404
|
|
|
401
405
|
|
|
402
406
|
def test_claude_daemon_hook_command_falls_back_to_native_ask_on_daemon_miss(tmp_path):
|
|
@@ -424,7 +428,11 @@ def test_claude_daemon_hook_command_falls_back_to_native_ask_on_daemon_miss(tmp_
|
|
|
424
428
|
assert result.stderr == ""
|
|
425
429
|
assert payload["hookSpecificOutput"]["hookEventName"] == "PreToolUse"
|
|
426
430
|
assert payload["hookSpecificOutput"]["permissionDecision"] == "ask"
|
|
427
|
-
|
|
431
|
+
reason = payload["hookSpecificOutput"]["permissionDecisionReason"]
|
|
432
|
+
assert "HOL Guard" in reason
|
|
433
|
+
assert "approval flow came from HOL Guard" in reason
|
|
434
|
+
assert "Allow once" in reason
|
|
435
|
+
assert "Keep blocked" in reason
|
|
428
436
|
|
|
429
437
|
|
|
430
438
|
def test_claude_install_replaces_prior_session_start_guard_handlers_when_context_changes(tmp_path):
|