plugin-scanner 2.0.106__tar.gz → 2.0.108__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.106 → plugin_scanner-2.0.108}/PKG-INFO +1 -1
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/pyproject.toml +1 -1
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +85 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/capabilities.py +19 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/commands.py +64 -12
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +93 -15
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +11 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +124 -2
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/version.py +1 -1
- plugin_scanner-2.0.108/tests/conftest.py +15 -0
- plugin_scanner-2.0.108/tests/test_guard_access_graph.py +395 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_cli.py +37 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_risk.py +56 -1
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_runtime.py +171 -3
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_runtime_detectors.py +3 -0
- plugin_scanner-2.0.106/tests/conftest.py +0 -9
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.dockerignore +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.gitignore +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/Dockerfile +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/requirements.txt +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_runtime_action_harnesses.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_runtime_actions.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_runtime_decisions.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_runtime_signals.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/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.108
|
|
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.108"
|
|
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.108"
|
|
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"
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
from dataclasses import dataclass
|
|
7
|
+
from hashlib import sha256
|
|
8
|
+
from pathlib import PurePath
|
|
6
9
|
|
|
7
10
|
from ..models import GuardArtifact, HarnessDetection
|
|
8
11
|
|
|
@@ -31,6 +34,23 @@ _GUARD_PROXY_COMMANDS = frozenset(
|
|
|
31
34
|
}
|
|
32
35
|
)
|
|
33
36
|
|
|
37
|
+
_STABLE_SLASH_FLAG_TOKENS = frozenset(
|
|
38
|
+
{
|
|
39
|
+
"/?",
|
|
40
|
+
"/debug",
|
|
41
|
+
"/dry-run",
|
|
42
|
+
"/h",
|
|
43
|
+
"/help",
|
|
44
|
+
"/quiet",
|
|
45
|
+
"/read-only",
|
|
46
|
+
"/readonly",
|
|
47
|
+
"/ro",
|
|
48
|
+
"/safe",
|
|
49
|
+
"/trace",
|
|
50
|
+
"/verbose",
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
34
54
|
|
|
35
55
|
def managed_stdio_servers(detection: HarnessDetection) -> tuple[ManagedMcpServer, ...]:
|
|
36
56
|
"""Extract local stdio MCP servers from a harness detection payload."""
|
|
@@ -76,6 +96,8 @@ def proxy_cli_args(
|
|
|
76
96
|
guard_home,
|
|
77
97
|
"--server-name",
|
|
78
98
|
server.name,
|
|
99
|
+
"--server-id",
|
|
100
|
+
stable_mcp_server_identifier(server),
|
|
79
101
|
"--source-scope",
|
|
80
102
|
server.source_scope,
|
|
81
103
|
"--config-path",
|
|
@@ -94,6 +116,24 @@ def proxy_cli_args(
|
|
|
94
116
|
return args
|
|
95
117
|
|
|
96
118
|
|
|
119
|
+
def stable_mcp_server_identifier(server: ManagedMcpServer) -> str:
|
|
120
|
+
"""Build a Cloud-stable MCP server ID without local config path material."""
|
|
121
|
+
|
|
122
|
+
harness = server.harness.strip().lower()
|
|
123
|
+
source_scope = server.source_scope.strip().lower()
|
|
124
|
+
server_name = _stable_server_name(server.name)
|
|
125
|
+
payload = {
|
|
126
|
+
"harness": harness,
|
|
127
|
+
"source_scope": source_scope,
|
|
128
|
+
"name": server_name,
|
|
129
|
+
"command": _stable_command_name(server.command),
|
|
130
|
+
"args": [_stable_arg_token(value) for value in server.args],
|
|
131
|
+
"transport": server.transport,
|
|
132
|
+
}
|
|
133
|
+
digest = sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()[:20]
|
|
134
|
+
return f"mcp_server:{harness}:{source_scope}:{server_name}:{digest}"
|
|
135
|
+
|
|
136
|
+
|
|
97
137
|
def _managed_stdio_server(artifact: GuardArtifact) -> ManagedMcpServer | None:
|
|
98
138
|
if artifact.artifact_type != "mcp_server":
|
|
99
139
|
return None
|
|
@@ -119,6 +159,50 @@ def _managed_stdio_server(artifact: GuardArtifact) -> ManagedMcpServer | None:
|
|
|
119
159
|
)
|
|
120
160
|
|
|
121
161
|
|
|
162
|
+
def _stable_arg_token(value: str) -> str:
|
|
163
|
+
key, separator, item = value.partition("=")
|
|
164
|
+
if separator and (_looks_like_path_assignment(key, item) or _looks_like_path_token(item)):
|
|
165
|
+
return f"{key}=<path>"
|
|
166
|
+
if _looks_like_path_token(value):
|
|
167
|
+
return "<path>"
|
|
168
|
+
return value
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _stable_server_name(value: str) -> str:
|
|
172
|
+
return value.strip().lower() or "unnamed"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _stable_command_name(value: str) -> str:
|
|
176
|
+
return PurePath(value.replace("\\", "/")).name.lower()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _looks_like_path_assignment(key: str, value: str) -> bool:
|
|
180
|
+
normalized_key = key.strip().lstrip("-/").lower().replace("_", "-")
|
|
181
|
+
path_keys = {
|
|
182
|
+
"cache",
|
|
183
|
+
"config",
|
|
184
|
+
"cwd",
|
|
185
|
+
"dir",
|
|
186
|
+
"directory",
|
|
187
|
+
"file",
|
|
188
|
+
"folder",
|
|
189
|
+
"path",
|
|
190
|
+
"root",
|
|
191
|
+
"workspace",
|
|
192
|
+
"workdir",
|
|
193
|
+
}
|
|
194
|
+
return normalized_key in path_keys and value.strip().replace("\\", "/").startswith("/")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _looks_like_path_token(value: str) -> bool:
|
|
198
|
+
normalized = value.strip().replace("\\", "/")
|
|
199
|
+
if normalized.startswith(("~/", "./", "../")):
|
|
200
|
+
return True
|
|
201
|
+
if normalized.startswith("/"):
|
|
202
|
+
return "/" in normalized[1:] or normalized.lower() not in _STABLE_SLASH_FLAG_TOKENS
|
|
203
|
+
return len(normalized) >= 3 and normalized[0].isalpha() and normalized[1:3] == ":/"
|
|
204
|
+
|
|
205
|
+
|
|
122
206
|
def _string_env(value: object) -> dict[str, str]:
|
|
123
207
|
if not isinstance(value, dict):
|
|
124
208
|
return {}
|
|
@@ -149,4 +233,5 @@ __all__ = [
|
|
|
149
233
|
"managed_stdio_servers",
|
|
150
234
|
"proxy_cli_args",
|
|
151
235
|
"skipped_stdio_server_names",
|
|
236
|
+
"stable_mcp_server_identifier",
|
|
152
237
|
]
|
{plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/capabilities.py
RENAMED
|
@@ -196,6 +196,25 @@ def severity_from_deltas(deltas: tuple[CapabilityDelta, ...]) -> int:
|
|
|
196
196
|
return max(delta.severity for delta in deltas)
|
|
197
197
|
|
|
198
198
|
|
|
199
|
+
def normalized_capability_categories(capabilities: CapabilitySet) -> tuple[str, ...]:
|
|
200
|
+
"""Return broad Cloud access graph categories for skills and MCP tools."""
|
|
201
|
+
|
|
202
|
+
categories: set[str] = set()
|
|
203
|
+
if capabilities.network_hosts or capabilities.network_schemes:
|
|
204
|
+
categories.add("network")
|
|
205
|
+
if capabilities.filesystem_paths:
|
|
206
|
+
categories.add("filesystem")
|
|
207
|
+
if capabilities.secret_classes:
|
|
208
|
+
categories.add("secret")
|
|
209
|
+
if capabilities.subprocess_invocation or capabilities.interpreters or capabilities.shell_wrappers:
|
|
210
|
+
categories.add("execution")
|
|
211
|
+
if capabilities.publisher:
|
|
212
|
+
categories.add("publisher")
|
|
213
|
+
if capabilities.transport != "local":
|
|
214
|
+
categories.add("transport")
|
|
215
|
+
return tuple(sorted(categories))
|
|
216
|
+
|
|
217
|
+
|
|
199
218
|
def _first_seen_deltas(after: CapabilitySet) -> list[CapabilityDelta]:
|
|
200
219
|
deltas: list[CapabilityDelta] = []
|
|
201
220
|
if after.network_hosts:
|
{plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -97,6 +97,7 @@ from ..runtime.secret_file_requests import (
|
|
|
97
97
|
extract_sensitive_tool_action_request,
|
|
98
98
|
is_explicitly_benign_tool_action_request,
|
|
99
99
|
)
|
|
100
|
+
from ..runtime.secret_sensitivity import SecretContentMatch, classify_secret_content
|
|
100
101
|
from ..runtime.surface_server import GuardSurfaceRuntime
|
|
101
102
|
from ..store import GuardStore
|
|
102
103
|
from .approval_commands import add_approval_parser, run_approval_command
|
|
@@ -539,6 +540,7 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
|
|
|
539
540
|
codex_proxy_parser = guard_subparsers.add_parser("codex-mcp-proxy", help=argparse.SUPPRESS)
|
|
540
541
|
_add_guard_common_args(codex_proxy_parser)
|
|
541
542
|
codex_proxy_parser.add_argument("--server-name", required=True)
|
|
543
|
+
codex_proxy_parser.add_argument("--server-id")
|
|
542
544
|
codex_proxy_parser.add_argument("--source-scope", default="project")
|
|
543
545
|
codex_proxy_parser.add_argument("--config-path", required=True)
|
|
544
546
|
codex_proxy_parser.add_argument("--transport", default="stdio")
|
|
@@ -548,6 +550,7 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
|
|
|
548
550
|
opencode_proxy_parser = guard_subparsers.add_parser("opencode-mcp-proxy", help=argparse.SUPPRESS)
|
|
549
551
|
_add_guard_common_args(opencode_proxy_parser)
|
|
550
552
|
opencode_proxy_parser.add_argument("--server-name", required=True)
|
|
553
|
+
opencode_proxy_parser.add_argument("--server-id")
|
|
551
554
|
opencode_proxy_parser.add_argument("--source-scope", default="project")
|
|
552
555
|
opencode_proxy_parser.add_argument("--config-path", required=True)
|
|
553
556
|
opencode_proxy_parser.add_argument("--transport", default="local")
|
|
@@ -557,6 +560,7 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
|
|
|
557
560
|
copilot_proxy_parser = guard_subparsers.add_parser("copilot-mcp-proxy", help=argparse.SUPPRESS)
|
|
558
561
|
_add_guard_common_args(copilot_proxy_parser)
|
|
559
562
|
copilot_proxy_parser.add_argument("--server-name", required=True)
|
|
563
|
+
copilot_proxy_parser.add_argument("--server-id")
|
|
560
564
|
copilot_proxy_parser.add_argument("--source-scope", default="project")
|
|
561
565
|
copilot_proxy_parser.add_argument("--config-path", required=True)
|
|
562
566
|
copilot_proxy_parser.add_argument("--transport", default="stdio")
|
|
@@ -807,6 +811,7 @@ def run_guard_command(
|
|
|
807
811
|
source_scope=args.source_scope,
|
|
808
812
|
config_path=args.config_path,
|
|
809
813
|
transport=args.transport,
|
|
814
|
+
server_id=args.server_id,
|
|
810
815
|
)
|
|
811
816
|
return proxy.serve()
|
|
812
817
|
|
|
@@ -820,6 +825,7 @@ def run_guard_command(
|
|
|
820
825
|
source_scope=args.source_scope,
|
|
821
826
|
config_path=args.config_path,
|
|
822
827
|
transport=args.transport,
|
|
828
|
+
server_id=args.server_id,
|
|
823
829
|
)
|
|
824
830
|
return proxy.serve()
|
|
825
831
|
|
|
@@ -833,6 +839,7 @@ def run_guard_command(
|
|
|
833
839
|
source_scope=args.source_scope,
|
|
834
840
|
config_path=args.config_path,
|
|
835
841
|
transport=args.transport,
|
|
842
|
+
server_id=args.server_id,
|
|
836
843
|
)
|
|
837
844
|
return proxy.serve()
|
|
838
845
|
|
|
@@ -1253,6 +1260,7 @@ def run_guard_command(
|
|
|
1253
1260
|
decision_source="pre-tool-hook",
|
|
1254
1261
|
now=now,
|
|
1255
1262
|
signals=decision.signals,
|
|
1263
|
+
risk_categories=decision.risk_categories,
|
|
1256
1264
|
remember=False,
|
|
1257
1265
|
)
|
|
1258
1266
|
if _should_emit_copilot_hook_response(args):
|
|
@@ -1267,6 +1275,7 @@ def run_guard_command(
|
|
|
1267
1275
|
decision_source="pre-tool-hook",
|
|
1268
1276
|
now=now,
|
|
1269
1277
|
signals=decision.signals,
|
|
1278
|
+
risk_categories=decision.risk_categories,
|
|
1270
1279
|
)
|
|
1271
1280
|
if _should_emit_copilot_hook_response(args):
|
|
1272
1281
|
_emit_copilot_hook_response(
|
|
@@ -1345,6 +1354,7 @@ def run_guard_command(
|
|
|
1345
1354
|
decision_source=decision.source,
|
|
1346
1355
|
now=now,
|
|
1347
1356
|
signals=decision.signals,
|
|
1357
|
+
risk_categories=decision.risk_categories,
|
|
1348
1358
|
remember=False,
|
|
1349
1359
|
)
|
|
1350
1360
|
if _should_emit_copilot_hook_response(args):
|
|
@@ -1359,6 +1369,7 @@ def run_guard_command(
|
|
|
1359
1369
|
decision_source="permission-request-hook",
|
|
1360
1370
|
now=now,
|
|
1361
1371
|
signals=decision.signals,
|
|
1372
|
+
risk_categories=decision.risk_categories,
|
|
1362
1373
|
)
|
|
1363
1374
|
approval_center_url = ensure_guard_daemon(guard_home)
|
|
1364
1375
|
approval_flow = get_adapter(args.harness).approval_flow(managed_install=managed_install)
|
|
@@ -2010,18 +2021,19 @@ def _should_emit_native_hook_json_response(
|
|
|
2010
2021
|
|
|
2011
2022
|
|
|
2012
2023
|
def _should_emit_native_hook_exit_block(args: argparse.Namespace, *, event_name: str, policy_action: str) -> bool:
|
|
2013
|
-
codex_runtime_marker = (
|
|
2014
|
-
os.environ.get("CODEX_HOME", "").strip() or os.environ.get("CODEX_MANAGED_BY_BUN", "").strip()
|
|
2015
|
-
)
|
|
2016
2024
|
return (
|
|
2017
2025
|
args.harness == "codex"
|
|
2018
2026
|
and event_name == "PreToolUse"
|
|
2019
2027
|
and policy_action in {"block", "sandbox-required", "require-reapproval"}
|
|
2020
2028
|
and not getattr(args, "json", False)
|
|
2021
|
-
and
|
|
2029
|
+
and _is_codex_native_runtime()
|
|
2022
2030
|
)
|
|
2023
2031
|
|
|
2024
2032
|
|
|
2033
|
+
def _is_codex_native_runtime() -> bool:
|
|
2034
|
+
return bool(os.environ.get("CODEX_HOME", "").strip() or os.environ.get("CODEX_MANAGED_BY_BUN", "").strip())
|
|
2035
|
+
|
|
2036
|
+
|
|
2025
2037
|
def _codex_browser_approval_decision(
|
|
2026
2038
|
*,
|
|
2027
2039
|
args: argparse.Namespace,
|
|
@@ -2040,6 +2052,8 @@ def _codex_browser_approval_decision(
|
|
|
2040
2052
|
return None
|
|
2041
2053
|
if policy_action not in {"block", "sandbox-required", "require-reapproval"}:
|
|
2042
2054
|
return None
|
|
2055
|
+
if event_name == "PreToolUse" and not _is_codex_native_runtime():
|
|
2056
|
+
return None
|
|
2043
2057
|
approval_requests = response_payload.get("approval_requests")
|
|
2044
2058
|
if not isinstance(approval_requests, list):
|
|
2045
2059
|
return None
|
|
@@ -3949,10 +3963,6 @@ def _hook_runtime_artifact(
|
|
|
3949
3963
|
)
|
|
3950
3964
|
|
|
3951
3965
|
|
|
3952
|
-
_CODEX_SECRET_OUTPUT_PATTERN = re.compile(
|
|
3953
|
-
r"(?i)(?:fake[_-]?credential|fake[_-]?secret|"
|
|
3954
|
-
r"(?:api[_-]?key|auth[_-]?token|credential|npm[_-]?token|private[_-]?key|secret|token|password)\s*[:=])"
|
|
3955
|
-
)
|
|
3956
3966
|
_CODEX_PROMPT_SECRET_KEY_MARKERS = ("TOKEN", "SECRET", "PASSWORD", "PASS", "API_KEY", "API-KEY", "AUTH", "CREDENTIAL")
|
|
3957
3967
|
_CODEX_TOOL_RESPONSE_MAX_DEPTH = 5
|
|
3958
3968
|
_CODEX_TOOL_RESPONSE_TEXT_LIMIT = 20000
|
|
@@ -3967,13 +3977,19 @@ def _codex_post_tool_output_artifact(
|
|
|
3967
3977
|
cwd: Path | None,
|
|
3968
3978
|
) -> GuardArtifact | None:
|
|
3969
3979
|
response_text = _collect_codex_tool_response_text(payload.get("tool_response"))
|
|
3970
|
-
|
|
3980
|
+
content_matches = classify_secret_content(response_text)
|
|
3981
|
+
if not content_matches:
|
|
3971
3982
|
return None
|
|
3972
3983
|
tool_name = _coalesce_string(payload.get("tool_name"), "Bash")
|
|
3973
3984
|
command_text = _codex_post_tool_command_text(payload)
|
|
3974
3985
|
if not command_text:
|
|
3975
3986
|
command_text = tool_name
|
|
3976
|
-
if
|
|
3987
|
+
if _codex_source_inspection_can_skip_secret_output(
|
|
3988
|
+
command_text=command_text,
|
|
3989
|
+
response_text=response_text,
|
|
3990
|
+
content_matches=content_matches,
|
|
3991
|
+
cwd=cwd,
|
|
3992
|
+
):
|
|
3977
3993
|
return None
|
|
3978
3994
|
fingerprint = hashlib.sha256(
|
|
3979
3995
|
json.dumps(
|
|
@@ -4096,6 +4112,10 @@ _CODEX_SOURCE_SEARCH_EXTENSIONS = frozenset(
|
|
|
4096
4112
|
".yml",
|
|
4097
4113
|
}
|
|
4098
4114
|
)
|
|
4115
|
+
_CODEX_BENIGN_SOURCE_DOTFILES = frozenset({".nvmrc"})
|
|
4116
|
+
_CODEX_BENIGN_SECRET_FIXTURE_ASSIGNMENT_PATTERN = re.compile(
|
|
4117
|
+
r"(?i)\s*fake[_-]?(?:credential|secret|token)\s*[:=]\s*(?:\"[^\r\n\"]+\"|'[^\r\n']+'|[^\s\"',}]+)\s*"
|
|
4118
|
+
)
|
|
4099
4119
|
_CODEX_SENSITIVE_SEARCH_BASENAMES = frozenset(
|
|
4100
4120
|
{
|
|
4101
4121
|
".aws",
|
|
@@ -4114,6 +4134,35 @@ _CODEX_SENSITIVE_SEARCH_BASENAMES = frozenset(
|
|
|
4114
4134
|
_CODEX_SED_PRINT_SCRIPT_PATTERN = re.compile(r"^\s*(?:\$|\d+)?(?:\s*,\s*(?:\$|\d+))?p\s*$")
|
|
4115
4135
|
|
|
4116
4136
|
|
|
4137
|
+
def _codex_source_inspection_can_skip_secret_output(
|
|
4138
|
+
*,
|
|
4139
|
+
command_text: str,
|
|
4140
|
+
response_text: str,
|
|
4141
|
+
content_matches: tuple[SecretContentMatch, ...],
|
|
4142
|
+
cwd: Path | None,
|
|
4143
|
+
) -> bool:
|
|
4144
|
+
if not _codex_command_is_read_only_source_inspection(command_text, cwd=cwd):
|
|
4145
|
+
return False
|
|
4146
|
+
if any(match.sensitivity != "medium" for match in content_matches):
|
|
4147
|
+
return False
|
|
4148
|
+
if _codex_command_references_benign_source_dotfile(command_text):
|
|
4149
|
+
return _codex_output_is_only_benign_secret_fixture(response_text)
|
|
4150
|
+
return True
|
|
4151
|
+
|
|
4152
|
+
|
|
4153
|
+
def _codex_output_is_only_benign_secret_fixture(response_text: str) -> bool:
|
|
4154
|
+
lines = [line for line in response_text.splitlines() if line.strip()]
|
|
4155
|
+
return bool(lines) and all(_CODEX_BENIGN_SECRET_FIXTURE_ASSIGNMENT_PATTERN.fullmatch(line) for line in lines)
|
|
4156
|
+
|
|
4157
|
+
|
|
4158
|
+
def _codex_command_references_benign_source_dotfile(command_text: str) -> bool:
|
|
4159
|
+
try:
|
|
4160
|
+
parts = shlex.split(command_text)
|
|
4161
|
+
except ValueError:
|
|
4162
|
+
return False
|
|
4163
|
+
return any(Path(part).name.lower() in _CODEX_BENIGN_SOURCE_DOTFILES for part in parts)
|
|
4164
|
+
|
|
4165
|
+
|
|
4117
4166
|
def _codex_command_is_read_only_source_inspection(command_text: str, *, cwd: Path | None) -> bool:
|
|
4118
4167
|
command = command_text.strip()
|
|
4119
4168
|
if not command:
|
|
@@ -4631,13 +4680,16 @@ def _codex_search_target_is_source_like(target: str, *, cwd: Path | None) -> boo
|
|
|
4631
4680
|
lowered_parts = [part.lower() for part in parts]
|
|
4632
4681
|
if any(part in _CODEX_SENSITIVE_SEARCH_BASENAMES for part in lowered_parts):
|
|
4633
4682
|
return False
|
|
4634
|
-
if
|
|
4683
|
+
hidden_parts = [part for part in lowered_parts if part.startswith(".")]
|
|
4684
|
+
if hidden_parts and not all(part in _CODEX_BENIGN_SOURCE_DOTFILES for part in hidden_parts):
|
|
4635
4685
|
return False
|
|
4636
4686
|
normalized = "/".join(parts)
|
|
4637
4687
|
if normalized in {prefix.rstrip("/") for prefix in _CODEX_SOURCE_SEARCH_PREFIXES}:
|
|
4638
4688
|
return True
|
|
4639
4689
|
if any(normalized.startswith(prefix) for prefix in _CODEX_SOURCE_SEARCH_PREFIXES):
|
|
4640
4690
|
return True
|
|
4691
|
+
if Path(stripped).name.lower() in _CODEX_BENIGN_SOURCE_DOTFILES:
|
|
4692
|
+
return True
|
|
4641
4693
|
return Path(stripped).suffix.lower() in _CODEX_SOURCE_SEARCH_EXTENSIONS
|
|
4642
4694
|
|
|
4643
4695
|
|
|
@@ -4732,7 +4784,7 @@ def _codex_prompt_credential_file_artifact(
|
|
|
4732
4784
|
content = handle.read(_PROMPT_CONTENT_SCAN_MAX_BYTES).decode("utf-8", errors="ignore")
|
|
4733
4785
|
except OSError:
|
|
4734
4786
|
continue
|
|
4735
|
-
if
|
|
4787
|
+
if not classify_secret_content(content):
|
|
4736
4788
|
continue
|
|
4737
4789
|
normalized_path = str(path)
|
|
4738
4790
|
fingerprint = hashlib.sha256(
|
{plugin_scanner-2.0.106 → plugin_scanner-2.0.108}/src/codex_plugin_scanner/guard/mcp_tool_calls.py
RENAMED
|
@@ -22,6 +22,7 @@ class ToolCallDecision:
|
|
|
22
22
|
source: str
|
|
23
23
|
signals: tuple[str, ...]
|
|
24
24
|
summary: str
|
|
25
|
+
risk_categories: tuple[str, ...] = ()
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
def build_tool_call_artifact(
|
|
@@ -32,9 +33,12 @@ def build_tool_call_artifact(
|
|
|
32
33
|
source_scope: str,
|
|
33
34
|
config_path: str,
|
|
34
35
|
transport: str,
|
|
36
|
+
server_id: str | None = None,
|
|
35
37
|
server_fingerprint: object | None = None,
|
|
36
38
|
) -> GuardArtifact:
|
|
37
39
|
metadata = {"server_name": server_name}
|
|
40
|
+
if server_id is not None:
|
|
41
|
+
metadata["server_id"] = server_id
|
|
38
42
|
if server_fingerprint is not None:
|
|
39
43
|
metadata["server_fingerprint"] = server_fingerprint
|
|
40
44
|
return GuardArtifact(
|
|
@@ -82,20 +86,24 @@ def evaluate_tool_call(
|
|
|
82
86
|
override = config.resolve_action_override(artifact.harness, artifact.artifact_id, artifact.publisher)
|
|
83
87
|
action = _coerce_guard_action(override) if isinstance(override, str) else None
|
|
84
88
|
if action is not None:
|
|
89
|
+
risk_categories = tool_call_risk_categories(artifact, arguments)
|
|
85
90
|
return ToolCallDecision(
|
|
86
91
|
action=action,
|
|
87
92
|
source="policy",
|
|
88
93
|
signals=tool_call_risk_signals(artifact, arguments),
|
|
89
94
|
summary="Local Guard policy matched this exact tool call.",
|
|
95
|
+
risk_categories=risk_categories,
|
|
90
96
|
)
|
|
91
97
|
|
|
92
98
|
signals = tool_call_risk_signals(artifact, arguments)
|
|
99
|
+
risk_categories = tool_call_risk_categories(artifact, arguments)
|
|
93
100
|
if len(signals) == 0:
|
|
94
101
|
return ToolCallDecision(
|
|
95
102
|
action="allow",
|
|
96
103
|
source="heuristic",
|
|
97
104
|
signals=(),
|
|
98
105
|
summary="Guard did not detect a high-risk signal in this tool call.",
|
|
106
|
+
risk_categories=(),
|
|
99
107
|
)
|
|
100
108
|
if config.mode == "prompt":
|
|
101
109
|
return ToolCallDecision(
|
|
@@ -103,37 +111,95 @@ def evaluate_tool_call(
|
|
|
103
111
|
source="heuristic",
|
|
104
112
|
signals=signals,
|
|
105
113
|
summary=tool_call_risk_summary(artifact, arguments),
|
|
114
|
+
risk_categories=risk_categories,
|
|
106
115
|
)
|
|
107
116
|
return ToolCallDecision(
|
|
108
117
|
action="block",
|
|
109
118
|
source="heuristic",
|
|
110
119
|
signals=signals,
|
|
111
120
|
summary=tool_call_risk_summary(artifact, arguments),
|
|
121
|
+
risk_categories=risk_categories,
|
|
112
122
|
)
|
|
113
123
|
|
|
114
124
|
|
|
115
125
|
def tool_call_risk_signals(artifact: GuardArtifact, arguments: object) -> tuple[str, ...]:
|
|
126
|
+
signals_by_category = {
|
|
127
|
+
"destructive_mutation": "tool name implies destructive file or system changes",
|
|
128
|
+
"command_execution": "tool name implies shell or command execution",
|
|
129
|
+
"outbound_network": "call arguments imply outbound network activity",
|
|
130
|
+
"secret_access": "call arguments mention sensitive local files or secrets",
|
|
131
|
+
"privileged_system_mutation": "call arguments imply privileged system mutation",
|
|
132
|
+
}
|
|
133
|
+
return tuple(signals_by_category[category] for category in tool_call_risk_categories(artifact, arguments))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def tool_call_risk_categories(artifact: GuardArtifact, arguments: object) -> tuple[str, ...]:
|
|
137
|
+
"""Return normalized Cloud risk categories for one MCP tool call."""
|
|
138
|
+
|
|
139
|
+
categories = _tool_call_risk_category_set(artifact, arguments)
|
|
140
|
+
order = (
|
|
141
|
+
"command_execution",
|
|
142
|
+
"destructive_mutation",
|
|
143
|
+
"outbound_network",
|
|
144
|
+
"privileged_system_mutation",
|
|
145
|
+
"secret_access",
|
|
146
|
+
)
|
|
147
|
+
return tuple(category for category in order if category in categories)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _tool_call_risk_category_set(artifact: GuardArtifact, arguments: object) -> set[str]:
|
|
116
151
|
tool_name = PurePath(artifact.command or artifact.name).name
|
|
117
|
-
serialized_arguments =
|
|
118
|
-
combined = f"{artifact.name
|
|
152
|
+
serialized_arguments = _serialized_tool_arguments(arguments)
|
|
153
|
+
combined = _risk_match_text(f"{artifact.name} {serialized_arguments}")
|
|
119
154
|
tool_name_tokens = set(_tool_name_tokens(tool_name))
|
|
120
|
-
|
|
155
|
+
categories: set[str] = set()
|
|
121
156
|
|
|
122
157
|
if len(tool_name_tokens.intersection({"delete", "remove", "rm", "destroy", "erase"})) > 0:
|
|
123
|
-
|
|
158
|
+
categories.add("destructive_mutation")
|
|
124
159
|
if len(tool_name_tokens.intersection({"shell", "bash", "exec", "execute", "command", "powershell"})) > 0:
|
|
125
|
-
|
|
126
|
-
if
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
160
|
+
categories.add("command_execution")
|
|
161
|
+
if _matches_any(
|
|
162
|
+
combined,
|
|
163
|
+
(
|
|
164
|
+
r"https?://",
|
|
165
|
+
_token_pattern("curl", "wget", "fetch", "axios", "requests"),
|
|
166
|
+
),
|
|
167
|
+
):
|
|
168
|
+
categories.add("outbound_network")
|
|
169
|
+
if _matches_any(
|
|
170
|
+
combined,
|
|
171
|
+
(
|
|
172
|
+
r"(?<![a-z0-9_-])\.env(?![a-z0-9_-])",
|
|
173
|
+
r"(?<![a-z0-9_-])\.ssh(?![a-z0-9_-])",
|
|
174
|
+
r"(?<![a-z0-9])(id[_-]?rsa|credentials|token|secret|passwd)(?![a-z0-9])",
|
|
175
|
+
r"(?<![a-z0-9_-])\.(npmrc|pypirc)(?![a-z0-9_-])",
|
|
176
|
+
),
|
|
131
177
|
):
|
|
132
|
-
|
|
133
|
-
if
|
|
134
|
-
|
|
178
|
+
categories.add("secret_access")
|
|
179
|
+
if _matches_any(
|
|
180
|
+
combined,
|
|
181
|
+
(_token_pattern("sudo", "chmod", "chown", "launchctl", "systemctl"),),
|
|
182
|
+
):
|
|
183
|
+
categories.add("privileged_system_mutation")
|
|
184
|
+
return categories
|
|
185
|
+
|
|
135
186
|
|
|
136
|
-
|
|
187
|
+
def _serialized_tool_arguments(arguments: object) -> str:
|
|
188
|
+
if arguments is None:
|
|
189
|
+
return ""
|
|
190
|
+
try:
|
|
191
|
+
return json.dumps(arguments, sort_keys=True, default=str)
|
|
192
|
+
except (TypeError, ValueError):
|
|
193
|
+
return str(arguments)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _matches_any(value: str, patterns: tuple[str, ...]) -> bool:
|
|
197
|
+
return any(re.search(pattern, value) is not None for pattern in patterns)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _token_pattern(*tokens: str) -> str:
|
|
201
|
+
alternatives = "|".join(re.escape(token) for token in tokens)
|
|
202
|
+
return rf"(?<![a-z0-9])({alternatives})(?![a-z0-9])"
|
|
137
203
|
|
|
138
204
|
|
|
139
205
|
def tool_call_risk_summary(artifact: GuardArtifact, arguments: object) -> str:
|
|
@@ -154,6 +220,7 @@ def allow_tool_call(
|
|
|
154
220
|
now: str,
|
|
155
221
|
signals: tuple[str, ...],
|
|
156
222
|
remember: bool,
|
|
223
|
+
risk_categories: tuple[str, ...] = (),
|
|
157
224
|
) -> GuardReceipt:
|
|
158
225
|
if remember:
|
|
159
226
|
store.upsert_policy(
|
|
@@ -196,6 +263,7 @@ def allow_tool_call(
|
|
|
196
263
|
"artifact_id": artifact.artifact_id,
|
|
197
264
|
"artifact_hash": artifact_hash,
|
|
198
265
|
"decision_source": decision_source,
|
|
266
|
+
"risk_categories": list(risk_categories),
|
|
199
267
|
"signals": list(signals),
|
|
200
268
|
},
|
|
201
269
|
now,
|
|
@@ -211,6 +279,7 @@ def block_tool_call(
|
|
|
211
279
|
decision_source: str,
|
|
212
280
|
now: str,
|
|
213
281
|
signals: tuple[str, ...],
|
|
282
|
+
risk_categories: tuple[str, ...] = (),
|
|
214
283
|
) -> GuardReceipt:
|
|
215
284
|
store.record_inventory_artifact(
|
|
216
285
|
artifact=artifact,
|
|
@@ -239,6 +308,7 @@ def block_tool_call(
|
|
|
239
308
|
"artifact_id": artifact.artifact_id,
|
|
240
309
|
"artifact_hash": artifact_hash,
|
|
241
310
|
"decision_source": decision_source,
|
|
311
|
+
"risk_categories": list(risk_categories),
|
|
242
312
|
"signals": list(signals),
|
|
243
313
|
},
|
|
244
314
|
now,
|
|
@@ -258,10 +328,18 @@ def _dedupe(values: list[str]) -> list[str]:
|
|
|
258
328
|
|
|
259
329
|
|
|
260
330
|
def _tool_name_tokens(tool_name: str) -> tuple[str, ...]:
|
|
261
|
-
camel_normalized =
|
|
331
|
+
camel_normalized = _camel_token_normalized(tool_name)
|
|
262
332
|
return tuple(token for token in re.findall(r"[a-z0-9]+", camel_normalized.lower()) if token)
|
|
263
333
|
|
|
264
334
|
|
|
335
|
+
def _risk_match_text(value: str) -> str:
|
|
336
|
+
return _camel_token_normalized(value).lower()
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _camel_token_normalized(value: str) -> str:
|
|
340
|
+
return re.sub(r"([a-z0-9])([A-Z])", r"\1 \2", value)
|
|
341
|
+
|
|
342
|
+
|
|
265
343
|
def _coerce_guard_action(value: str) -> GuardAction | None:
|
|
266
344
|
for action in GUARD_ACTION_VALUES:
|
|
267
345
|
if value == action:
|