plugin-scanner 2.0.103__tar.gz → 2.0.104__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 (348) hide show
  1. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/config.py +22 -0
  5. plugin_scanner-2.0.104/src/codex_plugin_scanner/guard/runtime/detectors.py +148 -0
  6. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/runtime/runner.py +37 -0
  7. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/version.py +1 -1
  8. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_config_paths.py +21 -0
  9. plugin_scanner-2.0.104/tests/test_guard_runtime_detectors.py +295 -0
  10. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.clusterfuzzlite/Dockerfile +0 -0
  11. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.clusterfuzzlite/build.sh +0 -0
  12. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.clusterfuzzlite/project.yaml +0 -0
  13. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  14. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.dockerignore +0 -0
  15. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/CODEOWNERS +0 -0
  16. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  17. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  18. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  19. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/dependabot.yml +0 -0
  20. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/workflows/ci.yml +0 -0
  21. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/workflows/codeql.yml +0 -0
  22. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/workflows/dependabot-uv-lock.yml +0 -0
  23. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/workflows/fuzz.yml +0 -0
  24. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/workflows/harness-smoke.yml +0 -0
  25. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/workflows/publish.yml +0 -0
  26. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.github/workflows/scorecard.yml +0 -0
  27. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.gitignore +0 -0
  28. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/.pre-commit-hooks.yaml +0 -0
  29. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/CONTRIBUTING.md +0 -0
  30. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/Dockerfile +0 -0
  31. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/LICENSE +0 -0
  32. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/README.md +0 -0
  33. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/SECURITY.md +0 -0
  34. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/index.html +0 -0
  35. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/package.json +0 -0
  36. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/pnpm-lock.yaml +0 -0
  37. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/public/apple-touch-icon.png +0 -0
  38. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  39. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/public/brand/Logo_Whole.png +0 -0
  40. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/public/favicon-16x16.png +0 -0
  41. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/public/favicon-32x32.png +0 -0
  42. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/public/favicon.ico +0 -0
  43. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/app.tsx +0 -0
  44. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/approval-center-layout.tsx +0 -0
  45. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/approval-center-primitives.tsx +0 -0
  46. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/approval-center-utils.ts +0 -0
  47. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/fleet-workspace.tsx +0 -0
  48. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/guard-api.test.ts +0 -0
  49. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/guard-api.ts +0 -0
  50. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/guard-demo.ts +0 -0
  51. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/guard-types.ts +0 -0
  52. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/main.tsx +0 -0
  53. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/receipts-workspace.tsx +0 -0
  54. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/runtime-overview.tsx +0 -0
  55. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/settings-workspace.tsx +0 -0
  56. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/styles.css +0 -0
  57. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/src/vite-env.d.ts +0 -0
  58. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/tsconfig.json +0 -0
  59. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/dashboard/vite.config.ts +0 -0
  60. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docker-requirements.txt +0 -0
  61. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/guard/approval-audit.md +0 -0
  62. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/guard/architecture.md +0 -0
  63. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/guard/get-started.md +0 -0
  64. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/guard/harness-support.md +0 -0
  65. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/guard/local-vs-cloud.md +0 -0
  66. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/guard/testing-matrix.md +0 -0
  67. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/trust/mcp-trust-draft.md +0 -0
  68. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/trust/plugin-trust-draft.md +0 -0
  69. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/docs/trust/skill-trust-local.md +0 -0
  70. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/fuzzers/manifest_fuzzer.py +0 -0
  71. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/requirements.txt +0 -0
  72. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/schemas/plugin-quality.v1.json +0 -0
  73. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/schemas/scan-result.v1.json +0 -0
  74. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/schemas/verify-result.v1.json +0 -0
  75. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/__init__.py +0 -0
  76. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/action_runner.py +0 -0
  77. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  78. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  79. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  80. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/claude.py +0 -0
  81. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  82. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  83. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  84. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  85. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  86. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  87. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  88. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  89. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  90. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/security.py +0 -0
  91. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  92. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/cli.py +0 -0
  93. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/cli_ui.py +0 -0
  94. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/config.py +0 -0
  95. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  96. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  97. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  98. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  99. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  100. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  101. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  102. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  103. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  104. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/github_reporting.py +0 -0
  105. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  106. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  107. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  108. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  109. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  110. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  111. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  112. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  113. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  114. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  115. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  116. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  117. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  118. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  119. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  120. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  121. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  122. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  123. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  124. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  125. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  126. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  127. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  128. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  129. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
  130. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  131. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  132. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  133. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  134. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  135. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  136. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  137. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  138. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  139. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  140. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  141. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  142. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  143. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  144. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  145. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  146. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  147. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  148. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  149. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  150. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  151. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  152. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  153. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/incident.py +0 -0
  154. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  155. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  156. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/models.py +0 -0
  157. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  158. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  159. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/protect.py +0 -0
  160. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  161. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  162. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  163. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  164. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  165. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  166. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  167. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/risk.py +0 -0
  168. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  169. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  170. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  171. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  172. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  173. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  174. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  175. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  176. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  177. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  178. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/shims.py +0 -0
  179. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/store.py +0 -0
  180. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  181. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  182. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/guard/types.py +0 -0
  183. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  184. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  185. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  186. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  187. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  188. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/models.py +0 -0
  189. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/path_support.py +0 -0
  190. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/policy.py +0 -0
  191. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  192. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/repo_detect.py +0 -0
  193. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/reporting.py +0 -0
  194. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  195. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/rules/registry.py +0 -0
  196. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/rules/specs.py +0 -0
  197. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/scanner.py +0 -0
  198. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/submission.py +0 -0
  199. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/suppressions.py +0 -0
  200. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  201. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  202. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  203. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_models.py +0 -0
  204. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  205. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  206. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  207. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/trust_specs.py +0 -0
  208. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/src/codex_plugin_scanner/verification.py +0 -0
  209. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/__init__.py +0 -0
  210. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/conftest.py +0 -0
  211. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/__init__.py +0 -0
  212. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  213. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  214. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/bad-plugin/secrets.js +0 -0
  215. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  216. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  217. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/claude-plugin-good/README.md +0 -0
  218. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  219. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  220. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  221. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/code-quality-bad/evil.js +0 -0
  222. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/code-quality-bad/inject.js +0 -0
  223. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  224. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  225. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/gemini-extension-good/README.md +0 -0
  226. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  227. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  228. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  229. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  230. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/.codexignore +0 -0
  231. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/LICENSE +0 -0
  232. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/README.md +0 -0
  233. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  234. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  235. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  236. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  237. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  238. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  239. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  240. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  241. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  242. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  243. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  244. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  245. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  246. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  247. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  248. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  249. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  250. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  251. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  252. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  253. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/mcp-canary-server.py +0 -0
  254. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  255. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  256. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/mit-license/LICENSE +0 -0
  257. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  258. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  259. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  260. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  261. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  262. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  263. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  264. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  265. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  266. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  267. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  268. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  269. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  270. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  271. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  272. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  273. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  274. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  275. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/opencode-good/LICENSE +0 -0
  276. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/opencode-good/README.md +0 -0
  277. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  278. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  279. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  280. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  281. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  282. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  283. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  284. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  285. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test-trust-scoring.py +0 -0
  286. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test-trust-specs.py +0 -0
  287. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_action_runner.py +0 -0
  288. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_best_practices.py +0 -0
  289. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_cisco_install_surfaces.py +0 -0
  290. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_cli.py +0 -0
  291. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_code_quality.py +0 -0
  292. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_config.py +0 -0
  293. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_coverage_remaining.py +0 -0
  294. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_ecosystems.py +0 -0
  295. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_edge_cases.py +0 -0
  296. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_final_coverage.py +0 -0
  297. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_approvals.py +0 -0
  298. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_bootstrap.py +0 -0
  299. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_capabilities.py +0 -0
  300. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_claude_adapter.py +0 -0
  301. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_cli.py +0 -0
  302. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_codex_e2e.py +0 -0
  303. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_codex_install.py +0 -0
  304. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_codex_proxy.py +0 -0
  305. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_connect_flow.py +0 -0
  306. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_consumer_mode.py +0 -0
  307. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_copilot_adapter.py +0 -0
  308. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_copilot_proxy.py +0 -0
  309. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_daemon_manager.py +0 -0
  310. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_event_schema_v1.py +0 -0
  311. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_events.py +0 -0
  312. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_launch_env.py +0 -0
  313. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_opencode_proxy.py +0 -0
  314. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_product_flow.py +0 -0
  315. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_protect.py +0 -0
  316. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_render.py +0 -0
  317. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_risk.py +0 -0
  318. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_runtime.py +0 -0
  319. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_runtime_action_harnesses.py +0 -0
  320. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_runtime_actions.py +0 -0
  321. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_runtime_decisions.py +0 -0
  322. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_runtime_signals.py +0 -0
  323. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_store_migrations.py +0 -0
  324. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_surface_server.py +0 -0
  325. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_guard_verdicts.py +0 -0
  326. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_hermes_adapter.py +0 -0
  327. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_integration.py +0 -0
  328. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_lint_fixes.py +0 -0
  329. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_live_cisco_smoke.py +0 -0
  330. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_manifest.py +0 -0
  331. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_marketplace.py +0 -0
  332. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_mcp_security.py +0 -0
  333. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_openclaw_adapter.py +0 -0
  334. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_operational_security.py +0 -0
  335. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_policy.py +0 -0
  336. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_quality_artifact.py +0 -0
  337. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_rule_registry.py +0 -0
  338. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_scanner.py +0 -0
  339. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_schema_contracts.py +0 -0
  340. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_security.py +0 -0
  341. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_security_ops.py +0 -0
  342. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_skill_security.py +0 -0
  343. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_submission.py +0 -0
  344. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_trust_scoring.py +0 -0
  345. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_trust_specs.py +0 -0
  346. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_verification.py +0 -0
  347. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/tests/test_versioning.py +0 -0
  348. {plugin_scanner-2.0.103 → plugin_scanner-2.0.104}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.103
