plugin-scanner 2.0.134__tar.gz → 2.0.136__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 (429) hide show
  1. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/approval_commands.py +59 -0
  5. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/commands.py +15 -1
  6. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/render.py +16 -0
  7. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/server.py +23 -1
  8. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/store.py +1 -0
  9. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/store_approvals.py +36 -5
  10. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/version.py +1 -1
  11. plugin_scanner-2.0.136/tests/test_guard_approval_copy_commands.py +203 -0
  12. plugin_scanner-2.0.136/tests/test_guard_approval_store_dedup.py +219 -0
  13. plugin_scanner-2.0.136/tests/test_guard_decision_propagation.py +162 -0
  14. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_render.py +31 -0
  15. plugin_scanner-2.0.136/tests/test_guard_resolution_copy.py +146 -0
  16. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.clusterfuzzlite/Dockerfile +0 -0
  17. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.clusterfuzzlite/build.sh +0 -0
  18. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.clusterfuzzlite/project.yaml +0 -0
  19. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  20. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.dockerignore +0 -0
  21. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/CODEOWNERS +0 -0
  22. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  23. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  24. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  25. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/dependabot.yml +0 -0
  26. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/workflows/ci.yml +0 -0
  27. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/workflows/codeql.yml +0 -0
  28. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/workflows/dependabot-uv-lock.yml +0 -0
  29. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/workflows/fuzz.yml +0 -0
  30. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/workflows/harness-smoke.yml +0 -0
  31. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/workflows/publish.yml +0 -0
  32. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.github/workflows/scorecard.yml +0 -0
  33. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.gitignore +0 -0
  34. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/.pre-commit-hooks.yaml +0 -0
  35. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/CONTRIBUTING.md +0 -0
  36. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/Dockerfile +0 -0
  37. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/LICENSE +0 -0
  38. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/README.md +0 -0
  39. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/SECURITY.md +0 -0
  40. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/index.html +0 -0
  41. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/package.json +0 -0
  42. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/pnpm-lock.yaml +0 -0
  43. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/public/apple-touch-icon.png +0 -0
  44. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  45. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/public/brand/Logo_Whole.png +0 -0
  46. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/public/favicon-16x16.png +0 -0
  47. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/public/favicon-32x32.png +0 -0
  48. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/public/favicon.ico +0 -0
  49. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/app.tsx +0 -0
  50. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/approval-center-layout.test.ts +0 -0
  51. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/approval-center-layout.tsx +0 -0
  52. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/approval-center-primitives.tsx +0 -0
  53. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/approval-center-review-cards.tsx +0 -0
  54. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/approval-center-utils.ts +0 -0
  55. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/data-flow-evidence-card.tsx +0 -0
  56. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/fleet-workspace.tsx +0 -0
  57. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/guard-api.test.ts +0 -0
  58. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/guard-api.ts +0 -0
  59. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/guard-demo.ts +0 -0
  60. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/guard-types.ts +0 -0
  61. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/main.tsx +0 -0
  62. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/receipts-workspace.test.ts +0 -0
  63. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/receipts-workspace.tsx +0 -0
  64. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/runtime-overview.test.ts +0 -0
  65. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/runtime-overview.tsx +0 -0
  66. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/settings-workspace.test.ts +0 -0
  67. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/settings-workspace.tsx +0 -0
  68. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/styles.css +0 -0
  69. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/src/vite-env.d.ts +0 -0
  70. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/tsconfig.json +0 -0
  71. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/dashboard/vite.config.ts +0 -0
  72. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docker-requirements.txt +0 -0
  73. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/approval-audit.md +0 -0
  74. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/architecture.md +0 -0
  75. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/get-started.md +0 -0
  76. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/harness-support.md +0 -0
  77. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/local-vs-cloud.md +0 -0
  78. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/release-checklist.md +0 -0
  79. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/smoke-tests.md +0 -0
  80. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/guard/testing-matrix.md +0 -0
  81. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/trust/mcp-trust-draft.md +0 -0
  82. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/trust/plugin-trust-draft.md +0 -0
  83. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/docs/trust/skill-trust-local.md +0 -0
  84. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/fuzzers/manifest_fuzzer.py +0 -0
  85. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/requirements.txt +0 -0
  86. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/schemas/plugin-quality.v1.json +0 -0
  87. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/schemas/scan-result.v1.json +0 -0
  88. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/schemas/verify-result.v1.json +0 -0
  89. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/__init__.py +0 -0
  90. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/action_runner.py +0 -0
  91. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  92. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  93. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  94. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/claude.py +0 -0
  95. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  96. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  97. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  98. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  99. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  100. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  101. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  102. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  103. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  104. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/security.py +0 -0
  105. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  106. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/cli.py +0 -0
  107. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/cli_ui.py +0 -0
  108. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/config.py +0 -0
  109. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  110. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  111. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  112. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  113. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  114. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  115. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  116. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  117. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  118. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/github_reporting.py +0 -0
  119. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  120. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
  121. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  122. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  123. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  124. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  125. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  126. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  127. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/contracts.py +0 -0
  128. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  129. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  130. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  131. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  132. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  133. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  134. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  135. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  136. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  137. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  138. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  139. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  140. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  141. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  142. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  143. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  144. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  145. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  146. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  147. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  148. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  149. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  150. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/config.py +0 -0
  151. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  152. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  153. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  154. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  155. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  156. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  157. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  158. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  159. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  160. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  161. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  162. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  163. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  164. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  165. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  166. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/incident.py +0 -0
  167. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  168. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  169. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/models.py +0 -0
  170. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  171. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  172. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/protect.py +0 -0
  173. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  174. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  175. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  176. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  177. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  178. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  179. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  180. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/risk.py +0 -0
  181. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  182. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/action_identity.py +0 -0
  183. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  184. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/advisory_escalation.py +0 -0
  185. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/advisory_matchers.py +0 -0
  186. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
  187. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
  188. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
  189. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  190. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
  191. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
  192. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
  193. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  194. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
  195. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
  196. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  197. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  198. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
  199. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
  200. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  201. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
  202. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
  203. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  204. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
  205. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/runtime/threat_intel.py +0 -0
  206. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  207. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  208. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  209. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  210. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/shims.py +0 -0
  211. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  212. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
  213. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/store_threat_intel.py +0 -0
  214. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/guard/types.py +0 -0
  215. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  216. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  217. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  218. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  219. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  220. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/models.py +0 -0
  221. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/path_support.py +0 -0
  222. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/policy.py +0 -0
  223. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  224. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/repo_detect.py +0 -0
  225. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/reporting.py +0 -0
  226. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  227. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/rules/registry.py +0 -0
  228. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/rules/specs.py +0 -0
  229. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/scanner.py +0 -0
  230. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/submission.py +0 -0
  231. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/suppressions.py +0 -0
  232. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  233. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  234. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  235. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_models.py +0 -0
  236. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  237. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  238. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  239. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/trust_specs.py +0 -0
  240. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/src/codex_plugin_scanner/verification.py +0 -0
  241. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/__init__.py +0 -0
  242. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/conftest.py +0 -0
  243. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/__init__.py +0 -0
  244. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  245. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  246. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/bad-plugin/secrets.js +0 -0
  247. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  248. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  249. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/claude-plugin-good/README.md +0 -0
  250. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  251. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  252. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  253. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/code-quality-bad/evil.js +0 -0
  254. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/code-quality-bad/inject.js +0 -0
  255. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  256. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  257. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/gemini-extension-good/README.md +0 -0
  258. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  259. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  260. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  261. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  262. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/.codexignore +0 -0
  263. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/LICENSE +0 -0
  264. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/README.md +0 -0
  265. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  266. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  267. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  268. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  269. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  270. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  271. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/README.md +0 -0
  272. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/benign-docs-fake-token.py +0 -0
  273. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/benign-health-endpoint.py +0 -0
  274. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/benign-nvmrc-fake-creds.py +0 -0
  275. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/benign-source-search.py +0 -0
  276. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/canary-exfil-encoded.py +0 -0
  277. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/canary-exfil.py +0 -0
  278. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/expected-decisions.json +0 -0
  279. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-dockerfile.txt +0 -0
  280. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-encoded-shell-exfil.py +0 -0
  281. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-github-action.yml +0 -0
  282. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-mcp-delete.md +0 -0
  283. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-mcp-secret-read.md +0 -0
  284. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-mcp-skill-exfil.md +0 -0
  285. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-npm-postinstall.js +0 -0
  286. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-prompt-env-read.md +0 -0
  287. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-prompt-guard-bypass.md +0 -0
  288. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-prompt-npmrc-read.md +0 -0
  289. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/malicious-python-setup.py +0 -0
  290. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/guard-red-team/smoke-evidence-template.json +0 -0
  291. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  292. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  293. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  294. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  295. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  296. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  297. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  298. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  299. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  300. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  301. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  302. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  303. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  304. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  305. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/mcp-canary-server.py +0 -0
  306. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  307. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  308. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/mit-license/LICENSE +0 -0
  309. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  310. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  311. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  312. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  313. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  314. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  315. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  316. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  317. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  318. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  319. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  320. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  321. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  322. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  323. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  324. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  325. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  326. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  327. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/opencode-good/LICENSE +0 -0
  328. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/opencode-good/README.md +0 -0
  329. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  330. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  331. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  332. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  333. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  334. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
  335. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
  336. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
  337. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
  338. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
  339. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
  340. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
  341. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  342. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  343. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  344. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test-trust-scoring.py +0 -0
  345. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test-trust-specs.py +0 -0
  346. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_action_runner.py +0 -0
  347. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_best_practices.py +0 -0
  348. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_cisco_install_surfaces.py +0 -0
  349. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_cli.py +0 -0
  350. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_code_quality.py +0 -0
  351. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_config.py +0 -0
  352. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_coverage_remaining.py +0 -0
  353. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_ecosystems.py +0 -0
  354. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_edge_cases.py +0 -0
  355. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_final_coverage.py +0 -0
  356. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_access_graph.py +0 -0
  357. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_action_identity.py +0 -0
  358. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_advisory_escalation.py +0 -0
  359. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_approval_continuity.py +0 -0
  360. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_approval_store_scale.py +0 -0
  361. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_approvals.py +0 -0
  362. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_bootstrap.py +0 -0
  363. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_canary_fixtures.py +0 -0
  364. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_capabilities.py +0 -0
  365. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_claude_adapter.py +0 -0
  366. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_cli.py +0 -0
  367. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_cloud_local_sync.py +0 -0
  368. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_codex_e2e.py +0 -0
  369. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_codex_install.py +0 -0
  370. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_codex_proxy.py +0 -0
  371. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_config_paths.py +0 -0
  372. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_connect_flow.py +0 -0
  373. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_consumer_mode.py +0 -0
  374. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_copilot_adapter.py +0 -0
  375. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_copilot_proxy.py +0 -0
  376. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_daemon_manager.py +0 -0
  377. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_daemon_perf.py +0 -0
  378. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_data_flow.py +0 -0
  379. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_event_schema_v1.py +0 -0
  380. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_events.py +0 -0
  381. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_evidence_store.py +0 -0
  382. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_harness_contracts.py +0 -0
  383. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_launch_env.py +0 -0
  384. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_mcp_protection.py +0 -0
  385. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_opencode_proxy.py +0 -0
  386. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_policy_dedup.py +0 -0
  387. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_product_flow.py +0 -0
  388. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_prompt_injection.py +0 -0
  389. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_protect.py +0 -0
  390. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_red_team.py +0 -0
  391. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_risk.py +0 -0
  392. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_runtime.py +0 -0
  393. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_runtime_action_harnesses.py +0 -0
  394. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_runtime_actions.py +0 -0
  395. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_runtime_decisions.py +0 -0
  396. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_runtime_detectors.py +0 -0
  397. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_runtime_signals.py +0 -0
  398. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_safe_decode.py +0 -0
  399. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_sandbox.py +0 -0
  400. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_skill_protection.py +0 -0
  401. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_store_migrations.py +0 -0
  402. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_supply_chain.py +0 -0
  403. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_surface_server.py +0 -0
  404. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_threat_intel.py +0 -0
  405. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_verdicts.py +0 -0
  406. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_guard_web_recovery.py +0 -0
  407. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_hermes_adapter.py +0 -0
  408. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_integration.py +0 -0
  409. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_lint_fixes.py +0 -0
  410. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_live_cisco_smoke.py +0 -0
  411. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_manifest.py +0 -0
  412. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_marketplace.py +0 -0
  413. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_mcp_security.py +0 -0
  414. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_openclaw_adapter.py +0 -0
  415. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_operational_security.py +0 -0
  416. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_policy.py +0 -0
  417. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_quality_artifact.py +0 -0
  418. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_rule_registry.py +0 -0
  419. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_scanner.py +0 -0
  420. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_schema_contracts.py +0 -0
  421. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_security.py +0 -0
  422. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_security_ops.py +0 -0
  423. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_skill_security.py +0 -0
  424. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_submission.py +0 -0
  425. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_trust_scoring.py +0 -0
  426. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_trust_specs.py +0 -0
  427. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_verification.py +0 -0
  428. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/tests/test_versioning.py +0 -0
  429. {plugin_scanner-2.0.134 → plugin_scanner-2.0.136}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.134
