plugin-scanner 2.0.66__tar.gz → 2.0.68__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.66 → plugin_scanner-2.0.68}/PKG-INFO +1 -1
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/pyproject.toml +1 -1
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/pyproject.toml.bak +1 -1
- plugin_scanner-2.0.68/src/codex_plugin_scanner/guard/cli/approval_commands.py +119 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/commands.py +205 -40
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/render.py +21 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/store.py +16 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_approvals.py +180 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_runtime.py +685 -67
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_surface_server.py +1 -1
- plugin_scanner-2.0.66/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -63
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.dockerignore +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.gitignore +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/Dockerfile +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/requirements.txt +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/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.68
|
|
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.68"
|
|
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.68"
|
|
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"
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""CLI helpers for Guard approval queue workflows."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ..approvals import apply_approval_resolution, build_runtime_snapshot
|
|
10
|
+
from ..daemon import load_guard_daemon_url
|
|
11
|
+
from ..store import GuardStore
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def add_approval_parser(
|
|
15
|
+
guard_subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
16
|
+
add_common_args,
|
|
17
|
+
) -> None:
|
|
18
|
+
approvals_parser = guard_subparsers.add_parser(
|
|
19
|
+
"approvals",
|
|
20
|
+
help="List, resolve, or clear Guard approval history",
|
|
21
|
+
)
|
|
22
|
+
approvals_subparsers = approvals_parser.add_subparsers(dest="approvals_command")
|
|
23
|
+
|
|
24
|
+
add_common_args(approvals_parser)
|
|
25
|
+
approvals_parser.add_argument("--json", action="store_true")
|
|
26
|
+
|
|
27
|
+
for name, action in (("approve", "allow"), ("deny", "block")):
|
|
28
|
+
decision_parser = approvals_subparsers.add_parser(name, help=f"{name.title()} a pending approval request")
|
|
29
|
+
decision_parser.add_argument("request_id")
|
|
30
|
+
decision_parser.add_argument(
|
|
31
|
+
"--scope",
|
|
32
|
+
choices=("artifact", "publisher", "workspace", "harness", "global"),
|
|
33
|
+
default="artifact",
|
|
34
|
+
)
|
|
35
|
+
decision_parser.add_argument("--reason")
|
|
36
|
+
add_common_args(decision_parser)
|
|
37
|
+
decision_parser.add_argument("--json", action="store_true")
|
|
38
|
+
decision_parser.set_defaults(approval_action=action)
|
|
39
|
+
|
|
40
|
+
clear_history_parser = approvals_subparsers.add_parser(
|
|
41
|
+
"clear-history",
|
|
42
|
+
help="Clear saved allow/deny history so flows can be re-tested",
|
|
43
|
+
)
|
|
44
|
+
clear_history_parser.add_argument(
|
|
45
|
+
"--harness",
|
|
46
|
+
help="The harness to clear history for (for example: codex, claude-code, opencode, copilot)",
|
|
47
|
+
)
|
|
48
|
+
clear_history_parser.add_argument(
|
|
49
|
+
"--all",
|
|
50
|
+
action="store_true",
|
|
51
|
+
help="Clear approval history across every harness; cannot be combined with --harness",
|
|
52
|
+
)
|
|
53
|
+
clear_history_parser.add_argument(
|
|
54
|
+
"--source",
|
|
55
|
+
help="Optional source filter for policy decisions (for example: manual, claude-ask-user-question)",
|
|
56
|
+
)
|
|
57
|
+
add_common_args(clear_history_parser)
|
|
58
|
+
clear_history_parser.add_argument("--json", action="store_true")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def run_approval_command(
|
|
62
|
+
args: argparse.Namespace,
|
|
63
|
+
*,
|
|
64
|
+
store: GuardStore,
|
|
65
|
+
workspace: Path | None,
|
|
66
|
+
) -> dict[str, object]:
|
|
67
|
+
command = getattr(args, "approvals_command", None)
|
|
68
|
+
if command is None:
|
|
69
|
+
return build_runtime_snapshot(
|
|
70
|
+
store=store,
|
|
71
|
+
approval_center_url=load_guard_daemon_url(store.guard_home),
|
|
72
|
+
now=_now(),
|
|
73
|
+
)
|
|
74
|
+
if command == "clear-history":
|
|
75
|
+
harness = getattr(args, "harness", None)
|
|
76
|
+
clear_all = bool(getattr(args, "all", False))
|
|
77
|
+
if clear_all and harness is not None:
|
|
78
|
+
return {
|
|
79
|
+
"history_cleared": False,
|
|
80
|
+
"error": "Choose either --all or --harness <name> when clearing approval history.",
|
|
81
|
+
"cleared_policies": 0,
|
|
82
|
+
"cleared_resolved_requests": 0,
|
|
83
|
+
"exit_code": 2,
|
|
84
|
+
}
|
|
85
|
+
if not clear_all and harness is None:
|
|
86
|
+
return {
|
|
87
|
+
"history_cleared": False,
|
|
88
|
+
"error": "Choose --harness <name> or --all when clearing approval history.",
|
|
89
|
+
"cleared_policies": 0,
|
|
90
|
+
"cleared_resolved_requests": 0,
|
|
91
|
+
"exit_code": 2,
|
|
92
|
+
}
|
|
93
|
+
target_harness = None if clear_all else harness
|
|
94
|
+
source = getattr(args, "source", None)
|
|
95
|
+
cleared_resolved_requests = 0
|
|
96
|
+
if source is None:
|
|
97
|
+
cleared_resolved_requests = store.clear_approval_requests(harness=target_harness, status="resolved")
|
|
98
|
+
return {
|
|
99
|
+
"history_cleared": True,
|
|
100
|
+
"harness": target_harness,
|
|
101
|
+
"source": source,
|
|
102
|
+
"cleared_policies": store.clear_policy_decisions(target_harness, source),
|
|
103
|
+
"cleared_resolved_requests": cleared_resolved_requests,
|
|
104
|
+
"exit_code": 0,
|
|
105
|
+
}
|
|
106
|
+
item = apply_approval_resolution(
|
|
107
|
+
store=store,
|
|
108
|
+
request_id=args.request_id,
|
|
109
|
+
action=args.approval_action,
|
|
110
|
+
scope=args.scope,
|
|
111
|
+
workspace=str(workspace) if workspace is not None else None,
|
|
112
|
+
reason=args.reason,
|
|
113
|
+
now=_now(),
|
|
114
|
+
)
|
|
115
|
+
return {"resolved": True, "item": item}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _now() -> str:
|
|
119
|
+
return datetime.now(timezone.utc).isoformat()
|
{plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -7,6 +7,7 @@ import hashlib
|
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
9
|
import re
|
|
10
|
+
import secrets
|
|
10
11
|
import shlex
|
|
11
12
|
import sqlite3
|
|
12
13
|
import subprocess
|
|
@@ -130,6 +131,13 @@ _GUARD_HELP_GROUPS = (
|
|
|
130
131
|
" update Update hol-guard in the current environment"
|
|
131
132
|
)
|
|
132
133
|
|
|
134
|
+
_CLAUDE_GUARD_APPROVAL_HEADER = "HOL Guard"
|
|
135
|
+
_CLAUDE_GUARD_APPROVAL_OPTIONS = (
|
|
136
|
+
"Allow once",
|
|
137
|
+
"Allow during this session",
|
|
138
|
+
"Keep blocked",
|
|
139
|
+
)
|
|
140
|
+
|
|
133
141
|
|
|
134
142
|
def _now() -> str:
|
|
135
143
|
return datetime.now(timezone.utc).isoformat()
|
|
@@ -855,7 +863,7 @@ def run_guard_command(
|
|
|
855
863
|
if args.guard_command == "approvals":
|
|
856
864
|
payload = run_approval_command(args, store=store, workspace=workspace)
|
|
857
865
|
_emit("approvals", payload, getattr(args, "json", False))
|
|
858
|
-
return 0
|
|
866
|
+
return int(payload.get("exit_code", 0))
|
|
859
867
|
|
|
860
868
|
if args.guard_command == "explain":
|
|
861
869
|
payload = _build_explain_payload_with_mode(store, args.target, cisco_mode=args.cisco_mode)
|
|
@@ -1859,6 +1867,10 @@ def _append_claude_pending_permission_key(
|
|
|
1859
1867
|
)
|
|
1860
1868
|
|
|
1861
1869
|
|
|
1870
|
+
def _claude_guard_approval_question_text(approval_code: str) -> str:
|
|
1871
|
+
return f"HOL Guard intercepted this sensitive action (approval code: {approval_code}). What should Claude do?"
|
|
1872
|
+
|
|
1873
|
+
|
|
1862
1874
|
def _record_claude_permission_notice(
|
|
1863
1875
|
*,
|
|
1864
1876
|
store: GuardStore,
|
|
@@ -1870,9 +1882,12 @@ def _record_claude_permission_notice(
|
|
|
1870
1882
|
session_id = _optional_string(payload.get("session_id"))
|
|
1871
1883
|
if session_id is None:
|
|
1872
1884
|
return
|
|
1885
|
+
saved_at = _now()
|
|
1873
1886
|
tool_name = _optional_string(payload.get("tool_name"))
|
|
1887
|
+
approval_code = secrets.token_hex(6)
|
|
1888
|
+
approval_question = _claude_guard_approval_question_text(approval_code)
|
|
1874
1889
|
notice_payload: dict[str, object] = {
|
|
1875
|
-
"saved_at":
|
|
1890
|
+
"saved_at": saved_at,
|
|
1876
1891
|
"reason": reason,
|
|
1877
1892
|
"artifact_id": artifact.artifact_id,
|
|
1878
1893
|
"artifact_hash": artifact_hash,
|
|
@@ -1880,14 +1895,18 @@ def _record_claude_permission_notice(
|
|
|
1880
1895
|
"artifact_type": artifact.artifact_type,
|
|
1881
1896
|
"config_path": artifact.config_path,
|
|
1882
1897
|
"source_scope": artifact.source_scope,
|
|
1898
|
+
"approval_header": _CLAUDE_GUARD_APPROVAL_HEADER,
|
|
1899
|
+
"approval_question": approval_question,
|
|
1900
|
+
"approval_options": list(_CLAUDE_GUARD_APPROVAL_OPTIONS),
|
|
1901
|
+
"approval_code": approval_code,
|
|
1883
1902
|
}
|
|
1884
1903
|
if tool_name is not None:
|
|
1885
1904
|
notice_payload["tool_name"] = tool_name
|
|
1886
1905
|
try:
|
|
1887
|
-
store.set_sync_payload(_claude_permission_notice_state_key(session_id, tool_name), notice_payload,
|
|
1906
|
+
store.set_sync_payload(_claude_permission_notice_state_key(session_id, tool_name), notice_payload, saved_at)
|
|
1888
1907
|
pending_key = _claude_pending_permission_state_key(session_id, artifact.artifact_id)
|
|
1889
|
-
store.set_sync_payload(pending_key, notice_payload,
|
|
1890
|
-
_append_claude_pending_permission_key(store, session_id=session_id, pending_key=pending_key, now=
|
|
1908
|
+
store.set_sync_payload(pending_key, notice_payload, saved_at)
|
|
1909
|
+
_append_claude_pending_permission_key(store, session_id=session_id, pending_key=pending_key, now=saved_at)
|
|
1891
1910
|
except (OSError, sqlite3.Error):
|
|
1892
1911
|
return
|
|
1893
1912
|
|
|
@@ -1903,8 +1922,17 @@ def _load_claude_permission_notice(store: GuardStore, payload: dict[str, object]
|
|
|
1903
1922
|
if persisted is None and tool_name is not None:
|
|
1904
1923
|
selected_key = _claude_permission_notice_state_key(session_id)
|
|
1905
1924
|
persisted = store.get_sync_payload(selected_key)
|
|
1906
|
-
if persisted
|
|
1907
|
-
|
|
1925
|
+
if isinstance(persisted, dict):
|
|
1926
|
+
artifact_id = _optional_string(persisted.get("artifact_id"))
|
|
1927
|
+
if artifact_id is not None:
|
|
1928
|
+
pending_key = _claude_pending_permission_state_key(session_id, artifact_id)
|
|
1929
|
+
pending = store.get_sync_payload(pending_key)
|
|
1930
|
+
if not isinstance(pending, dict):
|
|
1931
|
+
store.delete_sync_payload(selected_key)
|
|
1932
|
+
persisted = None
|
|
1933
|
+
else:
|
|
1934
|
+
store.delete_sync_payload(selected_key)
|
|
1935
|
+
persisted = None
|
|
1908
1936
|
except (OSError, sqlite3.Error):
|
|
1909
1937
|
return None
|
|
1910
1938
|
if isinstance(persisted, dict):
|
|
@@ -2214,51 +2242,185 @@ def _persist_claude_pending_permission_denials(store: GuardStore, payload: dict[
|
|
|
2214
2242
|
def _claude_guard_approval_question_message(notice: dict[str, object] | None) -> str:
|
|
2215
2243
|
tool_name = _optional_string((notice or {}).get("tool_name")) or "this tool"
|
|
2216
2244
|
reason = _optional_string((notice or {}).get("reason"))
|
|
2245
|
+
header = _optional_string((notice or {}).get("approval_header")) or _CLAUDE_GUARD_APPROVAL_HEADER
|
|
2246
|
+
question = _optional_string((notice or {}).get("approval_question")) or (
|
|
2247
|
+
"HOL Guard intercepted this sensitive action. What should Claude do?"
|
|
2248
|
+
)
|
|
2249
|
+
options = _claude_guard_approval_options_from_value((notice or {}).get("approval_options"))
|
|
2250
|
+
if not options:
|
|
2251
|
+
options = _CLAUDE_GUARD_APPROVAL_OPTIONS
|
|
2252
|
+
options_text = "', '".join(options)
|
|
2217
2253
|
reason_text = f" HOL Guard reason: {_ensure_terminal_punctuation(reason)}" if reason is not None else ""
|
|
2218
2254
|
return (
|
|
2219
2255
|
f"HOL Guard needs the user's explicit decision before {tool_name} can run.{reason_text} "
|
|
2220
2256
|
"The native Claude permission prompt is not the final decision surface for this request. Call "
|
|
2221
2257
|
"AskUserQuestion now with one HOL Guard approval question before retrying the tool. Use header "
|
|
2222
|
-
"'
|
|
2223
|
-
"
|
|
2224
|
-
"allow option, retry the same tool once. If the user chooses Keep blocked, do not retry the sensitive "
|
|
2225
|
-
"action."
|
|
2258
|
+
f"'{header}', question '{question}', and exactly these options: '{options_text}'. If the user chooses an "
|
|
2259
|
+
"allow option, retry the same tool once. If the user chooses Keep blocked, do not retry the sensitive action."
|
|
2226
2260
|
)
|
|
2227
2261
|
|
|
2228
2262
|
|
|
2229
|
-
def
|
|
2263
|
+
def _normalize_claude_guard_approval_text(value: str) -> str:
|
|
2264
|
+
return " ".join(value.strip().lower().split())
|
|
2265
|
+
|
|
2266
|
+
|
|
2267
|
+
def _claude_guard_approval_options_from_value(value: object) -> tuple[str, ...]:
|
|
2268
|
+
if not isinstance(value, list):
|
|
2269
|
+
return ()
|
|
2270
|
+
labels: list[str] = []
|
|
2271
|
+
for item in value:
|
|
2272
|
+
label: str | None
|
|
2273
|
+
if isinstance(item, dict):
|
|
2274
|
+
label = _optional_string(item.get("label"))
|
|
2275
|
+
elif isinstance(item, str):
|
|
2276
|
+
label = item.strip()
|
|
2277
|
+
else:
|
|
2278
|
+
label = None
|
|
2279
|
+
if label is None:
|
|
2280
|
+
return ()
|
|
2281
|
+
labels.append(label)
|
|
2282
|
+
return tuple(labels)
|
|
2283
|
+
|
|
2284
|
+
|
|
2285
|
+
def _claude_guard_prompt_contract_from_pending(
|
|
2286
|
+
pending: dict[str, object],
|
|
2287
|
+
) -> tuple[str, str, tuple[str, ...]] | None:
|
|
2288
|
+
header = _optional_string(pending.get("approval_header"))
|
|
2289
|
+
question = _optional_string(pending.get("approval_question"))
|
|
2290
|
+
approval_code = _optional_string(pending.get("approval_code"))
|
|
2291
|
+
options = _claude_guard_approval_options_from_value(pending.get("approval_options"))
|
|
2292
|
+
if approval_code is None:
|
|
2293
|
+
if header is None and question is None and not options:
|
|
2294
|
+
return (
|
|
2295
|
+
_CLAUDE_GUARD_APPROVAL_HEADER,
|
|
2296
|
+
"HOL Guard intercepted this sensitive action. What should Claude do?",
|
|
2297
|
+
_CLAUDE_GUARD_APPROVAL_OPTIONS,
|
|
2298
|
+
)
|
|
2299
|
+
if header is None or question is None or not options:
|
|
2300
|
+
return None
|
|
2301
|
+
expected_question = "HOL Guard intercepted this sensitive action. What should Claude do?"
|
|
2302
|
+
else:
|
|
2303
|
+
if header is None or question is None or not options:
|
|
2304
|
+
return None
|
|
2305
|
+
expected_question = _claude_guard_approval_question_text(approval_code)
|
|
2306
|
+
normalized_expected_options = tuple(
|
|
2307
|
+
_normalize_claude_guard_approval_text(option) for option in _CLAUDE_GUARD_APPROVAL_OPTIONS
|
|
2308
|
+
)
|
|
2309
|
+
normalized_pending_options = tuple(_normalize_claude_guard_approval_text(option) for option in options)
|
|
2310
|
+
if _normalize_claude_guard_approval_text(question) != _normalize_claude_guard_approval_text(expected_question):
|
|
2311
|
+
return None
|
|
2312
|
+
if normalized_pending_options != normalized_expected_options:
|
|
2313
|
+
return None
|
|
2314
|
+
return header, question, options
|
|
2315
|
+
|
|
2316
|
+
|
|
2317
|
+
def _claude_guard_prompt_contract_from_question_list(
|
|
2318
|
+
payload_section: object,
|
|
2319
|
+
) -> tuple[str, str, tuple[str, ...]] | None:
|
|
2320
|
+
if not isinstance(payload_section, dict):
|
|
2321
|
+
return None
|
|
2322
|
+
questions = payload_section.get("questions")
|
|
2323
|
+
if not isinstance(questions, list) or len(questions) != 1:
|
|
2324
|
+
return None
|
|
2325
|
+
first_question = questions[0]
|
|
2326
|
+
if not isinstance(first_question, dict):
|
|
2327
|
+
return None
|
|
2328
|
+
header = _optional_string(first_question.get("header"))
|
|
2329
|
+
question = _optional_string(first_question.get("question"))
|
|
2330
|
+
options = _claude_guard_approval_options_from_value(first_question.get("options"))
|
|
2331
|
+
if header is None or question is None or not options:
|
|
2332
|
+
return None
|
|
2333
|
+
return header, question, options
|
|
2334
|
+
|
|
2335
|
+
|
|
2336
|
+
def _claude_guard_prompt_contract_matches(
|
|
2337
|
+
expected_contract: tuple[str, str, tuple[str, ...]],
|
|
2338
|
+
actual_contract: tuple[str, str, tuple[str, ...]],
|
|
2339
|
+
) -> bool:
|
|
2340
|
+
expected_header, expected_question, expected_options = expected_contract
|
|
2341
|
+
actual_header, actual_question, actual_options = actual_contract
|
|
2342
|
+
if _normalize_claude_guard_approval_text(actual_header) != _normalize_claude_guard_approval_text(expected_header):
|
|
2343
|
+
return False
|
|
2344
|
+
if _normalize_claude_guard_approval_text(actual_question) != _normalize_claude_guard_approval_text(
|
|
2345
|
+
expected_question
|
|
2346
|
+
):
|
|
2347
|
+
return False
|
|
2348
|
+
expected_labels = tuple(_normalize_claude_guard_approval_text(option) for option in expected_options)
|
|
2349
|
+
actual_labels = tuple(_normalize_claude_guard_approval_text(option) for option in actual_options)
|
|
2350
|
+
return actual_labels == expected_labels
|
|
2351
|
+
|
|
2352
|
+
|
|
2353
|
+
def _is_claude_guard_approval_question(
|
|
2354
|
+
payload: dict[str, object],
|
|
2355
|
+
pending: dict[str, object],
|
|
2356
|
+
) -> bool:
|
|
2230
2357
|
if _hook_event_name(payload) != "PostToolUse":
|
|
2231
2358
|
return False
|
|
2232
2359
|
tool_name = _optional_string(payload.get("tool_name"))
|
|
2233
2360
|
if tool_name is None or tool_name.lower() != "askuserquestion":
|
|
2234
2361
|
return False
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2362
|
+
expected_contract = _claude_guard_prompt_contract_from_pending(pending)
|
|
2363
|
+
if expected_contract is None:
|
|
2364
|
+
return False
|
|
2365
|
+
tool_input_contract = _claude_guard_prompt_contract_from_question_list(payload.get("tool_input"))
|
|
2366
|
+
if tool_input_contract is None:
|
|
2367
|
+
return False
|
|
2368
|
+
if not _claude_guard_prompt_contract_matches(expected_contract, tool_input_contract):
|
|
2369
|
+
return False
|
|
2370
|
+
response_contract = _claude_guard_prompt_contract_from_question_list(payload.get("tool_response"))
|
|
2371
|
+
return response_contract is None or _claude_guard_prompt_contract_matches(expected_contract, response_contract)
|
|
2372
|
+
|
|
2373
|
+
|
|
2374
|
+
def _claude_guard_approval_action_for_answer(answer_text: str) -> str | None:
|
|
2375
|
+
normalized_answer = _normalize_claude_guard_approval_text(answer_text)
|
|
2376
|
+
if normalized_answer == _normalize_claude_guard_approval_text("Keep blocked"):
|
|
2377
|
+
return "block"
|
|
2378
|
+
if normalized_answer in {
|
|
2379
|
+
_normalize_claude_guard_approval_text("Allow once"),
|
|
2380
|
+
_normalize_claude_guard_approval_text("Allow during this session"),
|
|
2381
|
+
}:
|
|
2382
|
+
return "allow"
|
|
2383
|
+
return None
|
|
2246
2384
|
|
|
2247
2385
|
|
|
2248
|
-
def
|
|
2386
|
+
def _claude_guard_answer_text_from_value(value: object) -> str | None:
|
|
2387
|
+
if isinstance(value, str) and value.strip():
|
|
2388
|
+
return value
|
|
2389
|
+
if isinstance(value, dict):
|
|
2390
|
+
label = _optional_string(value.get("label"))
|
|
2391
|
+
if label is not None:
|
|
2392
|
+
return label
|
|
2393
|
+
return None
|
|
2394
|
+
|
|
2395
|
+
|
|
2396
|
+
def _claude_guard_approval_answer(payload: dict[str, object], *, expected_question: str | None = None) -> str | None:
|
|
2249
2397
|
response = payload.get("tool_response")
|
|
2250
2398
|
answer_text: str | None = None
|
|
2251
2399
|
if isinstance(response, dict):
|
|
2252
2400
|
answers = response.get("answers")
|
|
2253
2401
|
if isinstance(answers, dict):
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2402
|
+
normalized_expected_question = (
|
|
2403
|
+
_normalize_claude_guard_approval_text(expected_question) if isinstance(expected_question, str) else None
|
|
2404
|
+
)
|
|
2405
|
+
if normalized_expected_question is not None:
|
|
2406
|
+
for question, answer in answers.items():
|
|
2407
|
+
if not isinstance(question, str):
|
|
2408
|
+
continue
|
|
2409
|
+
if _normalize_claude_guard_approval_text(question) != normalized_expected_question:
|
|
2410
|
+
continue
|
|
2411
|
+
parsed_answer_text = _claude_guard_answer_text_from_value(answer)
|
|
2412
|
+
if parsed_answer_text is not None:
|
|
2413
|
+
answer_text = parsed_answer_text
|
|
2414
|
+
break
|
|
2415
|
+
if answer_text is None and len(answers) == 1:
|
|
2416
|
+
only_answer = next(iter(answers.values()))
|
|
2417
|
+
answer_text = _claude_guard_answer_text_from_value(only_answer)
|
|
2257
2418
|
if answer_text is None:
|
|
2258
2419
|
for key in ("answer", "selected_answer", "selected", "choice", "value", "label"):
|
|
2259
2420
|
value = response.get(key)
|
|
2260
|
-
|
|
2261
|
-
|
|
2421
|
+
parsed_answer_text = _claude_guard_answer_text_from_value(value)
|
|
2422
|
+
if parsed_answer_text is not None:
|
|
2423
|
+
answer_text = parsed_answer_text
|
|
2262
2424
|
break
|
|
2263
2425
|
if answer_text is None and "questions" not in response and "options" not in response:
|
|
2264
2426
|
content = response.get("content")
|
|
@@ -2268,24 +2430,25 @@ def _claude_guard_approval_answer(payload: dict[str, object]) -> str | None:
|
|
|
2268
2430
|
answer_text = response
|
|
2269
2431
|
if answer_text is None:
|
|
2270
2432
|
return None
|
|
2271
|
-
|
|
2272
|
-
if "keep blocked" in normalized_answer:
|
|
2273
|
-
return "block"
|
|
2274
|
-
if "allow during this session" in normalized_answer or "allow once" in normalized_answer:
|
|
2275
|
-
return "allow"
|
|
2276
|
-
return None
|
|
2433
|
+
return _claude_guard_approval_action_for_answer(answer_text)
|
|
2277
2434
|
|
|
2278
2435
|
|
|
2279
2436
|
def _persist_claude_guard_question_decision(store: GuardStore, payload: dict[str, object]) -> bool:
|
|
2280
|
-
if not _is_claude_guard_approval_question(payload):
|
|
2281
|
-
return False
|
|
2282
|
-
action = _claude_guard_approval_answer(payload)
|
|
2283
|
-
if action is None:
|
|
2284
|
-
return False
|
|
2285
2437
|
pending_pair = _load_single_claude_pending_permission(store, payload)
|
|
2286
2438
|
if pending_pair is None:
|
|
2287
2439
|
return False
|
|
2288
2440
|
pending_key, pending = pending_pair
|
|
2441
|
+
approval_code = _optional_string(pending.get("approval_code"))
|
|
2442
|
+
if approval_code is None and pending.get("permission_prompt_seen") is not True:
|
|
2443
|
+
return False
|
|
2444
|
+
if not _is_claude_guard_approval_question(payload, pending):
|
|
2445
|
+
return False
|
|
2446
|
+
action = _claude_guard_approval_answer(
|
|
2447
|
+
payload,
|
|
2448
|
+
expected_question=_optional_string(pending.get("approval_question")),
|
|
2449
|
+
)
|
|
2450
|
+
if action is None:
|
|
2451
|
+
return False
|
|
2289
2452
|
artifact_id = _optional_string(pending.get("artifact_id"))
|
|
2290
2453
|
artifact_hash_value = _optional_string(pending.get("artifact_hash"))
|
|
2291
2454
|
if artifact_id is None or artifact_hash_value is None:
|
|
@@ -2353,6 +2516,8 @@ def _claude_permission_prompt_system_message(
|
|
|
2353
2516
|
|
|
2354
2517
|
|
|
2355
2518
|
def _claude_permission_prompt_additional_context(notice: dict[str, object] | None) -> str:
|
|
2519
|
+
if notice is not None:
|
|
2520
|
+
return _claude_guard_approval_question_message(notice)
|
|
2356
2521
|
reason = _optional_string(notice.get("reason")) if notice is not None else None
|
|
2357
2522
|
if reason is not None:
|
|
2358
2523
|
return (
|
{plugin_scanner-2.0.66 → plugin_scanner-2.0.68}/src/codex_plugin_scanner/guard/cli/render.py
RENAMED
|
@@ -392,6 +392,27 @@ def _render_events(console: Console, payload: dict[str, object]) -> None:
|
|
|
392
392
|
|
|
393
393
|
|
|
394
394
|
def _render_approvals(console: Console, payload: dict[str, object]) -> None:
|
|
395
|
+
if "history_cleared" in payload or "cleared_policies" in payload:
|
|
396
|
+
error = payload.get("error")
|
|
397
|
+
body = Table.grid(padding=(0, 1))
|
|
398
|
+
body.add_row(
|
|
399
|
+
"Outcome",
|
|
400
|
+
str(error) if error else "approval history reset",
|
|
401
|
+
)
|
|
402
|
+
body.add_row("Harness", str(payload.get("harness") or "all harnesses"))
|
|
403
|
+
source = payload.get("source")
|
|
404
|
+
if source:
|
|
405
|
+
body.add_row("Source", str(source))
|
|
406
|
+
body.add_row("Policy decisions", str(int(payload.get("cleared_policies", 0) or 0)))
|
|
407
|
+
body.add_row("Resolved requests", str(int(payload.get("cleared_resolved_requests", 0) or 0)))
|
|
408
|
+
console.print(
|
|
409
|
+
Panel(
|
|
410
|
+
body,
|
|
411
|
+
title="Approval history",
|
|
412
|
+
border_style="red" if error else "green",
|
|
413
|
+
)
|
|
414
|
+
)
|
|
415
|
+
return
|
|
395
416
|
if payload.get("resolved"):
|
|
396
417
|
item = payload.get("item")
|
|
397
418
|
if isinstance(item, dict):
|
|
@@ -1526,6 +1526,22 @@ class GuardStore:
|
|
|
1526
1526
|
with self._connect() as connection:
|
|
1527
1527
|
return count_pending_approval_requests(connection, status=status)
|
|
1528
1528
|
|
|
1529
|
+
def clear_approval_requests(self, *, harness: str | None = None, status: str | None = None) -> int:
|
|
1530
|
+
conditions: list[str] = []
|
|
1531
|
+
params: list[object] = []
|
|
1532
|
+
if harness is not None:
|
|
1533
|
+
conditions.append("harness = ?")
|
|
1534
|
+
params.append(harness)
|
|
1535
|
+
if status is not None:
|
|
1536
|
+
conditions.append("status = ?")
|
|
1537
|
+
params.append(status)
|
|
1538
|
+
query = "delete from approval_requests"
|
|
1539
|
+
if conditions:
|
|
1540
|
+
query += " where " + " and ".join(conditions)
|
|
1541
|
+
with self._connect() as connection:
|
|
1542
|
+
cursor = connection.execute(query, tuple(params))
|
|
1543
|
+
return int(cursor.rowcount if cursor.rowcount is not None else 0)
|
|
1544
|
+
|
|
1529
1545
|
def list_policy_decisions(self, harness: str | None = None) -> list[dict[str, object]]:
|
|
1530
1546
|
query = """
|
|
1531
1547
|
select harness, scope, artifact_id, artifact_hash, workspace, publisher, action, reason, owner, source,
|