plugin-scanner 2.0.151__tar.gz → 2.0.153__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 (454) hide show
  1. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/commands.py +107 -3
  5. plugin_scanner-2.0.153/src/codex_plugin_scanner/guard/runtime/composition_rules.py +143 -0
  6. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/detectors.py +101 -0
  7. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/runner.py +9 -0
  8. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/version.py +1 -1
  9. plugin_scanner-2.0.153/tests/test_guard_bypass_detector.py +219 -0
  10. plugin_scanner-2.0.153/tests/test_guard_daemon_cli.py +144 -0
  11. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.clusterfuzzlite/Dockerfile +0 -0
  12. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.clusterfuzzlite/build.sh +0 -0
  13. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.clusterfuzzlite/project.yaml +0 -0
  14. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  15. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.dockerignore +0 -0
  16. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/CODEOWNERS +0 -0
  17. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  18. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  19. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  20. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/dependabot.yml +0 -0
  21. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/workflows/ci.yml +0 -0
  22. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/workflows/codeql.yml +0 -0
  23. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/workflows/dependabot-uv-lock.yml +0 -0
  24. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/workflows/fuzz.yml +0 -0
  25. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/workflows/harness-smoke.yml +0 -0
  26. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/workflows/publish.yml +0 -0
  27. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.github/workflows/scorecard.yml +0 -0
  28. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.gitignore +0 -0
  29. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/.pre-commit-hooks.yaml +0 -0
  30. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/CONTRIBUTING.md +0 -0
  31. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/Dockerfile +0 -0
  32. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/LICENSE +0 -0
  33. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/README.md +0 -0
  34. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/SECURITY.md +0 -0
  35. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/index.html +0 -0
  36. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/package.json +0 -0
  37. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/pnpm-lock.yaml +0 -0
  38. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/public/apple-touch-icon.png +0 -0
  39. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  40. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/public/brand/Logo_Whole.png +0 -0
  41. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/public/favicon-16x16.png +0 -0
  42. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/public/favicon-32x32.png +0 -0
  43. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/public/favicon.ico +0 -0
  44. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/app.tsx +0 -0
  45. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/approval-center-layout.test.ts +0 -0
  46. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/approval-center-layout.tsx +0 -0
  47. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/approval-center-primitives.tsx +0 -0
  48. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/approval-center-review-cards.tsx +0 -0
  49. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/approval-center-utils.ts +0 -0
  50. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/data-flow-evidence-card.tsx +0 -0
  51. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/fleet-workspace.tsx +0 -0
  52. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/guard-api.test.ts +0 -0
  53. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/guard-api.ts +0 -0
  54. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/guard-demo.ts +0 -0
  55. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/guard-types.ts +0 -0
  56. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/home-dashboard.tsx +0 -0
  57. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/main.tsx +0 -0
  58. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/queue-chip-filter.tsx +0 -0
  59. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/queue-state.test.ts +0 -0
  60. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/queue-state.ts +0 -0
  61. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/receipts-workspace.test.ts +0 -0
  62. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/receipts-workspace.tsx +0 -0
  63. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/risk-signal-cards.test.ts +0 -0
  64. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/risk-signal-cards.tsx +0 -0
  65. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/runtime-overview.test.ts +0 -0
  66. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/runtime-overview.tsx +0 -0
  67. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/scanner-evidence-badge.tsx +0 -0
  68. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/settings-workspace.test.ts +0 -0
  69. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/settings-workspace.tsx +0 -0
  70. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/styles.css +0 -0
  71. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/vite-env.d.ts +0 -0
  72. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/src/watched-app-card.tsx +0 -0
  73. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/tsconfig.json +0 -0
  74. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/dashboard/vite.config.ts +0 -0
  75. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docker-requirements.txt +0 -0
  76. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/approval-audit.md +0 -0
  77. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/architecture.md +0 -0
  78. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/get-started.md +0 -0
  79. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/harness-support.md +0 -0
  80. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/local-vs-cloud.md +0 -0
  81. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/release-checklist.md +0 -0
  82. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/release-notes.md +0 -0
  83. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/smoke-tests.md +0 -0
  84. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/guard/testing-matrix.md +0 -0
  85. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/trust/mcp-trust-draft.md +0 -0
  86. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/trust/plugin-trust-draft.md +0 -0
  87. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/docs/trust/skill-trust-local.md +0 -0
  88. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/fuzzers/manifest_fuzzer.py +0 -0
  89. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/requirements.txt +0 -0
  90. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/schemas/plugin-quality.v1.json +0 -0
  91. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/schemas/scan-result.v1.json +0 -0
  92. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/schemas/verify-result.v1.json +0 -0
  93. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/__init__.py +0 -0
  94. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/action_runner.py +0 -0
  95. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  96. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  97. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  98. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/claude.py +0 -0
  99. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  100. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  101. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  102. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  103. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  104. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  105. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  106. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  107. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  108. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/security.py +0 -0
  109. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  110. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/cli.py +0 -0
  111. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/cli_ui.py +0 -0
  112. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/config.py +0 -0
  113. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  114. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  115. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  116. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  117. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  118. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  119. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  120. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  121. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  122. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/github_reporting.py +0 -0
  123. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  124. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
  125. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  126. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  127. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  128. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  129. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  130. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  131. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/contracts.py +0 -0
  132. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  133. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  134. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  135. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  136. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  137. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  138. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  139. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  140. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  141. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  142. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  143. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  144. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  145. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  146. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  147. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  148. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  149. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  150. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  151. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  152. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  153. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  154. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  155. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  156. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/config.py +0 -0
  157. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  158. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  159. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  160. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  161. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  162. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  163. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  164. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  165. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  166. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  167. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  168. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  169. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  170. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  171. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  172. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  173. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/incident.py +0 -0
  174. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  175. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  176. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/models.py +0 -0
  177. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  178. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  179. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/protect.py +0 -0
  180. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  181. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  182. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  183. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  184. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  185. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  186. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  187. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/risk.py +0 -0
  188. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  189. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/action_identity.py +0 -0
  190. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  191. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/advisory_escalation.py +0 -0
  192. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/advisory_matchers.py +0 -0
  193. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/cisco_evidence.py +0 -0
  194. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/cisco_preflight.py +0 -0
  195. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
  196. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
  197. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
  198. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  199. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/false_positive_rules.py +0 -0
  200. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
  201. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/persistence_rules.py +0 -0
  202. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
  203. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
  204. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
  205. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/scanner_cache.py +0 -0
  206. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  207. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  208. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
  209. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
  210. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  211. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
  212. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
  213. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  214. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
  215. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/runtime/threat_intel.py +0 -0
  216. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  217. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  218. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  219. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  220. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/shims.py +0 -0
  221. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/store.py +0 -0
  222. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  223. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  224. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
  225. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/store_threat_intel.py +0 -0
  226. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/guard/types.py +0 -0
  227. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  228. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  229. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  230. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  231. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  232. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/models.py +0 -0
  233. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/path_support.py +0 -0
  234. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/policy.py +0 -0
  235. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  236. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/repo_detect.py +0 -0
  237. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/reporting.py +0 -0
  238. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  239. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/rules/registry.py +0 -0
  240. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/rules/specs.py +0 -0
  241. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/scanner.py +0 -0
  242. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/submission.py +0 -0
  243. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/suppressions.py +0 -0
  244. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  245. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  246. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  247. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_models.py +0 -0
  248. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  249. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  250. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  251. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/trust_specs.py +0 -0
  252. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/src/codex_plugin_scanner/verification.py +0 -0
  253. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/__init__.py +0 -0
  254. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/conftest.py +0 -0
  255. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/__init__.py +0 -0
  256. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  257. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  258. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/bad-plugin/secrets.js +0 -0
  259. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  260. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  261. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/claude-plugin-good/README.md +0 -0
  262. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  263. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  264. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  265. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/code-quality-bad/evil.js +0 -0
  266. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/code-quality-bad/inject.js +0 -0
  267. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  268. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  269. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/gemini-extension-good/README.md +0 -0
  270. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  271. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  272. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  273. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  274. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/.codexignore +0 -0
  275. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/LICENSE +0 -0
  276. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/README.md +0 -0
  277. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  278. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  279. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  280. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  281. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  282. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  283. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/README.md +0 -0
  284. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/benign-docs-fake-token.py +0 -0
  285. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/benign-health-endpoint.py +0 -0
  286. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/benign-nvmrc-fake-creds.py +0 -0
  287. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/benign-source-search.py +0 -0
  288. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/canary-exfil-encoded.py +0 -0
  289. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/canary-exfil.py +0 -0
  290. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/expected-decisions.json +0 -0
  291. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-dockerfile.txt +0 -0
  292. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-encoded-shell-exfil.py +0 -0
  293. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-github-action.yml +0 -0
  294. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-mcp-delete.md +0 -0
  295. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-mcp-secret-read.md +0 -0
  296. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-mcp-skill-exfil.md +0 -0
  297. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-npm-postinstall.js +0 -0
  298. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-prompt-env-read.md +0 -0
  299. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-prompt-guard-bypass.md +0 -0
  300. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-prompt-npmrc-read.md +0 -0
  301. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/malicious-python-setup.py +0 -0
  302. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/guard-red-team/smoke-evidence-template.json +0 -0
  303. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  304. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  305. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  306. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  307. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  308. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  309. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  310. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  311. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  312. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  313. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  314. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  315. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  316. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  317. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/mcp-canary-server.py +0 -0
  318. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  319. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  320. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/mit-license/LICENSE +0 -0
  321. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  322. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  323. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  324. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  325. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  326. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  327. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  328. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  329. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  330. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  331. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  332. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  333. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  334. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  335. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  336. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  337. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  338. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  339. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/opencode-good/LICENSE +0 -0
  340. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/opencode-good/README.md +0 -0
  341. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  342. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  343. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  344. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  345. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  346. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
  347. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
  348. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
  349. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
  350. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
  351. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
  352. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
  353. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  354. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  355. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  356. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test-trust-scoring.py +0 -0
  357. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test-trust-specs.py +0 -0
  358. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_action_runner.py +0 -0
  359. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_best_practices.py +0 -0
  360. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_cisco_install_surfaces.py +0 -0
  361. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_cli.py +0 -0
  362. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_code_quality.py +0 -0
  363. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_config.py +0 -0
  364. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_coverage_remaining.py +0 -0
  365. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_ecosystems.py +0 -0
  366. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_edge_cases.py +0 -0
  367. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_final_coverage.py +0 -0
  368. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_access_graph.py +0 -0
  369. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_action_identity.py +0 -0
  370. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_advisory_escalation.py +0 -0
  371. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_approval_continuity.py +0 -0
  372. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_approval_copy_commands.py +0 -0
  373. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_approval_store_dedup.py +0 -0
  374. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_approval_store_scale.py +0 -0
  375. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_approvals.py +0 -0
  376. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_bootstrap.py +0 -0
  377. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_canary_fixtures.py +0 -0
  378. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_capabilities.py +0 -0
  379. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_cisco_evidence.py +0 -0
  380. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_cisco_runtime_cli.py +0 -0
  381. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_claude_adapter.py +0 -0
  382. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_cli.py +0 -0
  383. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_cloud_local_sync.py +0 -0
  384. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_codex_e2e.py +0 -0
  385. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_codex_install.py +0 -0
  386. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_codex_proxy.py +0 -0
  387. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_config_paths.py +0 -0
  388. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_connect_flow.py +0 -0
  389. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_consumer_mode.py +0 -0
  390. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_copilot_adapter.py +0 -0
  391. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_copilot_proxy.py +0 -0
  392. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_daemon_manager.py +0 -0
  393. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_daemon_perf.py +0 -0
  394. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_daemon_repair_perf.py +0 -0
  395. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_data_flow.py +0 -0
  396. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_decision_propagation.py +0 -0
  397. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_detector_fp.py +0 -0
  398. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_event_schema_v1.py +0 -0
  399. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_events.py +0 -0
  400. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_evidence_store.py +0 -0
  401. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_harness_contracts.py +0 -0
  402. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_harness_setup.py +0 -0
  403. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_harness_smoke.py +0 -0
  404. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_launch_env.py +0 -0
  405. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_mcp_protection.py +0 -0
  406. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_opencode_proxy.py +0 -0
  407. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_policy_dedup.py +0 -0
  408. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_product_flow.py +0 -0
  409. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_prompt_injection.py +0 -0
  410. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_protect.py +0 -0
  411. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_queue_api_contract.py +0 -0
  412. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_queue_contract.py +0 -0
  413. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_red_team.py +0 -0
  414. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_render.py +0 -0
  415. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_resolution_copy.py +0 -0
  416. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_risk.py +0 -0
  417. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_runtime.py +0 -0
  418. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_runtime_action_harnesses.py +0 -0
  419. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_runtime_actions.py +0 -0
  420. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_runtime_decisions.py +0 -0
  421. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_runtime_detectors.py +0 -0
  422. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_runtime_signals.py +0 -0
  423. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_safe_decode.py +0 -0
  424. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_sandbox.py +0 -0
  425. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_skill_protection.py +0 -0
  426. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_store_migrations.py +0 -0
  427. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_supply_chain.py +0 -0
  428. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_surface_server.py +0 -0
  429. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_threat_intel.py +0 -0
  430. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_verdicts.py +0 -0
  431. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_guard_web_recovery.py +0 -0
  432. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_hermes_adapter.py +0 -0
  433. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_integration.py +0 -0
  434. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_lint_fixes.py +0 -0
  435. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_live_cisco_smoke.py +0 -0
  436. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_manifest.py +0 -0
  437. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_marketplace.py +0 -0
  438. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_mcp_security.py +0 -0
  439. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_openclaw_adapter.py +0 -0
  440. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_operational_security.py +0 -0
  441. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_policy.py +0 -0
  442. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_quality_artifact.py +0 -0
  443. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_rule_registry.py +0 -0
  444. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_scanner.py +0 -0
  445. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_schema_contracts.py +0 -0
  446. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_security.py +0 -0
  447. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_security_ops.py +0 -0
  448. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_skill_security.py +0 -0
  449. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_submission.py +0 -0
  450. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_trust_scoring.py +0 -0
  451. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_trust_specs.py +0 -0
  452. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_verification.py +0 -0
  453. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/tests/test_versioning.py +0 -0
  454. {plugin_scanner-2.0.151 → plugin_scanner-2.0.153}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.151
