plugin-scanner 2.0.65__tar.gz → 2.0.66__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.65 → plugin_scanner-2.0.66}/PKG-INFO +1 -1
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/pyproject.toml +1 -1
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/claude_code.py +1 -3
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/commands.py +99 -3
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/render.py +18 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store.py +27 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_approvals.py +86 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_claude_adapter.py +5 -15
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_cli.py +3 -3
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_runtime.py +72 -21
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_surface_server.py +1 -9
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.dockerignore +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.gitignore +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/Dockerfile +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/requirements.txt +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/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.66
|
|
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.66"
|
|
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.66"
|
|
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"
|
|
@@ -23,7 +23,6 @@ CLAUDE_GUARD_POST_TOOL_MATCHER = f"{CLAUDE_GUARD_TOOL_MATCHER}|AskUserQuestion"
|
|
|
23
23
|
CLAUDE_GUARD_NOTIFICATION_MATCHER = "permission_prompt"
|
|
24
24
|
CLAUDE_GUARD_SESSION_START_MATCHERS = ("startup", "resume", "clear", "compact")
|
|
25
25
|
CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS = 30
|
|
26
|
-
CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS = 20
|
|
27
26
|
CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS = 10
|
|
28
27
|
CLAUDE_GUARD_SESSION_START_TIMEOUT_SECONDS = 10
|
|
29
28
|
CLAUDE_GUARD_STOP_TIMEOUT_SECONDS = 10
|
|
@@ -47,7 +46,6 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
|
|
|
47
46
|
("PreToolUse", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
|
|
48
47
|
("PermissionRequest", CLAUDE_GUARD_TOOL_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
|
|
49
48
|
("PostToolUse", CLAUDE_GUARD_POST_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
|
|
50
|
-
("UserPromptSubmit", None, CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS),
|
|
51
49
|
("Notification", CLAUDE_GUARD_NOTIFICATION_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
|
|
52
50
|
("Stop", None, CLAUDE_GUARD_STOP_TIMEOUT_SECONDS),
|
|
53
51
|
):
|
|
@@ -60,7 +58,7 @@ def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> No
|
|
|
60
58
|
|
|
61
59
|
|
|
62
60
|
def _remove_unsupported_guard_hook_groups(hooks: dict[str, object]) -> None:
|
|
63
|
-
for key in ("PermissionDenied",):
|
|
61
|
+
for key in ("PermissionDenied", "UserPromptSubmit"):
|
|
64
62
|
entries = hooks.get(key)
|
|
65
63
|
if not isinstance(entries, list):
|
|
66
64
|
continue
|
{plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -311,8 +311,15 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
|
|
|
311
311
|
policy_parser.add_argument("--json", action="store_true")
|
|
312
312
|
policy_parser.set_defaults(policy_action=action)
|
|
313
313
|
|
|
314
|
-
policies_parser = guard_subparsers.add_parser("policies", help="List stored Guard policy decisions")
|
|
314
|
+
policies_parser = guard_subparsers.add_parser("policies", help="List or clear stored Guard policy decisions")
|
|
315
|
+
policies_parser.add_argument("policies_command", nargs="?", choices=("clear",))
|
|
315
316
|
policies_parser.add_argument("--harness")
|
|
317
|
+
policies_parser.add_argument("--source")
|
|
318
|
+
policies_parser.add_argument(
|
|
319
|
+
"--all",
|
|
320
|
+
action="store_true",
|
|
321
|
+
help="Clear decisions across every harness; cannot be combined with --harness",
|
|
322
|
+
)
|
|
316
323
|
_add_guard_common_args(policies_parser)
|
|
317
324
|
policies_parser.add_argument("--json", action="store_true")
|
|
318
325
|
|
|
@@ -775,6 +782,46 @@ def run_guard_command(
|
|
|
775
782
|
return 0
|
|
776
783
|
|
|
777
784
|
if args.guard_command == "policies":
|
|
785
|
+
if getattr(args, "policies_command", None) == "clear":
|
|
786
|
+
harness = getattr(args, "harness", None)
|
|
787
|
+
clear_all = bool(getattr(args, "all", False))
|
|
788
|
+
if clear_all and harness is not None:
|
|
789
|
+
_emit(
|
|
790
|
+
"policies",
|
|
791
|
+
{
|
|
792
|
+
"error": "Choose either --all or --harness <name> when clearing Guard policy decisions.",
|
|
793
|
+
"cleared": 0,
|
|
794
|
+
"harness": harness,
|
|
795
|
+
"source": getattr(args, "source", None),
|
|
796
|
+
},
|
|
797
|
+
getattr(args, "json", False),
|
|
798
|
+
)
|
|
799
|
+
return 2
|
|
800
|
+
if not clear_all and harness is None:
|
|
801
|
+
_emit(
|
|
802
|
+
"policies",
|
|
803
|
+
{
|
|
804
|
+
"error": "Choose --harness <name> or --all when clearing Guard policy decisions.",
|
|
805
|
+
"cleared": 0,
|
|
806
|
+
},
|
|
807
|
+
getattr(args, "json", False),
|
|
808
|
+
)
|
|
809
|
+
return 2
|
|
810
|
+
cleared = store.clear_policy_decisions(
|
|
811
|
+
None if clear_all else harness,
|
|
812
|
+
getattr(args, "source", None),
|
|
813
|
+
)
|
|
814
|
+
_emit(
|
|
815
|
+
"policies",
|
|
816
|
+
{
|
|
817
|
+
"generated_at": _now(),
|
|
818
|
+
"cleared": cleared,
|
|
819
|
+
"harness": None if clear_all else harness,
|
|
820
|
+
"source": getattr(args, "source", None),
|
|
821
|
+
},
|
|
822
|
+
getattr(args, "json", False),
|
|
823
|
+
)
|
|
824
|
+
return 0
|
|
778
825
|
policy_items = store.list_policy_decisions(getattr(args, "harness", None))
|
|
779
826
|
items = _filter_policy_items(policy_items, active_only=True)
|
|
780
827
|
_emit("policies", {"generated_at": _now(), "items": items}, getattr(args, "json", False))
|
|
@@ -1233,10 +1280,13 @@ def run_guard_command(
|
|
|
1233
1280
|
)
|
|
1234
1281
|
return 0
|
|
1235
1282
|
if _canonical_harness_name(args.harness) == "claude-code" and _hook_event_name(payload) == "Stop":
|
|
1236
|
-
|
|
1283
|
+
discarded = _discard_claude_pending_permissions(store, payload)
|
|
1237
1284
|
store.add_event(
|
|
1238
1285
|
"claude/turn_stop",
|
|
1239
|
-
{
|
|
1286
|
+
{
|
|
1287
|
+
"session_id": payload.get("session_id"),
|
|
1288
|
+
"discarded_pending_permissions": discarded,
|
|
1289
|
+
},
|
|
1240
1290
|
_now(),
|
|
1241
1291
|
)
|
|
1242
1292
|
return 0
|
|
@@ -1371,6 +1421,14 @@ def run_guard_command(
|
|
|
1371
1421
|
artifact=runtime_artifact,
|
|
1372
1422
|
artifact_hash=runtime_artifact_hash,
|
|
1373
1423
|
)
|
|
1424
|
+
if _should_allow_claude_user_prompt_submit_without_output(
|
|
1425
|
+
args,
|
|
1426
|
+
event_name=event_name,
|
|
1427
|
+
policy_action=policy_action,
|
|
1428
|
+
artifact=runtime_artifact,
|
|
1429
|
+
output_stream=output_stream,
|
|
1430
|
+
):
|
|
1431
|
+
return 0
|
|
1374
1432
|
if _should_emit_copilot_hook_response(args):
|
|
1375
1433
|
_emit_copilot_hook_response(
|
|
1376
1434
|
policy_action=policy_action,
|
|
@@ -1724,6 +1782,23 @@ def _should_emit_prequeue_native_hook_response(
|
|
|
1724
1782
|
return output_stream is not None
|
|
1725
1783
|
|
|
1726
1784
|
|
|
1785
|
+
def _should_allow_claude_user_prompt_submit_without_output(
|
|
1786
|
+
args: argparse.Namespace,
|
|
1787
|
+
*,
|
|
1788
|
+
event_name: str,
|
|
1789
|
+
policy_action: str,
|
|
1790
|
+
artifact: GuardArtifact,
|
|
1791
|
+
output_stream: TextIO | None,
|
|
1792
|
+
) -> bool:
|
|
1793
|
+
return (
|
|
1794
|
+
_canonical_harness_name(args.harness) == "claude-code"
|
|
1795
|
+
and event_name == "UserPromptSubmit"
|
|
1796
|
+
and policy_action == "require-reapproval"
|
|
1797
|
+
and not _prompt_requires_hard_block(artifact)
|
|
1798
|
+
and (not getattr(args, "json", False) or output_stream is not None)
|
|
1799
|
+
)
|
|
1800
|
+
|
|
1801
|
+
|
|
1727
1802
|
def _emit_claude_permission_request_passthrough(*, output_stream: TextIO | None = None) -> None:
|
|
1728
1803
|
if output_stream is not None:
|
|
1729
1804
|
output_stream.write("")
|
|
@@ -2044,6 +2119,27 @@ def _persist_claude_native_permission_for_runtime_artifact(
|
|
|
2044
2119
|
return True
|
|
2045
2120
|
|
|
2046
2121
|
|
|
2122
|
+
def _discard_claude_pending_permissions(store: GuardStore, payload: dict[str, object]) -> int:
|
|
2123
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
2124
|
+
if session_id is None:
|
|
2125
|
+
return 0
|
|
2126
|
+
index_key = _claude_pending_permission_index_key(session_id)
|
|
2127
|
+
try:
|
|
2128
|
+
index_payload = store.get_sync_payload(index_key)
|
|
2129
|
+
except (OSError, sqlite3.Error):
|
|
2130
|
+
return 0
|
|
2131
|
+
if not isinstance(index_payload, list):
|
|
2132
|
+
return 0
|
|
2133
|
+
pending_keys = [str(item) for item in index_payload]
|
|
2134
|
+
if not pending_keys:
|
|
2135
|
+
return 0
|
|
2136
|
+
try:
|
|
2137
|
+
store.delete_sync_payloads([*pending_keys, index_key])
|
|
2138
|
+
except (OSError, sqlite3.Error):
|
|
2139
|
+
return 0
|
|
2140
|
+
return len(pending_keys)
|
|
2141
|
+
|
|
2142
|
+
|
|
2047
2143
|
def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[str, object]) -> int:
|
|
2048
2144
|
session_id = _optional_string(payload.get("session_id"))
|
|
2049
2145
|
if session_id is None:
|
{plugin_scanner-2.0.65 → plugin_scanner-2.0.66}/src/codex_plugin_scanner/guard/cli/render.py
RENAMED
|
@@ -295,6 +295,24 @@ def _render_inventory(console: Console, payload: dict[str, object]) -> None:
|
|
|
295
295
|
|
|
296
296
|
|
|
297
297
|
def _render_policies(console: Console, payload: dict[str, object]) -> None:
|
|
298
|
+
if "cleared" in payload or "error" in payload:
|
|
299
|
+
error = payload.get("error")
|
|
300
|
+
cleared = int(payload.get("cleared", 0) or 0)
|
|
301
|
+
scope = str(payload.get("harness") or "all harnesses")
|
|
302
|
+
source = payload.get("source")
|
|
303
|
+
body = Table.grid(padding=(0, 1))
|
|
304
|
+
body.add_row("Outcome", str(error) if error else f"cleared {cleared} decision{'s' if cleared != 1 else ''}")
|
|
305
|
+
body.add_row("Harness", scope)
|
|
306
|
+
if source:
|
|
307
|
+
body.add_row("Source", str(source))
|
|
308
|
+
console.print(
|
|
309
|
+
Panel(
|
|
310
|
+
body,
|
|
311
|
+
title="Guard policy clear",
|
|
312
|
+
border_style="red" if error else "green",
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
return
|
|
298
316
|
items = _coerce_dict_list(payload.get("items"))
|
|
299
317
|
console.print(
|
|
300
318
|
Panel.fit(
|
|
@@ -1557,6 +1557,22 @@ class GuardStore:
|
|
|
1557
1557
|
for row in rows
|
|
1558
1558
|
]
|
|
1559
1559
|
|
|
1560
|
+
def clear_policy_decisions(self, harness: str | None = None, source: str | None = None) -> int:
|
|
1561
|
+
conditions: list[str] = []
|
|
1562
|
+
params: list[object] = []
|
|
1563
|
+
if harness is not None:
|
|
1564
|
+
conditions.append("harness = ?")
|
|
1565
|
+
params.append(harness)
|
|
1566
|
+
if source is not None:
|
|
1567
|
+
conditions.append("source = ?")
|
|
1568
|
+
params.append(source)
|
|
1569
|
+
query = "delete from policy_decisions"
|
|
1570
|
+
if conditions:
|
|
1571
|
+
query += " where " + " and ".join(conditions)
|
|
1572
|
+
with self._connect() as connection:
|
|
1573
|
+
cursor = connection.execute(query, tuple(params))
|
|
1574
|
+
return int(cursor.rowcount if cursor.rowcount is not None else 0)
|
|
1575
|
+
|
|
1560
1576
|
def get_latest_diff(self, harness: str, artifact_id: str) -> dict[str, object] | None:
|
|
1561
1577
|
with self._connect() as connection:
|
|
1562
1578
|
row = connection.execute(
|
|
@@ -1727,6 +1743,17 @@ class GuardStore:
|
|
|
1727
1743
|
(state_key,),
|
|
1728
1744
|
)
|
|
1729
1745
|
|
|
1746
|
+
def delete_sync_payloads(self, state_keys: list[str]) -> int:
|
|
1747
|
+
if not state_keys:
|
|
1748
|
+
return 0
|
|
1749
|
+
placeholders = ",".join("?" for _ in state_keys)
|
|
1750
|
+
with self._connect() as connection:
|
|
1751
|
+
cursor = connection.execute(
|
|
1752
|
+
f"delete from sync_state where state_key in ({placeholders})",
|
|
1753
|
+
tuple(state_keys),
|
|
1754
|
+
)
|
|
1755
|
+
return int(cursor.rowcount if cursor.rowcount is not None else 0)
|
|
1756
|
+
|
|
1730
1757
|
def add_guard_event_v1(self, event: GuardEventV1) -> None:
|
|
1731
1758
|
with self._connect() as connection:
|
|
1732
1759
|
self._add_guard_event_v1(connection, event)
|
|
@@ -1598,6 +1598,92 @@ class TestGuardApprovals:
|
|
|
1598
1598
|
assert approve_output["resolved"] is True
|
|
1599
1599
|
assert store.resolve_policy("codex", "codex:project:workspace_skill", "hash-789") == "allow"
|
|
1600
1600
|
|
|
1601
|
+
def test_guard_policies_cli_clears_local_decisions_for_harness(self, tmp_path, capsys):
|
|
1602
|
+
home_dir = tmp_path / "home"
|
|
1603
|
+
store = GuardStore(home_dir)
|
|
1604
|
+
store.upsert_policy(
|
|
1605
|
+
PolicyDecision(
|
|
1606
|
+
harness="claude-code",
|
|
1607
|
+
scope="artifact",
|
|
1608
|
+
action="block",
|
|
1609
|
+
artifact_id="claude-code:runtime:file-read:.npmrc",
|
|
1610
|
+
artifact_hash="hash-npmrc",
|
|
1611
|
+
reason="blocked during local test",
|
|
1612
|
+
source="claude-ask-user-question",
|
|
1613
|
+
),
|
|
1614
|
+
"2026-04-23T00:00:00+00:00",
|
|
1615
|
+
)
|
|
1616
|
+
store.upsert_policy(
|
|
1617
|
+
PolicyDecision(
|
|
1618
|
+
harness="codex",
|
|
1619
|
+
scope="harness",
|
|
1620
|
+
action="allow",
|
|
1621
|
+
reason="keep codex decisions",
|
|
1622
|
+
source="manual",
|
|
1623
|
+
),
|
|
1624
|
+
"2026-04-23T00:00:00+00:00",
|
|
1625
|
+
)
|
|
1626
|
+
|
|
1627
|
+
rc = main(["guard", "policies", "clear", "--home", str(home_dir), "--harness", "claude-code", "--json"])
|
|
1628
|
+
output = json.loads(capsys.readouterr().out)
|
|
1629
|
+
|
|
1630
|
+
assert rc == 0
|
|
1631
|
+
assert output["cleared"] == 1
|
|
1632
|
+
assert store.list_policy_decisions("claude-code") == []
|
|
1633
|
+
assert len(store.list_policy_decisions("codex")) == 1
|
|
1634
|
+
|
|
1635
|
+
def test_guard_policies_clear_renders_non_json_result(self, tmp_path, capsys):
|
|
1636
|
+
home_dir = tmp_path / "home"
|
|
1637
|
+
store = GuardStore(home_dir)
|
|
1638
|
+
store.upsert_policy(
|
|
1639
|
+
PolicyDecision(
|
|
1640
|
+
harness="claude-code",
|
|
1641
|
+
scope="artifact",
|
|
1642
|
+
action="block",
|
|
1643
|
+
artifact_id="claude-code:runtime:file-read:.npmrc",
|
|
1644
|
+
artifact_hash="hash-npmrc",
|
|
1645
|
+
reason="blocked during local test",
|
|
1646
|
+
source="claude-ask-user-question",
|
|
1647
|
+
),
|
|
1648
|
+
"2026-04-23T00:00:00+00:00",
|
|
1649
|
+
)
|
|
1650
|
+
|
|
1651
|
+
rc = main(["guard", "policies", "clear", "--home", str(home_dir), "--harness", "claude-code"])
|
|
1652
|
+
output = capsys.readouterr().out
|
|
1653
|
+
|
|
1654
|
+
assert rc == 0
|
|
1655
|
+
assert "Guard policy clear" in output
|
|
1656
|
+
assert "cleared 1 decision" in output
|
|
1657
|
+
assert store.list_policy_decisions("claude-code") == []
|
|
1658
|
+
|
|
1659
|
+
def test_guard_policies_clear_renders_non_json_validation_error(self, tmp_path, capsys):
|
|
1660
|
+
rc = main(["guard", "policies", "clear", "--home", str(tmp_path / "home")])
|
|
1661
|
+
output = capsys.readouterr().out
|
|
1662
|
+
|
|
1663
|
+
assert rc == 2
|
|
1664
|
+
assert "Guard policy clear" in output
|
|
1665
|
+
assert "Choose --harness <name> or --all" in output
|
|
1666
|
+
|
|
1667
|
+
def test_guard_policies_clear_rejects_all_with_harness(self, tmp_path, capsys):
|
|
1668
|
+
rc = main(
|
|
1669
|
+
[
|
|
1670
|
+
"guard",
|
|
1671
|
+
"policies",
|
|
1672
|
+
"clear",
|
|
1673
|
+
"--home",
|
|
1674
|
+
str(tmp_path / "home"),
|
|
1675
|
+
"--all",
|
|
1676
|
+
"--harness",
|
|
1677
|
+
"claude-code",
|
|
1678
|
+
"--json",
|
|
1679
|
+
]
|
|
1680
|
+
)
|
|
1681
|
+
output = json.loads(capsys.readouterr().out)
|
|
1682
|
+
|
|
1683
|
+
assert rc == 2
|
|
1684
|
+
assert output["cleared"] == 0
|
|
1685
|
+
assert "Choose either --all or --harness <name>" in output["error"]
|
|
1686
|
+
|
|
1601
1687
|
def test_guard_bridge_resolves_requests_against_guard_daemon_api(self, tmp_path, monkeypatch):
|
|
1602
1688
|
store = GuardStore(tmp_path / "guard-home")
|
|
1603
1689
|
bridge = GuardBridge(
|
|
@@ -47,8 +47,8 @@ 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", "PermissionRequest", "PostToolUse", "
|
|
51
|
-
entries = hooks[
|
|
50
|
+
for key in ("PreToolUse", "PermissionRequest", "PostToolUse", "Notification", "Stop"):
|
|
51
|
+
entries = hooks.get(key, [])
|
|
52
52
|
assert isinstance(entries, list)
|
|
53
53
|
for entry in entries:
|
|
54
54
|
assert isinstance(entry, dict)
|
|
@@ -146,7 +146,6 @@ def test_claude_install_writes_session_start_and_command_hook_schema_and_is_idem
|
|
|
146
146
|
pre_tool_use = payload["hooks"]["PreToolUse"]
|
|
147
147
|
permission_request = payload["hooks"]["PermissionRequest"]
|
|
148
148
|
post_tool_use = payload["hooks"]["PostToolUse"]
|
|
149
|
-
prompt_submit = payload["hooks"]["UserPromptSubmit"]
|
|
150
149
|
notification = payload["hooks"]["Notification"]
|
|
151
150
|
stop = payload["hooks"]["Stop"]
|
|
152
151
|
assert len(session_start) == 4
|
|
@@ -165,9 +164,7 @@ def test_claude_install_writes_session_start_and_command_hook_schema_and_is_idem
|
|
|
165
164
|
assert len(post_tool_use) == 1
|
|
166
165
|
assert post_tool_use[0]["matcher"] == "Bash|Read|Write|Edit|MultiEdit|WebFetch|WebSearch|mcp__.*|AskUserQuestion"
|
|
167
166
|
assert post_tool_use[0]["hooks"][0]["type"] == "command"
|
|
168
|
-
assert
|
|
169
|
-
assert "matcher" not in prompt_submit[0]
|
|
170
|
-
assert prompt_submit[0]["hooks"][0]["type"] == "command"
|
|
167
|
+
assert payload["hooks"].get("UserPromptSubmit", []) == []
|
|
171
168
|
assert len(notification) == 1
|
|
172
169
|
assert notification[0]["matcher"] == "permission_prompt"
|
|
173
170
|
assert notification[0]["hooks"][0]["type"] == "command"
|
|
@@ -255,8 +252,8 @@ def test_claude_install_replaces_legacy_http_guard_hooks(tmp_path):
|
|
|
255
252
|
"command",
|
|
256
253
|
"command",
|
|
257
254
|
"command",
|
|
258
|
-
"command",
|
|
259
255
|
]
|
|
256
|
+
assert payload["hooks"].get("UserPromptSubmit", []) == []
|
|
260
257
|
assert all(CLAUDE_GUARD_DAEMON_HOOK_MARKER in str(handler.get("command", "")) for handler in installed_handlers)
|
|
261
258
|
assert all("url" not in handler for handler in installed_handlers)
|
|
262
259
|
|
|
@@ -397,14 +394,7 @@ def test_claude_daemon_hook_command_falls_back_without_blocking_prompt_on_daemon
|
|
|
397
394
|
)
|
|
398
395
|
assert result.returncode == 0
|
|
399
396
|
assert result.stderr == ""
|
|
400
|
-
|
|
401
|
-
assert payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
|
|
402
|
-
assert "Do not ask for approval at the prompt stage" in payload["hookSpecificOutput"]["additionalContext"]
|
|
403
|
-
assert (
|
|
404
|
-
"route that concrete action into a HOL Guard approval question"
|
|
405
|
-
in payload["hookSpecificOutput"]["additionalContext"]
|
|
406
|
-
)
|
|
407
|
-
assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
|
|
397
|
+
assert result.stdout == ""
|
|
408
398
|
|
|
409
399
|
|
|
410
400
|
def test_claude_daemon_hook_command_falls_back_to_native_ask_on_daemon_miss(tmp_path):
|
|
@@ -2128,7 +2128,7 @@ args = ["workspace-skill.js", "--changed"]
|
|
|
2128
2128
|
assert install_settings_payload["hooks"]["PreToolUse"][0]["hooks"][0]["type"] == "command"
|
|
2129
2129
|
assert install_settings_payload["hooks"]["PreToolUse"][0]["hooks"][0]["command"] == expected_hook_command
|
|
2130
2130
|
assert "url" not in install_settings_payload["hooks"]["PreToolUse"][0]["hooks"][0]
|
|
2131
|
-
assert install_settings_payload["hooks"]
|
|
2131
|
+
assert install_settings_payload["hooks"].get("UserPromptSubmit", []) == []
|
|
2132
2132
|
assert install_settings_payload["hooks"]["Notification"][0]["matcher"] == "permission_prompt"
|
|
2133
2133
|
assert install_settings_payload["hooks"]["Notification"][0]["hooks"][0]["type"] == "command"
|
|
2134
2134
|
assert install_settings_payload["hooks"]["Notification"][0]["hooks"][0]["command"] == expected_hook_command
|
|
@@ -2226,7 +2226,7 @@ args = ["workspace-skill.js", "--changed"]
|
|
|
2226
2226
|
assert uninstall_rc == 0
|
|
2227
2227
|
assert output["managed_install"]["active"] is False
|
|
2228
2228
|
assert payload["hooks"]["PreToolUse"] == []
|
|
2229
|
-
assert payload["hooks"]
|
|
2229
|
+
assert payload["hooks"].get("UserPromptSubmit", []) == []
|
|
2230
2230
|
assert payload["hooks"]["Notification"] == []
|
|
2231
2231
|
assert payload["hooks"]["Stop"] == []
|
|
2232
2232
|
|
|
@@ -2357,7 +2357,7 @@ args = ["workspace-skill.js", "--changed"]
|
|
|
2357
2357
|
assert len(payload["hooks"]["SessionStart"]) == 4
|
|
2358
2358
|
assert len(payload["hooks"]["PreToolUse"]) == 1
|
|
2359
2359
|
assert len(payload["hooks"]["PostToolUse"]) == 1
|
|
2360
|
-
assert
|
|
2360
|
+
assert payload["hooks"].get("UserPromptSubmit", []) == []
|
|
2361
2361
|
assert len(payload["hooks"]["Notification"]) == 1
|
|
2362
2362
|
assert len(payload["hooks"]["Stop"]) == 1
|
|
2363
2363
|
pretool_hook_commands = [
|
|
@@ -4492,6 +4492,71 @@ def test_guard_hook_claude_ask_user_question_empty_answers_uses_explicit_fallbac
|
|
|
4492
4492
|
assert guard_commands_module._claude_guard_approval_answer(payload) == "allow"
|
|
4493
4493
|
|
|
4494
4494
|
|
|
4495
|
+
def test_guard_hook_claude_native_cancel_does_not_persist_flat_block(tmp_path, capsys, monkeypatch):
|
|
4496
|
+
home_dir = tmp_path / "home"
|
|
4497
|
+
workspace_dir = tmp_path / "workspace"
|
|
4498
|
+
_build_guard_fixture(home_dir, workspace_dir)
|
|
4499
|
+
first_event = {
|
|
4500
|
+
"session_id": "session-claude-native-cancel",
|
|
4501
|
+
"hook_event_name": "PreToolUse",
|
|
4502
|
+
"tool_name": "Read",
|
|
4503
|
+
"tool_input": {"file_path": str(workspace_dir / ".npmrc")},
|
|
4504
|
+
"source_scope": "project",
|
|
4505
|
+
}
|
|
4506
|
+
monkeypatch.setattr(guard_commands_module, "ensure_guard_daemon", lambda _guard_home: "http://127.0.0.1:4455")
|
|
4507
|
+
|
|
4508
|
+
first_rc, first_output = _run_guard_hook(
|
|
4509
|
+
home_dir=home_dir,
|
|
4510
|
+
workspace_dir=workspace_dir,
|
|
4511
|
+
harness="claude-code",
|
|
4512
|
+
event=first_event,
|
|
4513
|
+
capsys=capsys,
|
|
4514
|
+
monkeypatch=monkeypatch,
|
|
4515
|
+
)
|
|
4516
|
+
notification_rc, notification_output = _run_guard_hook(
|
|
4517
|
+
home_dir=home_dir,
|
|
4518
|
+
workspace_dir=workspace_dir,
|
|
4519
|
+
harness="claude-code",
|
|
4520
|
+
event={
|
|
4521
|
+
**first_event,
|
|
4522
|
+
"hook_event_name": "Notification",
|
|
4523
|
+
"notification_type": "permission_prompt",
|
|
4524
|
+
"message": "Claude needs your permission to use Read",
|
|
4525
|
+
},
|
|
4526
|
+
capsys=capsys,
|
|
4527
|
+
monkeypatch=monkeypatch,
|
|
4528
|
+
)
|
|
4529
|
+
stop_rc, stop_output = _run_guard_hook(
|
|
4530
|
+
home_dir=home_dir,
|
|
4531
|
+
workspace_dir=workspace_dir,
|
|
4532
|
+
harness="claude-code",
|
|
4533
|
+
event={"session_id": "session-claude-native-cancel", "hook_event_name": "Stop"},
|
|
4534
|
+
capsys=capsys,
|
|
4535
|
+
monkeypatch=monkeypatch,
|
|
4536
|
+
)
|
|
4537
|
+
second_rc, second_output = _run_guard_hook(
|
|
4538
|
+
home_dir=home_dir,
|
|
4539
|
+
workspace_dir=workspace_dir,
|
|
4540
|
+
harness="claude-code",
|
|
4541
|
+
event={**first_event, "session_id": "session-claude-native-cancel-retry"},
|
|
4542
|
+
capsys=capsys,
|
|
4543
|
+
monkeypatch=monkeypatch,
|
|
4544
|
+
)
|
|
4545
|
+
first_payload = json.loads(first_output)
|
|
4546
|
+
notification_payload = json.loads(notification_output)
|
|
4547
|
+
second_payload = json.loads(second_output)
|
|
4548
|
+
|
|
4549
|
+
assert first_rc == 0
|
|
4550
|
+
assert first_payload["hookSpecificOutput"]["permissionDecision"] == "ask"
|
|
4551
|
+
assert notification_rc == 0
|
|
4552
|
+
assert "HOL Guard approval question" in notification_payload["systemMessage"]
|
|
4553
|
+
assert stop_rc == 0
|
|
4554
|
+
assert stop_output == ""
|
|
4555
|
+
assert GuardStore(home_dir).list_policy_decisions("claude-code") == []
|
|
4556
|
+
assert second_rc == 0
|
|
4557
|
+
assert second_payload["hookSpecificOutput"]["permissionDecision"] == "ask"
|
|
4558
|
+
|
|
4559
|
+
|
|
4495
4560
|
def test_guard_hook_claude_alias_reuses_native_approval_policy_with_canonical_harness(tmp_path, capsys, monkeypatch):
|
|
4496
4561
|
home_dir = tmp_path / "home"
|
|
4497
4562
|
workspace_dir = tmp_path / "workspace"
|
|
@@ -4600,7 +4665,7 @@ def test_guard_hook_claude_alias_reuses_legacy_alias_policy_keys(tmp_path, capsy
|
|
|
4600
4665
|
assert payload["hookSpecificOutput"]["permissionDecision"] == "deny"
|
|
4601
4666
|
|
|
4602
4667
|
|
|
4603
|
-
def
|
|
4668
|
+
def test_guard_hook_claude_stop_keeps_native_cancel_transient(tmp_path, capsys, monkeypatch):
|
|
4604
4669
|
home_dir = tmp_path / "home"
|
|
4605
4670
|
workspace_dir = tmp_path / "workspace"
|
|
4606
4671
|
_build_guard_fixture(home_dir, workspace_dir)
|
|
@@ -4662,8 +4727,8 @@ def test_guard_hook_claude_stop_persists_unapproved_native_prompt_as_denied(tmp_
|
|
|
4662
4727
|
assert stop_rc == 0
|
|
4663
4728
|
assert stop_output == ""
|
|
4664
4729
|
assert second_rc == 0
|
|
4665
|
-
assert second_payload["hookSpecificOutput"]["permissionDecision"] == "
|
|
4666
|
-
assert "HOL Guard
|
|
4730
|
+
assert second_payload["hookSpecificOutput"]["permissionDecision"] == "ask"
|
|
4731
|
+
assert "HOL Guard intercepted Claude's attempt to use Read" in second_payload["systemMessage"]
|
|
4667
4732
|
|
|
4668
4733
|
|
|
4669
4734
|
def test_guard_hook_claude_stop_does_not_persist_denial_without_visible_prompt(
|
|
@@ -4873,7 +4938,7 @@ def test_runtime_artifact_native_reason_truncates_long_risk_summaries() -> None:
|
|
|
4873
4938
|
assert reason.endswith("...")
|
|
4874
4939
|
|
|
4875
4940
|
|
|
4876
|
-
def
|
|
4941
|
+
def test_guard_hook_allows_claude_user_prompt_submit_before_tool_approval(
|
|
4877
4942
|
tmp_path,
|
|
4878
4943
|
capsys,
|
|
4879
4944
|
monkeypatch,
|
|
@@ -4895,20 +4960,13 @@ def test_guard_hook_brands_claude_user_prompt_submit_before_native_approval(
|
|
|
4895
4960
|
monkeypatch=monkeypatch,
|
|
4896
4961
|
)
|
|
4897
4962
|
receipts = GuardStore(home_dir).list_receipts()
|
|
4898
|
-
payload = json.loads(output)
|
|
4899
4963
|
|
|
4900
4964
|
assert rc == 0
|
|
4901
|
-
assert
|
|
4902
|
-
assert "Do not ask for approval at the prompt stage" in payload["hookSpecificOutput"]["additionalContext"]
|
|
4903
|
-
assert (
|
|
4904
|
-
"route that concrete action into a HOL Guard approval question"
|
|
4905
|
-
in payload["hookSpecificOutput"]["additionalContext"]
|
|
4906
|
-
)
|
|
4907
|
-
assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
|
|
4965
|
+
assert output == ""
|
|
4908
4966
|
assert any(receipt["artifact_id"].startswith("claude-code:session:prompt") for receipt in receipts)
|
|
4909
4967
|
|
|
4910
4968
|
|
|
4911
|
-
def
|
|
4969
|
+
def test_guard_hook_allows_generic_claude_user_prompt_submit_before_tool_approval(
|
|
4912
4970
|
tmp_path,
|
|
4913
4971
|
capsys,
|
|
4914
4972
|
monkeypatch,
|
|
@@ -4929,16 +4987,9 @@ def test_guard_hook_brands_generic_claude_user_prompt_submit_before_native_appro
|
|
|
4929
4987
|
capsys=capsys,
|
|
4930
4988
|
monkeypatch=monkeypatch,
|
|
4931
4989
|
)
|
|
4932
|
-
payload = json.loads(output)
|
|
4933
4990
|
|
|
4934
4991
|
assert rc == 0
|
|
4935
|
-
assert
|
|
4936
|
-
assert "Do not ask for approval at the prompt stage" in payload["hookSpecificOutput"]["additionalContext"]
|
|
4937
|
-
assert (
|
|
4938
|
-
"route that concrete action into a HOL Guard approval question"
|
|
4939
|
-
in payload["hookSpecificOutput"]["additionalContext"]
|
|
4940
|
-
)
|
|
4941
|
-
assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
|
|
4992
|
+
assert output == ""
|
|
4942
4993
|
|
|
4943
4994
|
|
|
4944
4995
|
def test_guard_hook_emits_json_for_claude_user_prompt_submit_overridable_prompts(
|
|
@@ -253,15 +253,7 @@ class TestGuardSurfaceServer:
|
|
|
253
253
|
finally:
|
|
254
254
|
daemon.stop()
|
|
255
255
|
|
|
256
|
-
assert
|
|
257
|
-
assert hook_payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
|
|
258
|
-
assert "Do not ask for approval at the prompt stage" in hook_payload["hookSpecificOutput"]["additionalContext"]
|
|
259
|
-
assert (
|
|
260
|
-
"route that concrete action into a HOL Guard approval question"
|
|
261
|
-
in hook_payload["hookSpecificOutput"]["additionalContext"]
|
|
262
|
-
)
|
|
263
|
-
assert "Allow once" in hook_payload["hookSpecificOutput"]["additionalContext"]
|
|
264
|
-
assert "Keep blocked" in hook_payload["hookSpecificOutput"]["additionalContext"]
|
|
256
|
+
assert hook_payload == {}
|
|
265
257
|
|
|
266
258
|
def test_guard_daemon_claude_hook_endpoint_blocks_guard_bypass_user_prompt_submit(self, tmp_path) -> None:
|
|
267
259
|
home_dir = tmp_path / "home"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|