3
+ Version: 2.0.104
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.103"
7
+ version = "2.0.104"
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.103"
7
+ version = "2.0.104"
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"
@@ -176,6 +176,9 @@ class GuardConfig:
176
176
  telemetry: bool = False
177
177
  sync: bool = False
178
178
  billing: bool = False
179
+ runtime_detector_registry: bool = False
180
+ runtime_detector_timeout_ms: int = 50
181
+ runtime_detector_disabled_ids: tuple[str, ...] = ()
179
182
  risk_actions: dict[str, GuardAction] | None = None
180
183
  harness_risk_actions: dict[str, dict[str, GuardAction]] | None = None
181
184
  harness_actions: dict[str, GuardAction] | None = None
@@ -252,6 +255,9 @@ def load_guard_config(guard_home: Path, workspace: Path | None = None) -> GuardC
252
255
  telemetry=bool(merged.get("telemetry", False)),
253
256
  sync=bool(merged.get("sync", False)),
254
257
  billing=bool(merged.get("billing", False)),
258
+ runtime_detector_registry=_coerce_loaded_bool(merged.get("runtime_detector_registry", False)),
259
+ runtime_detector_timeout_ms=_coerce_loaded_positive_int(merged.get("runtime_detector_timeout_ms", 50), 50),
260
+ runtime_detector_disabled_ids=_coerce_loaded_string_tuple(merged.get("runtime_detector_disabled_ids")),
255
261
  harness_actions=_coerce_action_map(merged.get("harnesses")),