3
+ Version: 2.0.153
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.151"
7
+ version = "2.0.153"
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.151"
7
+ version = "2.0.153"
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"
@@ -60,8 +60,18 @@ from ..consumer import (
60
60
  record_policy,
61
61
  run_consumer_scan,
62
62
  )
63
- from ..daemon import GuardDaemonServer, ensure_guard_daemon, load_guard_surface_daemon_client
64
- from ..daemon.manager import load_guard_daemon_auth_token
63
+ from ..daemon import (
64
+ GuardDaemonServer,
65
+ ensure_guard_daemon,
66
+ load_guard_surface_daemon_client,
67
+ repair_approval_center_locator,
68
+ )
69
+ from ..daemon.manager import (
70
+ _guard_daemon_pid_is_running,
71
+ _guard_daemon_pid_matches_command,
72
+ load_guard_daemon_auth_token,
73
+ load_guard_daemon_url,
74
+ )
65
75
  from ..incident import build_incident_context
66
76
  from ..mcp_tool_calls import (
67
77
  allow_tool_call,
@@ -661,7 +671,16 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
661
671
  _add_guard_common_args(daemon_parser)
662
672
  daemon_parser.add_argument("--serve", action="store_true")
663
673
  daemon_parser.add_argument("--port", type=int)
664
- daemon_parser.add_argument("--json", action="store_true")
674
+ daemon_subparsers = daemon_parser.add_subparsers(dest="daemon_command")
675
+ status_p = daemon_subparsers.add_parser("status", help="Show local Guard daemon status")
676
+ _add_guard_common_args(status_p)
677
+ status_p.add_argument("--json", action="store_true")
678
+ repair_p = daemon_subparsers.add_parser("repair", help="Repair stale Guard daemon state")
679
+ _add_guard_common_args(repair_p)
680
+ repair_p.add_argument("--json", action="store_true")
681
+ stop_p = daemon_subparsers.add_parser("stop", help="Stop a running Guard daemon")
682
+ _add_guard_common_args(stop_p)
683
+ stop_p.add_argument("--json", action="store_true")
665
684
 
666
685
  codex_proxy_parser = guard_subparsers.add_parser("codex-mcp-proxy", help=argparse.SUPPRESS)
667
686
  _add_guard_common_args(codex_proxy_parser)
@@ -1502,6 +1521,13 @@ def run_guard_command(
1502
1521
  return 2
1503
1522
 
1504
1523
  if args.guard_command == "daemon":
1524
+ daemon_command = getattr(args, "daemon_command", None)
1525
+ if daemon_command == "status":
1526
+ return _handle_daemon_status(guard_home, getattr(args, "json", False))
1527
+ if daemon_command == "repair":
1528
+ return _handle_daemon_repair(guard_home, getattr(args, "json", False))
1529
+ if daemon_command == "stop":
1530
+ return _handle_daemon_stop(guard_home, getattr(args, "json", False))
1505
1531
  daemon = GuardDaemonServer(store, port=args.port or 0)
1506
1532
  if args.serve:
1507
1533
  daemon.serve()
@@ -6531,3 +6557,81 @@ def _matching_advisories(store: GuardStore, publisher: object) -> list[dict[str,
6531
6557
  if not isinstance(publisher, str) or not publisher.strip():
6532
6558
  return []
6533
6559
  return [item for item in store.list_cached_advisories() if item.get("publisher") == publisher]
6560
+
6561
+
6562
+ def _handle_daemon_status(guard_home: Path, as_json: bool) -> int:
6563
+ from codex_plugin_scanner.version import __version__
6564
+
6565
+ url = load_guard_daemon_url(guard_home)
6566
+ running = False
6567
+ port: int | None = None
6568
+ pid: int | None = None
6569
+ state_path = guard_home / "daemon-state.json"
6570
+ if state_path.is_file():
6571
+ import json as _json
6572
+
6573
+ try:
6574
+ state = _json.loads(state_path.read_text())
6575
+ pid = state.get("pid") if isinstance(state, dict) else None
6576
+ port = state.get("port") if isinstance(state, dict) else None
6577
+ if (
6578
+ isinstance(pid, int)
6579
+ and pid > 0
6580
+ and _guard_daemon_pid_is_running(pid)
6581
+ and _guard_daemon_pid_matches_command(pid, expected_guard_home=guard_home)
6582
+ ):
6583
+ running = True
6584
+ except Exception:
6585
+ pass
6586
+ payload: dict[str, object] = {
6587
+ "running": running,
6588
+ "guard_home": str(guard_home),
6589
+ "version": __version__,
6590
+ }
6591
+ if port is not None:
6592
+ payload["port"] = port
6593
+ if pid is not None:
6594
+ payload["pid"] = pid
6595
+ if url is not None:
6596
+ payload["url"] = url
6597
+ _emit("daemon", payload, as_json)
6598
+ return 0
6599
+
6600
+
6601
+ def _handle_daemon_repair(guard_home: Path, as_json: bool) -> int:
6602
+ result = repair_approval_center_locator(guard_home)
6603
+ _emit("daemon", result, as_json)
6604
+ return 0
6605
+
6606
+
6607
+ def _handle_daemon_stop(guard_home: Path, as_json: bool) -> int:
6608
+ import json as _json
6609
+ import os
6610
+ import signal as _signal
6611
+
6612
+ state_path = guard_home / "daemon-state.json"
6613
+ stopped = False
6614
+ pid: int | None = None
6615
+ if state_path.is_file():
6616
+ try:
6617
+ state = _json.loads(state_path.read_text())
6618
+ pid = state.get("pid") if isinstance(state, dict) else None
6619
+ if (
6620
+ isinstance(pid, int)
6621
+ and pid > 0
6622
+ and _guard_daemon_pid_is_running(pid)
6623
+ and _guard_daemon_pid_matches_command(pid, expected_guard_home=guard_home)
6624
+ ):
6625
+ os.kill(pid, _signal.SIGTERM)
6626
+ stopped = True
6627
+ except (ProcessLookupError, PermissionError, OSError, _json.JSONDecodeError, ValueError):
6628
+ pass
6629
+ from codex_plugin_scanner.guard.daemon.manager import clear_guard_daemon_state
6630
+
6631
+ with suppress(OSError):
6632
+ clear_guard_daemon_state(guard_home)
6633
+ payload: dict[str, object] = {"stopped": stopped, "running": False}
6634
+ if pid is not None:
6635
+ payload["pid"] = pid
6636
+ _emit("daemon", payload, as_json)
6637
+ return 0
@@ -0,0 +1,143 @@
1
+ """Signal composition rules for Guard detector action decisions.
2
+
3
+ Combines false-positive advisory signals with risk signals to produce a
4
+ calibrated action recommendation: allow, warn, ask, or block.
5
+
6
+ The base policy action (from config/approval policy) is used as the starting
7
+ point. Composition rules may only *downgrade* (block → ask, ask → warn,
8
+ warn → allow) when strong false-positive evidence is present, and may only
9
+ *upgrade* (warn → ask, ask → block) when high-confidence risk signals demand it.
10
+
11
+ Composition rules never override an explicit user policy choice.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass
17
+ from typing import Literal
18
+
19
+ from codex_plugin_scanner.guard.runtime.signals import RiskSignalV2
20
+
21
+ ComposedAction = Literal["allow", "warn", "ask", "block"]
22
+
23
+ _ACTION_RANK: dict[ComposedAction, int] = {
24
+ "allow": 0,
25
+ "warn": 1,
26
+ "ask": 2,
27
+ "block": 3,
28
+ }
29
+
30
+ _RANK_ACTION: dict[int, ComposedAction] = {v: k for k, v in _ACTION_RANK.items()}
31
+
32
+ _DOWNGRADE_BLOCK_CATEGORIES = frozenset({"bypass", "persistence"})
33
+ _DOWNGRADE_PROTECTED_SEVERITIES = frozenset({"critical", "high"})
34
+ _FP_DOWNGRADE_MAX_SEVERITY = frozenset({"info", "low"})
35
+
36
+
37
+ @dataclass(frozen=True, slots=True)
38
+ class CompositionResult:
39
+ """Result of applying composition rules to a set of signals."""
40
+
41
+ action: ComposedAction
42
+ reason: str
43
+ downgraded: bool
44
+ upgraded: bool
45
+
46
+
47
+ def compose_action_from_signals(
48
+ signals: tuple[RiskSignalV2, ...],
49
+ base_action: ComposedAction,
50
+ ) -> CompositionResult:
51
+ """Apply composition rules to signals and return a calibrated action.
52
+
53
+ Args:
54
+ signals: All signals from the detector run (risk + advisory).
55
+ base_action: The starting action from config/policy evaluation.
56
+
57
+ Returns:
58
+ A ``CompositionResult`` with the final action and an explanation.
59
+ """
60
+ risk_signals = tuple(s for s in signals if s.category != "false_positive")
61
+ fp_signals = tuple(s for s in signals if s.category == "false_positive")
62
+
63
+ if not signals:
64
+ return CompositionResult(
65
+ action=base_action,
66
+ reason="no detector signals; base policy action applies",
67
+ downgraded=False,
68
+ upgraded=False,
69
+ )
70
+
71
+ current_rank = _ACTION_RANK[base_action]
72
+ upgrade_reason: str | None = None
73
+ downgrade_reason: str | None = None
74
+
75
+ for signal in risk_signals:
76
+ if signal.category == "bypass" and signal.confidence in ("likely", "strong"):
77
+ new_rank = max(current_rank, _ACTION_RANK["block"])
78
+ if new_rank > current_rank:
79
+ upgrade_reason = f"bypass signal '{signal.detector}' forces block"
80
+ current_rank = new_rank
81
+
82
+ if signal.category == "persistence" and signal.severity in ("high", "critical"):
83
+ new_rank = max(current_rank, _ACTION_RANK["ask"])
84
+ if new_rank > current_rank:
85
+ upgrade_reason = upgrade_reason or f"persistence signal '{signal.detector}' requires review"
86
+ current_rank = new_rank
87
+
88
+ if signal.severity == "critical" and signal.confidence in ("likely", "strong"):
89
+ new_rank = max(current_rank, _ACTION_RANK["block"])
90
+ if new_rank > current_rank:
91
+ upgrade_reason = upgrade_reason or f"critical signal '{signal.detector}' forces block"
92
+ current_rank = new_rank
93
+
94
+ final_rank = current_rank
95
+
96
+ if fp_signals and not upgrade_reason:
97
+ all_fp_strong = all(s.confidence == "strong" for s in fp_signals)
98
+ risk_severities = frozenset(s.severity for s in risk_signals)
99
+ only_low_risk = risk_severities.issubset(_FP_DOWNGRADE_MAX_SEVERITY)
100
+ risk_cats = frozenset(s.category for s in risk_signals)
101
+ no_protected_cats = not risk_cats.intersection(_DOWNGRADE_BLOCK_CATEGORIES)
102
+
103
+ if all_fp_strong and only_low_risk and no_protected_cats and base_action == "block":
104
+ final_rank = min(final_rank, _ACTION_RANK["ask"])
105
+ downgrade_reason = "strong false-positive signals with only low-severity risk; downgraded block → ask"
106
+
107
+ elif all_fp_strong and only_low_risk and no_protected_cats and base_action == "ask":
108
+ source_search_present = any(s.signal_id.startswith("fp:source-search:") for s in fp_signals)
109
+ if source_search_present and not risk_signals:
110
+ final_rank = min(final_rank, _ACTION_RANK["warn"])
111
+ downgrade_reason = "strong source-search false-positive with no risk signals; downgraded ask → warn"
112
+
113
+ if upgrade_reason:
114
+ return CompositionResult(
115
+ action=_RANK_ACTION[final_rank],
116
+ reason=upgrade_reason,
117
+ downgraded=False,
118
+ upgraded=final_rank > _ACTION_RANK[base_action],
119
+ )
120
+
121
+ if downgrade_reason:
122
+ return CompositionResult(
123
+ action=_RANK_ACTION[final_rank],
124
+ reason=downgrade_reason,
125
+ downgraded=True,
126
+ upgraded=False,
127
+ )
128
+
129
+ if risk_signals:
130
+ top = max(risk_signals, key=lambda s: (_ACTION_RANK.get("ask", 2), s.severity))
131
+ return CompositionResult(
132
+ action=_RANK_ACTION[final_rank],
133
+ reason=f"risk signal '{top.detector}' ({top.severity}/{top.confidence}); base action applies",
134
+ downgraded=False,
135
+ upgraded=False,
136
+ )
137
+
138
+ return CompositionResult(
139
+ action=_RANK_ACTION[final_rank],
140
+ reason="advisory false-positive signals only; base action applies",
141
+ downgraded=False,
142
+ upgraded=False,
143
+ )
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import re
5
6
  import time
6
7
  from collections.abc import Callable, Iterable, Mapping, Sequence
7
8
  from dataclasses import dataclass
@@ -451,6 +452,105 @@ class PersistenceDetector:
451
452
  )
452
453
 
453
454
 
455
+ class GuardBypassDetector:
456
+ """Detects shell commands that uninstall, disable, or circumvent HOL Guard."""
457
+
458
+ detector_id = "bypass.shell"
459
+ categories: tuple[RiskSignalCategory, ...] = ("bypass",)
460
+
461
+ _UNINSTALL_PATTERN = re.compile(
462
+ r"(?:^|[\s;&|])"
463
+ r"(?:"
464
+ r"pip(?:3)?\s+uninstall\s+(?:-y\s+)?(?:holguard|hol[_-]guard|codex[_-]plugin[_-]scanner)\b|"
465
+ r"brew\s+(?:uninstall|remove)\s+hol[_-]guard\b|"
466
+ r"npm\s+(?:uninstall|remove)\s+(?:-g\s+)?hol[_-]guard\b|"
467
+ r"apt(?:-get)?\s+(?:remove|purge)\s+hol[_-]guard\b"
468
+ r")",
469
+ re.IGNORECASE,
470
+ )
471
+
472
+ _CONFIG_DESTROY_PATTERN = re.compile(
473
+ r"(?:^|[\s;&|])"
474
+ r"(?:rm|rmdir)\b[^\r\n;&|]{0,100}"
475
+ r"(?:~?/?\.hol[_-]guard|guard[_-]home|(?<![a-zA-Z0-9_])guard\.db(?![a-zA-Z0-9_])|(?<![a-zA-Z0-9_])guard\.lock(?![a-zA-Z0-9_]))",
476
+ re.IGNORECASE,
477
+ )
478
+
479
+ _DAEMON_KILL_PATTERN = re.compile(
480
+ r"(?:^|[\s;&|])"
481
+ r"(?:"
482
+ r"kill\b[^\r\n;&|]{0,80}hol[_-]guard|"
483
+ r"pkill\b[^\r\n;&|]{0,40}(?<![a-zA-Z0-9_])hol[_-]guard(?![a-zA-Z0-9_])|"
484
+ r"launchctl\s+(?:unload|disable)\b[^\r\n;&|]{0,80}(?:hol[_-]guard|com\.hol\.guard)"
485
+ r")",
486
+ re.IGNORECASE,
487
+ )
488
+
489
+ def detect(self, action: GuardActionEnvelope, context: DetectorContext) -> tuple[RiskSignalV2, ...]:
490
+ del context
491
+ if action.action_type not in ("shell_command", "prompt") or action.command is None:
492
+ return ()
493
+ signals: list[RiskSignalV2] = []
494
+ if self._UNINSTALL_PATTERN.search(action.command):
495
+ signals.append(
496
+ RiskSignalV2(
497
+ signal_id="bypass:guard-uninstall",
498
+ category="bypass",
499
+ severity="critical",
500
+ confidence="strong",
501
+ detector=self.detector_id,
502
+ title="Command uninstalls HOL Guard",
503
+ plain_reason=("This command removes HOL Guard, which would disable all AI harness protection."),
504
+ technical_detail="matched guard uninstall pattern",
505
+ evidence_ref="command",
506
+ redaction_level="summary",
507
+ false_positive_hint=("Allow only if you intentionally want to remove Guard from this machine."),
508
+ advisory_id=None,
509
+ )
510
+ )
511
+ if self._CONFIG_DESTROY_PATTERN.search(action.command):
512
+ signals.append(
513
+ RiskSignalV2(
514
+ signal_id="bypass:guard-config-destroy",
515
+ category="bypass",
516
+ severity="critical",
517
+ confidence="strong",
518
+ detector=self.detector_id,
519
+ title="Command destroys Guard configuration or data",
520
+ plain_reason=(
521
+ "This command deletes HOL Guard configuration or state files,"
522
+ " which would reset all protection settings and history."
523
+ ),
524
+ technical_detail="matched guard config/data deletion pattern",
525
+ evidence_ref="command",
526
+ redaction_level="summary",
527
+ false_positive_hint=("Allow only if you intend to fully reset Guard and are aware of data loss."),
528
+ advisory_id=None,
529
+ )
530
+ )
531
+ if self._DAEMON_KILL_PATTERN.search(action.command):
532
+ signals.append(
533
+ RiskSignalV2(
534
+ signal_id="bypass:guard-daemon-kill",
535
+ category="bypass",
536
+ severity="high",
537
+ confidence="strong",
538
+ detector=self.detector_id,
539
+ title="Command stops HOL Guard daemon",
540
+ plain_reason=(
541
+ "This command stops the HOL Guard background service,"
542
+ " which temporarily disables harness protection."
543
+ ),
544
+ technical_detail="matched guard daemon kill/unload pattern",
545
+ evidence_ref="command",
546
+ redaction_level="summary",
547
+ false_positive_hint="Allow only if you intentionally want to pause Guard for maintenance.",
548
+ advisory_id=None,
549
+ )
550
+ )
551
+ return tuple(signals)
552
+
553
+
454
554
  def register_default_detectors() -> tuple[GuardDetector, ...]:
455
555
  """Return the default ordered detector list.
456
556
 
@@ -468,6 +568,7 @@ def register_default_detectors() -> tuple[GuardDetector, ...]:
468
568
  CiscoSkillPreflightDetector(),
469
569
  FalsePositiveSuppressorDetector(),
470
570
  DataFlowExfiltrationDetector(),
571
+ GuardBypassDetector(),
471
572
  PersistenceDetector(),
472
573
  PromptInjectionDetector(),
473
574
  SafeDecodeDetector(),
@@ -29,6 +29,7 @@ from ..redaction import redact_sensitive_text
29
29
  from ..store import GuardStore
30
30
  from ..types import PromptRequest, RemediationAction
31
31
  from .actions import GuardActionEnvelope, redacted_workspace_label
32
+ from .composition_rules import compose_action_from_signals
32
33
  from .detectors import DetectorContext, DetectorRegistry, DetectorRunResult, register_default_detectors
33
34
  from .prompt_injection import detect_prompt_injection_requests
34
35
 
@@ -403,6 +404,14 @@ def _evaluation_with_detector_registry(
403
404
  "runtime_detector_signals_v2": [signal.to_dict() for signal in result.signals],
404
405
  "runtime_detector_telemetry": [item.to_dict() for item in result.telemetry],
405
406
  }
407
+ base_action: str = "block" if evaluation.get("blocked") else "allow"
408
+ composition = compose_action_from_signals(result.signals, base_action) # type: ignore[arg-type]
409
+ next_evaluation["runtime_detector_composition"] = {
410
+ "action": composition.action,
411
+ "reason": composition.reason,
412
+ "downgraded": composition.downgraded,
413
+ "upgraded": composition.upgraded,
414
+ }
406
415
  if trace_error is not None:
407
416
  next_evaluation["runtime_detector_trace_error"] = trace_error
408
417
  return next_evaluation
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.151"
3
+ __version__ = "2.0.153"
@@ -0,0 +1,219 @@
1
+ """Tests for GuardBypassDetector and composition_rules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ from codex_plugin_scanner.guard.config import GuardConfig
10
+ from codex_plugin_scanner.guard.runtime.actions import GuardActionEnvelope
11
+ from codex_plugin_scanner.guard.runtime.composition_rules import compose_action_from_signals
12
+ from codex_plugin_scanner.guard.runtime.detectors import DetectorContext, GuardBypassDetector
13
+ from codex_plugin_scanner.guard.runtime.signals import RiskSignalV2
14
+
15
+
16
+ def _make_signal(
17
+ signal_id: str = "test:signal",
18
+ category: str = "bypass",
19
+ severity: str = "high",
20
+ confidence: str = "strong",
21
+ detector: str = "test.detector",
22
+ ) -> RiskSignalV2:
23
+ return RiskSignalV2(
24
+ signal_id=signal_id,
25
+ category=category,
26
+ severity=severity,
27
+ confidence=confidence,
28
+ detector=detector,
29
+ title="Test",
30
+ plain_reason="test reason",
31
+ technical_detail=None,
32
+ evidence_ref=None,
33
+ redaction_level="none",
34
+ false_positive_hint=None,
35
+ advisory_id=None,
36
+ )
37
+
38
+
39
+ def _bypass_envelope(command: str) -> GuardActionEnvelope:
40
+ return GuardActionEnvelope(
41
+ schema_version=1,
42
+ action_id="",
43
+ harness="codex",
44
+ event_name="BashCommand",
45
+ action_type="shell_command",
46
+ workspace=None,
47
+ workspace_hash=None,
48
+ tool_name="bash",
49
+ command=command,
50
+ prompt_excerpt=None,
51
+ prompt_text=None,
52
+ target_paths=(),
53
+ network_hosts=(),
54
+ mcp_server=None,
55
+ mcp_tool=None,
56
+ package_manager=None,
57
+ package_name=None,
58
+ script_name=None,
59
+ raw_payload_redacted={},
60
+ )
61
+
62
+
63
+ def _detector_context(tmp_path: Path) -> DetectorContext:
64
+ return DetectorContext(
65
+ config=GuardConfig(guard_home=tmp_path / "guard-home", workspace=tmp_path / "workspace"),
66
+ workspace=tmp_path / "workspace",
67
+ prior_decisions={},
68
+ threat_intel={},
69
+ redaction_settings={},
70
+ )
71
+
72
+
73
+ class TestGuardBypassDetector:
74
+ detector = GuardBypassDetector()
75
+
76
+ def _detect(self, command: str, tmp_path: Path) -> tuple:
77
+ ctx = _detector_context(tmp_path)
78
+ env = _bypass_envelope(command)
79
+ return self.detector.detect(env, ctx)
80
+
81
+ def test_pip_uninstall_holguard(self, tmp_path: Path) -> None:
82
+ signals = self._detect("pip uninstall holguard", tmp_path)
83
+ assert any(s.signal_id == "bypass:guard-uninstall" for s in signals)
84
+ assert all(s.severity == "critical" for s in signals)
85
+
86
+ def test_pip3_uninstall_holguard_with_y(self, tmp_path: Path) -> None:
87
+ signals = self._detect("pip3 uninstall -y holguard", tmp_path)
88
+ assert any(s.signal_id == "bypass:guard-uninstall" for s in signals)
89
+
90
+ def test_pip_uninstall_codex_plugin_scanner(self, tmp_path: Path) -> None:
91
+ signals = self._detect("pip uninstall codex-plugin-scanner", tmp_path)
92
+ assert any(s.signal_id == "bypass:guard-uninstall" for s in signals)
93
+
94
+ def test_brew_uninstall_hol_guard(self, tmp_path: Path) -> None:
95
+ signals = self._detect("brew uninstall hol-guard", tmp_path)
96
+ assert any(s.signal_id == "bypass:guard-uninstall" for s in signals)
97
+
98
+ def test_rm_guard_db(self, tmp_path: Path) -> None:
99
+ signals = self._detect("rm ~/.hol-guard/guard.db", tmp_path)
100
+ assert any(s.signal_id == "bypass:guard-config-destroy" for s in signals)
101
+
102
+ def test_rm_guard_home(self, tmp_path: Path) -> None:
103
+ signals = self._detect("rm -rf ~/.hol-guard", tmp_path)
104
+ assert any(s.signal_id == "bypass:guard-config-destroy" for s in signals)
105
+
106
+ def test_kill_guard_daemon(self, tmp_path: Path) -> None:
107
+ signals = self._detect("kill -9 $(pgrep hol-guard)", tmp_path)
108
+ assert any(s.signal_id == "bypass:guard-daemon-kill" for s in signals)
109
+
110
+ def test_pkill_hol_guard(self, tmp_path: Path) -> None:
111
+ signals = self._detect("pkill -f hol-guard", tmp_path)
112
+ assert any(s.signal_id == "bypass:guard-daemon-kill" for s in signals)
113
+
114
+ def test_pkill_wireguard_is_not_bypass(self, tmp_path: Path) -> None:
115
+ signals = self._detect("pkill -f wireguard", tmp_path)
116
+ assert not any(s.signal_id == "bypass:guard-daemon-kill" for s in signals)
117
+
118
+ def test_rm_myguard_db_backup_is_not_bypass(self, tmp_path: Path) -> None:
119
+ signals = self._detect("rm ./myguard.db.backup", tmp_path)
120
+ assert not any(s.signal_id == "bypass:guard-config-destroy" for s in signals)
121
+
122
+ def test_pkill_safeguard_worker_is_not_bypass(self, tmp_path: Path) -> None:
123
+ signals = self._detect("pkill -f safeguard-worker", tmp_path)
124
+ assert not any(s.signal_id == "bypass:guard-daemon-kill" for s in signals)
125
+
126
+ def test_launchctl_unload_guard(self, tmp_path: Path) -> None:
127
+ signals = self._detect("launchctl unload ~/Library/LaunchAgents/hol-guard.plist", tmp_path)
128
+ assert any(s.signal_id == "bypass:guard-daemon-kill" for s in signals)
129
+
130
+ def test_benign_pip_install_not_flagged(self, tmp_path: Path) -> None:
131
+ signals = self._detect("pip install requests", tmp_path)
132
+ assert signals == ()
133
+
134
+ def test_benign_rm_log_not_flagged(self, tmp_path: Path) -> None:
135
+ signals = self._detect("rm -rf /tmp/myproject.log", tmp_path)
136
+ assert signals == ()
137
+
138
+ def test_benign_kill_other_process_not_flagged(self, tmp_path: Path) -> None:
139
+ signals = self._detect("kill -9 12345", tmp_path)
140
+ assert signals == ()
141
+
142
+ def test_npm_uninstall_hol_guard(self, tmp_path: Path) -> None:
143
+ signals = self._detect("npm uninstall -g hol-guard", tmp_path)
144
+ assert any(s.signal_id == "bypass:guard-uninstall" for s in signals)
145
+
146
+
147
+ class TestComposeActionFromSignals:
148
+ """Tests for composition_rules.compose_action_from_signals."""
149
+
150
+ def test_no_signals_returns_base_action(self) -> None:
151
+ result = compose_action_from_signals((), "allow")
152
+ assert result.action == "allow"
153
+ assert not result.upgraded
154
+ assert not result.downgraded
155
+
156
+ def test_no_signals_block_base(self) -> None:
157
+ result = compose_action_from_signals((), "block")
158
+ assert result.action == "block"
159
+
160
+ def test_bypass_signal_upgrades_to_block(self) -> None:
161
+ signal = _make_signal(
162
+ signal_id="bypass:guard-uninstall", category="bypass", severity="critical", confidence="strong"
163
+ )
164
+ result = compose_action_from_signals((signal,), "allow")
165
+ assert result.action == "block"
166
+ assert result.upgraded
167
+
168
+ def test_persistence_signal_upgrades_to_ask(self) -> None:
169
+ signal = _make_signal(signal_id="persist:cron", category="persistence", severity="high", confidence="strong")
170
+ result = compose_action_from_signals((signal,), "allow")
171
+ assert result.action in ("ask", "block")
172
+ assert result.upgraded
173
+
174
+ def test_critical_risk_signal_upgrades_to_block(self) -> None:
175
+ signal = _make_signal(signal_id="risk:critical", category="data_flow", severity="critical", confidence="strong")
176
+ result = compose_action_from_signals((signal,), "allow")
177
+ assert result.action == "block"
178
+ assert result.upgraded
179
+
180
+ def test_strong_fp_with_no_risk_downgrades_block_to_ask(self) -> None:
181
+ fp = _make_signal(
182
+ signal_id="fp:source-search:grep", category="false_positive", severity="info", confidence="strong"
183
+ )
184
+ result = compose_action_from_signals((fp,), "block")
185
+ assert result.action == "ask"
186
+ assert result.downgraded
187
+
188
+ def test_strong_fp_source_search_downgrades_ask_to_warn(self) -> None:
189
+ fp = _make_signal(
190
+ signal_id="fp:source-search:grep", category="false_positive", severity="info", confidence="strong"
191
+ )
192
+ result = compose_action_from_signals((fp,), "ask")
193
+ assert result.action == "warn"
194
+ assert result.downgraded
195
+
196
+ def test_bypass_beats_fp_downgrade(self) -> None:
197
+ bypass = _make_signal(
198
+ signal_id="bypass:guard-uninstall", category="bypass", severity="critical", confidence="strong"
199
+ )
200
+ fp = _make_signal(
201
+ signal_id="fp:source-search:grep", category="false_positive", severity="info", confidence="strong"
202
+ )
203
+ result = compose_action_from_signals((bypass, fp), "allow")
204
+ assert result.action == "block"
205
+ assert result.upgraded
206
+ assert not result.downgraded
207
+
208
+ def test_fp_weak_confidence_does_not_downgrade(self) -> None:
209
+ fp = _make_signal(
210
+ signal_id="fp:source-search:grep", category="false_positive", severity="info", confidence="weak"
211
+ )
212
+ result = compose_action_from_signals((fp,), "block")
213
+ assert result.action == "block"
214
+ assert not result.downgraded
215
+
216
+ def test_composition_result_is_frozen(self) -> None:
217
+ result = compose_action_from_signals((), "allow")
218
+ with pytest.raises((AttributeError, TypeError)):
219
+ result.action = "block" # type: ignore[misc]