plugin-scanner 2.0.99__tar.gz → 2.0.101__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.99 → plugin_scanner-2.0.101}/PKG-INFO +1 -1
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/pyproject.toml +1 -1
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/approvals.py +8 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/commands.py +14 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/consumer/service.py +7 -1
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/models.py +1 -0
- plugin_scanner-2.0.101/src/codex_plugin_scanner/guard/policy/__init__.py +5 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/policy/engine.py +31 -0
- plugin_scanner-2.0.101/src/codex_plugin_scanner/guard/runtime/decisions.py +211 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/store.py +1 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/store_approvals.py +13 -9
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_approvals.py +29 -3
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime.py +48 -1
- plugin_scanner-2.0.101/tests/test_guard_runtime_decisions.py +129 -0
- plugin_scanner-2.0.99/src/codex_plugin_scanner/guard/policy/__init__.py +0 -5
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.dockerignore +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.gitignore +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/Dockerfile +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/requirements.txt +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime_action_harnesses.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime_actions.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_runtime_signals.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/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.101
|
|
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.101"
|
|
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.101"
|
|
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"
|
{plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/approvals.py
RENAMED
|
@@ -88,6 +88,7 @@ def queue_blocked_approvals(
|
|
|
88
88
|
launch_summary=incident["launch_summary"],
|
|
89
89
|
risk_headline=incident["risk_headline"],
|
|
90
90
|
action_envelope_json=_item_action_envelope_json(item),
|
|
91
|
+
decision_v2_json=_item_decision_v2_json(item),
|
|
91
92
|
)
|
|
92
93
|
persisted_request_id = store.add_approval_request(request, timestamp)
|
|
93
94
|
if persisted_request_id != request.request_id:
|
|
@@ -342,6 +343,13 @@ def _item_action_envelope_json(item: dict[str, object]) -> dict[str, object] | N
|
|
|
342
343
|
return {str(key): item_value for key, item_value in value.items() if isinstance(key, str)}
|
|
343
344
|
|
|
344
345
|
|
|
346
|
+
def _item_decision_v2_json(item: dict[str, object]) -> dict[str, object] | None:
|
|
347
|
+
value = item.get("decision_v2_json")
|
|
348
|
+
if not isinstance(value, Mapping):
|
|
349
|
+
return None
|
|
350
|
+
return {str(key): item_value for key, item_value in value.items() if isinstance(key, str)}
|
|
351
|
+
|
|
352
|
+
|
|
345
353
|
def _string_list(value: object) -> list[str]:
|
|
346
354
|
if not isinstance(value, list):
|
|
347
355
|
return []
|
{plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -15,6 +15,7 @@ import sys
|
|
|
15
15
|
import urllib.error
|
|
16
16
|
import urllib.parse
|
|
17
17
|
import webbrowser
|
|
18
|
+
from collections.abc import Mapping
|
|
18
19
|
from contextlib import suppress
|
|
19
20
|
from dataclasses import replace
|
|
20
21
|
from datetime import datetime, timedelta, timezone
|
|
@@ -3130,6 +3131,9 @@ def _native_prompt_context(artifact: GuardArtifact) -> str:
|
|
|
3130
3131
|
|
|
3131
3132
|
|
|
3132
3133
|
def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: dict[str, object]) -> str:
|
|
3134
|
+
decision_message = _decision_v2_harness_message(response_payload)
|
|
3135
|
+
if decision_message is not None:
|
|
3136
|
+
return decision_message
|
|
3133
3137
|
if artifact.artifact_type == "prompt_request":
|
|
3134
3138
|
harness = response_payload.get("harness")
|
|
3135
3139
|
prompt_classes = _prompt_request_classes(artifact)
|
|
@@ -3173,6 +3177,16 @@ def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: d
|
|
|
3173
3177
|
return "HOL Guard flagged this request for review."
|
|
3174
3178
|
|
|
3175
3179
|
|
|
3180
|
+
def _decision_v2_harness_message(response_payload: dict[str, object]) -> str | None:
|
|
3181
|
+
decision_v2 = response_payload.get("decision_v2_json")
|
|
3182
|
+
if not isinstance(decision_v2, Mapping):
|
|
3183
|
+
return None
|
|
3184
|
+
message = decision_v2.get("harness_message")
|
|
3185
|
+
if isinstance(message, str) and message.strip():
|
|
3186
|
+
return message.strip()
|
|
3187
|
+
return None
|
|
3188
|
+
|
|
3189
|
+
|
|
3176
3190
|
def _claude_prompt_additional_context(
|
|
3177
3191
|
*,
|
|
3178
3192
|
harness: str,
|
{plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/consumer/service.py
RENAMED
|
@@ -15,9 +15,10 @@ from ..capabilities import compute_capability_delta, normalize_artifact_capabili
|
|
|
15
15
|
from ..config import GuardConfig
|
|
16
16
|
from ..incident import build_incident_context
|
|
17
17
|
from ..models import GuardArtifact, HarnessDetection, PolicyDecision
|
|
18
|
-
from ..policy import decide_action
|
|
18
|
+
from ..policy import build_decision_v2, decide_action
|
|
19
19
|
from ..receipts import build_receipt
|
|
20
20
|
from ..risk import artifact_risk_signals_typed, artifact_risk_summary, summarize_signals
|
|
21
|
+
from ..runtime.signals import RiskSignalV2
|
|
21
22
|
from ..schemas import build_consumer_mode_contract
|
|
22
23
|
from ..store import GuardStore
|
|
23
24
|
from ..types import (
|
|
@@ -384,6 +385,7 @@ def evaluate_detection(
|
|
|
384
385
|
current_capabilities = normalize_artifact_capabilities(artifact)
|
|
385
386
|
capability_delta = compute_capability_delta(previous_capabilities, current_capabilities)
|
|
386
387
|
structured_signals = artifact_risk_signals_typed(artifact)
|
|
388
|
+
risk_signals_v2 = tuple(RiskSignalV2.from_guard_signal(signal) for signal in structured_signals)
|
|
387
389
|
history_context = build_history_context(store, detection.harness, artifact.artifact_id, artifact.publisher)
|
|
388
390
|
provenance_bundle = build_provenance_bundle(store, artifact.publisher)
|
|
389
391
|
verdict = score_verdict(structured_signals, capability_delta, provenance_bundle, history_context)
|
|
@@ -405,6 +407,7 @@ def evaluate_detection(
|
|
|
405
407
|
)
|
|
406
408
|
if _is_blocking_action(policy_action):
|
|
407
409
|
blocked = True
|
|
410
|
+
decision_v2 = build_decision_v2(policy_action, reason=policy_action, signals=risk_signals_v2)
|
|
408
411
|
risk_signals = tuple(signal.explanation for signal in structured_signals)
|
|
409
412
|
risk_summary = artifact_risk_summary(artifact) if structured_signals else summarize_signals(())
|
|
410
413
|
changed_capabilities = [delta.delta_type for delta in capability_delta] or list(diff["changed_fields"])
|
|
@@ -495,6 +498,7 @@ def evaluate_detection(
|
|
|
495
498
|
"changed": diff["changed"],
|
|
496
499
|
"changed_fields": diff["changed_fields"],
|
|
497
500
|
"policy_action": policy_action,
|
|
501
|
+
"decision_v2_json": decision_v2.to_dict(),
|
|
498
502
|
"artifact_hash": diff["current_hash"],
|
|
499
503
|
"risk_signals": list(risk_signals),
|
|
500
504
|
"risk_summary": risk_summary,
|
|
@@ -536,6 +540,7 @@ def evaluate_detection(
|
|
|
536
540
|
)
|
|
537
541
|
if _is_blocking_action(policy_action):
|
|
538
542
|
blocked = True
|
|
543
|
+
decision_v2 = build_decision_v2(policy_action, reason=policy_action)
|
|
539
544
|
artifact_name = previous.get("name")
|
|
540
545
|
source_scope = previous.get("source_scope")
|
|
541
546
|
config_path = previous.get("config_path")
|
|
@@ -605,6 +610,7 @@ def evaluate_detection(
|
|
|
605
610
|
"changed": True,
|
|
606
611
|
"changed_fields": ["removed"],
|
|
607
612
|
"policy_action": policy_action,
|
|
613
|
+
"decision_v2_json": decision_v2.to_dict(),
|
|
608
614
|
"artifact_hash": previous_hash,
|
|
609
615
|
"removed": True,
|
|
610
616
|
"risk_signals": ["artifact removed from local harness configuration"],
|
|
@@ -179,6 +179,7 @@ class GuardApprovalRequest:
|
|
|
179
179
|
launch_summary: str | None = None
|
|
180
180
|
risk_headline: str | None = None
|
|
181
181
|
action_envelope_json: dict[str, object] | None = None
|
|
182
|
+
decision_v2_json: dict[str, object] | None = None
|
|
182
183
|
|
|
183
184
|
def to_dict(self) -> dict[str, object]:
|
|
184
185
|
payload = asdict(self)
|
{plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/policy/engine.py
RENAMED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
|
|
5
7
|
from ..config import GuardConfig
|
|
6
8
|
from ..models import GuardAction
|
|
9
|
+
from ..runtime.decisions import GuardDecisionV2, decision_from_legacy_policy_action
|
|
10
|
+
from ..runtime.signals import RiskSignalV2
|
|
7
11
|
|
|
8
12
|
VALID_GUARD_ACTIONS = {"allow", "warn", "review", "block", "sandbox-required", "require-reapproval"}
|
|
9
13
|
SAFE_CHANGED_HASH_ACTION: GuardAction = "require-reapproval"
|
|
@@ -29,3 +33,30 @@ def decide_action(
|
|
|
29
33
|
if config.default_action in VALID_GUARD_ACTIONS:
|
|
30
34
|
return config.default_action
|
|
31
35
|
return SAFE_DEFAULT_ACTION
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def build_decision_v2(
|
|
39
|
+
policy_action: GuardAction,
|
|
40
|
+
*,
|
|
41
|
+
reason: str,
|
|
42
|
+
signals: Sequence[RiskSignalV2] = (),
|
|
43
|
+
) -> GuardDecisionV2:
|
|
44
|
+
return decision_from_legacy_policy_action(policy_action, reason=reason, signals=signals)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def decide_action_with_v2(
|
|
48
|
+
configured_action: str | None,
|
|
49
|
+
default_action: str | None,
|
|
50
|
+
config: GuardConfig,
|
|
51
|
+
changed: bool,
|
|
52
|
+
*,
|
|
53
|
+
reason: str,
|
|
54
|
+
signals: Sequence[RiskSignalV2] = (),
|
|
55
|
+
) -> tuple[GuardAction, GuardDecisionV2]:
|
|
56
|
+
action = decide_action(
|
|
57
|
+
configured_action=configured_action,
|
|
58
|
+
default_action=default_action,
|
|
59
|
+
config=config,
|
|
60
|
+
changed=changed,
|
|
61
|
+
)
|
|
62
|
+
return action, build_decision_v2(action, reason=reason, signals=signals)
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Typed runtime decisions for Guard pause and approval UX."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping, Sequence
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
from codex_plugin_scanner.guard.models import GuardAction
|
|
10
|
+
from codex_plugin_scanner.guard.runtime.signals import (
|
|
11
|
+
RiskConfidenceLabel,
|
|
12
|
+
RiskSignalV2,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
GuardDecisionAction = Literal["allow", "warn", "ask", "block"]
|
|
16
|
+
|
|
17
|
+
_ACTION_MESSAGES: dict[GuardAction, tuple[GuardDecisionAction, str, str, str]] = {
|
|
18
|
+
"allow": (
|
|
19
|
+
"allow",
|
|
20
|
+
"Allowed by policy",
|
|
21
|
+
"Policy allows this action.",
|
|
22
|
+
"HOL Guard allowed this action because policy already trusts it.",
|
|
23
|
+
),
|
|
24
|
+
"warn": (
|
|
25
|
+
"warn",
|
|
26
|
+
"Risk signals found",
|
|
27
|
+
"HOL Guard noticed risk signals, but policy allows the harness to continue.",
|
|
28
|
+
"Review the warning if this action was unexpected.",
|
|
29
|
+
),
|
|
30
|
+
"review": (
|
|
31
|
+
"ask",
|
|
32
|
+
"Approval required",
|
|
33
|
+
"HOL Guard needs your approval before this action can run.",
|
|
34
|
+
"Choose an approval scope, then retry in the harness.",
|
|
35
|
+
),
|
|
36
|
+
"sandbox-required": (
|
|
37
|
+
"ask",
|
|
38
|
+
"Sandbox review required",
|
|
39
|
+
"HOL Guard wants this action reviewed and run in a sandboxed path.",
|
|
40
|
+
"Run it in a sandbox or choose a scoped approval before retrying.",
|
|
41
|
+
),
|
|
42
|
+
"require-reapproval": (
|
|
43
|
+
"ask",
|
|
44
|
+
"Fresh approval required",
|
|
45
|
+
"HOL Guard needs a fresh approval because this action changed.",
|
|
46
|
+
"Choose the smallest approval scope that matches your intent, then retry.",
|
|
47
|
+
),
|
|
48
|
+
"block": (
|
|
49
|
+
"block",
|
|
50
|
+
"Blocked by policy",
|
|
51
|
+
"HOL Guard blocked this action.",
|
|
52
|
+
"Review the details before changing policy or retrying.",
|
|
53
|
+
),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True, slots=True)
|
|
58
|
+
class GuardDecisionV2:
|
|
59
|
+
"""Product-facing Guard decision with harness and dashboard copy."""
|
|
60
|
+
|
|
61
|
+
action: GuardDecisionAction
|
|
62
|
+
reason: str
|
|
63
|
+
user_title: str
|
|
64
|
+
user_body: str
|
|
65
|
+
harness_message: str
|
|
66
|
+
dashboard_primary_detail: str
|
|
67
|
+
approval_scopes: tuple[str, ...]
|
|
68
|
+
retry_instruction: str | None
|
|
69
|
+
signals: tuple[RiskSignalV2, ...]
|
|
70
|
+
confidence: RiskConfidenceLabel
|
|
71
|
+
|
|
72
|
+
def to_dict(self) -> dict[str, object]:
|
|
73
|
+
return {
|
|
74
|
+
"action": self.action,
|
|
75
|
+
"reason": self.reason,
|
|
76
|
+
"user_title": self.user_title,
|
|
77
|
+
"user_body": self.user_body,
|
|
78
|
+
"harness_message": self.harness_message,
|
|
79
|
+
"dashboard_primary_detail": self.dashboard_primary_detail,
|
|
80
|
+
"approval_scopes": list(self.approval_scopes),
|
|
81
|
+
"retry_instruction": self.retry_instruction,
|
|
82
|
+
"signals": [signal.to_dict() for signal in self.signals],
|
|
83
|
+
"confidence": self.confidence,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def from_dict(cls, payload: Mapping[str, object]) -> GuardDecisionV2:
|
|
88
|
+
return cls(
|
|
89
|
+
action=_parse_action(payload.get("action")),
|
|
90
|
+
reason=_required_string(payload, "reason"),
|
|
91
|
+
user_title=_required_string(payload, "user_title"),
|
|
92
|
+
user_body=_required_string(payload, "user_body"),
|
|
93
|
+
harness_message=_required_string(payload, "harness_message"),
|
|
94
|
+
dashboard_primary_detail=_required_string(payload, "dashboard_primary_detail"),
|
|
95
|
+
approval_scopes=_parse_string_tuple(payload.get("approval_scopes"), "approval_scopes"),
|
|
96
|
+
retry_instruction=_optional_string(payload, "retry_instruction"),
|
|
97
|
+
signals=_parse_signals(payload.get("signals")),
|
|
98
|
+
confidence=_parse_confidence(payload.get("confidence")),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def decision_from_legacy_policy_action(
|
|
103
|
+
policy_action: GuardAction,
|
|
104
|
+
*,
|
|
105
|
+
reason: str,
|
|
106
|
+
signals: Sequence[RiskSignalV2] = (),
|
|
107
|
+
) -> GuardDecisionV2:
|
|
108
|
+
action, user_title, harness_message, retry_instruction = _ACTION_MESSAGES[policy_action]
|
|
109
|
+
signal_tuple = tuple(signals)
|
|
110
|
+
confidence = _highest_confidence(signal_tuple)
|
|
111
|
+
dashboard_detail = _dashboard_detail_from_signals(signal_tuple, harness_message)
|
|
112
|
+
return GuardDecisionV2(
|
|
113
|
+
action=action,
|
|
114
|
+
reason=reason,
|
|
115
|
+
user_title=user_title,
|
|
116
|
+
user_body=dashboard_detail,
|
|
117
|
+
harness_message=harness_message,
|
|
118
|
+
dashboard_primary_detail=dashboard_detail,
|
|
119
|
+
approval_scopes=_approval_scopes_for_action(action),
|
|
120
|
+
retry_instruction=None if action in {"allow", "warn"} else retry_instruction,
|
|
121
|
+
signals=signal_tuple,
|
|
122
|
+
confidence=confidence,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _approval_scopes_for_action(action: GuardDecisionAction) -> tuple[str, ...]:
|
|
127
|
+
if action != "ask":
|
|
128
|
+
return ()
|
|
129
|
+
return ("artifact", "workspace", "publisher", "harness")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _dashboard_detail_from_signals(signals: tuple[RiskSignalV2, ...], fallback: str) -> str:
|
|
133
|
+
if not signals:
|
|
134
|
+
return fallback
|
|
135
|
+
strongest = max(signals, key=lambda item: _confidence_rank(item.confidence))
|
|
136
|
+
return strongest.plain_reason
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _highest_confidence(signals: tuple[RiskSignalV2, ...]) -> RiskConfidenceLabel:
|
|
140
|
+
if not signals:
|
|
141
|
+
return "likely"
|
|
142
|
+
return max((signal.confidence for signal in signals), key=_confidence_rank)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _confidence_rank(confidence: RiskConfidenceLabel) -> int:
|
|
146
|
+
match confidence:
|
|
147
|
+
case "strong":
|
|
148
|
+
return 3
|
|
149
|
+
case "likely":
|
|
150
|
+
return 2
|
|
151
|
+
case "weak":
|
|
152
|
+
return 1
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _parse_action(value: object) -> GuardDecisionAction:
|
|
156
|
+
match value:
|
|
157
|
+
case "allow":
|
|
158
|
+
return "allow"
|
|
159
|
+
case "warn":
|
|
160
|
+
return "warn"
|
|
161
|
+
case "ask":
|
|
162
|
+
return "ask"
|
|
163
|
+
case "block":
|
|
164
|
+
return "block"
|
|
165
|
+
case _:
|
|
166
|
+
raise ValueError("action must be a known Guard decision action")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _parse_confidence(value: object) -> RiskConfidenceLabel:
|
|
170
|
+
match value:
|
|
171
|
+
case "weak":
|
|
172
|
+
return "weak"
|
|
173
|
+
case "likely":
|
|
174
|
+
return "likely"
|
|
175
|
+
case "strong":
|
|
176
|
+
return "strong"
|
|
177
|
+
case _:
|
|
178
|
+
raise ValueError("confidence must be a known confidence label")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _parse_signals(value: object) -> tuple[RiskSignalV2, ...]:
|
|
182
|
+
if not isinstance(value, list):
|
|
183
|
+
raise ValueError("signals must be a list")
|
|
184
|
+
signals: list[RiskSignalV2] = []
|
|
185
|
+
for item in value:
|
|
186
|
+
if not isinstance(item, Mapping):
|
|
187
|
+
raise ValueError(f"signal item must be an object, got {type(item).__name__}")
|
|
188
|
+
signals.append(RiskSignalV2.from_dict(item))
|
|
189
|
+
return tuple(signals)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _parse_string_tuple(value: object, key: str) -> tuple[str, ...]:
|
|
193
|
+
if not isinstance(value, list) or not all(isinstance(item, str) and item.strip() for item in value):
|
|
194
|
+
raise ValueError(f"{key} must be a list of non-empty strings")
|
|
195
|
+
return tuple(value)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _required_string(payload: Mapping[str, object], key: str) -> str:
|
|
199
|
+
value = payload.get(key)
|
|
200
|
+
if not isinstance(value, str) or not value.strip():
|
|
201
|
+
raise ValueError(f"{key} must be a non-empty string")
|
|
202
|
+
return value
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _optional_string(payload: Mapping[str, object], key: str) -> str | None:
|
|
206
|
+
value = payload.get(key)
|
|
207
|
+
if value is None:
|
|
208
|
+
return None
|
|
209
|
+
if not isinstance(value, str):
|
|
210
|
+
raise ValueError(f"{key} must be a string or null")
|
|
211
|
+
return value
|
|
@@ -667,6 +667,7 @@ class GuardStore:
|
|
|
667
667
|
self._ensure_approval_column(connection, "launch_summary", "text")
|
|
668
668
|
self._ensure_approval_column(connection, "risk_headline", "text")
|
|
669
669
|
self._ensure_approval_column(connection, "action_envelope_json", "text")
|
|
670
|
+
self._ensure_approval_column(connection, "decision_v2_json", "text")
|
|
670
671
|
self._ensure_approval_column(connection, "workspace", "text")
|
|
671
672
|
self._ensure_attachment_column(connection, "lease_id", "text not null default ''")
|
|
672
673
|
self._ensure_attachment_column(connection, "lease_expires_at", "text")
|
{plugin_scanner-2.0.99 → plugin_scanner-2.0.101}/src/codex_plugin_scanner/guard/store_approvals.py
RENAMED
|
@@ -34,8 +34,9 @@ def approval_schema_statement() -> str:
|
|
|
34
34
|
why_now text,
|
|
35
35
|
launch_summary text,
|
|
36
36
|
risk_headline text,
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
action_envelope_json text,
|
|
38
|
+
decision_v2_json text,
|
|
39
|
+
review_command text not null,
|
|
39
40
|
approval_url text not null,
|
|
40
41
|
status text not null,
|
|
41
42
|
resolution_action text,
|
|
@@ -69,7 +70,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
69
70
|
recommended_scope = ?, changed_fields_json = ?, source_scope = ?, config_path = ?, workspace = ?,
|
|
70
71
|
launch_target = ?, transport = ?, risk_summary = ?, risk_signals_json = ?,
|
|
71
72
|
artifact_label = ?, source_label = ?, trigger_summary = ?, why_now = ?, launch_summary = ?,
|
|
72
|
-
risk_headline = ?, action_envelope_json = ?,
|
|
73
|
+
risk_headline = ?, action_envelope_json = ?, decision_v2_json = ?,
|
|
73
74
|
review_command = ?, approval_url = ?, created_at = ?
|
|
74
75
|
where request_id = ?
|
|
75
76
|
""",
|
|
@@ -95,6 +96,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
95
96
|
request.launch_summary,
|
|
96
97
|
request.risk_headline,
|
|
97
98
|
json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
|
|
99
|
+
json.dumps(request.decision_v2_json) if request.decision_v2_json is not None else None,
|
|
98
100
|
review_command,
|
|
99
101
|
approval_url,
|
|
100
102
|
now,
|
|
@@ -109,10 +111,10 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
109
111
|
recommended_scope, changed_fields_json, source_scope, config_path, workspace,
|
|
110
112
|
launch_target, transport, risk_summary,
|
|
111
113
|
risk_signals_json, artifact_label, source_label, trigger_summary, why_now, launch_summary, risk_headline,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
action_envelope_json, decision_v2_json, review_command,
|
|
115
|
+
approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
|
|
116
|
+
)
|
|
117
|
+
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
116
118
|
""",
|
|
117
119
|
(
|
|
118
120
|
request.request_id,
|
|
@@ -139,6 +141,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
139
141
|
request.launch_summary,
|
|
140
142
|
request.risk_headline,
|
|
141
143
|
json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
|
|
144
|
+
json.dumps(request.decision_v2_json) if request.decision_v2_json is not None else None,
|
|
142
145
|
request.review_command,
|
|
143
146
|
request.approval_url,
|
|
144
147
|
"pending",
|
|
@@ -186,7 +189,7 @@ def list_approval_requests(
|
|
|
186
189
|
select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
|
|
187
190
|
recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
|
|
188
191
|
risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
|
|
189
|
-
launch_summary, risk_headline, action_envelope_json, review_command,
|
|
192
|
+
launch_summary, risk_headline, action_envelope_json, decision_v2_json, review_command,
|
|
190
193
|
approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
|
|
191
194
|
from approval_requests
|
|
192
195
|
{where_clause}
|
|
@@ -205,7 +208,7 @@ def get_approval_request(connection: sqlite3.Connection, request_id: str) -> dic
|
|
|
205
208
|
select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
|
|
206
209
|
recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
|
|
207
210
|
risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
|
|
208
|
-
launch_summary, risk_headline, action_envelope_json, review_command,
|
|
211
|
+
launch_summary, risk_headline, action_envelope_json, decision_v2_json, review_command,
|
|
209
212
|
approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
|
|
210
213
|
from approval_requests
|
|
211
214
|
where request_id = ?
|
|
@@ -277,6 +280,7 @@ def _row_to_payload(row: sqlite3.Row) -> dict[str, object]:
|
|
|
277
280
|
"launch_summary": row["launch_summary"],
|
|
278
281
|
"risk_headline": row["risk_headline"],
|
|
279
282
|
"action_envelope_json": _optional_json_object(row["action_envelope_json"]),
|
|
283
|
+
"decision_v2_json": _optional_json_object(row["decision_v2_json"]),
|
|
280
284
|
"review_command": str(row["review_command"]),
|
|
281
285
|
"approval_url": str(row["approval_url"]),
|
|
282
286
|
"status": str(row["status"]),
|
|
@@ -90,6 +90,18 @@ class TestGuardApprovals:
|
|
|
90
90
|
"script_name": None,
|
|
91
91
|
"raw_payload_redacted": {"tool_name": "Bash"},
|
|
92
92
|
}
|
|
93
|
+
decision_v2_json = {
|
|
94
|
+
"action": "ask",
|
|
95
|
+
"reason": "require-reapproval",
|
|
96
|
+
"user_title": "Review workspace_skill",
|
|
97
|
+
"user_body": "HOL Guard needs approval before this action continues.",
|
|
98
|
+
"harness_message": "HOL Guard paused this action for approval.",
|
|
99
|
+
"dashboard_primary_detail": "Shell command can read a local secret file.",
|
|
100
|
+
"approval_scopes": ["artifact", "workspace"],
|
|
101
|
+
"retry_instruction": "Approve it in HOL Guard, then retry.",
|
|
102
|
+
"signals": [],
|
|
103
|
+
"confidence": "likely",
|
|
104
|
+
}
|
|
93
105
|
request = GuardApprovalRequest(
|
|
94
106
|
request_id="req-123",
|
|
95
107
|
harness="codex",
|
|
@@ -105,6 +117,7 @@ class TestGuardApprovals:
|
|
|
105
117
|
review_command="hol-guard approvals approve req-123",
|
|
106
118
|
approval_url="http://127.0.0.1:4455/approvals/req-123",
|
|
107
119
|
action_envelope_json=action_envelope_json,
|
|
120
|
+
decision_v2_json=decision_v2_json,
|
|
108
121
|
)
|
|
109
122
|
|
|
110
123
|
store.add_approval_request(request, "2026-04-11T00:00:00+00:00")
|
|
@@ -122,11 +135,13 @@ class TestGuardApprovals:
|
|
|
122
135
|
assert pending[0]["approval_url"] == "http://127.0.0.1:4455/approvals/req-123"
|
|
123
136
|
assert pending[0]["workspace"] == str(workspace_dir)
|
|
124
137
|
assert pending[0]["action_envelope_json"] == action_envelope_json
|
|
138
|
+
assert pending[0]["decision_v2_json"] == decision_v2_json
|
|
125
139
|
assert resolved is not None
|
|
126
140
|
assert resolved["status"] == "resolved"
|
|
127
141
|
assert resolved["resolution_action"] == "allow"
|
|
128
142
|
assert resolved["resolution_scope"] == "artifact"
|
|
129
143
|
assert resolved["action_envelope_json"] == action_envelope_json
|
|
144
|
+
assert resolved["decision_v2_json"] == decision_v2_json
|
|
130
145
|
|
|
131
146
|
def test_guard_store_loads_old_approval_rows_without_action_envelope(self, tmp_path):
|
|
132
147
|
guard_home = tmp_path / "guard-home"
|
|
@@ -225,8 +240,9 @@ class TestGuardApprovals:
|
|
|
225
240
|
assert request is not None
|
|
226
241
|
assert request["request_id"] == "req-old"
|
|
227
242
|
assert request["action_envelope_json"] is None
|
|
243
|
+
assert request["decision_v2_json"] is None
|
|
228
244
|
|
|
229
|
-
def
|
|
245
|
+
def test_guard_store_ignores_malformed_action_and_decision_json(self, tmp_path):
|
|
230
246
|
store = GuardStore(tmp_path / "guard-home")
|
|
231
247
|
connection = sqlite3.connect(store.path)
|
|
232
248
|
try:
|
|
@@ -236,10 +252,13 @@ class TestGuardApprovals:
|
|
|
236
252
|
request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher,
|
|
237
253
|
policy_action, recommended_scope, changed_fields_json, source_scope, config_path, workspace,
|
|
238
254
|
launch_target, transport, risk_summary, risk_signals_json, artifact_label, source_label,
|
|
239
|
-
trigger_summary, why_now, launch_summary, risk_headline, action_envelope_json,
|
|
255
|
+
trigger_summary, why_now, launch_summary, risk_headline, action_envelope_json, decision_v2_json,
|
|
256
|
+
review_command,
|
|
240
257
|
approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
|
|
241
258
|
)
|
|
242
|
-
values (
|
|
259
|
+
values (
|
|
260
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
261
|
+
)
|
|
243
262
|
""",
|
|
244
263
|
(
|
|
245
264
|
"req-bad-envelope",
|
|
@@ -266,6 +285,7 @@ class TestGuardApprovals:
|
|
|
266
285
|
None,
|
|
267
286
|
None,
|
|
268
287
|
"{not-json",
|
|
288
|
+
"{also-not-json",
|
|
269
289
|
"hol-guard approvals approve req-bad-envelope",
|
|
270
290
|
"http://127.0.0.1/pending",
|
|
271
291
|
"pending",
|
|
@@ -284,6 +304,7 @@ class TestGuardApprovals:
|
|
|
284
304
|
|
|
285
305
|
assert request is not None
|
|
286
306
|
assert request["action_envelope_json"] is None
|
|
307
|
+
assert request["decision_v2_json"] is None
|
|
287
308
|
|
|
288
309
|
def test_guard_surface_daemon_client_recovers_missing_auth_token(self, tmp_path, monkeypatch):
|
|
289
310
|
guard_home = tmp_path / "guard-home"
|
|
@@ -1960,6 +1981,11 @@ class TestGuardApprovals:
|
|
|
1960
1981
|
assert output["approval_center_url"].startswith("http://127.0.0.1:")
|
|
1961
1982
|
assert approvals[0]["harness"] == "codex"
|
|
1962
1983
|
assert approvals[0]["status"] == "pending"
|
|
1984
|
+
assert approvals[0]["decision_v2_json"]["action"] == "ask"
|
|
1985
|
+
assert (
|
|
1986
|
+
approvals[0]["decision_v2_json"]["harness_message"]
|
|
1987
|
+
== "HOL Guard needs a fresh approval because this action changed."
|
|
1988
|
+
)
|
|
1963
1989
|
|
|
1964
1990
|
def test_guard_approvals_cli_lists_and_resolves_requests(self, tmp_path, capsys):
|
|
1965
1991
|
home_dir = tmp_path / "home"
|