plugin-scanner 2.0.146__tar.gz → 2.0.148__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.146 → plugin_scanner-2.0.148}/PKG-INFO +1 -1
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/pyproject.toml +1 -1
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/base.py +24 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/contracts.py +146 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/commands.py +116 -6
- plugin_scanner-2.0.148/src/codex_plugin_scanner/guard/cli/install_commands.py +273 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/server.py +107 -0
- plugin_scanner-2.0.148/src/codex_plugin_scanner/guard/runtime/cisco_evidence.py +175 -0
- plugin_scanner-2.0.148/src/codex_plugin_scanner/guard/runtime/scanner_cache.py +10 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/signals.py +115 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store.py +83 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +14 -2
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +81 -6
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/version.py +1 -1
- plugin_scanner-2.0.148/tests/test_guard_cisco_evidence.py +245 -0
- plugin_scanner-2.0.148/tests/test_guard_harness_setup.py +335 -0
- plugin_scanner-2.0.146/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -131
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.dockerignore +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.gitignore +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/Dockerfile +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-layout.test.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-review-cards.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/data-flow-evidence-card.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/home-dashboard.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/queue-state.test.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/queue-state.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/receipts-workspace.test.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/risk-signal-cards.test.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/risk-signal-cards.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/runtime-overview.test.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/settings-workspace.test.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/release-checklist.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/release-notes.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/smoke-tests.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/requirements.txt +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/action_identity.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/advisory_escalation.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/advisory_matchers.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/threat_intel.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_threat_intel.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-docs-fake-token.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-health-endpoint.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-nvmrc-fake-creds.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-source-search.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/canary-exfil-encoded.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/canary-exfil.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/expected-decisions.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-dockerfile.txt +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-encoded-shell-exfil.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-github-action.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-mcp-delete.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-mcp-secret-read.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-mcp-skill-exfil.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-npm-postinstall.js +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-prompt-env-read.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-prompt-guard-bypass.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-prompt-npmrc-read.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-python-setup.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/smoke-evidence-template.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_access_graph.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_action_identity.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_advisory_escalation.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_continuity.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_copy_commands.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_store_dedup.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_store_scale.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_canary_fixtures.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_cloud_local_sync.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_daemon_perf.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_daemon_repair_perf.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_data_flow.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_decision_propagation.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_evidence_store.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_harness_contracts.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_harness_smoke.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_mcp_protection.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_policy_dedup.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_prompt_injection.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_queue_api_contract.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_queue_contract.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_red_team.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_resolution_copy.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_action_harnesses.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_actions.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_decisions.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_detectors.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_signals.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_safe_decode.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_sandbox.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_skill_protection.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_supply_chain.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_threat_intel.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_web_recovery.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/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.148
|
|
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.148"
|
|
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.148"
|
|
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.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/base.py
RENAMED
|
@@ -7,11 +7,13 @@ import os
|
|
|
7
7
|
import shutil
|
|
8
8
|
import subprocess
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
+
from functools import cached_property
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
13
|
from ...path_support import resolves_within_root
|
|
13
14
|
from ..models import GuardArtifact, HarnessDetection
|
|
14
15
|
from ..shims import install_guard_shim, remove_guard_shim
|
|
16
|
+
from .contracts import HarnessCoverageSummary, HarnessSetupContract, HarnessSetupStep, setup_contract_for
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
@dataclass(frozen=True, slots=True)
|
|
@@ -118,6 +120,28 @@ class HarnessAdapter:
|
|
|
118
120
|
**shim_manifest,
|
|
119
121
|
}
|
|
120
122
|
|
|
123
|
+
@cached_property
|
|
124
|
+
def _setup_contract(self) -> HarnessSetupContract:
|
|
125
|
+
contract = setup_contract_for(self.harness)
|
|
126
|
+
if contract is None:
|
|
127
|
+
raise ValueError(f"Unsupported harness setup contract: {self.harness}")
|
|
128
|
+
return contract
|
|
129
|
+
|
|
130
|
+
def setup_contract(self) -> HarnessSetupContract:
|
|
131
|
+
return self._setup_contract
|
|
132
|
+
|
|
133
|
+
def setup_steps(self) -> tuple[HarnessSetupStep, ...]:
|
|
134
|
+
return self.setup_contract().setup_steps
|
|
135
|
+
|
|
136
|
+
def verify_steps(self) -> tuple[HarnessSetupStep, ...]:
|
|
137
|
+
return self.setup_contract().verify_steps
|
|
138
|
+
|
|
139
|
+
def repair_steps(self) -> tuple[HarnessSetupStep, ...]:
|
|
140
|
+
return self.setup_contract().repair_steps
|
|
141
|
+
|
|
142
|
+
def coverage_summary(self) -> HarnessCoverageSummary:
|
|
143
|
+
return self.setup_contract().coverage
|
|
144
|
+
|
|
121
145
|
def executable_candidates(self, context: HarnessContext) -> tuple[Path, ...]:
|
|
122
146
|
del context
|
|
123
147
|
return ()
|
|
@@ -44,6 +44,85 @@ class HarnessProtectionContract:
|
|
|
44
44
|
smoke_command: str
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
@dataclass(frozen=True, slots=True)
|
|
48
|
+
class HarnessSetupStep:
|
|
49
|
+
"""Plain-language action for connecting or checking one harness."""
|
|
50
|
+
|
|
51
|
+
step_id: str
|
|
52
|
+
title: str
|
|
53
|
+
body: str
|
|
54
|
+
command: tuple[str, ...] = ()
|
|
55
|
+
writes_config: bool = False
|
|
56
|
+
requires_confirmation: bool = False
|
|
57
|
+
|
|
58
|
+
def to_dict(self) -> dict[str, object]:
|
|
59
|
+
return {
|
|
60
|
+
"step_id": self.step_id,
|
|
61
|
+
"title": self.title,
|
|
62
|
+
"body": self.body,
|
|
63
|
+
"command": list(self.command),
|
|
64
|
+
"writes_config": self.writes_config,
|
|
65
|
+
"requires_confirmation": self.requires_confirmation,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True, slots=True)
|
|
70
|
+
class HarnessCoverageSummary:
|
|
71
|
+
"""Summary of what Guard can and cannot observe for one harness."""
|
|
72
|
+
|
|
73
|
+
native_hooks: bool
|
|
74
|
+
browser_fallback: bool
|
|
75
|
+
mcp_proxy: bool
|
|
76
|
+
prompt_hooks: bool
|
|
77
|
+
blind_spots: tuple[str, ...]
|
|
78
|
+
|
|
79
|
+
def to_dict(self) -> dict[str, object]:
|
|
80
|
+
return {
|
|
81
|
+
"native_hooks": self.native_hooks,
|
|
82
|
+
"browser_fallback": self.browser_fallback,
|
|
83
|
+
"mcp_proxy": self.mcp_proxy,
|
|
84
|
+
"prompt_hooks": self.prompt_hooks,
|
|
85
|
+
"blind_spots": list(self.blind_spots),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass(frozen=True, slots=True)
|
|
90
|
+
class HarnessSetupContract:
|
|
91
|
+
"""Dashboard and CLI setup contract for one supported harness."""
|
|
92
|
+
|
|
93
|
+
harness: str
|
|
94
|
+
display_name: str
|
|
95
|
+
install_aliases: tuple[str, ...]
|
|
96
|
+
setup_steps: tuple[HarnessSetupStep, ...]
|
|
97
|
+
verify_steps: tuple[HarnessSetupStep, ...]
|
|
98
|
+
repair_steps: tuple[HarnessSetupStep, ...]
|
|
99
|
+
coverage: HarnessCoverageSummary
|
|
100
|
+
|
|
101
|
+
def to_dict(self) -> dict[str, object]:
|
|
102
|
+
return {
|
|
103
|
+
"harness": self.harness,
|
|
104
|
+
"display_name": self.display_name,
|
|
105
|
+
"install_aliases": list(self.install_aliases),
|
|
106
|
+
"setup_steps": [step.to_dict() for step in self.setup_steps],
|
|
107
|
+
"verify_steps": [step.to_dict() for step in self.verify_steps],
|
|
108
|
+
"repair_steps": [step.to_dict() for step in self.repair_steps],
|
|
109
|
+
"coverage": self.coverage.to_dict(),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
_DISPLAY_NAMES = {
|
|
114
|
+
"codex": "Codex",
|
|
115
|
+
"claude-code": "Claude Code",
|
|
116
|
+
"opencode": "OpenCode",
|
|
117
|
+
"copilot": "Copilot",
|
|
118
|
+
"cursor": "Cursor",
|
|
119
|
+
"gemini": "Gemini",
|
|
120
|
+
"hermes": "Hermes",
|
|
121
|
+
"openclaw": "OpenClaw",
|
|
122
|
+
"antigravity": "Antigravity",
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
47
126
|
HARNESS_CONTRACTS: tuple[HarnessProtectionContract, ...] = (
|
|
48
127
|
HarnessProtectionContract(
|
|
49
128
|
harness="codex",
|
|
@@ -183,6 +262,73 @@ def contract_for(harness: str) -> HarnessProtectionContract | None:
|
|
|
183
262
|
return _CONTRACT_BY_ALIAS.get(harness)
|
|
184
263
|
|
|
185
264
|
|
|
265
|
+
def setup_contract_for(harness: str) -> HarnessSetupContract | None:
|
|
266
|
+
"""Return guided setup metadata for a harness name or install alias."""
|
|
267
|
+
|
|
268
|
+
contract = contract_for(harness)
|
|
269
|
+
if contract is None:
|
|
270
|
+
return None
|
|
271
|
+
alias = contract.install_aliases[0] if contract.install_aliases else contract.harness
|
|
272
|
+
display_name = _DISPLAY_NAMES.get(contract.harness, contract.harness)
|
|
273
|
+
coverage = HarnessCoverageSummary(
|
|
274
|
+
native_hooks=contract.native_approval,
|
|
275
|
+
browser_fallback=contract.browser_fallback,
|
|
276
|
+
mcp_proxy="mcp_tool" in contract.event_surfaces,
|
|
277
|
+
prompt_hooks="prompt" in contract.event_surfaces,
|
|
278
|
+
blind_spots=(contract.known_blind_spots,),
|
|
279
|
+
)
|
|
280
|
+
setup_steps = (
|
|
281
|
+
HarnessSetupStep(
|
|
282
|
+
step_id="connect",
|
|
283
|
+
title=f"Connect {display_name}",
|
|
284
|
+
body=f"Add Guard's local protection hooks for {display_name}.",
|
|
285
|
+
command=("hol-guard", "apps", "connect", alias),
|
|
286
|
+
writes_config=True,
|
|
287
|
+
),
|
|
288
|
+
HarnessSetupStep(
|
|
289
|
+
step_id="review-coverage",
|
|
290
|
+
title="Review what Guard can see",
|
|
291
|
+
body="Check covered events and known blind spots before relying on this app.",
|
|
292
|
+
),
|
|
293
|
+
)
|
|
294
|
+
verify_steps = (
|
|
295
|
+
HarnessSetupStep(
|
|
296
|
+
step_id="safe-test",
|
|
297
|
+
title="Run a safe protection test",
|
|
298
|
+
body="Confirm Guard can detect the app without reading secrets or changing app config.",
|
|
299
|
+
command=("hol-guard", "apps", "test", alias),
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
repair_steps = (
|
|
303
|
+
HarnessSetupStep(
|
|
304
|
+
step_id="repair",
|
|
305
|
+
title=f"Repair {display_name} protection",
|
|
306
|
+
body="Re-apply Guard managed config if hooks were removed or changed.",
|
|
307
|
+
command=("hol-guard", "apps", "repair", alias),
|
|
308
|
+
writes_config=True,
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
return HarnessSetupContract(
|
|
312
|
+
harness=contract.harness,
|
|
313
|
+
display_name=display_name,
|
|
314
|
+
install_aliases=contract.install_aliases,
|
|
315
|
+
setup_steps=setup_steps,
|
|
316
|
+
verify_steps=verify_steps,
|
|
317
|
+
repair_steps=repair_steps,
|
|
318
|
+
coverage=coverage,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def all_setup_contracts() -> tuple[HarnessSetupContract, ...]:
|
|
323
|
+
"""Return guided setup metadata for all supported harnesses."""
|
|
324
|
+
|
|
325
|
+
return tuple(
|
|
326
|
+
setup_contract
|
|
327
|
+
for contract in HARNESS_CONTRACTS
|
|
328
|
+
if (setup_contract := setup_contract_for(contract.harness)) is not None
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
|
|
186
332
|
def harness_contracts_table() -> str:
|
|
187
333
|
"""Return a Markdown table summarising all harness contracts."""
|
|
188
334
|
header = (
|
{plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -121,7 +121,13 @@ from .connect_flow import (
|
|
|
121
121
|
DEFAULT_GUARD_SYNC_URL,
|
|
122
122
|
run_guard_connect_command,
|
|
123
123
|
)
|
|
124
|
-
from .install_commands import
|
|
124
|
+
from .install_commands import (
|
|
125
|
+
apply_managed_install,
|
|
126
|
+
build_harness_setup_plan,
|
|
127
|
+
build_harness_verification,
|
|
128
|
+
list_harness_setup_items,
|
|
129
|
+
uninstall_confirmation_token,
|
|
130
|
+
)
|
|
125
131
|
from .product import build_guard_start_payload, build_guard_status_payload
|
|
126
132
|
from .update_commands import run_guard_update
|
|
127
133
|
|
|
@@ -134,6 +140,7 @@ _GUARD_HELP_GROUPS = (
|
|
|
134
140
|
" start First-run setup and the Guard operating loop\n"
|
|
135
141
|
" status Current local protection state and next actions\n"
|
|
136
142
|
" dashboard Open the local Guard dashboard in your browser\n"
|
|
143
|
+
" apps Connect, test, repair, or disconnect protected apps\n"
|
|
137
144
|
" run Enforce Guard before a harness launch\n"
|
|
138
145
|
" approvals Resolve the current request queue\n"
|
|
139
146
|
" receipts Review recent local decisions\n"
|
|
@@ -235,7 +242,7 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
|
|
|
235
242
|
required=True,
|
|
236
243
|
parser_class=FriendlyArgumentParser,
|
|
237
244
|
metavar=(
|
|
238
|
-
"{start,status,dashboard,bootstrap,detect,install,update,uninstall,run,protect,preflight,scan,diff,receipts,inventory,abom,"
|
|
245
|
+
"{start,status,dashboard,apps,bootstrap,detect,install,update,uninstall,run,protect,preflight,scan,diff,receipts,inventory,abom,"
|
|
239
246
|
"approvals,explain,allow,deny,policies,exceptions,advisories,events,doctor,connect,login,sync,device,bridge}"
|
|
240
247
|
),
|
|
241
248
|
)
|
|
@@ -255,6 +262,25 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
|
|
|
255
262
|
_add_guard_common_args(dashboard_parser)
|
|
256
263
|
dashboard_parser.add_argument("--json", action="store_true")
|
|
257
264
|
|
|
265
|
+
apps_parser = guard_subparsers.add_parser("apps", help="Connect, test, repair, or disconnect protected apps")
|
|
266
|
+
_add_guard_common_args(apps_parser)
|
|
267
|
+
apps_parser.add_argument("--json", action="store_true")
|
|
268
|
+
apps_subparsers = apps_parser.add_subparsers(dest="apps_command", parser_class=FriendlyArgumentParser)
|
|
269
|
+
for app_command, help_text in (
|
|
270
|
+
("connect", "Connect an app to local Guard protection"),
|
|
271
|
+
("test", "Run a safe local Guard protection check"),
|
|
272
|
+
("repair", "Repair Guard-managed app config"),
|
|
273
|
+
("disconnect", "Remove Guard-managed app config"),
|
|
274
|
+
):
|
|
275
|
+
app_parser = apps_subparsers.add_parser(app_command, help=help_text)
|
|
276
|
+
app_parser.add_argument("harness")
|
|
277
|
+
_add_guard_common_args(app_parser, suppress_defaults=True)
|
|
278
|
+
app_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS)
|
|
279
|
+
if app_command in {"connect", "repair"}:
|
|
280
|
+
app_parser.add_argument("--dry-run", action="store_true")
|
|
281
|
+
if app_command == "disconnect":
|
|
282
|
+
app_parser.add_argument("--confirm")
|
|
283
|
+
|
|
258
284
|
admin_parser = guard_subparsers.add_parser("admin", help=argparse.SUPPRESS)
|
|
259
285
|
_add_guard_common_args(admin_parser)
|
|
260
286
|
admin_parser.add_argument("--json", action="store_true")
|
|
@@ -680,10 +706,15 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
|
|
|
680
706
|
]
|
|
681
707
|
|
|
682
708
|
|
|
683
|
-
def _add_guard_common_args(
|
|
684
|
-
parser.
|
|
685
|
-
|
|
686
|
-
|
|
709
|
+
def _add_guard_common_args(
|
|
710
|
+
parser: argparse.ArgumentParser,
|
|
711
|
+
*,
|
|
712
|
+
suppress_defaults: bool = False,
|
|
713
|
+
) -> None:
|
|
714
|
+
default = argparse.SUPPRESS if suppress_defaults else None
|
|
715
|
+
parser.add_argument("--home", default=default)
|
|
716
|
+
parser.add_argument("--guard-home", default=default)
|
|
717
|
+
parser.add_argument("--workspace", default=default)
|
|
687
718
|
|
|
688
719
|
|
|
689
720
|
def _add_guard_cisco_mode_arg(parser: argparse.ArgumentParser) -> None:
|
|
@@ -702,6 +733,82 @@ def _guard_http_url(value: str) -> str:
|
|
|
702
733
|
return value
|
|
703
734
|
|
|
704
735
|
|
|
736
|
+
def _run_apps_command(
|
|
737
|
+
args: argparse.Namespace,
|
|
738
|
+
context: HarnessContext,
|
|
739
|
+
store: GuardStore,
|
|
740
|
+
workspace: str | None,
|
|
741
|
+
) -> int:
|
|
742
|
+
apps_command = getattr(args, "apps_command", None)
|
|
743
|
+
if apps_command is None:
|
|
744
|
+
_emit(
|
|
745
|
+
"apps",
|
|
746
|
+
{
|
|
747
|
+
"generated_at": _now(),
|
|
748
|
+
"items": list_harness_setup_items(context, store),
|
|
749
|
+
},
|
|
750
|
+
getattr(args, "json", False),
|
|
751
|
+
)
|
|
752
|
+
return 0
|
|
753
|
+
|
|
754
|
+
harness = str(getattr(args, "harness", "")).strip()
|
|
755
|
+
if not harness:
|
|
756
|
+
print("guard apps requires a harness.", file=sys.stderr)
|
|
757
|
+
return 2
|
|
758
|
+
if apps_command == "test":
|
|
759
|
+
try:
|
|
760
|
+
payload = build_harness_verification(harness, context, store)
|
|
761
|
+
except ValueError as error:
|
|
762
|
+
print(str(error), file=sys.stderr)
|
|
763
|
+
return 2
|
|
764
|
+
_emit("apps", payload, getattr(args, "json", False))
|
|
765
|
+
return 0
|
|
766
|
+
|
|
767
|
+
if apps_command in {"connect", "repair"} and bool(getattr(args, "dry_run", False)):
|
|
768
|
+
try:
|
|
769
|
+
payload = build_harness_setup_plan(apps_command, harness, context, dry_run=True)
|
|
770
|
+
except ValueError as error:
|
|
771
|
+
print(str(error), file=sys.stderr)
|
|
772
|
+
return 2
|
|
773
|
+
_emit("apps", payload, getattr(args, "json", False))
|
|
774
|
+
return 0
|
|
775
|
+
|
|
776
|
+
if apps_command == "disconnect":
|
|
777
|
+
try:
|
|
778
|
+
canonical_harness = get_adapter(harness).harness
|
|
779
|
+
except ValueError as error:
|
|
780
|
+
print(str(error), file=sys.stderr)
|
|
781
|
+
return 2
|
|
782
|
+
expected_confirmation = uninstall_confirmation_token(canonical_harness)
|
|
783
|
+
if getattr(args, "confirm", None) != expected_confirmation:
|
|
784
|
+
payload = {
|
|
785
|
+
"error": "confirmation_required",
|
|
786
|
+
"harness": canonical_harness,
|
|
787
|
+
"confirmation_phrase": expected_confirmation,
|
|
788
|
+
"confirm_command": f"hol-guard apps disconnect {canonical_harness} --confirm {expected_confirmation}",
|
|
789
|
+
}
|
|
790
|
+
_emit("apps", payload, getattr(args, "json", False))
|
|
791
|
+
return 2
|
|
792
|
+
|
|
793
|
+
install_command = "uninstall" if apps_command == "disconnect" else "install"
|
|
794
|
+
try:
|
|
795
|
+
payload = apply_managed_install(
|
|
796
|
+
install_command,
|
|
797
|
+
harness,
|
|
798
|
+
False,
|
|
799
|
+
context,
|
|
800
|
+
store,
|
|
801
|
+
workspace,
|
|
802
|
+
_now(),
|
|
803
|
+
)
|
|
804
|
+
except ValueError as error:
|
|
805
|
+
print(str(error), file=sys.stderr)
|
|
806
|
+
return 2
|
|
807
|
+
payload["action"] = apps_command
|
|
808
|
+
_emit("apps", payload, getattr(args, "json", False))
|
|
809
|
+
return 0
|
|
810
|
+
|
|
811
|
+
|
|
705
812
|
def _build_cisco_scan_options(mode: str) -> ScanOptions:
|
|
706
813
|
return ScanOptions(cisco_skill_scan=mode, cisco_mcp_scan=mode)
|
|
707
814
|
|
|
@@ -879,6 +986,9 @@ def run_guard_command(
|
|
|
879
986
|
_emit("detect", payload, getattr(args, "json", False))
|
|
880
987
|
return 0
|
|
881
988
|
|
|
989
|
+
if args.guard_command == "apps":
|
|
990
|
+
return _run_apps_command(args, context, store, str(workspace) if workspace else None)
|
|
991
|
+
|
|
882
992
|
if args.guard_command == "install":
|
|
883
993
|
try:
|
|
884
994
|
payload = apply_managed_install(
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Helpers for Guard harness install and uninstall flows."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import glob as globlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from ..adapters import get_adapter, list_adapters
|
|
9
|
+
from ..adapters.base import HarnessAdapter, HarnessContext
|
|
10
|
+
from ..adapters.contracts import contract_for
|
|
11
|
+
from ..consumer import detect_all
|
|
12
|
+
from ..runtime.skill_protection import build_skill_identity, detect_skill_content_risk
|
|
13
|
+
from ..store import GuardStore
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def apply_managed_install(
|
|
17
|
+
command: str,
|
|
18
|
+
requested_harness: str | None,
|
|
19
|
+
install_all: bool,
|
|
20
|
+
context: HarnessContext,
|
|
21
|
+
store: GuardStore,
|
|
22
|
+
workspace: str | None,
|
|
23
|
+
now: str,
|
|
24
|
+
) -> dict[str, object]:
|
|
25
|
+
targets = _resolve_targets(command, requested_harness, install_all, context, store)
|
|
26
|
+
active = command == "install"
|
|
27
|
+
managed_installs: list[dict[str, object]] = []
|
|
28
|
+
for harness in targets:
|
|
29
|
+
adapter = get_adapter(harness)
|
|
30
|
+
canonical_harness = adapter.harness
|
|
31
|
+
manifest = adapter.install(context) if active else adapter.uninstall(context)
|
|
32
|
+
store.set_managed_install(canonical_harness, active, workspace, manifest, now)
|
|
33
|
+
managed_install = store.get_managed_install(canonical_harness)
|
|
34
|
+
if managed_install is not None:
|
|
35
|
+
managed_installs.append(managed_install)
|
|
36
|
+
payload: dict[str, object] = {
|
|
37
|
+
"managed_installs": managed_installs,
|
|
38
|
+
"auto_detected": requested_harness is None or install_all,
|
|
39
|
+
}
|
|
40
|
+
if len(managed_installs) == 1:
|
|
41
|
+
payload["managed_install"] = managed_installs[0]
|
|
42
|
+
if active and context.workspace_dir is not None:
|
|
43
|
+
skill_scan = scan_workspace_skills(context.workspace_dir, store, now)
|
|
44
|
+
if skill_scan:
|
|
45
|
+
payload["skill_scan"] = skill_scan
|
|
46
|
+
return payload
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def list_harness_setup_items(context: HarnessContext, store: GuardStore | None = None) -> list[dict[str, object]]:
|
|
50
|
+
items: list[dict[str, object]] = []
|
|
51
|
+
for adapter in list_adapters():
|
|
52
|
+
detection = _safe_setup_detection(adapter, context, store)
|
|
53
|
+
detected = detection["installed"] or detection["command_available"] or bool(detection["config_paths"])
|
|
54
|
+
if detection["installed"]:
|
|
55
|
+
status = "protected"
|
|
56
|
+
elif detected:
|
|
57
|
+
status = "found"
|
|
58
|
+
else:
|
|
59
|
+
status = "not_found"
|
|
60
|
+
items.append(
|
|
61
|
+
{
|
|
62
|
+
"harness": adapter.harness,
|
|
63
|
+
"status": status,
|
|
64
|
+
"installed": detection["installed"],
|
|
65
|
+
"command_available": detection["command_available"],
|
|
66
|
+
"config_paths": detection["config_paths"],
|
|
67
|
+
"artifact_count": 0,
|
|
68
|
+
**adapter.setup_contract().to_dict(),
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
return items
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def build_harness_setup_plan(
|
|
75
|
+
action: str,
|
|
76
|
+
requested_harness: str,
|
|
77
|
+
context: HarnessContext,
|
|
78
|
+
*,
|
|
79
|
+
dry_run: bool,
|
|
80
|
+
) -> dict[str, object]:
|
|
81
|
+
adapter = get_adapter(requested_harness)
|
|
82
|
+
contract = adapter.setup_contract()
|
|
83
|
+
if action == "repair":
|
|
84
|
+
steps = adapter.repair_steps()
|
|
85
|
+
elif action == "uninstall":
|
|
86
|
+
steps = ()
|
|
87
|
+
else:
|
|
88
|
+
steps = adapter.setup_steps()
|
|
89
|
+
payload: dict[str, object] = {
|
|
90
|
+
"harness": adapter.harness,
|
|
91
|
+
"action": action,
|
|
92
|
+
"dry_run": dry_run,
|
|
93
|
+
"contract": contract.to_dict(),
|
|
94
|
+
"steps": [step.to_dict() for step in steps],
|
|
95
|
+
"workspace": str(context.workspace_dir) if context.workspace_dir is not None else None,
|
|
96
|
+
}
|
|
97
|
+
if action == "uninstall":
|
|
98
|
+
confirmation_phrase = uninstall_confirmation_token(adapter.harness)
|
|
99
|
+
payload["confirmation_phrase"] = confirmation_phrase
|
|
100
|
+
payload["confirm_command"] = f"hol-guard apps disconnect {adapter.harness} --confirm {confirmation_phrase}"
|
|
101
|
+
payload["steps"] = [
|
|
102
|
+
{
|
|
103
|
+
"step_id": "disconnect",
|
|
104
|
+
"title": f"Disconnect {contract.display_name}",
|
|
105
|
+
"body": "Remove Guard managed config for this app.",
|
|
106
|
+
"command": ["hol-guard", "apps", "disconnect", adapter.harness],
|
|
107
|
+
"writes_config": True,
|
|
108
|
+
"requires_confirmation": True,
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
return payload
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def build_harness_verification(
|
|
115
|
+
requested_harness: str,
|
|
116
|
+
context: HarnessContext,
|
|
117
|
+
store: GuardStore | None = None,
|
|
118
|
+
) -> dict[str, object]:
|
|
119
|
+
adapter = get_adapter(requested_harness)
|
|
120
|
+
detection = _safe_setup_detection(adapter, context, store)
|
|
121
|
+
return {
|
|
122
|
+
"harness": adapter.harness,
|
|
123
|
+
"safe": True,
|
|
124
|
+
"contract": adapter.setup_contract().to_dict(),
|
|
125
|
+
"verification": {
|
|
126
|
+
"checked": True,
|
|
127
|
+
"writes_config": False,
|
|
128
|
+
"installed": detection["installed"],
|
|
129
|
+
"command_available": detection["command_available"],
|
|
130
|
+
"config_paths": detection["config_paths"],
|
|
131
|
+
"artifact_count": 0,
|
|
132
|
+
"warnings": [],
|
|
133
|
+
"steps": [step.to_dict() for step in adapter.verify_steps()],
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def uninstall_confirmation_token(harness: str) -> str:
|
|
139
|
+
return f"disconnect-{harness}"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _safe_setup_detection(
|
|
143
|
+
adapter: HarnessAdapter,
|
|
144
|
+
context: HarnessContext,
|
|
145
|
+
store: GuardStore | None,
|
|
146
|
+
) -> dict[str, object]:
|
|
147
|
+
managed = store.get_managed_install(adapter.harness) if store is not None else None
|
|
148
|
+
protection_contract = contract_for(adapter.harness)
|
|
149
|
+
config_paths = protection_contract.config_paths if protection_contract is not None else ()
|
|
150
|
+
return {
|
|
151
|
+
"installed": bool(managed and managed.get("active")),
|
|
152
|
+
"command_available": adapter.resolved_executable(context) is not None,
|
|
153
|
+
"config_paths": _existing_contract_config_paths(config_paths, context),
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _existing_contract_config_paths(config_paths: tuple[str, ...], context: HarnessContext) -> list[str]:
|
|
158
|
+
existing: list[str] = []
|
|
159
|
+
for config_path in config_paths:
|
|
160
|
+
for candidate in _contract_config_path_candidates(config_path, context):
|
|
161
|
+
if candidate.exists():
|
|
162
|
+
existing.append(str(candidate))
|
|
163
|
+
return sorted(dict.fromkeys(existing))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _contract_config_path_candidates(config_path: str, context: HarnessContext) -> tuple[Path, ...]:
|
|
167
|
+
expanded_path = _expand_contract_config_path(config_path, context)
|
|
168
|
+
if globlib.has_magic(str(expanded_path)):
|
|
169
|
+
return tuple(sorted(Path(path) for path in globlib.glob(str(expanded_path))))
|
|
170
|
+
return (expanded_path,)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _expand_contract_config_path(config_path: str, context: HarnessContext) -> Path:
|
|
174
|
+
path = Path(config_path)
|
|
175
|
+
if path.parts and path.parts[0] == "~":
|
|
176
|
+
return context.home_dir.joinpath(*path.parts[1:])
|
|
177
|
+
if path.is_absolute():
|
|
178
|
+
return path
|
|
179
|
+
return context.home_dir / path
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def scan_workspace_skills(
|
|
183
|
+
workspace_dir: Path,
|
|
184
|
+
store: GuardStore,
|
|
185
|
+
now: str,
|
|
186
|
+
) -> list[dict[str, object]]:
|
|
187
|
+
"""Scan SKILL.md files in workspace and return risk summaries for any findings."""
|
|
188
|
+
results: list[dict[str, object]] = []
|
|
189
|
+
skills_dirs = [
|
|
190
|
+
workspace_dir / ".codex" / "skills",
|
|
191
|
+
workspace_dir / ".agents" / "skills",
|
|
192
|
+
workspace_dir / "skills",
|
|
193
|
+
]
|
|
194
|
+
for skills_dir in skills_dirs:
|
|
195
|
+
if not skills_dir.is_dir():
|
|
196
|
+
continue
|
|
197
|
+
for skill_path in sorted(skills_dir.rglob("SKILL.md")):
|
|
198
|
+
try:
|
|
199
|
+
content = skill_path.read_text(encoding="utf-8", errors="replace")
|
|
200
|
+
except OSError:
|
|
201
|
+
continue
|
|
202
|
+
identity = build_skill_identity(content, skill_path=str(skill_path))
|
|
203
|
+
artifact_id = f"skill-path:{skill_path}"
|
|
204
|
+
stored = store.get_snapshot("skill_scan", artifact_id)
|
|
205
|
+
stored_hash = stored.get("identity_hash") if stored else None
|
|
206
|
+
if stored_hash == identity.identity_hash:
|
|
207
|
+
continue
|
|
208
|
+
signals = detect_skill_content_risk(content, skill_path=str(skill_path))
|
|
209
|
+
store.save_snapshot(
|
|
210
|
+
"skill_scan",
|
|
211
|
+
artifact_id,
|
|
212
|
+
{"identity_hash": identity.identity_hash, "skill_path": str(skill_path)},
|
|
213
|
+
identity.identity_hash,
|
|
214
|
+
now,
|
|
215
|
+
)
|
|
216
|
+
if signals:
|
|
217
|
+
results.append(
|
|
218
|
+
{
|
|
219
|
+
"skill_path": str(skill_path.relative_to(workspace_dir)),
|
|
220
|
+
"identity_hash": identity.identity_hash,
|
|
221
|
+
"risk_count": len(signals),
|
|
222
|
+
"severities": sorted({s.severity for s in signals}),
|
|
223
|
+
"signal_ids": [s.signal_id for s in signals],
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
return results
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _resolve_targets(
|
|
230
|
+
command: str,
|
|
231
|
+
requested_harness: str | None,
|
|
232
|
+
install_all: bool,
|
|
233
|
+
context: HarnessContext,
|
|
234
|
+
store: GuardStore,
|
|
235
|
+
) -> list[str]:
|
|
236
|
+
if requested_harness is not None and install_all:
|
|
237
|
+
raise ValueError("Pass either a harness or --all, not both.")
|
|
238
|
+
if requested_harness is not None and not install_all:
|
|
239
|
+
return [get_adapter(requested_harness).harness]
|
|
240
|
+
if not install_all:
|
|
241
|
+
action = "install" if command == "install" else "uninstall"
|
|
242
|
+
raise ValueError(f"Guard {action} requires a harness or --all.")
|
|
243
|
+
detected = {
|
|
244
|
+
detection.harness
|
|
245
|
+
for detection in detect_all(context)
|
|
246
|
+
if detection.installed
|
|
247
|
+
or detection.command_available
|
|
248
|
+
or len(detection.config_paths) > 0
|
|
249
|
+
or len(detection.artifacts) > 0
|
|
250
|
+
}
|
|
251
|
+
if command == "uninstall":
|
|
252
|
+
detected.update(
|
|
253
|
+
str(item.get("harness"))
|
|
254
|
+
for item in store.list_managed_installs()
|
|
255
|
+
if bool(item.get("active")) and isinstance(item.get("harness"), str)
|
|
256
|
+
)
|
|
257
|
+
targets = sorted(detected)
|
|
258
|
+
if targets:
|
|
259
|
+
return targets
|
|
260
|
+
action = "install" if command == "install" else "remove"
|
|
261
|
+
raise ValueError(
|
|
262
|
+
f"No supported harnesses were detected for Guard {action}. Pass a harness explicitly or configure one first."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
__all__ = [
|
|
267
|
+
"apply_managed_install",
|
|
268
|
+
"build_harness_setup_plan",
|
|
269
|
+
"build_harness_verification",
|
|
270
|
+
"list_harness_setup_items",
|
|
271
|
+
"scan_workspace_skills",
|
|
272
|
+
"uninstall_confirmation_token",
|
|
273
|
+
]
|