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.
Files changed (444) hide show
  1. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/base.py +24 -0
  5. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/contracts.py +146 -0
  6. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/commands.py +116 -6
  7. plugin_scanner-2.0.148/src/codex_plugin_scanner/guard/cli/install_commands.py +273 -0
  8. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/server.py +107 -0
  9. plugin_scanner-2.0.148/src/codex_plugin_scanner/guard/runtime/cisco_evidence.py +175 -0
  10. plugin_scanner-2.0.148/src/codex_plugin_scanner/guard/runtime/scanner_cache.py +10 -0
  11. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/signals.py +115 -0
  12. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store.py +83 -0
  13. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +14 -2
  14. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +81 -6
  15. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/version.py +1 -1
  16. plugin_scanner-2.0.148/tests/test_guard_cisco_evidence.py +245 -0
  17. plugin_scanner-2.0.148/tests/test_guard_harness_setup.py +335 -0
  18. plugin_scanner-2.0.146/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -131
  19. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/Dockerfile +0 -0
  20. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/build.sh +0 -0
  21. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/project.yaml +0 -0
  22. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  23. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.dockerignore +0 -0
  24. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/CODEOWNERS +0 -0
  25. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  26. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  27. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  28. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/dependabot.yml +0 -0
  29. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/ci.yml +0 -0
  30. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/codeql.yml +0 -0
  31. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/dependabot-uv-lock.yml +0 -0
  32. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/fuzz.yml +0 -0
  33. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/harness-smoke.yml +0 -0
  34. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/publish.yml +0 -0
  35. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.github/workflows/scorecard.yml +0 -0
  36. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.gitignore +0 -0
  37. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/.pre-commit-hooks.yaml +0 -0
  38. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/CONTRIBUTING.md +0 -0
  39. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/Dockerfile +0 -0
  40. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/LICENSE +0 -0
  41. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/README.md +0 -0
  42. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/SECURITY.md +0 -0
  43. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/index.html +0 -0
  44. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/package.json +0 -0
  45. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/pnpm-lock.yaml +0 -0
  46. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/apple-touch-icon.png +0 -0
  47. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  48. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/brand/Logo_Whole.png +0 -0
  49. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/favicon-16x16.png +0 -0
  50. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/favicon-32x32.png +0 -0
  51. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/public/favicon.ico +0 -0
  52. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/app.tsx +0 -0
  53. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-layout.test.ts +0 -0
  54. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-layout.tsx +0 -0
  55. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-primitives.tsx +0 -0
  56. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-review-cards.tsx +0 -0
  57. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/approval-center-utils.ts +0 -0
  58. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/data-flow-evidence-card.tsx +0 -0
  59. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/fleet-workspace.tsx +0 -0
  60. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-api.test.ts +0 -0
  61. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-api.ts +0 -0
  62. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-demo.ts +0 -0
  63. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/guard-types.ts +0 -0
  64. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/home-dashboard.tsx +0 -0
  65. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/main.tsx +0 -0
  66. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/queue-state.test.ts +0 -0
  67. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/queue-state.ts +0 -0
  68. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/receipts-workspace.test.ts +0 -0
  69. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/receipts-workspace.tsx +0 -0
  70. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/risk-signal-cards.test.ts +0 -0
  71. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/risk-signal-cards.tsx +0 -0
  72. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/runtime-overview.test.ts +0 -0
  73. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/runtime-overview.tsx +0 -0
  74. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/settings-workspace.test.ts +0 -0
  75. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/settings-workspace.tsx +0 -0
  76. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/styles.css +0 -0
  77. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/src/vite-env.d.ts +0 -0
  78. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/tsconfig.json +0 -0
  79. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/dashboard/vite.config.ts +0 -0
  80. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docker-requirements.txt +0 -0
  81. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/approval-audit.md +0 -0
  82. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/architecture.md +0 -0
  83. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/get-started.md +0 -0
  84. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/harness-support.md +0 -0
  85. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/local-vs-cloud.md +0 -0
  86. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/release-checklist.md +0 -0
  87. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/release-notes.md +0 -0
  88. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/smoke-tests.md +0 -0
  89. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/guard/testing-matrix.md +0 -0
  90. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/trust/mcp-trust-draft.md +0 -0
  91. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/trust/plugin-trust-draft.md +0 -0
  92. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/docs/trust/skill-trust-local.md +0 -0
  93. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/fuzzers/manifest_fuzzer.py +0 -0
  94. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/requirements.txt +0 -0
  95. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/schemas/plugin-quality.v1.json +0 -0
  96. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/schemas/scan-result.v1.json +0 -0
  97. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/schemas/verify-result.v1.json +0 -0
  98. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/__init__.py +0 -0
  99. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/action_runner.py +0 -0
  100. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  101. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  102. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  103. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/claude.py +0 -0
  104. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  105. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  106. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  107. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  108. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  109. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  110. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  111. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  112. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  113. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/security.py +0 -0
  114. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  115. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/cli.py +0 -0
  116. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/cli_ui.py +0 -0
  117. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/config.py +0 -0
  118. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  119. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  120. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  121. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  122. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  123. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  124. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  125. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  126. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  127. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/github_reporting.py +0 -0
  128. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  129. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
  130. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  131. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  132. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  133. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  134. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  135. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  136. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  137. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  138. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  139. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  140. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  141. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  142. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  143. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  144. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  145. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  146. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  147. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  148. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  149. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  150. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  151. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  152. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  153. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  154. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  155. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  156. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  157. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  158. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/config.py +0 -0
  159. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  160. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  161. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  162. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  163. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  164. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  165. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  166. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  167. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  168. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  169. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  170. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  171. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  172. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  173. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  174. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/incident.py +0 -0
  175. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  176. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  177. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/models.py +0 -0
  178. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  179. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  180. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/protect.py +0 -0
  181. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  182. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  183. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  184. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  185. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  186. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  187. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  188. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/risk.py +0 -0
  189. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  190. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/action_identity.py +0 -0
  191. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  192. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/advisory_escalation.py +0 -0
  193. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/advisory_matchers.py +0 -0
  194. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
  195. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
  196. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
  197. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  198. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
  199. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
  200. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
  201. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  202. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
  203. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
  204. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  205. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  206. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
  207. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
  208. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
  209. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
  210. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  211. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
  212. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/runtime/threat_intel.py +0 -0
  213. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  214. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  215. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  216. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  217. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/shims.py +0 -0
  218. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  219. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  220. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
  221. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/store_threat_intel.py +0 -0
  222. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/guard/types.py +0 -0
  223. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  224. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  225. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  226. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/models.py +0 -0
  227. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/path_support.py +0 -0
  228. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/policy.py +0 -0
  229. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  230. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/repo_detect.py +0 -0
  231. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/reporting.py +0 -0
  232. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  233. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/rules/registry.py +0 -0
  234. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/rules/specs.py +0 -0
  235. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/scanner.py +0 -0
  236. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/submission.py +0 -0
  237. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/suppressions.py +0 -0
  238. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  239. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  240. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  241. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_models.py +0 -0
  242. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  243. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  244. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  245. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/trust_specs.py +0 -0
  246. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/src/codex_plugin_scanner/verification.py +0 -0
  247. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/__init__.py +0 -0
  248. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/conftest.py +0 -0
  249. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/__init__.py +0 -0
  250. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  251. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  252. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/bad-plugin/secrets.js +0 -0
  253. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  254. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  255. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/README.md +0 -0
  256. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  257. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  258. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  259. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/code-quality-bad/evil.js +0 -0
  260. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/code-quality-bad/inject.js +0 -0
  261. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  262. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  263. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/README.md +0 -0
  264. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  265. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  266. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  267. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  268. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/.codexignore +0 -0
  269. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/LICENSE +0 -0
  270. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/README.md +0 -0
  271. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  272. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  273. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  274. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  275. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  276. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  277. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/README.md +0 -0
  278. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-docs-fake-token.py +0 -0
  279. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-health-endpoint.py +0 -0
  280. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-nvmrc-fake-creds.py +0 -0
  281. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/benign-source-search.py +0 -0
  282. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/canary-exfil-encoded.py +0 -0
  283. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/canary-exfil.py +0 -0
  284. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/expected-decisions.json +0 -0
  285. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-dockerfile.txt +0 -0
  286. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-encoded-shell-exfil.py +0 -0
  287. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-github-action.yml +0 -0
  288. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-mcp-delete.md +0 -0
  289. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-mcp-secret-read.md +0 -0
  290. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-mcp-skill-exfil.md +0 -0
  291. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-npm-postinstall.js +0 -0
  292. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-prompt-env-read.md +0 -0
  293. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-prompt-guard-bypass.md +0 -0
  294. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-prompt-npmrc-read.md +0 -0
  295. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/malicious-python-setup.py +0 -0
  296. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/guard-red-team/smoke-evidence-template.json +0 -0
  297. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  298. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  299. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  300. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  301. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  302. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  303. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  304. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  305. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  306. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  307. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  308. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  309. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  310. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  311. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/mcp-canary-server.py +0 -0
  312. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  313. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  314. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/mit-license/LICENSE +0 -0
  315. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  316. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  317. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  318. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  319. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  320. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  321. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  322. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  323. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  324. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  325. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  326. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  327. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  328. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  329. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  330. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  331. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  332. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  333. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/LICENSE +0 -0
  334. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/README.md +0 -0
  335. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  336. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  337. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  338. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  339. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  340. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
  341. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
  342. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
  343. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
  344. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
  345. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
  346. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
  347. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  348. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  349. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  350. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test-trust-scoring.py +0 -0
  351. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test-trust-specs.py +0 -0
  352. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_action_runner.py +0 -0
  353. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_best_practices.py +0 -0
  354. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_cisco_install_surfaces.py +0 -0
  355. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_cli.py +0 -0
  356. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_code_quality.py +0 -0
  357. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_config.py +0 -0
  358. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_coverage_remaining.py +0 -0
  359. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_ecosystems.py +0 -0
  360. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_edge_cases.py +0 -0
  361. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_final_coverage.py +0 -0
  362. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_access_graph.py +0 -0
  363. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_action_identity.py +0 -0
  364. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_advisory_escalation.py +0 -0
  365. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_continuity.py +0 -0
  366. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_copy_commands.py +0 -0
  367. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_store_dedup.py +0 -0
  368. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approval_store_scale.py +0 -0
  369. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_approvals.py +0 -0
  370. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_bootstrap.py +0 -0
  371. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_canary_fixtures.py +0 -0
  372. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_capabilities.py +0 -0
  373. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_claude_adapter.py +0 -0
  374. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_cli.py +0 -0
  375. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_cloud_local_sync.py +0 -0
  376. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_codex_e2e.py +0 -0
  377. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_codex_install.py +0 -0
  378. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_codex_proxy.py +0 -0
  379. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_config_paths.py +0 -0
  380. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_connect_flow.py +0 -0
  381. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_consumer_mode.py +0 -0
  382. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_copilot_adapter.py +0 -0
  383. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_copilot_proxy.py +0 -0
  384. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_daemon_manager.py +0 -0
  385. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_daemon_perf.py +0 -0
  386. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_daemon_repair_perf.py +0 -0
  387. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_data_flow.py +0 -0
  388. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_decision_propagation.py +0 -0
  389. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_event_schema_v1.py +0 -0
  390. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_events.py +0 -0
  391. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_evidence_store.py +0 -0
  392. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_harness_contracts.py +0 -0
  393. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_harness_smoke.py +0 -0
  394. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_launch_env.py +0 -0
  395. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_mcp_protection.py +0 -0
  396. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_opencode_proxy.py +0 -0
  397. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_policy_dedup.py +0 -0
  398. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_product_flow.py +0 -0
  399. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_prompt_injection.py +0 -0
  400. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_protect.py +0 -0
  401. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_queue_api_contract.py +0 -0
  402. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_queue_contract.py +0 -0
  403. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_red_team.py +0 -0
  404. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_render.py +0 -0
  405. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_resolution_copy.py +0 -0
  406. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_risk.py +0 -0
  407. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime.py +0 -0
  408. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_action_harnesses.py +0 -0
  409. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_actions.py +0 -0
  410. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_decisions.py +0 -0
  411. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_detectors.py +0 -0
  412. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_runtime_signals.py +0 -0
  413. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_safe_decode.py +0 -0
  414. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_sandbox.py +0 -0
  415. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_skill_protection.py +0 -0
  416. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_store_migrations.py +0 -0
  417. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_supply_chain.py +0 -0
  418. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_surface_server.py +0 -0
  419. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_threat_intel.py +0 -0
  420. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_verdicts.py +0 -0
  421. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_guard_web_recovery.py +0 -0
  422. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_hermes_adapter.py +0 -0
  423. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_integration.py +0 -0
  424. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_lint_fixes.py +0 -0
  425. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_live_cisco_smoke.py +0 -0
  426. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_manifest.py +0 -0
  427. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_marketplace.py +0 -0
  428. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_mcp_security.py +0 -0
  429. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_openclaw_adapter.py +0 -0
  430. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_operational_security.py +0 -0
  431. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_policy.py +0 -0
  432. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_quality_artifact.py +0 -0
  433. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_rule_registry.py +0 -0
  434. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_scanner.py +0 -0
  435. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_schema_contracts.py +0 -0
  436. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_security.py +0 -0
  437. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_security_ops.py +0 -0
  438. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_skill_security.py +0 -0
  439. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_submission.py +0 -0
  440. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_trust_scoring.py +0 -0
  441. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_trust_specs.py +0 -0
  442. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_verification.py +0 -0
  443. {plugin_scanner-2.0.146 → plugin_scanner-2.0.148}/tests/test_versioning.py +0 -0
  444. {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.146
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.146"
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.146"
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"
@@ -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 = (
@@ -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 apply_managed_install
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(parser: argparse.ArgumentParser) -> None:
684
- parser.add_argument("--home")
685
- parser.add_argument("--guard-home")
686
- parser.add_argument("--workspace")
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
+ ]