plugin-scanner 2.0.132__tar.gz → 2.0.134__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.132 → plugin_scanner-2.0.134}/PKG-INFO +1 -1
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/pyproject.toml +1 -1
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/approvals.py +10 -2
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/server.py +76 -7
- plugin_scanner-2.0.134/src/codex_plugin_scanner/guard/runtime/action_identity.py +87 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/version.py +1 -1
- plugin_scanner-2.0.134/tests/test_guard_action_identity.py +155 -0
- plugin_scanner-2.0.134/tests/test_guard_policy_dedup.py +301 -0
- plugin_scanner-2.0.134/tests/test_guard_web_recovery.py +190 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.dockerignore +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.gitignore +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/Dockerfile +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-layout.test.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-review-cards.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/data-flow-evidence-card.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/receipts-workspace.test.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/runtime-overview.test.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/settings-workspace.test.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/release-checklist.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/smoke-tests.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/requirements.txt +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/contracts.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/advisory_escalation.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/advisory_matchers.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/threat_intel.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_threat_intel.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-docs-fake-token.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-health-endpoint.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-nvmrc-fake-creds.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-source-search.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/canary-exfil-encoded.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/canary-exfil.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/expected-decisions.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-dockerfile.txt +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-encoded-shell-exfil.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-github-action.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-mcp-delete.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-mcp-secret-read.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-mcp-skill-exfil.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-npm-postinstall.js +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-prompt-env-read.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-prompt-guard-bypass.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-prompt-npmrc-read.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-python-setup.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/smoke-evidence-template.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_access_graph.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_advisory_escalation.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_approval_continuity.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_approval_store_scale.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_canary_fixtures.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_cloud_local_sync.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_daemon_perf.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_data_flow.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_evidence_store.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_harness_contracts.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_mcp_protection.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_prompt_injection.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_red_team.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_action_harnesses.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_actions.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_decisions.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_detectors.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_signals.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_safe_decode.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_sandbox.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_skill_protection.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_supply_chain.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_threat_intel.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/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.134
|
|
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.134"
|
|
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.134"
|
|
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.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/approvals.py
RENAMED
|
@@ -24,6 +24,14 @@ GUARD_FLEET_URL = f"{GUARD_DASHBOARD_URL}/fleet"
|
|
|
24
24
|
GUARD_CONNECT_URL = f"{GUARD_DASHBOARD_URL}/connect"
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
class ApprovalRequestNotFoundError(ValueError):
|
|
28
|
+
"""Raised when an approval request ID does not exist."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ApprovalRequestAlreadyResolvedError(ValueError):
|
|
32
|
+
"""Raised when an approval request was already resolved."""
|
|
33
|
+
|
|
34
|
+
|
|
27
35
|
def queue_blocked_approvals(
|
|
28
36
|
*,
|
|
29
37
|
detection: HarnessDetection,
|
|
@@ -114,9 +122,9 @@ def apply_approval_resolution(
|
|
|
114
122
|
) -> dict[str, object]:
|
|
115
123
|
request = store.get_approval_request(request_id)
|
|
116
124
|
if request is None:
|
|
117
|
-
raise
|
|
125
|
+
raise ApprovalRequestNotFoundError(f"Unknown approval request: {request_id}")
|
|
118
126
|
if request["status"] != "pending":
|
|
119
|
-
raise
|
|
127
|
+
raise ApprovalRequestAlreadyResolvedError(f"Approval request already resolved: {request_id}")
|
|
120
128
|
if scope == "workspace" and not workspace:
|
|
121
129
|
raise ValueError(f"Approval request {request_id} requires --workspace for workspace scope.")
|
|
122
130
|
if scope == "publisher" and not isinstance(request.get("publisher"), str):
|
{plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/server.py
RENAMED
|
@@ -18,7 +18,12 @@ from typing import Any
|
|
|
18
18
|
from urllib.parse import parse_qs, parse_qsl, unquote, urlencode, urlparse, urlunparse
|
|
19
19
|
|
|
20
20
|
from ...version import __version__
|
|
21
|
-
from ..approvals import
|
|
21
|
+
from ..approvals import (
|
|
22
|
+
ApprovalRequestAlreadyResolvedError,
|
|
23
|
+
ApprovalRequestNotFoundError,
|
|
24
|
+
apply_approval_resolution,
|
|
25
|
+
build_runtime_snapshot,
|
|
26
|
+
)
|
|
22
27
|
from ..config import editable_guard_settings, load_guard_config, update_guard_settings
|
|
23
28
|
from ..models import DECISION_SCOPE_VALUES, GUARD_ACTION_VALUES
|
|
24
29
|
from ..runtime.surface_server import GuardSurfaceRuntime
|
|
@@ -183,7 +188,17 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
|
|
|
183
188
|
if len(path_parts) == 3 and path_parts[:2] == ["v1", "requests"]:
|
|
184
189
|
approval = store.get_approval_request(path_parts[2])
|
|
185
190
|
if approval is None:
|
|
186
|
-
self._write_json(
|
|
191
|
+
self._write_json(
|
|
192
|
+
{
|
|
193
|
+
"error": "not_found",
|
|
194
|
+
"recovery": {
|
|
195
|
+
"code": "request_unknown",
|
|
196
|
+
"title": "This request is no longer waiting.",
|
|
197
|
+
"body": "The request was either already resolved or expired. You can close this tab.",
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
status=404,
|
|
201
|
+
)
|
|
187
202
|
return
|
|
188
203
|
self._write_json(approval)
|
|
189
204
|
return
|
|
@@ -313,11 +328,29 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
|
|
|
313
328
|
self._write_json({"error": "forbidden_origin"}, status=403)
|
|
314
329
|
return
|
|
315
330
|
if self._requires_header_token(parsed.path, path_parts) and not self._header_token_is_valid():
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
331
|
+
if len(path_parts) == 4 and path_parts[:2] == ["v1", "requests"] and path_parts[3] in {"approve", "block"}:
|
|
332
|
+
host = self.server.server_address[0] # type: ignore[attr-defined]
|
|
333
|
+
port = self.server.server_address[1] # type: ignore[attr-defined]
|
|
334
|
+
reconnect_url = _build_local_url(host, port, "/#/reconnect")
|
|
335
|
+
self._write_json(
|
|
336
|
+
{
|
|
337
|
+
"error": "unauthorized",
|
|
338
|
+
"recovery": {
|
|
339
|
+
"code": "session_stale",
|
|
340
|
+
"title": "Your session with the local Guard daemon has expired.",
|
|
341
|
+
"body": "Click the link below to reconnect, then retry your approval.",
|
|
342
|
+
"reconnect_url": reconnect_url,
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
status=401,
|
|
346
|
+
extra_headers=self._cors_headers_for_request(),
|
|
347
|
+
)
|
|
348
|
+
else:
|
|
349
|
+
self._write_json(
|
|
350
|
+
{"error": "unauthorized"},
|
|
351
|
+
status=401,
|
|
352
|
+
extra_headers=self._cors_headers_for_request(),
|
|
353
|
+
)
|
|
321
354
|
return
|
|
322
355
|
payload, body_error = self._load_request_body()
|
|
323
356
|
if body_error is not None:
|
|
@@ -389,6 +422,37 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
|
|
|
389
422
|
workspace=self._optional_string(payload.get("workspace")),
|
|
390
423
|
reason=self._optional_string(payload.get("reason")),
|
|
391
424
|
)
|
|
425
|
+
except ApprovalRequestNotFoundError:
|
|
426
|
+
self._write_json(
|
|
427
|
+
{
|
|
428
|
+
"resolved": False,
|
|
429
|
+
"error": "not_found",
|
|
430
|
+
"recovery": {
|
|
431
|
+
"code": "request_unknown",
|
|
432
|
+
"title": "This request is no longer waiting.",
|
|
433
|
+
"body": "The request was either already resolved or expired. You can close this tab.",
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
status=404,
|
|
437
|
+
)
|
|
438
|
+
return
|
|
439
|
+
except ApprovalRequestAlreadyResolvedError:
|
|
440
|
+
self._write_json(
|
|
441
|
+
{
|
|
442
|
+
"resolved": False,
|
|
443
|
+
"error": "already_resolved",
|
|
444
|
+
"recovery": {
|
|
445
|
+
"code": "request_resolved",
|
|
446
|
+
"title": "This request has already been resolved.",
|
|
447
|
+
"body": (
|
|
448
|
+
"If the action is blocked and you believe it should be allowed, "
|
|
449
|
+
"you can re-submit from your AI assistant."
|
|
450
|
+
),
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
status=409,
|
|
454
|
+
)
|
|
455
|
+
return
|
|
392
456
|
except ValueError as error:
|
|
393
457
|
self._write_json({"resolved": False, "error": str(error)}, status=400)
|
|
394
458
|
return
|
|
@@ -1319,6 +1383,11 @@ def _approval_center_browser_url(approval_center_url: str, auth_token: str) -> s
|
|
|
1319
1383
|
return urlunparse(parsed._replace(fragment=urlencode(fragment_pairs)))
|
|
1320
1384
|
|
|
1321
1385
|
|
|
1386
|
+
def _build_local_url(host: str, port: int, path: str) -> str:
|
|
1387
|
+
host_part = f"[{host}]" if ":" in host else host
|
|
1388
|
+
return f"http://{host_part}:{port}{path}"
|
|
1389
|
+
|
|
1390
|
+
|
|
1322
1391
|
def _now() -> str:
|
|
1323
1392
|
from datetime import datetime, timezone
|
|
1324
1393
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Stable action identity normalization for Guard policy deduplication.
|
|
2
|
+
|
|
3
|
+
Provides normalizers for command, prompt, and MCP tool call identities.
|
|
4
|
+
The output of each normalizer is a stable string suitable for comparison
|
|
5
|
+
or hashing across repeated calls with transient variation stripped out.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
_ANSI_ESCAPE = re.compile(r"\x1b\[[0-9;]*[mABCDEFGHJKSTfhinsu]")
|
|
15
|
+
|
|
16
|
+
_REQUEST_ID_PATTERN = re.compile(
|
|
17
|
+
r"\b(?:req|request|approval|id)[-_][a-zA-Z0-9_-]{4,64}\b",
|
|
18
|
+
re.IGNORECASE,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_TIMESTAMP_PATTERN = re.compile(r"\b\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))?\b")
|
|
22
|
+
|
|
23
|
+
_PORT_FLAG_PATTERN = re.compile(
|
|
24
|
+
r"(?:--port|-p)\s+\d{2,5}\b",
|
|
25
|
+
re.IGNORECASE,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
_MARKDOWN_BOLD_ITALIC = re.compile(r"\*{1,3}|(?<!\w)_{1,3}(?=\w)|(?<=\w)_{1,3}(?!\w)")
|
|
29
|
+
|
|
30
|
+
_BACKTICK_INLINE_CODE = re.compile(r"`([^`]+)`")
|
|
31
|
+
|
|
32
|
+
_GENERIC_REQUEST_ID_IN_ARGS = re.compile(r"\b[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\b")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def normalize_command_identity(command: str) -> str:
|
|
36
|
+
"""Return a stable identity string for a shell command.
|
|
37
|
+
|
|
38
|
+
Strips ANSI codes, daemon ports, timestamps, approval request IDs,
|
|
39
|
+
UUIDs, and excess whitespace while preserving the command name,
|
|
40
|
+
meaningful arguments, target paths, and network hosts.
|
|
41
|
+
"""
|
|
42
|
+
normalized = _ANSI_ESCAPE.sub("", command)
|
|
43
|
+
normalized = _REQUEST_ID_PATTERN.sub("<request-id>", normalized)
|
|
44
|
+
normalized = _GENERIC_REQUEST_ID_IN_ARGS.sub("<uuid>", normalized)
|
|
45
|
+
normalized = _TIMESTAMP_PATTERN.sub("<timestamp>", normalized)
|
|
46
|
+
normalized = _PORT_FLAG_PATTERN.sub("<port-flag>", normalized)
|
|
47
|
+
normalized = re.sub(r"\s+", " ", normalized).strip()
|
|
48
|
+
return normalized
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def normalize_prompt_identity(prompt: str) -> str:
|
|
52
|
+
"""Return a stable identity string for a prompt text.
|
|
53
|
+
|
|
54
|
+
Strips markdown bold/italic formatting and normalises whitespace,
|
|
55
|
+
while preserving the requested sensitive targets (file paths, keys,
|
|
56
|
+
tokens, named secrets).
|
|
57
|
+
"""
|
|
58
|
+
normalized = _MARKDOWN_BOLD_ITALIC.sub("", prompt)
|
|
59
|
+
normalized = _BACKTICK_INLINE_CODE.sub(r"\1", normalized)
|
|
60
|
+
normalized = re.sub(r"\s+", " ", normalized).strip()
|
|
61
|
+
normalized = normalized.lower()
|
|
62
|
+
return normalized
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def normalize_mcp_identity(call: dict[str, object]) -> str:
|
|
66
|
+
"""Return a stable identity hash for an MCP tool call.
|
|
67
|
+
|
|
68
|
+
The identity is derived from:
|
|
69
|
+
- server_id
|
|
70
|
+
- tool_name
|
|
71
|
+
- arguments (sorted keys, stable JSON)
|
|
72
|
+
- schema_hash (when present)
|
|
73
|
+
|
|
74
|
+
Returns a hex digest suitable for equality comparison.
|
|
75
|
+
"""
|
|
76
|
+
server_id = str(call.get("server_id", ""))
|
|
77
|
+
tool_name = str(call.get("tool_name", ""))
|
|
78
|
+
arguments = call.get("arguments", {})
|
|
79
|
+
schema_hash = str(call.get("schema_hash", ""))
|
|
80
|
+
|
|
81
|
+
stable_args = json.dumps(
|
|
82
|
+
{k: v for k, v in sorted(arguments.items())} if isinstance(arguments, dict) else arguments,
|
|
83
|
+
sort_keys=True,
|
|
84
|
+
ensure_ascii=True,
|
|
85
|
+
)
|
|
86
|
+
identity_source = f"{server_id}:{tool_name}:{stable_args}:{schema_hash}"
|
|
87
|
+
return hashlib.sha256(identity_source.encode("utf-8")).hexdigest()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Tests for action identity normalization (T700-T718)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
action_identity_mod = pytest.importorskip(
|
|
8
|
+
"codex_plugin_scanner.guard.runtime.action_identity",
|
|
9
|
+
reason="action_identity module not yet implemented",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
normalize_command_identity = action_identity_mod.normalize_command_identity
|
|
13
|
+
normalize_prompt_identity = action_identity_mod.normalize_prompt_identity
|
|
14
|
+
normalize_mcp_identity = action_identity_mod.normalize_mcp_identity
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestCommandIdentityNormalization:
|
|
18
|
+
"""T700-T705: Command identity normalizer."""
|
|
19
|
+
|
|
20
|
+
def test_same_command_with_different_request_ids_maps_to_same_identity(self) -> None:
|
|
21
|
+
"""T703: Different approval request IDs in command must not change identity."""
|
|
22
|
+
cmd_a = "hol-guard approvals approve req-abc-001 --scope artifact"
|
|
23
|
+
cmd_b = "hol-guard approvals approve req-xyz-999 --scope artifact"
|
|
24
|
+
assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
|
|
25
|
+
|
|
26
|
+
def test_ansi_codes_removed_before_normalization(self) -> None:
|
|
27
|
+
"""T701: ANSI escape codes must be stripped before normalization."""
|
|
28
|
+
cmd_with_ansi = "\x1b[32mnode\x1b[0m server.js"
|
|
29
|
+
cmd_clean = "node server.js"
|
|
30
|
+
assert normalize_command_identity(cmd_with_ansi) == normalize_command_identity(cmd_clean)
|
|
31
|
+
|
|
32
|
+
def test_daemon_port_numbers_removed(self) -> None:
|
|
33
|
+
"""T701: Ephemeral port numbers (like approval center ports) must not affect identity."""
|
|
34
|
+
cmd_a = "hol-guard approvals --port 6174"
|
|
35
|
+
cmd_b = "hol-guard approvals --port 7890"
|
|
36
|
+
assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
|
|
37
|
+
|
|
38
|
+
def test_timestamps_removed(self) -> None:
|
|
39
|
+
"""T701: Timestamps in commands must not affect identity."""
|
|
40
|
+
cmd_a = "backup.sh --at 2026-01-01T00:00:00Z"
|
|
41
|
+
cmd_b = "backup.sh --at 2026-06-15T12:30:00Z"
|
|
42
|
+
assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
|
|
43
|
+
|
|
44
|
+
def test_different_network_hosts_produce_different_identity(self) -> None:
|
|
45
|
+
"""T704: Commands targeting different network hosts must have different identity."""
|
|
46
|
+
cmd_internal = "curl http://internal.corp/api/data"
|
|
47
|
+
cmd_external = "curl http://evil.example.com/api/data"
|
|
48
|
+
assert normalize_command_identity(cmd_internal) != normalize_command_identity(cmd_external)
|
|
49
|
+
|
|
50
|
+
def test_different_secret_paths_produce_different_identity(self) -> None:
|
|
51
|
+
"""T705: Commands targeting different secret paths must have different identity."""
|
|
52
|
+
cmd_npmrc = "cat /Users/me/.npmrc"
|
|
53
|
+
cmd_env = "cat /Users/me/.env"
|
|
54
|
+
assert normalize_command_identity(cmd_npmrc) != normalize_command_identity(cmd_env)
|
|
55
|
+
|
|
56
|
+
def test_meaningful_command_preserved(self) -> None:
|
|
57
|
+
"""T702: Core command and meaningful args must be preserved in normalized identity."""
|
|
58
|
+
id_a = normalize_command_identity("node server.js --port 3000")
|
|
59
|
+
id_b = normalize_command_identity("python server.py --port 3000")
|
|
60
|
+
assert id_a != id_b, "Different commands must produce different identities"
|
|
61
|
+
|
|
62
|
+
def test_whitespace_normalized(self) -> None:
|
|
63
|
+
"""T701: Extra whitespace must not affect identity."""
|
|
64
|
+
cmd_a = "node server.js --port 3000"
|
|
65
|
+
cmd_b = "node server.js --port 3000"
|
|
66
|
+
assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TestPromptIdentityNormalization:
|
|
70
|
+
"""T706-T708: Prompt identity normalizer."""
|
|
71
|
+
|
|
72
|
+
def test_repeated_read_npmrc_prompt_maps_to_same_identity(self) -> None:
|
|
73
|
+
"""T707: Same prompt repeated with minor formatting differences maps to same identity."""
|
|
74
|
+
prompt_a = "Read the **`.npmrc`** file and tell me the registry config."
|
|
75
|
+
prompt_b = "Read the `.npmrc` file and tell me the registry config."
|
|
76
|
+
assert normalize_prompt_identity(prompt_a) == normalize_prompt_identity(prompt_b)
|
|
77
|
+
|
|
78
|
+
def test_npmrc_vs_env_prompt_maps_to_different_identity(self) -> None:
|
|
79
|
+
"""T708: Prompts targeting different secrets must have different identity."""
|
|
80
|
+
prompt_npmrc = "Read the .npmrc file."
|
|
81
|
+
prompt_env = "Read the .env file."
|
|
82
|
+
assert normalize_prompt_identity(prompt_npmrc) != normalize_prompt_identity(prompt_env)
|
|
83
|
+
|
|
84
|
+
def test_model_formatting_tokens_removed(self) -> None:
|
|
85
|
+
"""T706: Transient model formatting (bold, markdown etc.) must not affect identity."""
|
|
86
|
+
prompt_formatted = "**Read** the `.npmrc` file and extract the _auth token_."
|
|
87
|
+
prompt_plain = "Read the .npmrc file and extract the auth token."
|
|
88
|
+
assert normalize_prompt_identity(prompt_formatted) == normalize_prompt_identity(prompt_plain)
|
|
89
|
+
|
|
90
|
+
def test_underscores_in_identifiers_preserved(self) -> None:
|
|
91
|
+
"""T706b: Underscores inside identifiers must not be stripped."""
|
|
92
|
+
prompt_key = "Send OPENAI_API_KEY to the server."
|
|
93
|
+
prompt_file = "Read my_secret_file from disk."
|
|
94
|
+
normalized_key = normalize_prompt_identity(prompt_key)
|
|
95
|
+
normalized_file = normalize_prompt_identity(prompt_file)
|
|
96
|
+
assert "openai_api_key" in normalized_key, "Internal underscores in env var names must be preserved"
|
|
97
|
+
assert "my_secret_file" in normalized_file, "Internal underscores in file names must be preserved"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TestMcpIdentityNormalization:
|
|
101
|
+
"""T709-T711: MCP identity normalizer."""
|
|
102
|
+
|
|
103
|
+
def test_same_tool_and_target_maps_to_same_identity(self) -> None:
|
|
104
|
+
"""T710: Same MCP server, tool, and target must produce same identity."""
|
|
105
|
+
call_a = {
|
|
106
|
+
"server_id": "github-mcp",
|
|
107
|
+
"tool_name": "read_file",
|
|
108
|
+
"arguments": {"path": "/Users/me/.npmrc"},
|
|
109
|
+
}
|
|
110
|
+
call_b = {
|
|
111
|
+
"server_id": "github-mcp",
|
|
112
|
+
"tool_name": "read_file",
|
|
113
|
+
"arguments": {"path": "/Users/me/.npmrc"},
|
|
114
|
+
}
|
|
115
|
+
assert normalize_mcp_identity(call_a) == normalize_mcp_identity(call_b)
|
|
116
|
+
|
|
117
|
+
def test_different_mcp_target_produces_different_identity(self) -> None:
|
|
118
|
+
"""T710: Same MCP tool with different target must produce different identity."""
|
|
119
|
+
call_npmrc = {
|
|
120
|
+
"server_id": "github-mcp",
|
|
121
|
+
"tool_name": "read_file",
|
|
122
|
+
"arguments": {"path": "/Users/me/.npmrc"},
|
|
123
|
+
}
|
|
124
|
+
call_env = {
|
|
125
|
+
"server_id": "github-mcp",
|
|
126
|
+
"tool_name": "read_file",
|
|
127
|
+
"arguments": {"path": "/Users/me/.env"},
|
|
128
|
+
}
|
|
129
|
+
assert normalize_mcp_identity(call_npmrc) != normalize_mcp_identity(call_env)
|
|
130
|
+
|
|
131
|
+
def test_mcp_schema_change_produces_different_identity(self) -> None:
|
|
132
|
+
"""T711: Change in MCP tool schema hash must invalidate previous approval identity."""
|
|
133
|
+
call_v1 = {
|
|
134
|
+
"server_id": "custom-mcp",
|
|
135
|
+
"tool_name": "execute",
|
|
136
|
+
"arguments": {"cmd": "ls"},
|
|
137
|
+
"schema_hash": "v1-abc123",
|
|
138
|
+
}
|
|
139
|
+
call_v2 = {
|
|
140
|
+
"server_id": "custom-mcp",
|
|
141
|
+
"tool_name": "execute",
|
|
142
|
+
"arguments": {"cmd": "ls"},
|
|
143
|
+
"schema_hash": "v2-def456",
|
|
144
|
+
}
|
|
145
|
+
assert normalize_mcp_identity(call_v1) != normalize_mcp_identity(call_v2)
|
|
146
|
+
|
|
147
|
+
def test_mcp_identity_is_deterministic(self) -> None:
|
|
148
|
+
"""T709: normalize_mcp_identity must return same hash on repeated calls."""
|
|
149
|
+
call = {
|
|
150
|
+
"server_id": "test-mcp",
|
|
151
|
+
"tool_name": "read_file",
|
|
152
|
+
"arguments": {"path": "/tmp/test"},
|
|
153
|
+
"schema_hash": "hash-001",
|
|
154
|
+
}
|
|
155
|
+
assert normalize_mcp_identity(call) == normalize_mcp_identity(call)
|