plugin-scanner 2.0.95__tar.gz → 2.0.97__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.95 → plugin_scanner-2.0.97}/PKG-INFO +1 -1
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/pyproject.toml +1 -1
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/approvals.py +9 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/commands.py +35 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/models.py +2 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/actions.py +215 -14
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/runner.py +63 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/store.py +1 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/store_approvals.py +35 -19
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_approvals.py +181 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_runtime.py +4 -0
- plugin_scanner-2.0.97/tests/test_guard_runtime_action_harnesses.py +316 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.dockerignore +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.gitignore +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/Dockerfile +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/requirements.txt +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_runtime_actions.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/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.97
|
|
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.97"
|
|
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.97"
|
|
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"
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import time
|
|
6
6
|
import uuid
|
|
7
|
+
from collections.abc import Mapping
|
|
7
8
|
from dataclasses import replace
|
|
8
9
|
from datetime import datetime, timezone
|
|
9
10
|
from pathlib import Path
|
|
@@ -86,6 +87,7 @@ def queue_blocked_approvals(
|
|
|
86
87
|
why_now=incident["why_now"],
|
|
87
88
|
launch_summary=incident["launch_summary"],
|
|
88
89
|
risk_headline=incident["risk_headline"],
|
|
90
|
+
action_envelope_json=_item_action_envelope_json(item),
|
|
89
91
|
)
|
|
90
92
|
persisted_request_id = store.add_approval_request(request, timestamp)
|
|
91
93
|
if persisted_request_id != request.request_id:
|
|
@@ -333,6 +335,13 @@ def _item_risk_signals(item: dict[str, object], artifact) -> tuple[str, ...]:
|
|
|
333
335
|
return artifact_risk_signals(artifact) if artifact is not None else ()
|
|
334
336
|
|
|
335
337
|
|
|
338
|
+
def _item_action_envelope_json(item: dict[str, object]) -> dict[str, object] | None:
|
|
339
|
+
value = item.get("action_envelope_json")
|
|
340
|
+
if not isinstance(value, Mapping):
|
|
341
|
+
return None
|
|
342
|
+
return {str(key): item_value for key, item_value in value.items() if isinstance(key, str)}
|
|
343
|
+
|
|
344
|
+
|
|
336
345
|
def _string_list(value: object) -> list[str]:
|
|
337
346
|
if not isinstance(value, list):
|
|
338
347
|
return []
|
{plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -80,6 +80,7 @@ from ..proxy import (
|
|
|
80
80
|
)
|
|
81
81
|
from ..receipts import build_receipt
|
|
82
82
|
from ..risk import artifact_risk_signals, artifact_risk_summary
|
|
83
|
+
from ..runtime.actions import GuardActionEnvelope, normalize_harness_payload
|
|
83
84
|
from ..runtime.runner import (
|
|
84
85
|
GuardSyncNotConfiguredError,
|
|
85
86
|
extract_prompt_requests,
|
|
@@ -1206,6 +1207,12 @@ def run_guard_command(
|
|
|
1206
1207
|
runtime_workspace = current_workspace
|
|
1207
1208
|
if args.harness == "copilot":
|
|
1208
1209
|
runtime_workspace = _resolve_copilot_workspace_root(runtime_workspace)
|
|
1210
|
+
action_envelope = _hook_action_envelope(
|
|
1211
|
+
harness=args.harness,
|
|
1212
|
+
payload=payload,
|
|
1213
|
+
home_dir=context.home_dir,
|
|
1214
|
+
workspace=runtime_workspace,
|
|
1215
|
+
)
|
|
1209
1216
|
copilot_hook_stage = _copilot_hook_stage(payload) if args.harness == "copilot" else None
|
|
1210
1217
|
copilot_runtime_tool_call = (
|
|
1211
1218
|
_copilot_runtime_tool_call(
|
|
@@ -1309,6 +1316,7 @@ def run_guard_command(
|
|
|
1309
1316
|
"launch_target": json.dumps(runtime_arguments, sort_keys=True)
|
|
1310
1317
|
if runtime_arguments is not None
|
|
1311
1318
|
else runtime_artifact.command,
|
|
1319
|
+
"action_envelope_json": _action_envelope_json(action_envelope),
|
|
1312
1320
|
}
|
|
1313
1321
|
]
|
|
1314
1322
|
}
|
|
@@ -1667,6 +1675,7 @@ def run_guard_command(
|
|
|
1667
1675
|
"source_scope": runtime_artifact.source_scope,
|
|
1668
1676
|
"config_path": runtime_artifact.config_path,
|
|
1669
1677
|
"launch_target": _runtime_request_summary(runtime_artifact),
|
|
1678
|
+
"action_envelope_json": _action_envelope_json(action_envelope),
|
|
1670
1679
|
}
|
|
1671
1680
|
]
|
|
1672
1681
|
}
|
|
@@ -3619,6 +3628,32 @@ def _load_hook_payload(event_file: str | None, *, input_text: str | None = None)
|
|
|
3619
3628
|
return _normalize_hook_payload(payload) if isinstance(payload, dict) else {}
|
|
3620
3629
|
|
|
3621
3630
|
|
|
3631
|
+
_ACTION_ENVELOPE_HARNESSES = frozenset({"codex", "claude-code", "opencode", "copilot", "gemini"})
|
|
3632
|
+
|
|
3633
|
+
|
|
3634
|
+
def _hook_action_envelope(
|
|
3635
|
+
*,
|
|
3636
|
+
harness: str,
|
|
3637
|
+
payload: dict[str, object],
|
|
3638
|
+
home_dir: Path,
|
|
3639
|
+
workspace: Path | None,
|
|
3640
|
+
) -> GuardActionEnvelope | None:
|
|
3641
|
+
canonical_harness = _canonical_harness_name(harness)
|
|
3642
|
+
if canonical_harness not in _ACTION_ENVELOPE_HARNESSES:
|
|
3643
|
+
return None
|
|
3644
|
+
return normalize_harness_payload(
|
|
3645
|
+
canonical_harness,
|
|
3646
|
+
_hook_event_name(payload) or "PreToolUse",
|
|
3647
|
+
payload,
|
|
3648
|
+
workspace=workspace,
|
|
3649
|
+
home_dir=home_dir,
|
|
3650
|
+
)
|
|
3651
|
+
|
|
3652
|
+
|
|
3653
|
+
def _action_envelope_json(envelope: GuardActionEnvelope | None) -> dict[str, object] | None:
|
|
3654
|
+
return envelope.to_dict() if envelope is not None else None
|
|
3655
|
+
|
|
3656
|
+
|
|
3622
3657
|
def _normalize_hook_payload(payload: dict[str, object]) -> dict[str, object]:
|
|
3623
3658
|
normalized = dict(payload)
|
|
3624
3659
|
for source_key, target_key in (
|
|
@@ -141,6 +141,7 @@ class GuardReceipt:
|
|
|
141
141
|
user_override: str | None = None
|
|
142
142
|
artifact_name: str | None = None
|
|
143
143
|
source_scope: str | None = None
|
|
144
|
+
action_envelope_json: dict[str, object] | None = None
|
|
144
145
|
|
|
145
146
|
def to_dict(self) -> dict[str, object]:
|
|
146
147
|
payload = asdict(self)
|
|
@@ -177,6 +178,7 @@ class GuardApprovalRequest:
|
|
|
177
178
|
why_now: str | None = None
|
|
178
179
|
launch_summary: str | None = None
|
|
179
180
|
risk_headline: str | None = None
|
|
181
|
+
action_envelope_json: dict[str, object] | None = None
|
|
180
182
|
|
|
181
183
|
def to_dict(self) -> dict[str, object]:
|
|
182
184
|
payload = asdict(self)
|
{plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/actions.py
RENAMED
|
@@ -73,9 +73,12 @@ _SENSITIVE_RAW_KEYS = frozenset(
|
|
|
73
73
|
)
|
|
74
74
|
_SENSITIVE_RAW_KEY_ALIASES = frozenset(key.replace("_", "") for key in _SENSITIVE_RAW_KEYS)
|
|
75
75
|
_HOOK_EVENT_NAME_MAP = {
|
|
76
|
+
"prompt": "UserPromptSubmit",
|
|
76
77
|
"userpromptsubmit": "UserPromptSubmit",
|
|
77
78
|
"userpromptsubmitted": "UserPromptSubmit",
|
|
79
|
+
"pretool": "PreToolUse",
|
|
78
80
|
"pretooluse": "PreToolUse",
|
|
81
|
+
"posttool": "PostToolUse",
|
|
79
82
|
"posttooluse": "PostToolUse",
|
|
80
83
|
"permissionrequest": "PermissionRequest",
|
|
81
84
|
}
|
|
@@ -229,11 +232,122 @@ def normalize_codex_hook_payload(
|
|
|
229
232
|
) -> GuardActionEnvelope:
|
|
230
233
|
"""Normalize a Codex hook payload into a typed action envelope."""
|
|
231
234
|
|
|
235
|
+
return _normalize_action_payload(
|
|
236
|
+
payload,
|
|
237
|
+
harness="codex",
|
|
238
|
+
default_event_name=None,
|
|
239
|
+
workspace=workspace,
|
|
240
|
+
home_dir=home_dir,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def normalize_claude_hook_payload(
|
|
245
|
+
payload: Mapping[str, object],
|
|
246
|
+
*,
|
|
247
|
+
workspace: Path | str | None = None,
|
|
248
|
+
home_dir: Path | str | None = None,
|
|
249
|
+
) -> GuardActionEnvelope:
|
|
250
|
+
"""Normalize a Claude Code hook payload into a typed action envelope."""
|
|
251
|
+
|
|
252
|
+
return _normalize_action_payload(
|
|
253
|
+
payload,
|
|
254
|
+
harness="claude-code",
|
|
255
|
+
default_event_name=None,
|
|
256
|
+
workspace=workspace,
|
|
257
|
+
home_dir=home_dir,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def normalize_opencode_payload(
|
|
262
|
+
payload: Mapping[str, object],
|
|
263
|
+
*,
|
|
264
|
+
workspace: Path | str | None = None,
|
|
265
|
+
home_dir: Path | str | None = None,
|
|
266
|
+
) -> GuardActionEnvelope:
|
|
267
|
+
"""Normalize an OpenCode runtime payload into a typed action envelope."""
|
|
268
|
+
|
|
269
|
+
return _normalize_action_payload(
|
|
270
|
+
payload,
|
|
271
|
+
harness="opencode",
|
|
272
|
+
default_event_name=None,
|
|
273
|
+
workspace=workspace,
|
|
274
|
+
home_dir=home_dir,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def normalize_copilot_payload(
|
|
279
|
+
payload: Mapping[str, object],
|
|
280
|
+
*,
|
|
281
|
+
workspace: Path | str | None = None,
|
|
282
|
+
home_dir: Path | str | None = None,
|
|
283
|
+
) -> GuardActionEnvelope:
|
|
284
|
+
"""Normalize a Copilot runtime payload into a typed action envelope."""
|
|
285
|
+
|
|
286
|
+
return _normalize_action_payload(
|
|
287
|
+
payload,
|
|
288
|
+
harness="copilot",
|
|
289
|
+
default_event_name=None,
|
|
290
|
+
workspace=workspace,
|
|
291
|
+
home_dir=home_dir,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def normalize_gemini_payload(
|
|
296
|
+
payload: Mapping[str, object],
|
|
297
|
+
*,
|
|
298
|
+
workspace: Path | str | None = None,
|
|
299
|
+
home_dir: Path | str | None = None,
|
|
300
|
+
) -> GuardActionEnvelope:
|
|
301
|
+
"""Normalize a Gemini runtime payload into a typed action envelope."""
|
|
302
|
+
|
|
303
|
+
return _normalize_action_payload(
|
|
304
|
+
payload,
|
|
305
|
+
harness="gemini",
|
|
306
|
+
default_event_name=None,
|
|
307
|
+
workspace=workspace,
|
|
308
|
+
home_dir=home_dir,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def normalize_harness_payload(
|
|
313
|
+
harness: str,
|
|
314
|
+
event_name: str,
|
|
315
|
+
payload: Mapping[str, object],
|
|
316
|
+
*,
|
|
317
|
+
workspace: Path | str | None = None,
|
|
318
|
+
home_dir: Path | str | None = None,
|
|
319
|
+
) -> GuardActionEnvelope:
|
|
320
|
+
"""Normalize any supported Guard harness payload into a typed action envelope."""
|
|
321
|
+
|
|
322
|
+
normalized_harness = harness.strip().lower()
|
|
323
|
+
normalizers = {
|
|
324
|
+
"codex": normalize_codex_hook_payload,
|
|
325
|
+
"claude": normalize_claude_hook_payload,
|
|
326
|
+
"claude-code": normalize_claude_hook_payload,
|
|
327
|
+
"opencode": normalize_opencode_payload,
|
|
328
|
+
"copilot": normalize_copilot_payload,
|
|
329
|
+
"gemini": normalize_gemini_payload,
|
|
330
|
+
}
|
|
331
|
+
normalizer = normalizers.get(normalized_harness)
|
|
332
|
+
if normalizer is None:
|
|
333
|
+
raise ValueError(f"Unsupported Guard harness for action normalization: {harness}")
|
|
334
|
+
normalized_payload = _payload_with_default_event(payload, event_name)
|
|
335
|
+
return normalizer(normalized_payload, workspace=workspace, home_dir=home_dir)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _normalize_action_payload(
|
|
339
|
+
payload: Mapping[str, object],
|
|
340
|
+
*,
|
|
341
|
+
harness: str,
|
|
342
|
+
default_event_name: str | None,
|
|
343
|
+
workspace: Path | str | None,
|
|
344
|
+
home_dir: Path | str | None,
|
|
345
|
+
) -> GuardActionEnvelope:
|
|
232
346
|
normalized_payload = dict(payload)
|
|
347
|
+
if default_event_name is not None:
|
|
348
|
+
normalized_payload = _payload_with_default_event(normalized_payload, default_event_name)
|
|
233
349
|
event_name = _hook_event_name(normalized_payload)
|
|
234
|
-
explicit_tool_name =
|
|
235
|
-
normalized_payload.get("toolName")
|
|
236
|
-
)
|
|
350
|
+
explicit_tool_name = _tool_name_from_payload(normalized_payload)
|
|
237
351
|
tool_call_name, tool_call_input = _tool_call_from_payload(
|
|
238
352
|
normalized_payload.get("toolCalls"),
|
|
239
353
|
expected_tool_name=explicit_tool_name,
|
|
@@ -244,10 +358,10 @@ def normalize_codex_hook_payload(
|
|
|
244
358
|
tool_input = tool_call_input
|
|
245
359
|
raw_command = _command_from_payload(tool_input)
|
|
246
360
|
command = _command_detail(raw_command, home_dir=home_dir)
|
|
247
|
-
prompt_text = _prompt_text(normalized_payload
|
|
361
|
+
prompt_text = _prompt_text(_prompt_value(normalized_payload))
|
|
248
362
|
prompt_excerpt = _prompt_excerpt(prompt_text)
|
|
249
|
-
mcp_server, mcp_tool =
|
|
250
|
-
action_type =
|
|
363
|
+
mcp_server, mcp_tool = _mcp_details(normalized_payload, tool_name)
|
|
364
|
+
action_type = _action_type(
|
|
251
365
|
event_name=event_name,
|
|
252
366
|
tool_name=tool_name,
|
|
253
367
|
command=raw_command,
|
|
@@ -266,7 +380,7 @@ def normalize_codex_hook_payload(
|
|
|
266
380
|
return GuardActionEnvelope(
|
|
267
381
|
schema_version=_SCHEMA_VERSION,
|
|
268
382
|
action_id="",
|
|
269
|
-
harness=
|
|
383
|
+
harness=harness,
|
|
270
384
|
event_name=event_name,
|
|
271
385
|
action_type=action_type,
|
|
272
386
|
workspace=workspace_label,
|
|
@@ -329,6 +443,26 @@ def _mapping_value(value: object) -> Mapping[str, object]:
|
|
|
329
443
|
return {}
|
|
330
444
|
|
|
331
445
|
|
|
446
|
+
def _payload_with_default_event(payload: Mapping[str, object], event_name: str) -> dict[str, object]:
|
|
447
|
+
normalized_payload = dict(payload)
|
|
448
|
+
if not event_name.strip():
|
|
449
|
+
return normalized_payload
|
|
450
|
+
for key in ("event", "eventName", "hook_event_name", "hookEventName", "hook_name", "hookName"):
|
|
451
|
+
value = normalized_payload.get(key)
|
|
452
|
+
if isinstance(value, str) and value.strip():
|
|
453
|
+
return normalized_payload
|
|
454
|
+
normalized_payload["hook_event_name"] = event_name
|
|
455
|
+
return normalized_payload
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _tool_name_from_payload(payload: Mapping[str, object]) -> str | None:
|
|
459
|
+
for key in ("tool_name", "toolName", "name", "tool"):
|
|
460
|
+
value = _string_value(payload.get(key))
|
|
461
|
+
if value is not None:
|
|
462
|
+
return value
|
|
463
|
+
return None
|
|
464
|
+
|
|
465
|
+
|
|
332
466
|
def _tool_input_from_payload(payload: Mapping[str, object]) -> Mapping[str, object]:
|
|
333
467
|
for key in ("tool_input", "toolInput", "toolArgs", "arguments"):
|
|
334
468
|
parsed = _mapping_from_value(payload.get(key))
|
|
@@ -375,7 +509,7 @@ def _mapping_from_value(value: object) -> Mapping[str, object] | None:
|
|
|
375
509
|
|
|
376
510
|
|
|
377
511
|
def _hook_event_name(payload: Mapping[str, object]) -> str:
|
|
378
|
-
for key in ("event", "hook_event_name", "hookEventName", "hook_name"):
|
|
512
|
+
for key in ("event", "eventName", "hook_event_name", "hookEventName", "hook_name", "hookName"):
|
|
379
513
|
value = payload.get(key)
|
|
380
514
|
if isinstance(value, str) and value.strip():
|
|
381
515
|
stripped = value.strip()
|
|
@@ -383,6 +517,14 @@ def _hook_event_name(payload: Mapping[str, object]) -> str:
|
|
|
383
517
|
return "PreToolUse"
|
|
384
518
|
|
|
385
519
|
|
|
520
|
+
def _prompt_value(payload: Mapping[str, object]) -> object:
|
|
521
|
+
for key in ("prompt", "userPrompt", "user_prompt", "message", "text", "input"):
|
|
522
|
+
value = payload.get(key)
|
|
523
|
+
if isinstance(value, str) and value.strip():
|
|
524
|
+
return value
|
|
525
|
+
return None
|
|
526
|
+
|
|
527
|
+
|
|
386
528
|
def _command_from_payload(tool_input: Mapping[str, object]) -> str | None:
|
|
387
529
|
for key in _COMMAND_KEYS:
|
|
388
530
|
value = tool_input.get(key)
|
|
@@ -413,16 +555,70 @@ def _prompt_excerpt(prompt_text: str | None) -> str | None:
|
|
|
413
555
|
return prompt_text[:_PROMPT_EXCERPT_LIMIT]
|
|
414
556
|
|
|
415
557
|
|
|
416
|
-
def
|
|
417
|
-
|
|
558
|
+
def _mcp_details(payload: Mapping[str, object], tool_name: str | None) -> tuple[str | None, str | None]:
|
|
559
|
+
explicit_server = _string_from_keys(payload, ("mcp_server", "mcpServer", "server", "serverName"))
|
|
560
|
+
explicit_tool = _string_from_keys(payload, ("mcp_tool", "mcpTool"))
|
|
561
|
+
tool_name_value = _string_from_keys(payload, ("tool_name", "toolName"))
|
|
562
|
+
parts_server, parts_tool = _mcp_parts(tool_name, known_servers=_known_mcp_servers(payload))
|
|
563
|
+
server = explicit_server or parts_server
|
|
564
|
+
tool = explicit_tool or parts_tool
|
|
565
|
+
if tool is None and server is not None and tool_name_value is not None:
|
|
566
|
+
tool = tool_name_value
|
|
567
|
+
return server, tool
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def _string_from_keys(payload: Mapping[str, object], keys: tuple[str, ...]) -> str | None:
|
|
571
|
+
for key in keys:
|
|
572
|
+
value = _string_value(payload.get(key))
|
|
573
|
+
if value is not None:
|
|
574
|
+
return value
|
|
575
|
+
return None
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _known_mcp_servers(payload: Mapping[str, object]) -> tuple[str, ...]:
|
|
579
|
+
servers: set[str] = set()
|
|
580
|
+
for key in ("mcp_servers", "mcpServers", "servers"):
|
|
581
|
+
value = payload.get(key)
|
|
582
|
+
if isinstance(value, Mapping):
|
|
583
|
+
servers.update(str(server_name).strip() for server_name in value if isinstance(server_name, str))
|
|
584
|
+
elif isinstance(value, list):
|
|
585
|
+
servers.update(item.strip() for item in value if isinstance(item, str) and item.strip())
|
|
586
|
+
return tuple(
|
|
587
|
+
sorted(
|
|
588
|
+
(server for server in servers if server), key=lambda server: len(_mcp_server_token(server)), reverse=True
|
|
589
|
+
)
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def _mcp_parts(tool_name: str | None, *, known_servers: tuple[str, ...] = ()) -> tuple[str | None, str | None]:
|
|
594
|
+
if tool_name is None:
|
|
595
|
+
return None, None
|
|
596
|
+
if "/" in tool_name:
|
|
597
|
+
server, tool = tool_name.split("/", 1)
|
|
598
|
+
return (server, tool) if server and tool else (None, None)
|
|
599
|
+
if tool_name.startswith("mcp__"):
|
|
600
|
+
parts = tool_name.split("__", 2)
|
|
601
|
+
if len(parts) == 3 and parts[1] and parts[2]:
|
|
602
|
+
return parts[1], parts[2]
|
|
418
603
|
return None, None
|
|
419
|
-
|
|
420
|
-
|
|
604
|
+
if tool_name.startswith("mcp_"):
|
|
605
|
+
suffix = tool_name[len("mcp_") :]
|
|
606
|
+
for server in known_servers:
|
|
607
|
+
server_token = _mcp_server_token(server)
|
|
608
|
+
prefix = f"{server_token}_"
|
|
609
|
+
if suffix.startswith(prefix):
|
|
610
|
+
tool = suffix[len(prefix) :]
|
|
611
|
+
return (server, tool) if tool else (None, None)
|
|
421
612
|
return None, None
|
|
422
|
-
return
|
|
613
|
+
return None, None
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def _mcp_server_token(value: str) -> str:
|
|
617
|
+
token = re.sub(r"[^a-z0-9]+", "_", value.strip().lower())
|
|
618
|
+
return token.strip("_")
|
|
423
619
|
|
|
424
620
|
|
|
425
|
-
def
|
|
621
|
+
def _action_type(
|
|
426
622
|
*,
|
|
427
623
|
event_name: str,
|
|
428
624
|
tool_name: str | None,
|
|
@@ -573,7 +769,12 @@ def _normalized_command(command: str | None) -> str | None:
|
|
|
573
769
|
__all__ = [
|
|
574
770
|
"GuardActionEnvelope",
|
|
575
771
|
"GuardActionType",
|
|
772
|
+
"normalize_claude_hook_payload",
|
|
576
773
|
"normalize_codex_hook_payload",
|
|
774
|
+
"normalize_copilot_payload",
|
|
775
|
+
"normalize_gemini_payload",
|
|
776
|
+
"normalize_harness_payload",
|
|
777
|
+
"normalize_opencode_payload",
|
|
577
778
|
"redacted_workspace_label",
|
|
578
779
|
"stable_action_hash",
|
|
579
780
|
]
|
{plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/runtime/runner.py
RENAMED
|
@@ -12,6 +12,7 @@ import urllib.error
|
|
|
12
12
|
import urllib.parse
|
|
13
13
|
import urllib.request
|
|
14
14
|
from collections.abc import Callable
|
|
15
|
+
from contextlib import suppress
|
|
15
16
|
from datetime import datetime, timezone
|
|
16
17
|
from pathlib import Path
|
|
17
18
|
from typing import Any
|
|
@@ -24,6 +25,7 @@ from ..consumer import detect_harness, evaluate_detection
|
|
|
24
25
|
from ..models import GuardArtifact, HarnessDetection, PolicyDecision
|
|
25
26
|
from ..store import GuardStore
|
|
26
27
|
from ..types import PromptRequest, RemediationAction
|
|
28
|
+
from .actions import GuardActionEnvelope, redacted_workspace_label
|
|
27
29
|
|
|
28
30
|
_APPROVAL_METADATA_KEYS = (
|
|
29
31
|
"approval_center_url",
|
|
@@ -233,12 +235,18 @@ def guard_run(
|
|
|
233
235
|
if not evaluation["blocked"]:
|
|
234
236
|
evaluation = evaluate_detection(detection, store, config, default_action=default_action, persist=True)
|
|
235
237
|
|
|
238
|
+
action_envelope = _guard_run_action_envelope(harness, context, passthrough_args)
|
|
239
|
+
if evaluation["blocked"]:
|
|
240
|
+
evaluation = _evaluation_with_action_envelope(evaluation, action_envelope)
|
|
241
|
+
|
|
236
242
|
if not dry_run and interactive_resolver is not None and evaluation["blocked"]:
|
|
237
243
|
evaluation = interactive_resolver(detection, evaluation)
|
|
238
244
|
elif not dry_run and blocked_resolver is not None and evaluation["blocked"]:
|
|
239
245
|
pending_evaluation = blocked_resolver(detection, evaluation)
|
|
240
246
|
detection = _detection_with_prompt_artifacts(detect_harness(harness, context), context, passthrough_args)
|
|
241
247
|
reevaluated = evaluate_detection(detection, store, config, default_action=default_action, persist=True)
|
|
248
|
+
if reevaluated["blocked"]:
|
|
249
|
+
reevaluated = _evaluation_with_action_envelope(reevaluated, action_envelope)
|
|
242
250
|
for key in _APPROVAL_METADATA_KEYS:
|
|
243
251
|
if key in pending_evaluation:
|
|
244
252
|
reevaluated[key] = pending_evaluation[key]
|
|
@@ -274,6 +282,61 @@ def guard_run(
|
|
|
274
282
|
return evaluation
|
|
275
283
|
|
|
276
284
|
|
|
285
|
+
def _guard_run_action_envelope(
|
|
286
|
+
harness: str,
|
|
287
|
+
context: HarnessContext,
|
|
288
|
+
passthrough_args: list[str],
|
|
289
|
+
) -> GuardActionEnvelope:
|
|
290
|
+
workspace = context.workspace_dir
|
|
291
|
+
workspace_hash = None
|
|
292
|
+
if workspace is not None:
|
|
293
|
+
workspace_path = workspace.expanduser()
|
|
294
|
+
with suppress(OSError):
|
|
295
|
+
workspace_path = workspace_path.resolve()
|
|
296
|
+
workspace_hash = hashlib.sha256(str(workspace_path).encode("utf-8")).hexdigest()
|
|
297
|
+
return GuardActionEnvelope(
|
|
298
|
+
schema_version=1,
|
|
299
|
+
action_id="",
|
|
300
|
+
harness=harness,
|
|
301
|
+
event_name="HarnessStart",
|
|
302
|
+
action_type="harness_start",
|
|
303
|
+
workspace=redacted_workspace_label(workspace, home_dir=context.home_dir),
|
|
304
|
+
workspace_hash=workspace_hash,
|
|
305
|
+
tool_name=None,
|
|
306
|
+
command=None,
|
|
307
|
+
prompt_excerpt=None,
|
|
308
|
+
target_paths=(),
|
|
309
|
+
network_hosts=(),
|
|
310
|
+
mcp_server=None,
|
|
311
|
+
mcp_tool=None,
|
|
312
|
+
package_manager=None,
|
|
313
|
+
package_name=None,
|
|
314
|
+
script_name=None,
|
|
315
|
+
raw_payload_redacted={"passthrough_arg_count": len(passthrough_args)},
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _evaluation_with_action_envelope(
|
|
320
|
+
evaluation: dict[str, Any],
|
|
321
|
+
action_envelope: GuardActionEnvelope,
|
|
322
|
+
) -> dict[str, Any]:
|
|
323
|
+
artifacts = evaluation.get("artifacts")
|
|
324
|
+
if not isinstance(artifacts, list):
|
|
325
|
+
return evaluation
|
|
326
|
+
action_payload = action_envelope.to_dict()
|
|
327
|
+
normalized_artifacts: list[object] = []
|
|
328
|
+
changed = False
|
|
329
|
+
for item in artifacts:
|
|
330
|
+
if isinstance(item, dict) and "action_envelope_json" not in item:
|
|
331
|
+
normalized_artifacts.append({**item, "action_envelope_json": action_payload})
|
|
332
|
+
changed = True
|
|
333
|
+
else:
|
|
334
|
+
normalized_artifacts.append(item)
|
|
335
|
+
if not changed:
|
|
336
|
+
return evaluation
|
|
337
|
+
return {**evaluation, "artifacts": normalized_artifacts}
|
|
338
|
+
|
|
339
|
+
|
|
277
340
|
def _guard_run_config_paths(
|
|
278
341
|
*,
|
|
279
342
|
detection: HarnessDetection,
|
|
@@ -666,6 +666,7 @@ class GuardStore:
|
|
|
666
666
|
self._ensure_approval_column(connection, "why_now", "text")
|
|
667
667
|
self._ensure_approval_column(connection, "launch_summary", "text")
|
|
668
668
|
self._ensure_approval_column(connection, "risk_headline", "text")
|
|
669
|
+
self._ensure_approval_column(connection, "action_envelope_json", "text")
|
|
669
670
|
self._ensure_approval_column(connection, "workspace", "text")
|
|
670
671
|
self._ensure_attachment_column(connection, "lease_id", "text not null default ''")
|
|
671
672
|
self._ensure_attachment_column(connection, "lease_expires_at", "text")
|
{plugin_scanner-2.0.95 → plugin_scanner-2.0.97}/src/codex_plugin_scanner/guard/store_approvals.py
RENAMED
|
@@ -30,12 +30,13 @@ def approval_schema_statement() -> str:
|
|
|
30
30
|
risk_signals_json text not null default '[]',
|
|
31
31
|
artifact_label text,
|
|
32
32
|
source_label text,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
trigger_summary text,
|
|
34
|
+
why_now text,
|
|
35
|
+
launch_summary text,
|
|
36
|
+
risk_headline text,
|
|
37
|
+
action_envelope_json text,
|
|
38
|
+
review_command text not null,
|
|
39
|
+
approval_url text not null,
|
|
39
40
|
status text not null,
|
|
40
41
|
resolution_action text,
|
|
41
42
|
resolution_scope text,
|
|
@@ -68,7 +69,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
68
69
|
recommended_scope = ?, changed_fields_json = ?, source_scope = ?, config_path = ?, workspace = ?,
|
|
69
70
|
launch_target = ?, transport = ?, risk_summary = ?, risk_signals_json = ?,
|
|
70
71
|
artifact_label = ?, source_label = ?, trigger_summary = ?, why_now = ?, launch_summary = ?,
|
|
71
|
-
risk_headline = ?,
|
|
72
|
+
risk_headline = ?, action_envelope_json = ?,
|
|
72
73
|
review_command = ?, approval_url = ?, created_at = ?
|
|
73
74
|
where request_id = ?
|
|
74
75
|
""",
|
|
@@ -93,6 +94,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
93
94
|
request.why_now,
|
|
94
95
|
request.launch_summary,
|
|
95
96
|
request.risk_headline,
|
|
97
|
+
json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
|
|
96
98
|
review_command,
|
|
97
99
|
approval_url,
|
|
98
100
|
now,
|
|
@@ -105,12 +107,12 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
105
107
|
insert into approval_requests (
|
|
106
108
|
request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
|
|
107
109
|
recommended_scope, changed_fields_json, source_scope, config_path, workspace,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
launch_target, transport, risk_summary,
|
|
111
|
+
risk_signals_json, artifact_label, source_label, trigger_summary, why_now, launch_summary, risk_headline,
|
|
112
|
+
action_envelope_json, review_command,
|
|
113
|
+
approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
|
|
114
|
+
)
|
|
115
|
+
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
114
116
|
""",
|
|
115
117
|
(
|
|
116
118
|
request.request_id,
|
|
@@ -136,6 +138,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
|
|
|
136
138
|
request.why_now,
|
|
137
139
|
request.launch_summary,
|
|
138
140
|
request.risk_headline,
|
|
141
|
+
json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
|
|
139
142
|
request.review_command,
|
|
140
143
|
request.approval_url,
|
|
141
144
|
"pending",
|
|
@@ -182,9 +185,9 @@ def list_approval_requests(
|
|
|
182
185
|
query = f"""
|
|
183
186
|
select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
|
|
184
187
|
recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
+
risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
|
|
189
|
+
launch_summary, risk_headline, action_envelope_json, review_command,
|
|
190
|
+
approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
|
|
188
191
|
from approval_requests
|
|
189
192
|
{where_clause}
|
|
190
193
|
order by created_at desc
|
|
@@ -201,9 +204,9 @@ def get_approval_request(connection: sqlite3.Connection, request_id: str) -> dic
|
|
|
201
204
|
"""
|
|
202
205
|
select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
|
|
203
206
|
recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
+
risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
|
|
208
|
+
launch_summary, risk_headline, action_envelope_json, review_command,
|
|
209
|
+
approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
|
|
207
210
|
from approval_requests
|
|
208
211
|
where request_id = ?
|
|
209
212
|
""",
|
|
@@ -273,6 +276,7 @@ def _row_to_payload(row: sqlite3.Row) -> dict[str, object]:
|
|
|
273
276
|
"why_now": row["why_now"],
|
|
274
277
|
"launch_summary": row["launch_summary"],
|
|
275
278
|
"risk_headline": row["risk_headline"],
|
|
279
|
+
"action_envelope_json": _optional_json_object(row["action_envelope_json"]),
|
|
276
280
|
"review_command": str(row["review_command"]),
|
|
277
281
|
"approval_url": str(row["approval_url"]),
|
|
278
282
|
"status": str(row["status"]),
|
|
@@ -282,3 +286,15 @@ def _row_to_payload(row: sqlite3.Row) -> dict[str, object]:
|
|
|
282
286
|
"created_at": str(row["created_at"]),
|
|
283
287
|
"resolved_at": row["resolved_at"],
|
|
284
288
|
}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _optional_json_object(value: object) -> dict[str, object] | None:
|
|
292
|
+
if value is None:
|
|
293
|
+
return None
|
|
294
|
+
try:
|
|
295
|
+
parsed = json.loads(str(value))
|
|
296
|
+
except (json.JSONDecodeError, TypeError, ValueError):
|
|
297
|
+
return None
|
|
298
|
+
if isinstance(parsed, dict):
|
|
299
|
+
return {str(key): item for key, item in parsed.items() if isinstance(key, str)}
|
|
300
|
+
return None
|