plugin-scanner 2.0.61__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.61 → plugin_scanner-2.0.63}/PKG-INFO +1 -1
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/pyproject.toml +1 -1
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/claude_code.py +45 -4
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/commands.py +264 -37
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_claude_adapter.py +65 -3
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_runtime.py +496 -42
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_surface_server.py +9 -15
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/uv.lock +22 -22
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.dockerignore +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.gitignore +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/Dockerfile +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/requirements.txt +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/tests/test_versioning.py +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
|
|
@@ -44,7 +45,8 @@ def _shell_command(command: tuple[str, ...], *, windows: bool | None = None) ->
|
|
|
44
45
|
def _sync_runtime_hook_groups(hooks: dict[str, object], hook_command: str) -> None:
|
|
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),
|
|
49
|
+
("PostToolUse", CLAUDE_GUARD_POST_TOOL_MATCHER, CLAUDE_GUARD_TOOL_TIMEOUT_SECONDS),
|
|
48
50
|
("UserPromptSubmit", None, CLAUDE_GUARD_PROMPT_TIMEOUT_SECONDS),
|
|
49
51
|
("Notification", CLAUDE_GUARD_NOTIFICATION_MATCHER, CLAUDE_GUARD_NOTIFICATION_TIMEOUT_SECONDS),
|
|
50
52
|
("Stop", None, CLAUDE_GUARD_STOP_TIMEOUT_SECONDS),
|
|
@@ -486,6 +488,7 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
486
488
|
def _daemon_hook_command_parts(context: HarnessContext) -> tuple[str, ...]:
|
|
487
489
|
fallback_daemon_url = load_guard_daemon_url(context.guard_home) or guard_daemon_url_for_home(context.guard_home)
|
|
488
490
|
state_path = context.guard_home / "daemon-state.json"
|
|
491
|
+
fallback_command = ClaudeCodeHarnessAdapter._hook_command_parts(context)
|
|
489
492
|
query: dict[str, str] = {"guard-home": str(context.guard_home)}
|
|
490
493
|
if context.home_dir.resolve() != Path.home().resolve():
|
|
491
494
|
query["home"] = str(context.home_dir)
|
|
@@ -496,9 +499,11 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
496
499
|
"void MARKER;"
|
|
497
500
|
"const fs=require('fs');"
|
|
498
501
|
"const http=require('http');"
|
|
502
|
+
"const cp=require('child_process');"
|
|
499
503
|
"const {URL}=require('url');"
|
|
500
504
|
f"const statePath={str(state_path)!r};"
|
|
501
505
|
f"const fallbackUrl={fallback_daemon_url!r};"
|
|
506
|
+
f"const fallbackCommand={list(fallback_command)!r};"
|
|
502
507
|
f"const query={urlencode(query)!r};"
|
|
503
508
|
"function daemonUrl(){"
|
|
504
509
|
"try{"
|
|
@@ -507,9 +512,42 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
507
512
|
"}catch(_error){}"
|
|
508
513
|
"return fallbackUrl;"
|
|
509
514
|
"}"
|
|
515
|
+
"function eventName(data){"
|
|
516
|
+
"try{const payload=JSON.parse(data||'{}');"
|
|
517
|
+
"return String(payload.hook_event_name||payload.event||'PreToolUse');}"
|
|
518
|
+
"catch(_error){return 'PreToolUse';}"
|
|
519
|
+
"}"
|
|
520
|
+
"function degraded(reason,data){"
|
|
521
|
+
"const event=eventName(data);"
|
|
522
|
+
"const message=`HOL Guard could not reach the local daemon (${reason}), so it is using Claude's native "
|
|
523
|
+
"approval prompt as a temporary safety fallback.`;"
|
|
524
|
+
"if(event==='UserPromptSubmit'){return '';}"
|
|
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.`}});}"
|
|
530
|
+
"return '{}';"
|
|
531
|
+
"}"
|
|
532
|
+
"function shouldSuppressOutput(data,responseBody){"
|
|
533
|
+
"if(eventName(data)!=='UserPromptSubmit')return false;"
|
|
534
|
+
"const trimmed=(responseBody||'').trim();"
|
|
535
|
+
"return trimmed===''||trimmed==='{}';"
|
|
536
|
+
"}"
|
|
537
|
+
"function runLocalFallback(reason,data){"
|
|
538
|
+
"try{"
|
|
539
|
+
"const result=cp.spawnSync(fallbackCommand[0],fallbackCommand.slice(1),{input:data,encoding:'utf8',"
|
|
540
|
+
"timeout:30000,env:process.env});"
|
|
541
|
+
"if(result.error)return degraded(`${reason}; fallback failed: ${result.error.message}`,data);"
|
|
542
|
+
"if(result.status===0){"
|
|
543
|
+
"if(shouldSuppressOutput(data,result.stdout)){process.exit(0);}"
|
|
544
|
+
"process.stdout.write(result.stdout&&result.stdout.trim()?result.stdout:'{}');"
|
|
545
|
+
"process.exit(0);}"
|
|
546
|
+
"return degraded(`${reason}; fallback exited ${result.status}`,data);"
|
|
547
|
+
"}catch(error){return degraded(`${reason}; fallback crashed: ${error.message}`,data);}"
|
|
548
|
+
"}"
|
|
510
549
|
"function fail(reason){"
|
|
511
|
-
"
|
|
512
|
-
"process.stdout.write(JSON.stringify({decision:'block',reason:message}));"
|
|
550
|
+
"process.stdout.write(runLocalFallback(reason,body.trim()?body:'{}'));"
|
|
513
551
|
"process.exit(0);"
|
|
514
552
|
"}"
|
|
515
553
|
"let body='';"
|
|
@@ -526,7 +564,9 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
526
564
|
"response.setEncoding('utf8');"
|
|
527
565
|
"response.on('data',chunk=>{responseBody+=chunk;});"
|
|
528
566
|
"response.on('end',()=>{"
|
|
529
|
-
"if(response.statusCode>=200&&response.statusCode<300){
|
|
567
|
+
"if(response.statusCode>=200&&response.statusCode<300){"
|
|
568
|
+
"if(shouldSuppressOutput(data,responseBody)){process.exit(0);}"
|
|
569
|
+
"process.stdout.write(responseBody);process.exit(0);}"
|
|
530
570
|
"fail(`daemon returned HTTP ${response.statusCode||0}`);"
|
|
531
571
|
"});"
|
|
532
572
|
"});"
|
|
@@ -672,6 +712,7 @@ class ClaudeCodeHarnessAdapter(HarnessAdapter):
|
|
|
672
712
|
for key in (
|
|
673
713
|
"SessionStart",
|
|
674
714
|
"PreToolUse",
|
|
715
|
+
"PermissionRequest",
|
|
675
716
|
"PostToolUse",
|
|
676
717
|
"UserPromptSubmit",
|
|
677
718
|
"Notification",
|
{plugin_scanner-2.0.61 → plugin_scanner-2.0.63}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -1187,8 +1187,25 @@ def run_guard_command(
|
|
|
1187
1187
|
guard_home=context.guard_home,
|
|
1188
1188
|
workspace=runtime_workspace,
|
|
1189
1189
|
)
|
|
1190
|
+
if _is_claude_permission_request(args, payload):
|
|
1191
|
+
notice = _peek_claude_permission_notice(store, payload)
|
|
1192
|
+
if notice is None:
|
|
1193
|
+
_emit_claude_permission_request_passthrough(output_stream=output_stream)
|
|
1194
|
+
return 0
|
|
1195
|
+
_mark_claude_pending_permission_prompt_seen(store=store, payload=payload, notice=notice)
|
|
1196
|
+
_emit_native_hook_response(
|
|
1197
|
+
harness=args.harness,
|
|
1198
|
+
policy_action="block",
|
|
1199
|
+
event_name="PermissionRequest",
|
|
1200
|
+
reason="HOL Guard is routing this approval through AskUserQuestion.",
|
|
1201
|
+
system_message=_claude_permission_prompt_system_message(payload=payload, notice=notice),
|
|
1202
|
+
additional_context=_claude_guard_approval_question_message(notice),
|
|
1203
|
+
output_stream=output_stream,
|
|
1204
|
+
)
|
|
1205
|
+
return 0
|
|
1190
1206
|
if _is_claude_permission_prompt_notification(args, payload):
|
|
1191
1207
|
notice = _load_claude_permission_notice(store, payload)
|
|
1208
|
+
_mark_claude_pending_permission_prompt_seen(store=store, payload=payload, notice=notice)
|
|
1192
1209
|
store.add_event(
|
|
1193
1210
|
"claude/permission_prompt",
|
|
1194
1211
|
{
|
|
@@ -1205,12 +1222,11 @@ def run_guard_command(
|
|
|
1205
1222
|
_emit_native_hook_notification_stderr(
|
|
1206
1223
|
_claude_permission_prompt_terminal_notice(payload=payload, notice=notice)
|
|
1207
1224
|
)
|
|
1208
|
-
return 2
|
|
1209
1225
|
_emit_native_hook_response(
|
|
1210
1226
|
harness=args.harness,
|
|
1211
1227
|
policy_action="allow",
|
|
1212
1228
|
event_name="Notification",
|
|
1213
|
-
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.",
|
|
1214
1230
|
system_message=system_message,
|
|
1215
1231
|
additional_context=additional_context,
|
|
1216
1232
|
output_stream=output_stream,
|
|
@@ -1224,6 +1240,10 @@ def run_guard_command(
|
|
|
1224
1240
|
_now(),
|
|
1225
1241
|
)
|
|
1226
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
|
|
1227
1247
|
if runtime_artifact is not None:
|
|
1228
1248
|
event_name = _hook_event_name(payload) or "PreToolUse"
|
|
1229
1249
|
runtime_artifact_hash = artifact_hash(runtime_artifact)
|
|
@@ -1704,6 +1724,11 @@ def _should_emit_prequeue_native_hook_response(
|
|
|
1704
1724
|
return output_stream is not None
|
|
1705
1725
|
|
|
1706
1726
|
|
|
1727
|
+
def _emit_claude_permission_request_passthrough(*, output_stream: TextIO | None = None) -> None:
|
|
1728
|
+
if output_stream is not None:
|
|
1729
|
+
output_stream.write("")
|
|
1730
|
+
|
|
1731
|
+
|
|
1707
1732
|
def _claude_permission_notice_state_key(session_id: str, tool_name: str | None = None) -> str:
|
|
1708
1733
|
if tool_name is not None:
|
|
1709
1734
|
return f"claude_permission_notice:{session_id}:{tool_name}"
|
|
@@ -1812,6 +1837,82 @@ def _load_claude_permission_notice(store: GuardStore, payload: dict[str, object]
|
|
|
1812
1837
|
return None
|
|
1813
1838
|
|
|
1814
1839
|
|
|
1840
|
+
def _peek_claude_permission_notice(store: GuardStore, payload: dict[str, object]) -> dict[str, object] | None:
|
|
1841
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
1842
|
+
if session_id is None:
|
|
1843
|
+
return None
|
|
1844
|
+
tool_name = _claude_notification_tool_name(payload)
|
|
1845
|
+
try:
|
|
1846
|
+
persisted = store.get_sync_payload(_claude_permission_notice_state_key(session_id, tool_name))
|
|
1847
|
+
if persisted is None and tool_name is not None:
|
|
1848
|
+
persisted = store.get_sync_payload(_claude_permission_notice_state_key(session_id))
|
|
1849
|
+
except (OSError, sqlite3.Error):
|
|
1850
|
+
return None
|
|
1851
|
+
return persisted if isinstance(persisted, dict) else None
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
def _mark_claude_pending_permission_prompt_seen(
|
|
1855
|
+
*,
|
|
1856
|
+
store: GuardStore,
|
|
1857
|
+
payload: dict[str, object],
|
|
1858
|
+
notice: dict[str, object] | None,
|
|
1859
|
+
) -> None:
|
|
1860
|
+
session_id = _optional_string(payload.get("session_id"))
|
|
1861
|
+
artifact_id = _optional_string((notice or {}).get("artifact_id"))
|
|
1862
|
+
if session_id is None or artifact_id is None:
|
|
1863
|
+
return
|
|
1864
|
+
pending_key = _claude_pending_permission_state_key(session_id, artifact_id)
|
|
1865
|
+
try:
|
|
1866
|
+
pending = store.get_sync_payload(pending_key)
|
|
1867
|
+
except (OSError, sqlite3.Error):
|
|
1868
|
+
return
|
|
1869
|
+
if not isinstance(pending, dict):
|
|
1870
|
+
return
|
|
1871
|
+
updated = dict(pending)
|
|
1872
|
+
updated["permission_prompt_seen"] = True
|
|
1873
|
+
updated["permission_prompt_seen_at"] = _now()
|
|
1874
|
+
try:
|
|
1875
|
+
store.set_sync_payload(pending_key, updated, _now())
|
|
1876
|
+
except (OSError, sqlite3.Error):
|
|
1877
|
+
return
|
|
1878
|
+
|
|
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
|
+
|
|
1815
1916
|
def _load_claude_pending_permission(
|
|
1816
1917
|
store: GuardStore,
|
|
1817
1918
|
payload: dict[str, object],
|
|
@@ -1869,6 +1970,7 @@ def _persist_claude_native_permission_policy(
|
|
|
1869
1970
|
action: str,
|
|
1870
1971
|
reason: str,
|
|
1871
1972
|
now: str,
|
|
1973
|
+
source: str = "claude-native-approval",
|
|
1872
1974
|
) -> bool:
|
|
1873
1975
|
try:
|
|
1874
1976
|
store.upsert_policy(
|
|
@@ -1879,7 +1981,7 @@ def _persist_claude_native_permission_policy(
|
|
|
1879
1981
|
artifact_id=artifact_id,
|
|
1880
1982
|
artifact_hash=artifact_hash,
|
|
1881
1983
|
reason=reason,
|
|
1882
|
-
source=
|
|
1984
|
+
source=source,
|
|
1883
1985
|
),
|
|
1884
1986
|
now,
|
|
1885
1987
|
)
|
|
@@ -1963,6 +2065,8 @@ def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[
|
|
|
1963
2065
|
continue
|
|
1964
2066
|
if not isinstance(pending, dict):
|
|
1965
2067
|
continue
|
|
2068
|
+
if pending.get("permission_prompt_seen") is not True:
|
|
2069
|
+
continue
|
|
1966
2070
|
artifact_id = _optional_string(pending.get("artifact_id"))
|
|
1967
2071
|
artifact_hash_value = _optional_string(pending.get("artifact_hash"))
|
|
1968
2072
|
if artifact_id is None or artifact_hash_value is None:
|
|
@@ -2011,6 +2115,106 @@ def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[
|
|
|
2011
2115
|
return denied
|
|
2012
2116
|
|
|
2013
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
|
+
|
|
2014
2218
|
def _is_claude_permission_prompt_notification(args: argparse.Namespace, payload: dict[str, object]) -> bool:
|
|
2015
2219
|
return (
|
|
2016
2220
|
_canonical_harness_name(args.harness) == "claude-code"
|
|
@@ -2019,6 +2223,10 @@ def _is_claude_permission_prompt_notification(args: argparse.Namespace, payload:
|
|
|
2019
2223
|
)
|
|
2020
2224
|
|
|
2021
2225
|
|
|
2226
|
+
def _is_claude_permission_request(args: argparse.Namespace, payload: dict[str, object]) -> bool:
|
|
2227
|
+
return _canonical_harness_name(args.harness) == "claude-code" and _hook_event_name(payload) == "PermissionRequest"
|
|
2228
|
+
|
|
2229
|
+
|
|
2022
2230
|
def _claude_permission_prompt_system_message(
|
|
2023
2231
|
*,
|
|
2024
2232
|
payload: dict[str, object],
|
|
@@ -2028,20 +2236,23 @@ def _claude_permission_prompt_system_message(
|
|
|
2028
2236
|
if tool_name is None and notice is not None:
|
|
2029
2237
|
tool_name = _optional_string(notice.get("tool_name"))
|
|
2030
2238
|
reason = _optional_string(notice.get("reason")) if notice is not None else None
|
|
2031
|
-
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."
|
|
2032
2240
|
if tool_name is not None:
|
|
2033
|
-
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
|
+
)
|
|
2034
2245
|
if reason is not None:
|
|
2035
2246
|
return (
|
|
2036
|
-
f"{intro} This approval
|
|
2247
|
+
f"{intro} This approval flow came from HOL Guard, not from Claude alone. "
|
|
2037
2248
|
f"{_ensure_terminal_punctuation(reason)} "
|
|
2038
|
-
"
|
|
2039
|
-
"
|
|
2249
|
+
"HOL Guard will ask the user to choose Allow once, Allow during this session, or Keep blocked before "
|
|
2250
|
+
"Claude retries the action."
|
|
2040
2251
|
)
|
|
2041
2252
|
return (
|
|
2042
|
-
f"{intro} This approval
|
|
2043
|
-
"
|
|
2044
|
-
"
|
|
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."
|
|
2045
2256
|
)
|
|
2046
2257
|
|
|
2047
2258
|
|
|
@@ -2049,17 +2260,17 @@ def _claude_permission_prompt_additional_context(notice: dict[str, object] | Non
|
|
|
2049
2260
|
reason = _optional_string(notice.get("reason")) if notice is not None else None
|
|
2050
2261
|
if reason is not None:
|
|
2051
2262
|
return (
|
|
2052
|
-
"HOL Guard intercepted the sensitive request and
|
|
2053
|
-
"
|
|
2054
|
-
"
|
|
2055
|
-
|
|
2056
|
-
"
|
|
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."
|
|
2057
2268
|
)
|
|
2058
2269
|
return (
|
|
2059
|
-
"HOL Guard intercepted the sensitive request and
|
|
2060
|
-
"This approval
|
|
2061
|
-
"
|
|
2062
|
-
"
|
|
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."
|
|
2063
2274
|
)
|
|
2064
2275
|
|
|
2065
2276
|
|
|
@@ -2072,28 +2283,32 @@ def _claude_permission_prompt_terminal_notice(
|
|
|
2072
2283
|
reason = _optional_string(notice.get("reason")) if notice is not None else None
|
|
2073
2284
|
if tool_name is not None and reason is not None:
|
|
2074
2285
|
return (
|
|
2075
|
-
f"HOL Guard
|
|
2286
|
+
f"HOL Guard is routing this Claude approval request for {tool_name} into a HOL Guard decision prompt. "
|
|
2076
2287
|
f"{_ensure_terminal_punctuation(reason)} "
|
|
2077
|
-
"
|
|
2288
|
+
"Choose Allow once, Allow during this session, or Keep blocked in the HOL Guard prompt."
|
|
2078
2289
|
)
|
|
2079
2290
|
if tool_name is not None:
|
|
2080
2291
|
return (
|
|
2081
|
-
f"HOL Guard
|
|
2082
|
-
"
|
|
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."
|
|
2083
2294
|
)
|
|
2084
2295
|
return (
|
|
2085
|
-
"HOL Guard
|
|
2086
|
-
"
|
|
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."
|
|
2087
2298
|
)
|
|
2088
2299
|
|
|
2089
2300
|
|
|
2090
2301
|
def _claude_native_pretooluse_terminal_notice(*, payload: dict[str, object], reason: str) -> str:
|
|
2091
2302
|
tool_name = _claude_notification_tool_name(payload)
|
|
2092
2303
|
if tool_name is not None:
|
|
2093
|
-
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
|
+
)
|
|
2094
2308
|
return (
|
|
2095
|
-
"HOL Guard
|
|
2096
|
-
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."
|
|
2097
2312
|
)
|
|
2098
2313
|
|
|
2099
2314
|
|
|
@@ -2191,9 +2406,8 @@ def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: d
|
|
|
2191
2406
|
if harness == "claude-code" and policy_action == "require-reapproval":
|
|
2192
2407
|
return (
|
|
2193
2408
|
f"HOL Guard intercepted Claude's attempt to use {tool_name} for {path_class} to protect your local "
|
|
2194
|
-
"secrets.
|
|
2195
|
-
"
|
|
2196
|
-
"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."
|
|
2197
2411
|
)
|
|
2198
2412
|
return (
|
|
2199
2413
|
f"HOL Guard blocked Claude's attempt to use {tool_name} for {path_class} to protect your local secrets. "
|
|
@@ -2236,7 +2450,10 @@ def _claude_prompt_additional_context(
|
|
|
2236
2450
|
)
|
|
2237
2451
|
return (
|
|
2238
2452
|
f"{_ensure_terminal_punctuation(native_reason)} "
|
|
2239
|
-
"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: "
|
|
2240
2457
|
f"'{briefing_sentence}' "
|
|
2241
2458
|
"Attempt that sensitive tool at most once. If HOL Guard or Claude denies it, do not retry the same sensitive "
|
|
2242
2459
|
"action automatically. Instead, tell the user approval is required in Claude to continue."
|
|
@@ -2255,12 +2472,11 @@ def _claude_prompt_system_message(
|
|
|
2255
2472
|
if "secret_read" in _prompt_request_classes(artifact):
|
|
2256
2473
|
return (
|
|
2257
2474
|
"HOL Guard intercepted this prompt because it asks Claude to access local secrets. "
|
|
2258
|
-
"If Claude
|
|
2259
|
-
"Guard."
|
|
2475
|
+
"If Claude asks to continue, HOL Guard will route the decision through a branded approval prompt."
|
|
2260
2476
|
)
|
|
2261
2477
|
return (
|
|
2262
2478
|
"HOL Guard intercepted this prompt because it leads to a sensitive action. "
|
|
2263
|
-
"If Claude
|
|
2479
|
+
"If Claude asks to continue, HOL Guard will route the decision through a branded approval prompt."
|
|
2264
2480
|
)
|
|
2265
2481
|
if policy_action in {"block", "sandbox-required"}:
|
|
2266
2482
|
return _ensure_terminal_punctuation(native_reason)
|
|
@@ -2377,7 +2593,18 @@ def _emit_native_hook_response(
|
|
|
2377
2593
|
if payload:
|
|
2378
2594
|
_write_json_line(payload, output_stream=output_stream)
|
|
2379
2595
|
return
|
|
2380
|
-
if event_name
|
|
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
|
|
2381
2608
|
if additional_context:
|
|
2382
2609
|
payload["hookSpecificOutput"] = {
|
|
2383
2610
|
"hookEventName": event_name,
|