256
262
  publisher_actions=_coerce_action_map(merged.get("publishers")),
257
263
  artifact_actions=_coerce_action_map(merged.get("artifacts")),
@@ -346,6 +352,22 @@ def _coerce_loaded_security_level(value: object) -> str:
346
352
  return DEFAULT_SECURITY_LEVEL
347
353
 
348
354
 
355
+ def _coerce_loaded_bool(value: object) -> bool:
356
+ return value if isinstance(value, bool) else False
357
+
358
+
359
+ def _coerce_loaded_positive_int(value: object, fallback: int) -> int:
360
+ if isinstance(value, int) and not isinstance(value, bool) and value > 0:
361
+ return value
362
+ return fallback
363
+
364
+
365
+ def _coerce_loaded_string_tuple(value: object) -> tuple[str, ...]:
366
+ if not isinstance(value, list):
367
+ return ()
368
+ return tuple(item for item in value if isinstance(item, str) and item.strip())
369
+
370
+
349
371
  def _coerce_risk_action_payload(value: object) -> dict[str, GuardAction]:
350
372
  if not isinstance(value, dict):
351
373
  raise ValueError("Risk actions must be a table.")
@@ -0,0 +1,148 @@
1
+ """Runtime detector registry primitives for Guard actions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from collections.abc import Callable, Iterable, Mapping, Sequence
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Literal, Protocol
10
+
11
+ from codex_plugin_scanner.guard.config import GuardConfig
12
+ from codex_plugin_scanner.guard.runtime.actions import GuardActionEnvelope
13
+ from codex_plugin_scanner.guard.runtime.signals import RiskSignalCategory, RiskSignalV2
14
+
15
+ DETECTOR_CATEGORY_TAGS: tuple[RiskSignalCategory, ...] = (
16
+ "secret",
17
+ "network",
18
+ "prompt",
19
+ "mcp",
20
+ "skill",
21
+ "supply_chain",
22
+ "encoded",
23
+ "persistence",
24
+ "bypass",
25
+ "false_positive",
26
+ )
27
+ DetectorRunStatus = Literal["ok", "disabled", "filtered", "timeout", "error"]
28
+
29
+
30
+ @dataclass(frozen=True, slots=True)
31
+ class DetectorContext:
32
+ """Context shared with runtime detectors."""
33
+
34
+ config: GuardConfig
35
+ workspace: Path | None
36
+ prior_decisions: Mapping[str, object]
37
+ threat_intel: Mapping[str, object]
38
+ redaction_settings: Mapping[str, object]
39
+
40
+
41
+ class GuardDetector(Protocol):
42
+ """Detector interface for runtime Guard actions."""
43
+
44
+ detector_id: str
45
+ categories: tuple[RiskSignalCategory, ...]
46
+
47
+ def detect(self, action: GuardActionEnvelope, context: DetectorContext) -> tuple[RiskSignalV2, ...]:
48
+ """Return typed risk signals for the runtime action."""
49
+
50
+
51
+ @dataclass(frozen=True, slots=True)
52
+ class DetectorTelemetry:
53
+ """Debug-safe detector execution telemetry."""
54
+
55
+ detector_id: str
56
+ categories: tuple[RiskSignalCategory, ...]
57
+ status: DetectorRunStatus
58
+ elapsed_ms: int
59
+ error_type: str | None = None
60
+
61
+ def to_dict(self) -> dict[str, object]:
62
+ return {
63
+ "detector_id": self.detector_id,
64
+ "categories": list(self.categories),
65
+ "status": self.status,
66
+ "elapsed_ms": self.elapsed_ms,
67
+ "error_type": self.error_type,
68
+ }
69
+
70
+
71
+ @dataclass(frozen=True, slots=True)
72
+ class DetectorRunResult:
73
+ """Signals and telemetry produced by a registry run."""
74
+
75
+ signals: tuple[RiskSignalV2, ...]
76
+ telemetry: tuple[DetectorTelemetry, ...]
77
+
78
+
79
+ class DetectorRegistry:
80
+ """Runs detectors in deterministic order with failure isolation."""
81
+
82
+ def __init__(
83
+ self,
84
+ detectors: Iterable[GuardDetector],
85
+ *,
86
+ clock: Callable[[], float] | None = None,
87
+ ) -> None:
88
+ self._detectors = tuple(sorted(detectors, key=lambda detector: detector.detector_id))
89
+ self._clock = clock or time.monotonic
90
+
91
+ def run(
92
+ self,
93
+ action: GuardActionEnvelope,
94
+ context: DetectorContext,
95
+ *,
96
+ timeout_ms: int = 50,
97
+ disabled_detector_ids: Sequence[str] = (),
98
+ enabled_categories: Sequence[RiskSignalCategory] | None = None,
99
+ ) -> DetectorRunResult:
100
+ disabled_ids = frozenset(disabled_detector_ids)
101
+ category_filter = frozenset(enabled_categories) if enabled_categories is not None else None
102
+ signals: list[RiskSignalV2] = []
103
+ telemetry: list[DetectorTelemetry] = []
104
+ for detector in self._detectors:
105
+ if detector.detector_id in disabled_ids:
106
+ telemetry.append(_telemetry(detector, "disabled", elapsed_ms=0))
107
+ continue
108
+ if category_filter is not None and not category_filter.intersection(detector.categories):
109
+ telemetry.append(_telemetry(detector, "filtered", elapsed_ms=0))
110
+ continue
111
+ started_at = self._clock()
112
+ try:
113
+ detector_signals = detector.detect(action, context)
114
+ elapsed_ms = _elapsed_ms(started_at, self._clock())
115
+ except Exception as error:
116
+ elapsed_ms = _elapsed_ms(started_at, self._clock())
117
+ telemetry.append(_telemetry(detector, "error", elapsed_ms=elapsed_ms, error_type=type(error).__name__))
118
+ continue
119
+ if elapsed_ms > timeout_ms:
120
+ telemetry.append(_telemetry(detector, "timeout", elapsed_ms=elapsed_ms))
121
+ continue
122
+ signals.extend(detector_signals)
123
+ telemetry.append(_telemetry(detector, "ok", elapsed_ms=elapsed_ms))
124
+ return DetectorRunResult(signals=tuple(signals), telemetry=tuple(telemetry))
125
+
126
+
127
+ def register_default_detectors() -> tuple[GuardDetector, ...]:
128
+ return ()
129
+
130
+
131
+ def _elapsed_ms(started_at: float, finished_at: float) -> int:
132
+ return max(0, round((finished_at - started_at) * 1000))
133
+
134
+
135
+ def _telemetry(
136
+ detector: GuardDetector,
137
+ status: DetectorRunStatus,
138
+ *,
139
+ elapsed_ms: int,
140
+ error_type: str | None = None,
141
+ ) -> DetectorTelemetry:
142
+ return DetectorTelemetry(
143
+ detector_id=detector.detector_id,
144
+ categories=detector.categories,
145
+ status=status,
146
+ elapsed_ms=elapsed_ms,
147
+ error_type=error_type,
148
+ )
@@ -26,6 +26,7 @@ from ..models import GuardArtifact, HarnessDetection, PolicyDecision
26
26
  from ..store import GuardStore
27
27
  from ..types import PromptRequest, RemediationAction
28
28
  from .actions import GuardActionEnvelope, redacted_workspace_label
29
+ from .detectors import DetectorContext, DetectorRegistry, register_default_detectors
29
30
 
30
31
  _APPROVAL_METADATA_KEYS = (
31
32
  "approval_center_url",
@@ -251,6 +252,12 @@ def guard_run(
251
252
  if key in pending_evaluation:
252
253
  reevaluated[key] = pending_evaluation[key]
253
254
  evaluation = reevaluated
255
+ evaluation = _evaluation_with_detector_registry(
256
+ evaluation,
257
+ action_envelope,
258
+ context,
259
+ config,
260
+ )
254
261
  if "config_paths" not in evaluation:
255
262
  evaluation["config_paths"] = list(detection.config_paths) or _guard_run_config_paths(
256
263
  detection=detection,
@@ -337,6 +344,36 @@ def _evaluation_with_action_envelope(
337
344
  return {**evaluation, "artifacts": normalized_artifacts}
338
345
 
339
346
 
347
+ def _evaluation_with_detector_registry(
348
+ evaluation: dict[str, Any],
349
+ action_envelope: GuardActionEnvelope,
350
+ context: HarnessContext,
351
+ config: GuardConfig,
352
+ ) -> dict[str, Any]:
353
+ if not config.runtime_detector_registry:
354
+ return evaluation
355
+ detector_context = DetectorContext(
356
+ config=config,
357
+ workspace=context.workspace_dir,
358
+ prior_decisions={},
359
+ threat_intel={},
360
+ redaction_settings={},
361
+ )
362
+ result = DetectorRegistry(register_default_detectors()).run(
363
+ action_envelope,
364
+ detector_context,
365
+ timeout_ms=config.runtime_detector_timeout_ms,
366
+ disabled_detector_ids=config.runtime_detector_disabled_ids,
367
+ )
368
+ if not result.signals and not result.telemetry:
369
+ return evaluation
370
+ return {
371
+ **evaluation,
372
+ "runtime_detector_signals_v2": [signal.to_dict() for signal in result.signals],
373
+ "runtime_detector_telemetry": [item.to_dict() for item in result.telemetry],
374
+ }
375
+
376
+
340
377
  def _guard_run_config_paths(
341
378
  *,
342
379
  detection: HarnessDetection,
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.103"
3
+ __version__ = "2.0.104"
@@ -110,6 +110,27 @@ def test_guard_config_loads_security_level_and_risk_action_overrides(tmp_path):
110
110
  assert resolve_risk_action(config, "credential_exfiltration", harness="codex") == "block"
111
111
 
112
112
 
113
+ def test_load_guard_config_parses_hidden_runtime_detector_registry(tmp_path):
114
+ guard_home = tmp_path / ".hol-guard"
115
+ _write_text(
116
+ guard_home / "config.toml",
117
+ "\n".join(
118
+ [
119
+ "runtime_detector_registry = true",
120
+ "runtime_detector_timeout_ms = 75",
121
+ 'runtime_detector_disabled_ids = ["secret.local"]',
122
+ ]
123
+ )
124
+ + "\n",
125
+ )
126
+
127
+ config = load_guard_config(guard_home)
128
+
129
+ assert config.runtime_detector_registry is True
130
+ assert config.runtime_detector_timeout_ms == 75
131
+ assert config.runtime_detector_disabled_ids == ("secret.local",)
132
+
133
+
113
134
  def test_guard_config_strict_profile_defaults_review_sensitive_risks(tmp_path):
114
135
  guard_home = tmp_path / ".hol-guard"
115
136
  _write_text(guard_home / "config.toml", 'security_level = "strict"\n')
@@ -0,0 +1,295 @@
1
+ """Behavior tests for Guard runtime detector registry plumbing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from codex_plugin_scanner.guard.adapters.base import HarnessContext
8
+ from codex_plugin_scanner.guard.config import GuardConfig
9
+ from codex_plugin_scanner.guard.models import HarnessDetection
10
+ from codex_plugin_scanner.guard.runtime import runner as guard_runner_module
11
+ from codex_plugin_scanner.guard.runtime.actions import GuardActionEnvelope
12
+ from codex_plugin_scanner.guard.runtime.detectors import (
13
+ DETECTOR_CATEGORY_TAGS,
14
+ DetectorContext,
15
+ DetectorRegistry,
16
+ register_default_detectors,
17
+ )
18
+ from codex_plugin_scanner.guard.runtime.signals import RiskSignalCategory, RiskSignalV2
19
+ from codex_plugin_scanner.guard.store import GuardStore
20
+
21
+
22
+ class StepClock:
23
+ def __init__(self, values: list[float]) -> None:
24
+ self.values = values
25
+
26
+ def __call__(self) -> float:
27
+ return self.values.pop(0)
28
+
29
+
30
+ class RecordingDetector:
31
+ def __init__(
32
+ self,
33
+ detector_id: str,
34
+ categories: tuple[RiskSignalCategory, ...],
35
+ calls: list[str],
36
+ signal: RiskSignalV2 | None = None,
37
+ raises: Exception | None = None,
38
+ ) -> None:
39
+ self.detector_id = detector_id
40
+ self.categories = categories
41
+ self.calls = calls
42
+ self.signal = signal
43
+ self.raises = raises
44
+
45
+ def detect(self, action: GuardActionEnvelope, context: DetectorContext) -> tuple[RiskSignalV2, ...]:
46
+ self.calls.append(self.detector_id)
47
+ assert action.action_type == "harness_start"
48
+ assert context.workspace is not None
49
+ if self.raises is not None:
50
+ raise self.raises
51
+ if self.signal is None:
52
+ return ()
53
+ return (self.signal,)
54
+
55
+
56
+ def _signal(signal_id: str, category: RiskSignalCategory) -> RiskSignalV2:
57
+ return RiskSignalV2(
58
+ signal_id=signal_id,
59
+ category=category,
60
+ severity="medium",
61
+ confidence="likely",
62
+ detector="test-detector",
63
+ title="Detector signal",
64
+ plain_reason="Detector found a risky runtime action.",
65
+ technical_detail=None,
66
+ evidence_ref=None,
67
+ redaction_level="summary",
68
+ false_positive_hint=None,
69
+ advisory_id=None,
70
+ )
71
+
72
+
73
+ def _action() -> GuardActionEnvelope:
74
+ return GuardActionEnvelope(
75
+ schema_version=1,
76
+ action_id="action-1",
77
+ harness="codex",
78
+ event_name="HarnessStart",
79
+ action_type="harness_start",
80
+ workspace="~/workspace",
81
+ workspace_hash="workspace-hash",
82
+ tool_name=None,
83
+ command=None,
84
+ prompt_excerpt=None,
85
+ target_paths=(),
86
+ network_hosts=(),
87
+ mcp_server=None,
88
+ mcp_tool=None,
89
+ package_manager=None,
90
+ package_name=None,
91
+ script_name=None,
92
+ raw_payload_redacted={},
93
+ )
94
+
95
+
96
+ def _context(tmp_path: Path) -> DetectorContext:
97
+ return DetectorContext(
98
+ config=GuardConfig(guard_home=tmp_path / "guard-home", workspace=tmp_path / "workspace"),
99
+ workspace=tmp_path / "workspace",
100
+ prior_decisions={"artifact": "allow"},
101
+ threat_intel={"source": "unit-test"},
102
+ redaction_settings={"level": "summary"},
103
+ )
104
+
105
+
106
+ def test_detector_registry_runs_in_deterministic_detector_id_order(tmp_path):
107
+ calls: list[str] = []
108
+ registry = DetectorRegistry(
109
+ (
110
+ RecordingDetector("network.egress", ("network",), calls, _signal("network:egress", "network")),
111
+ RecordingDetector("secret.local", ("secret",), calls, _signal("secret:local", "secret")),
112
+ ),
113
+ clock=StepClock([0.0, 0.001, 0.002, 0.003]),
114
+ )
115
+
116
+ result = registry.run(_action(), _context(tmp_path))
117
+
118
+ assert calls == ["network.egress", "secret.local"]
119
+ assert [signal.signal_id for signal in result.signals] == ["network:egress", "secret:local"]
120
+ assert [item.status for item in result.telemetry] == ["ok", "ok"]
121
+
122
+
123
+ def test_detector_registry_skips_disabled_detector_ids(tmp_path):
124
+ calls: list[str] = []
125
+ registry = DetectorRegistry(
126
+ (
127
+ RecordingDetector("network.egress", ("network",), calls, _signal("network:egress", "network")),
128
+ RecordingDetector("secret.local", ("secret",), calls, _signal("secret:local", "secret")),
129
+ ),
130
+ clock=StepClock([0.0, 0.001]),
131
+ )
132
+
133
+ result = registry.run(_action(), _context(tmp_path), disabled_detector_ids=("network.egress",))
134
+
135
+ assert calls == ["secret.local"]
136
+ assert [signal.signal_id for signal in result.signals] == ["secret:local"]
137
+ assert [(item.detector_id, item.status) for item in result.telemetry] == [
138
+ ("network.egress", "disabled"),
139
+ ("secret.local", "ok"),
140
+ ]
141
+
142
+
143
+ def test_detector_registry_discards_signals_when_detector_exceeds_timeout(tmp_path):
144
+ calls: list[str] = []
145
+ registry = DetectorRegistry(
146
+ (RecordingDetector("secret.slow", ("secret",), calls, _signal("secret:slow", "secret")),),
147
+ clock=StepClock([0.0, 0.075]),
148
+ )
149
+
150
+ result = registry.run(_action(), _context(tmp_path), timeout_ms=50)
151
+
152
+ assert calls == ["secret.slow"]
153
+ assert result.signals == ()
154
+ assert result.telemetry[0].status == "timeout"
155
+ assert result.telemetry[0].elapsed_ms == 75
156
+
157
+
158
+ def test_detector_registry_isolates_detector_exceptions_as_telemetry(tmp_path):
159
+ calls: list[str] = []
160
+ registry = DetectorRegistry(
161
+ (
162
+ RecordingDetector("secret.broken", ("secret",), calls, raises=RuntimeError("boom")),
163
+ RecordingDetector("secret.healthy", ("secret",), calls, signal=_signal("secret:healthy", "secret")),
164
+ ),
165
+ clock=StepClock([0.0, 0.001, 0.002, 0.003]),
166
+ )
167
+
168
+ result = registry.run(_action(), _context(tmp_path))
169
+
170
+ assert calls == ["secret.broken", "secret.healthy"]
171
+ assert [signal.signal_id for signal in result.signals] == ["secret:healthy"]
172
+ assert result.telemetry[0].status == "error"
173
+ assert result.telemetry[0].error_type == "RuntimeError"
174
+
175
+
176
+ def test_detector_registry_filters_by_detector_categories(tmp_path):
177
+ calls: list[str] = []
178
+ registry = DetectorRegistry(
179
+ (
180
+ RecordingDetector("network.egress", ("network",), calls, _signal("network:egress", "network")),
181
+ RecordingDetector("secret.local", ("secret",), calls, _signal("secret:local", "secret")),
182
+ ),
183
+ clock=StepClock([0.0, 0.001]),
184
+ )
185
+
186
+ result = registry.run(_action(), _context(tmp_path), enabled_categories=("secret",))
187
+
188
+ assert calls == ["secret.local"]
189
+ assert [signal.signal_id for signal in result.signals] == ["secret:local"]
190
+ assert result.telemetry[0].status == "filtered"
191
+ assert result.telemetry[0].detector_id == "network.egress"
192
+
193
+
194
+ def test_register_default_detectors_is_empty_until_specific_detectors_land():
195
+ assert register_default_detectors() == ()
196
+ planned_categories = {
197
+ "secret",
198
+ "network",
199
+ "prompt",
200
+ "mcp",
201
+ "skill",
202
+ "supply_chain",
203
+ "encoded",
204
+ "persistence",
205
+ "bypass",
206
+ "false_positive",
207
+ }
208
+ assert planned_categories.issubset(set(DETECTOR_CATEGORY_TAGS))
209
+
210
+
211
+ def test_guard_run_invokes_detector_registry_only_when_feature_flag_enabled(tmp_path, monkeypatch):
212
+ calls: list[str] = []
213
+ detector = RecordingDetector("secret.local", ("secret",), calls, _signal("secret:local", "secret"))
214
+ detection = HarnessDetection(
215
+ harness="codex",
216
+ installed=True,
217
+ command_available=True,
218
+ config_paths=(),
219
+ artifacts=(),
220
+ )
221
+
222
+ def evaluate_stub(*_args: object, **_kwargs: object) -> dict[str, object]:
223
+ return {"blocked": False, "artifacts": [], "receipts_recorded": 0}
224
+
225
+ monkeypatch.setattr(guard_runner_module, "detect_harness", lambda _harness, _context: detection)
226
+ monkeypatch.setattr(guard_runner_module, "evaluate_detection", evaluate_stub)
227
+ monkeypatch.setattr(guard_runner_module, "register_default_detectors", lambda: (detector,))
228
+
229
+ context = HarnessContext(
230
+ home_dir=tmp_path / "home",
231
+ workspace_dir=tmp_path / "workspace",
232
+ guard_home=tmp_path / "guard-home",
233
+ )
234
+ store = GuardStore(tmp_path / "guard-home")
235
+ disabled = GuardConfig(guard_home=tmp_path / "guard-home", workspace=tmp_path / "workspace")
236
+ enabled = GuardConfig(
237
+ guard_home=tmp_path / "guard-home",
238
+ workspace=tmp_path / "workspace",
239
+ runtime_detector_registry=True,
240
+ )
241
+
242
+ disabled_result = guard_runner_module.guard_run("codex", context, store, disabled, True, [])
243
+ enabled_result = guard_runner_module.guard_run("codex", context, store, enabled, True, [])
244
+
245
+ assert calls == ["secret.local"]
246
+ assert "runtime_detector_signals_v2" not in disabled_result
247
+ assert enabled_result["runtime_detector_signals_v2"] == [_signal("secret:local", "secret").to_dict()]
248
+ assert isinstance(enabled_result["runtime_detector_telemetry"], list)
249
+
250
+
251
+ def test_guard_run_keeps_detector_results_after_blocked_resolver_reevaluation(tmp_path, monkeypatch):
252
+ calls: list[str] = []
253
+ detector = RecordingDetector("secret.local", ("secret",), calls, _signal("secret:local", "secret"))
254
+ detection = HarnessDetection(
255
+ harness="codex",
256
+ installed=True,
257
+ command_available=True,
258
+ config_paths=(),
259
+ artifacts=(),
260
+ )
261
+
262
+ def evaluate_stub(*_args: object, **_kwargs: object) -> dict[str, object]:
263
+ return {"blocked": True, "artifacts": [], "receipts_recorded": 0}
264
+
265
+ def blocked_resolver_stub(_detection: HarnessDetection, _evaluation: dict[str, object]) -> dict[str, object]:
266
+ return {"blocked": True, "artifacts": [], "approval_delivery": "queued"}
267
+
268
+ monkeypatch.setattr(guard_runner_module, "detect_harness", lambda _harness, _context: detection)
269
+ monkeypatch.setattr(guard_runner_module, "evaluate_detection", evaluate_stub)
270
+ monkeypatch.setattr(guard_runner_module, "register_default_detectors", lambda: (detector,))
271
+
272
+ context = HarnessContext(
273
+ home_dir=tmp_path / "home",
274
+ workspace_dir=tmp_path / "workspace",
275
+ guard_home=tmp_path / "guard-home",
276
+ )
277
+ config = GuardConfig(
278
+ guard_home=tmp_path / "guard-home",
279
+ workspace=tmp_path / "workspace",
280
+ runtime_detector_registry=True,
281
+ )
282
+
283
+ result = guard_runner_module.guard_run(
284
+ "codex",
285
+ context,
286
+ GuardStore(tmp_path / "guard-home"),
287
+ config,
288
+ False,
289
+ [],
290
+ blocked_resolver=blocked_resolver_stub,
291
+ )
292
+
293
+ assert calls == ["secret.local"]
294
+ assert result["runtime_detector_signals_v2"] == [_signal("secret:local", "secret").to_dict()]
295
+ assert result["approval_delivery"] == "queued"