3
+ Version: 2.0.136
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.134"
7
+ version = "2.0.136"
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.134"
7
+ version = "2.0.136"
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"
@@ -10,6 +10,19 @@ from ..approvals import apply_approval_resolution, build_runtime_snapshot
10
10
  from ..daemon import load_guard_daemon_url
11
11
  from ..store import GuardStore
12
12
 
13
+ _HARNESS_RETRY_COPY: dict[str, str] = {
14
+ "codex": "Return to Codex and retry",
15
+ "claude-code": "Return to Claude and retry",
16
+ "opencode": "Return to OpenCode and retry",
17
+ "copilot": "Return to Copilot and retry",
18
+ }
19
+ _DEFAULT_RETRY_COPY = "Return to your AI assistant and retry"
20
+
21
+
22
+ def _build_retry_hint(action: str, harness: str) -> dict[str, str]:
23
+ title = "Approved. Retry in chat." if action == "allow" else "Blocked. Guard will remember this decision."
24
+ return {"title": title, "body": _HARNESS_RETRY_COPY.get(harness, _DEFAULT_RETRY_COPY)}
25
+
13
26
 
14
27
  def add_approval_parser(
15
28
  guard_subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
@@ -57,6 +70,22 @@ def add_approval_parser(
57
70
  add_common_args(clear_history_parser)
58
71
  clear_history_parser.add_argument("--json", action="store_true")
59
72
 
73
+ open_parser = approvals_subparsers.add_parser(
74
+ "open",
75
+ help="Show the approval URL for a pending request",
76
+ )
77
+ open_parser.add_argument("request_id", help="The approval request ID to open")
78
+ add_common_args(open_parser)
79
+ open_parser.add_argument("--json", action="store_true")
80
+
81
+ retry_hint_parser = approvals_subparsers.add_parser(
82
+ "retry-hint",
83
+ help="Print the retry hint for a resolved approval request",
84
+ )
85
+ retry_hint_parser.add_argument("request_id", help="The approval request ID to get the retry hint for")
86
+ add_common_args(retry_hint_parser)
87
+ retry_hint_parser.add_argument("--json", action="store_true")
88
+
60
89
 
61
90
  def run_approval_command(
62
91
  args: argparse.Namespace,
@@ -115,5 +144,35 @@ def run_approval_command(
115
144
  return {"resolved": True, "item": item}
116
145
 
117
146
 
147
+ def run_approval_open_command(
148
+ args: argparse.Namespace,
149
+ *,
150
+ store: GuardStore,
151
+ ) -> tuple[dict[str, object], int]:
152
+ request_id = args.request_id
153
+ item = store.get_approval_request(request_id)
154
+ if item is None:
155
+ return {"error": "not_found", "request_id": request_id}, 1
156
+ return {"request_id": request_id, "approval_url": str(item.get("approval_url", ""))}, 0
157
+
158
+
159
+ def run_approval_retry_hint_command(
160
+ args: argparse.Namespace,
161
+ *,
162
+ store: GuardStore,
163
+ ) -> tuple[dict[str, object], int]:
164
+ request_id = args.request_id
165
+ item = store.get_approval_request(request_id)
166
+ if item is None:
167
+ return {"error": "not_found", "request_id": request_id}, 1
168
+ status = str(item.get("status", ""))
169
+ if status != "resolved":
170
+ return {"error": "not_resolved", "status": status, "request_id": request_id}, 1
171
+ resolution_action = str(item.get("resolution_action", ""))
172
+ harness = str(item.get("harness", ""))
173
+ hint: dict[str, object] = dict(_build_retry_hint(resolution_action, harness))
174
+ return hint, 0
175
+
176
+
118
177
  def _now() -> str:
119
178
  return datetime.now(timezone.utc).isoformat()
@@ -109,7 +109,12 @@ from ..runtime.secret_sensitivity import (
109
109
  from ..runtime.signals import RiskSignalV2
110
110
  from ..runtime.surface_server import GuardSurfaceRuntime
111
111
  from ..store import GuardStore
112
- from .approval_commands import add_approval_parser, run_approval_command
112
+ from .approval_commands import (
113
+ add_approval_parser,
114
+ run_approval_command,
115
+ run_approval_open_command,
116
+ run_approval_retry_hint_command,
117
+ )
113
118
  from .bootstrap import DEFAULT_ALIAS_NAME, build_guard_bootstrap_payload
114
119
  from .connect_flow import (
115
120
  DEFAULT_GUARD_CONNECT_URL,
@@ -1152,6 +1157,15 @@ def run_guard_command(
1152
1157
  return 0
1153
1158
 
1154
1159
  if args.guard_command == "approvals":
1160
+ approvals_command = getattr(args, "approvals_command", None)
1161
+ if approvals_command == "open":
1162
+ payload, exit_code = run_approval_open_command(args, store=store)
1163
+ _emit("approvals", payload, getattr(args, "json", False))
1164
+ return exit_code
1165
+ if approvals_command == "retry-hint":
1166
+ payload, exit_code = run_approval_retry_hint_command(args, store=store)
1167
+ _emit("approvals", payload, getattr(args, "json", False))
1168
+ return exit_code
1155
1169
  payload = run_approval_command(args, store=store, workspace=workspace)
1156
1170
  _emit("approvals", payload, getattr(args, "json", False))
1157
1171
  return int(payload.get("exit_code", 0))
@@ -566,6 +566,22 @@ def _render_events(console: Console, payload: dict[str, object]) -> None:
566
566
 
567
567
 
568
568
  def _render_approvals(console: Console, payload: dict[str, object]) -> None:
569
+ approval_url = payload.get("approval_url")
570
+ if isinstance(approval_url, str) and approval_url:
571
+ body = Table.grid(padding=(0, 1))
572
+ body.add_row("Request", str(payload.get("request_id", "")))
573
+ body.add_row("URL", f"[bold]{approval_url}[/bold]")
574
+ console.print(Panel(body, title="Open approval", border_style="cyan"))
575
+ return
576
+ if "title" in payload and "body" in payload:
577
+ body = Table.grid(padding=(0, 1))
578
+ body.add_row("Status", str(payload["title"]))
579
+ body.add_row("Next step", str(payload["body"]))
580
+ console.print(Panel(body, title="Approval resolved", border_style="green"))
581
+ return
582
+ if payload.get("error"):
583
+ console.print(Panel(str(payload.get("error")), title="Approval error", border_style="red"))
584
+ return
569
585
  if "history_cleared" in payload or "cleared_policies" in payload:
570
586
  error = payload.get("error")
571
587
  body = Table.grid(padding=(0, 1))
@@ -456,7 +456,15 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
456
456
  except ValueError as error:
457
457
  self._write_json({"resolved": False, "error": str(error)}, status=400)
458
458
  return
459
- self._write_json({"resolved": True, "item": updated})
459
+ normalized_scope = scope.strip()
460
+ harness_str = str(updated.get("harness", ""))
461
+ self.server.store.add_event( # type: ignore[attr-defined]
462
+ "approval_resolved",
463
+ {"request_id": request_id, "action": action, "scope": normalized_scope, "harness": harness_str},
464
+ _now(),
465
+ )
466
+ harness = str(updated.get("harness", ""))
467
+ self._write_json({"resolved": True, "item": updated, "copy": _build_resolution_copy(action, harness)})
460
468
 
461
469
  def log_message(self, fmt: str, *args: Any) -> None:
462
470
  return
@@ -1388,6 +1396,20 @@ def _build_local_url(host: str, port: int, path: str) -> str:
1388
1396
  return f"http://{host_part}:{port}{path}"
1389
1397
 
1390
1398
 
1399
+ _HARNESS_RETRY_COPY: dict[str, str] = {
1400
+ "codex": "Return to Codex and retry",
1401
+ "claude-code": "Return to Claude and retry",
1402
+ "opencode": "Return to OpenCode and retry",
1403
+ "copilot": "Return to Copilot and retry",
1404
+ }
1405
+ _DEFAULT_RETRY_COPY = "Return to your AI assistant and retry"
1406
+
1407
+
1408
+ def _build_resolution_copy(action: str, harness: str) -> dict[str, str]:
1409
+ title = "Approved. Retry in chat." if action == "allow" else "Blocked. Guard will remember this decision."
1410
+ return {"title": title, "body": _HARNESS_RETRY_COPY.get(harness, _DEFAULT_RETRY_COPY)}
1411
+
1412
+
1391
1413
  def _now() -> str:
1392
1414
  from datetime import datetime, timezone
1393
1415
 
@@ -686,6 +686,7 @@ class GuardStore:
686
686
  self._ensure_approval_column(connection, "action_envelope_json", "text")
687
687
  self._ensure_approval_column(connection, "decision_v2_json", "text")
688
688
  self._ensure_approval_column(connection, "workspace", "text")
689
+ self._ensure_approval_column(connection, "normalized_identity_key", "text")
689
690
  self._ensure_approval_column(connection, "fallback_cli_command", "text")
690
691
  self._ensure_attachment_column(connection, "lease_id", "text not null default ''")
691
692
  self._ensure_attachment_column(connection, "lease_expires_at", "text")
@@ -6,6 +6,11 @@ import json
6
6
  import sqlite3
7
7
 
8
8
  from .models import GuardApprovalRequest
9
+ from .runtime.action_identity import normalize_command_identity
10
+
11
+
12
+ def _normalized_identity_key(launch_target: str | None) -> str:
13
+ return normalize_command_identity(launch_target or "")
9
14
 
10
15
 
11
16
  def approval_schema_statement() -> str:
@@ -25,6 +30,7 @@ def approval_schema_statement() -> str:
25
30
  config_path text not null,
26
31
  workspace text,
27
32
  launch_target text,
33
+ normalized_identity_key text,
28
34
  transport text,
29
35
  risk_summary text,
30
36
  risk_signals_json text not null default '[]',
@@ -50,16 +56,37 @@ def approval_schema_statement() -> str:
50
56
 
51
57
 
52
58
  def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalRequest, now: str) -> str:
59
+ identity_key = _normalized_identity_key(request.launch_target)
53
60
  existing = connection.execute(
54
61
  """
55
62
  select request_id
56
63
  from approval_requests
57
- where harness = ? and artifact_id = ? and status = 'pending'
64
+ where harness = ?
65
+ and artifact_id = ?
66
+ and workspace IS ?
67
+ and normalized_identity_key = ?
68
+ and status = 'pending'
58
69
  order by created_at desc
59
70
  limit 1
60
71
  """,
61
- (request.harness, request.artifact_id),
72
+ (request.harness, request.artifact_id, request.workspace, identity_key),
62
73
  ).fetchone()
74
+ if existing is None:
75
+ existing = connection.execute(
76
+ """
77
+ select request_id
78
+ from approval_requests
79
+ where harness = ?
80
+ and artifact_id = ?
81
+ and workspace IS ?
82
+ and launch_target IS ?
83
+ and normalized_identity_key IS NULL
84
+ and status = 'pending'
85
+ order by created_at desc
86
+ limit 1
87
+ """,
88
+ (request.harness, request.artifact_id, request.workspace, request.launch_target),
89
+ ).fetchone()
63
90
  request_id = str(existing["request_id"]) if existing is not None else request.request_id
64
91
  if existing is not None:
65
92
  review_command = _rewrite_review_command(request.review_command, request_id)
@@ -69,7 +96,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
69
96
  update approval_requests
70
97
  set artifact_name = ?, artifact_type = ?, artifact_hash = ?, publisher = ?, policy_action = ?,
71
98
  recommended_scope = ?, changed_fields_json = ?, source_scope = ?, config_path = ?, workspace = ?,
72
- launch_target = ?, transport = ?, risk_summary = ?, risk_signals_json = ?,
99
+ launch_target = ?, normalized_identity_key = ?, transport = ?, risk_summary = ?, risk_signals_json = ?,
73
100
  artifact_label = ?, source_label = ?, trigger_summary = ?, why_now = ?, launch_summary = ?,
74
101
  risk_headline = ?, action_envelope_json = ?, decision_v2_json = ?, fallback_cli_command = ?,
75
102
  review_command = ?, approval_url = ?, created_at = ?
@@ -87,6 +114,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
87
114
  request.config_path,
88
115
  request.workspace,
89
116
  request.launch_target,
117
+ _normalized_identity_key(request.launch_target),
90
118
  request.transport,
91
119
  request.risk_summary,
92
120
  json.dumps(list(request.risk_signals)),
@@ -115,12 +143,14 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
115
143
  insert into approval_requests (
116
144
  request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
117
145
  recommended_scope, changed_fields_json, source_scope, config_path, workspace,
118
- launch_target, transport, risk_summary,
146
+ launch_target, normalized_identity_key, transport, risk_summary,
119
147
  risk_signals_json, artifact_label, source_label, trigger_summary, why_now, launch_summary, risk_headline,
120
148
  action_envelope_json, decision_v2_json, fallback_cli_command, review_command,
121
149
  approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
122
150
  )
123
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
151
+ values (
152
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
153
+ )
124
154
  """,
125
155
  (
126
156
  request.request_id,
@@ -137,6 +167,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
137
167
  request.config_path,
138
168
  request.workspace,
139
169
  request.launch_target,
170
+ _normalized_identity_key(request.launch_target),
140
171
  request.transport,
141
172
  request.risk_summary,
142
173
  json.dumps(list(request.risk_signals)),
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.134"
3
+ __version__ = "2.0.136"
@@ -0,0 +1,203 @@
1
+ """Tests for T732-T737: harness copy rule and approval CLI commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+ from codex_plugin_scanner.cli import main
11
+ from codex_plugin_scanner.guard.adapters.base import HarnessContext
12
+ from codex_plugin_scanner.guard.approvals import approval_center_hint
13
+ from codex_plugin_scanner.guard.models import GuardApprovalRequest
14
+ from codex_plugin_scanner.guard.store import GuardStore
15
+
16
+ _KNOWN_HARNESSES = ["codex", "claude-code", "opencode", "copilot", "gemini"]
17
+
18
+
19
+ def _make_queue_item(harness: str, request_id: str) -> dict:
20
+ return {
21
+ "request_id": request_id,
22
+ "harness": harness,
23
+ "artifact_id": f"{harness}:project:tool",
24
+ "artifact_name": "Test tool",
25
+ "policy_action": "block",
26
+ "recommended_scope": "artifact",
27
+ }
28
+
29
+
30
+ def _make_request(
31
+ *,
32
+ request_id: str,
33
+ harness: str = "codex",
34
+ status: str = "pending",
35
+ ) -> GuardApprovalRequest:
36
+ return GuardApprovalRequest(
37
+ request_id=request_id,
38
+ harness=harness,
39
+ artifact_id=f"{harness}:project:tool",
40
+ artifact_name="Test tool",
41
+ artifact_hash="hash-abc",
42
+ policy_action="require-reapproval",
43
+ recommended_scope="artifact",
44
+ changed_fields=("tool_action_request",),
45
+ source_scope="project",
46
+ config_path="/tmp/config.toml",
47
+ review_command=f"hol-guard approvals approve {request_id}",
48
+ approval_url=f"http://127.0.0.1:5474/approvals/{request_id}",
49
+ )
50
+
51
+
52
+ class TestHarnessBlockMessageCopyRule:
53
+ """T732: harness block messages must never tell users to run 'hol-guard dashboard' as primary path."""
54
+
55
+ @pytest.mark.parametrize("harness", _KNOWN_HARNESSES)
56
+ def test_approval_center_hint_does_not_require_manual_dashboard_launch(
57
+ self, tmp_path: Path, harness: str
58
+ ) -> None:
59
+ """T732: approval_center_hint must not instruct users to run 'hol-guard dashboard'."""
60
+ context = HarnessContext(
61
+ home_dir=tmp_path,
62
+ guard_home=tmp_path / ".hol-guard",
63
+ workspace_dir=tmp_path / "workspace",
64
+ )
65
+ queued = [_make_queue_item(harness, "req-rule-01")]
66
+ hint = approval_center_hint(
67
+ context=context,
68
+ harness=harness,
69
+ approval_center_url="http://127.0.0.1:5474",
70
+ queued=queued,
71
+ )
72
+ assert "hol-guard dashboard" not in hint, (
73
+ f"Harness hint for '{harness}' must not tell users to run 'hol-guard dashboard' as primary path. "
74
+ f"Got: {hint!r}"
75
+ )
76
+
77
+
78
+ class TestBlockMessageNoDashboardLaunchRequired:
79
+ """T733: CLI block message must not require manual dashboard launch."""
80
+
81
+ def test_block_approval_center_hint_no_manual_dashboard_command(self, tmp_path: Path) -> None:
82
+ """T733: block flow copy does not contain 'hol-guard dashboard' for any harness."""
83
+ context = HarnessContext(
84
+ home_dir=tmp_path,
85
+ guard_home=tmp_path / ".hol-guard",
86
+ workspace_dir=tmp_path / "workspace",
87
+ )
88
+ for harness in _KNOWN_HARNESSES:
89
+ queued = [_make_queue_item(harness, f"req-t733-{harness}")]
90
+ hint = approval_center_hint(
91
+ context=context,
92
+ harness=harness,
93
+ approval_center_url="http://127.0.0.1:5474",
94
+ queued=queued,
95
+ )
96
+ assert "hol-guard dashboard" not in hint, (
97
+ f"CLI block message for '{harness}' must not say 'hol-guard dashboard'. Got: {hint!r}"
98
+ )
99
+
100
+
101
+ class TestApprovalsOpenCommand:
102
+ """T734-T735: 'hol-guard approvals open <request_id>' command."""
103
+
104
+ def test_approvals_open_returns_approval_url_for_known_request(self, tmp_path: Path, capsys) -> None:
105
+ """T734: approvals open prints the approval URL for an existing pending request."""
106
+ home_dir = tmp_path / "guard-home"
107
+ store = GuardStore(home_dir)
108
+ store.add_approval_request(_make_request(request_id="req-open-01"), "2026-01-01T00:00:00Z")
109
+
110
+ rc = main(["guard", "approvals", "open", "req-open-01", "--home", str(home_dir), "--json"])
111
+ output = json.loads(capsys.readouterr().out)
112
+
113
+ assert rc == 0
114
+ assert output["request_id"] == "req-open-01"
115
+ assert "approval_url" in output
116
+
117
+ def test_approvals_open_returns_error_for_missing_request(self, tmp_path: Path, capsys) -> None:
118
+ """T735: approvals open with daemon stopped returns a clear error, not a crash."""
119
+ home_dir = tmp_path / "guard-home"
120
+
121
+ rc = main(["guard", "approvals", "open", "req-missing", "--home", str(home_dir), "--json"])
122
+ output = json.loads(capsys.readouterr().out)
123
+
124
+ assert rc != 0
125
+ assert "error" in output
126
+
127
+
128
+ class TestApprovalsRetryHintCommand:
129
+ """T736-T737: 'hol-guard approvals retry-hint <request_id>' command."""
130
+
131
+ def test_retry_hint_allow_resolution(self, tmp_path: Path, capsys) -> None:
132
+ """T737: retry-hint returns allow copy after approval."""
133
+ home_dir = tmp_path / "guard-home"
134
+ store = GuardStore(home_dir)
135
+ store.add_approval_request(_make_request(request_id="req-hint-allow"), "2026-01-01T00:00:00Z")
136
+ main(
137
+ [
138
+ "guard",
139
+ "approvals",
140
+ "approve",
141
+ "req-hint-allow",
142
+ "--home",
143
+ str(home_dir),
144
+ "--scope",
145
+ "artifact",
146
+ "--json",
147
+ ]
148
+ )
149
+ capsys.readouterr()
150
+
151
+ rc = main(["guard", "approvals", "retry-hint", "req-hint-allow", "--home", str(home_dir), "--json"])
152
+ output = json.loads(capsys.readouterr().out)
153
+
154
+ assert rc == 0
155
+ assert output["title"] == "Approved. Retry in chat."
156
+
157
+ def test_retry_hint_block_resolution(self, tmp_path: Path, capsys) -> None:
158
+ """T737: retry-hint returns block copy after block decision."""
159
+ home_dir = tmp_path / "guard-home"
160
+ store = GuardStore(home_dir)
161
+ store.add_approval_request(_make_request(request_id="req-hint-block"), "2026-01-01T00:00:00Z")
162
+ main(
163
+ [
164
+ "guard",
165
+ "approvals",
166
+ "deny",
167
+ "req-hint-block",
168
+ "--home",
169
+ str(home_dir),
170
+ "--scope",
171
+ "artifact",
172
+ "--json",
173
+ ]
174
+ )
175
+ capsys.readouterr()
176
+
177
+ rc = main(["guard", "approvals", "retry-hint", "req-hint-block", "--home", str(home_dir), "--json"])
178
+ output = json.loads(capsys.readouterr().out)
179
+
180
+ assert rc == 0
181
+ assert output["title"] == "Blocked. Guard will remember this decision."
182
+
183
+ def test_retry_hint_missing_request(self, tmp_path: Path, capsys) -> None:
184
+ """T737: retry-hint with unknown request_id returns error."""
185
+ home_dir = tmp_path / "guard-home"
186
+
187
+ rc = main(["guard", "approvals", "retry-hint", "req-hint-missing", "--home", str(home_dir), "--json"])
188
+ output = json.loads(capsys.readouterr().out)
189
+
190
+ assert rc != 0
191
+ assert "error" in output
192
+
193
+ def test_retry_hint_pending_request(self, tmp_path: Path, capsys) -> None:
194
+ """T737: retry-hint with still-pending request returns not_resolved status."""
195
+ home_dir = tmp_path / "guard-home"
196
+ store = GuardStore(home_dir)
197
+ store.add_approval_request(_make_request(request_id="req-hint-pending"), "2026-01-01T00:00:00Z")
198
+
199
+ rc = main(["guard", "approvals", "retry-hint", "req-hint-pending", "--home", str(home_dir), "--json"])
200
+ output = json.loads(capsys.readouterr().out)
201
+
202
+ assert rc != 0
203
+ assert output.get("status") == "pending" or "error" in output