plugin-scanner 2.0.132__tar.gz → 2.0.134__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 (425) hide show
  1. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/approvals.py +10 -2
  5. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/server.py +76 -7
  6. plugin_scanner-2.0.134/src/codex_plugin_scanner/guard/runtime/action_identity.py +87 -0
  7. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/version.py +1 -1
  8. plugin_scanner-2.0.134/tests/test_guard_action_identity.py +155 -0
  9. plugin_scanner-2.0.134/tests/test_guard_policy_dedup.py +301 -0
  10. plugin_scanner-2.0.134/tests/test_guard_web_recovery.py +190 -0
  11. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/Dockerfile +0 -0
  12. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/build.sh +0 -0
  13. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/project.yaml +0 -0
  14. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  15. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.dockerignore +0 -0
  16. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/CODEOWNERS +0 -0
  17. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  18. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  19. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  20. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/dependabot.yml +0 -0
  21. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/ci.yml +0 -0
  22. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/codeql.yml +0 -0
  23. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/dependabot-uv-lock.yml +0 -0
  24. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/fuzz.yml +0 -0
  25. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/harness-smoke.yml +0 -0
  26. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/publish.yml +0 -0
  27. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.github/workflows/scorecard.yml +0 -0
  28. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.gitignore +0 -0
  29. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/.pre-commit-hooks.yaml +0 -0
  30. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/CONTRIBUTING.md +0 -0
  31. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/Dockerfile +0 -0
  32. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/LICENSE +0 -0
  33. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/README.md +0 -0
  34. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/SECURITY.md +0 -0
  35. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/index.html +0 -0
  36. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/package.json +0 -0
  37. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/pnpm-lock.yaml +0 -0
  38. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/apple-touch-icon.png +0 -0
  39. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  40. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/brand/Logo_Whole.png +0 -0
  41. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/favicon-16x16.png +0 -0
  42. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/favicon-32x32.png +0 -0
  43. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/public/favicon.ico +0 -0
  44. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/app.tsx +0 -0
  45. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-layout.test.ts +0 -0
  46. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-layout.tsx +0 -0
  47. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-primitives.tsx +0 -0
  48. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-review-cards.tsx +0 -0
  49. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/approval-center-utils.ts +0 -0
  50. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/data-flow-evidence-card.tsx +0 -0
  51. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/fleet-workspace.tsx +0 -0
  52. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-api.test.ts +0 -0
  53. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-api.ts +0 -0
  54. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-demo.ts +0 -0
  55. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/guard-types.ts +0 -0
  56. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/main.tsx +0 -0
  57. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/receipts-workspace.test.ts +0 -0
  58. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/receipts-workspace.tsx +0 -0
  59. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/runtime-overview.test.ts +0 -0
  60. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/runtime-overview.tsx +0 -0
  61. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/settings-workspace.test.ts +0 -0
  62. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/settings-workspace.tsx +0 -0
  63. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/styles.css +0 -0
  64. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/src/vite-env.d.ts +0 -0
  65. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/tsconfig.json +0 -0
  66. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/dashboard/vite.config.ts +0 -0
  67. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docker-requirements.txt +0 -0
  68. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/approval-audit.md +0 -0
  69. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/architecture.md +0 -0
  70. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/get-started.md +0 -0
  71. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/harness-support.md +0 -0
  72. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/local-vs-cloud.md +0 -0
  73. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/release-checklist.md +0 -0
  74. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/smoke-tests.md +0 -0
  75. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/guard/testing-matrix.md +0 -0
  76. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/trust/mcp-trust-draft.md +0 -0
  77. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/trust/plugin-trust-draft.md +0 -0
  78. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/docs/trust/skill-trust-local.md +0 -0
  79. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/fuzzers/manifest_fuzzer.py +0 -0
  80. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/requirements.txt +0 -0
  81. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/schemas/plugin-quality.v1.json +0 -0
  82. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/schemas/scan-result.v1.json +0 -0
  83. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/schemas/verify-result.v1.json +0 -0
  84. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/__init__.py +0 -0
  85. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/action_runner.py +0 -0
  86. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  87. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  88. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  89. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/claude.py +0 -0
  90. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  91. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  92. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  93. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  94. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  95. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  96. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  97. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  98. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  99. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/security.py +0 -0
  100. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  101. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/cli.py +0 -0
  102. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/cli_ui.py +0 -0
  103. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/config.py +0 -0
  104. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  105. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  106. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  107. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  108. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  109. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  110. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  111. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  112. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  113. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/github_reporting.py +0 -0
  114. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  115. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
  116. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  117. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  118. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  119. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  120. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  121. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  122. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/contracts.py +0 -0
  123. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  124. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  125. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  126. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  127. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  128. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  129. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  130. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  131. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  132. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  133. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  134. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  135. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  136. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  137. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  138. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  139. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
  140. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  141. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  142. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  143. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  144. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  145. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  146. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  147. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/config.py +0 -0
  148. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  149. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  150. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  151. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  152. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  153. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  154. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  155. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  156. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  157. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  158. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  159. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  160. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  161. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  162. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  163. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/incident.py +0 -0
  164. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  165. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  166. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/models.py +0 -0
  167. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  168. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  169. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/protect.py +0 -0
  170. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  171. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  172. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  173. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  174. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  175. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  176. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  177. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/risk.py +0 -0
  178. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  179. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  180. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/advisory_escalation.py +0 -0
  181. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/advisory_matchers.py +0 -0
  182. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
  183. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
  184. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
  185. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  186. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
  187. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
  188. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
  189. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  190. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
  191. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
  192. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  193. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  194. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
  195. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
  196. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  197. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
  198. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
  199. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  200. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
  201. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/runtime/threat_intel.py +0 -0
  202. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  203. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  204. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  205. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  206. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/shims.py +0 -0
  207. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store.py +0 -0
  208. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  209. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  210. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
  211. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/store_threat_intel.py +0 -0
  212. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/guard/types.py +0 -0
  213. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  214. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  215. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  216. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  217. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  218. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/models.py +0 -0
  219. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/path_support.py +0 -0
  220. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/policy.py +0 -0
  221. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  222. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/repo_detect.py +0 -0
  223. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/reporting.py +0 -0
  224. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  225. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/rules/registry.py +0 -0
  226. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/rules/specs.py +0 -0
  227. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/scanner.py +0 -0
  228. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/submission.py +0 -0
  229. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/suppressions.py +0 -0
  230. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  231. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  232. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  233. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_models.py +0 -0
  234. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  235. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  236. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  237. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/trust_specs.py +0 -0
  238. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/src/codex_plugin_scanner/verification.py +0 -0
  239. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/__init__.py +0 -0
  240. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/conftest.py +0 -0
  241. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/__init__.py +0 -0
  242. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  243. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  244. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/bad-plugin/secrets.js +0 -0
  245. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  246. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  247. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/README.md +0 -0
  248. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  249. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  250. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  251. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/code-quality-bad/evil.js +0 -0
  252. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/code-quality-bad/inject.js +0 -0
  253. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  254. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  255. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/README.md +0 -0
  256. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  257. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  258. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  259. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  260. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/.codexignore +0 -0
  261. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/LICENSE +0 -0
  262. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/README.md +0 -0
  263. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  264. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  265. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  266. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  267. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  268. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  269. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/README.md +0 -0
  270. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-docs-fake-token.py +0 -0
  271. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-health-endpoint.py +0 -0
  272. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-nvmrc-fake-creds.py +0 -0
  273. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/benign-source-search.py +0 -0
  274. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/canary-exfil-encoded.py +0 -0
  275. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/canary-exfil.py +0 -0
  276. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/expected-decisions.json +0 -0
  277. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-dockerfile.txt +0 -0
  278. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-encoded-shell-exfil.py +0 -0
  279. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-github-action.yml +0 -0
  280. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-mcp-delete.md +0 -0
  281. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-mcp-secret-read.md +0 -0
  282. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-mcp-skill-exfil.md +0 -0
  283. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-npm-postinstall.js +0 -0
  284. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-prompt-env-read.md +0 -0
  285. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-prompt-guard-bypass.md +0 -0
  286. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-prompt-npmrc-read.md +0 -0
  287. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/malicious-python-setup.py +0 -0
  288. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/guard-red-team/smoke-evidence-template.json +0 -0
  289. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  290. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  291. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  292. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  293. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  294. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  295. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  296. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  297. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  298. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  299. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  300. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  301. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  302. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  303. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/mcp-canary-server.py +0 -0
  304. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  305. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  306. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/mit-license/LICENSE +0 -0
  307. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  308. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  309. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  310. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  311. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  312. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  313. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  314. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  315. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  316. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  317. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  318. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  319. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  320. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  321. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  322. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  323. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  324. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  325. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/LICENSE +0 -0
  326. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/README.md +0 -0
  327. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  328. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  329. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  330. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  331. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  332. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
  333. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
  334. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
  335. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
  336. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
  337. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
  338. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
  339. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  340. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  341. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  342. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test-trust-scoring.py +0 -0
  343. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test-trust-specs.py +0 -0
  344. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_action_runner.py +0 -0
  345. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_best_practices.py +0 -0
  346. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_cisco_install_surfaces.py +0 -0
  347. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_cli.py +0 -0
  348. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_code_quality.py +0 -0
  349. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_config.py +0 -0
  350. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_coverage_remaining.py +0 -0
  351. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_ecosystems.py +0 -0
  352. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_edge_cases.py +0 -0
  353. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_final_coverage.py +0 -0
  354. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_access_graph.py +0 -0
  355. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_advisory_escalation.py +0 -0
  356. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_approval_continuity.py +0 -0
  357. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_approval_store_scale.py +0 -0
  358. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_approvals.py +0 -0
  359. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_bootstrap.py +0 -0
  360. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_canary_fixtures.py +0 -0
  361. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_capabilities.py +0 -0
  362. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_claude_adapter.py +0 -0
  363. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_cli.py +0 -0
  364. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_cloud_local_sync.py +0 -0
  365. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_codex_e2e.py +0 -0
  366. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_codex_install.py +0 -0
  367. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_codex_proxy.py +0 -0
  368. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_config_paths.py +0 -0
  369. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_connect_flow.py +0 -0
  370. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_consumer_mode.py +0 -0
  371. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_copilot_adapter.py +0 -0
  372. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_copilot_proxy.py +0 -0
  373. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_daemon_manager.py +0 -0
  374. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_daemon_perf.py +0 -0
  375. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_data_flow.py +0 -0
  376. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_event_schema_v1.py +0 -0
  377. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_events.py +0 -0
  378. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_evidence_store.py +0 -0
  379. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_harness_contracts.py +0 -0
  380. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_launch_env.py +0 -0
  381. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_mcp_protection.py +0 -0
  382. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_opencode_proxy.py +0 -0
  383. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_product_flow.py +0 -0
  384. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_prompt_injection.py +0 -0
  385. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_protect.py +0 -0
  386. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_red_team.py +0 -0
  387. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_render.py +0 -0
  388. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_risk.py +0 -0
  389. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime.py +0 -0
  390. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_action_harnesses.py +0 -0
  391. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_actions.py +0 -0
  392. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_decisions.py +0 -0
  393. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_detectors.py +0 -0
  394. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_runtime_signals.py +0 -0
  395. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_safe_decode.py +0 -0
  396. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_sandbox.py +0 -0
  397. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_skill_protection.py +0 -0
  398. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_store_migrations.py +0 -0
  399. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_supply_chain.py +0 -0
  400. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_surface_server.py +0 -0
  401. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_threat_intel.py +0 -0
  402. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_guard_verdicts.py +0 -0
  403. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_hermes_adapter.py +0 -0
  404. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_integration.py +0 -0
  405. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_lint_fixes.py +0 -0
  406. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_live_cisco_smoke.py +0 -0
  407. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_manifest.py +0 -0
  408. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_marketplace.py +0 -0
  409. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_mcp_security.py +0 -0
  410. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_openclaw_adapter.py +0 -0
  411. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_operational_security.py +0 -0
  412. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_policy.py +0 -0
  413. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_quality_artifact.py +0 -0
  414. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_rule_registry.py +0 -0
  415. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_scanner.py +0 -0
  416. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_schema_contracts.py +0 -0
  417. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_security.py +0 -0
  418. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_security_ops.py +0 -0
  419. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_skill_security.py +0 -0
  420. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_submission.py +0 -0
  421. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_trust_scoring.py +0 -0
  422. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_trust_specs.py +0 -0
  423. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_verification.py +0 -0
  424. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/tests/test_versioning.py +0 -0
  425. {plugin_scanner-2.0.132 → plugin_scanner-2.0.134}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.132
3
+ Version: 2.0.134
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.132"
7
+ version = "2.0.134"
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.132"
7
+ version = "2.0.134"
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"
@@ -24,6 +24,14 @@ GUARD_FLEET_URL = f"{GUARD_DASHBOARD_URL}/fleet"
24
24
  GUARD_CONNECT_URL = f"{GUARD_DASHBOARD_URL}/connect"
25
25
 
26
26
 
27
+ class ApprovalRequestNotFoundError(ValueError):
28
+ """Raised when an approval request ID does not exist."""
29
+
30
+
31
+ class ApprovalRequestAlreadyResolvedError(ValueError):
32
+ """Raised when an approval request was already resolved."""
33
+
34
+
27
35
  def queue_blocked_approvals(
28
36
  *,
29
37
  detection: HarnessDetection,
@@ -114,9 +122,9 @@ def apply_approval_resolution(
114
122
  ) -> dict[str, object]:
115
123
  request = store.get_approval_request(request_id)
116
124
  if request is None:
117
- raise ValueError(f"Unknown approval request: {request_id}")
125
+ raise ApprovalRequestNotFoundError(f"Unknown approval request: {request_id}")
118
126
  if request["status"] != "pending":
119
- raise ValueError(f"Approval request already resolved: {request_id}")
127
+ raise ApprovalRequestAlreadyResolvedError(f"Approval request already resolved: {request_id}")
120
128
  if scope == "workspace" and not workspace:
121
129
  raise ValueError(f"Approval request {request_id} requires --workspace for workspace scope.")
122
130
  if scope == "publisher" and not isinstance(request.get("publisher"), str):
@@ -18,7 +18,12 @@ from typing import Any
18
18
  from urllib.parse import parse_qs, parse_qsl, unquote, urlencode, urlparse, urlunparse
19
19
 
20
20
  from ...version import __version__
21
- from ..approvals import apply_approval_resolution, build_runtime_snapshot
21
+ from ..approvals import (
22
+ ApprovalRequestAlreadyResolvedError,
23
+ ApprovalRequestNotFoundError,
24
+ apply_approval_resolution,
25
+ build_runtime_snapshot,
26
+ )
22
27
  from ..config import editable_guard_settings, load_guard_config, update_guard_settings
23
28
  from ..models import DECISION_SCOPE_VALUES, GUARD_ACTION_VALUES
24
29
  from ..runtime.surface_server import GuardSurfaceRuntime
@@ -183,7 +188,17 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
183
188
  if len(path_parts) == 3 and path_parts[:2] == ["v1", "requests"]:
184
189
  approval = store.get_approval_request(path_parts[2])
185
190
  if approval is None:
186
- self._write_json({"error": "not_found"}, status=404)
191
+ self._write_json(
192
+ {
193
+ "error": "not_found",
194
+ "recovery": {
195
+ "code": "request_unknown",
196
+ "title": "This request is no longer waiting.",
197
+ "body": "The request was either already resolved or expired. You can close this tab.",
198
+ },
199
+ },
200
+ status=404,
201
+ )
187
202
  return
188
203
  self._write_json(approval)
189
204
  return
@@ -313,11 +328,29 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
313
328
  self._write_json({"error": "forbidden_origin"}, status=403)
314
329
  return
315
330
  if self._requires_header_token(parsed.path, path_parts) and not self._header_token_is_valid():
316
- self._write_json(
317
- {"error": "unauthorized"},
318
- status=401,
319
- extra_headers=self._cors_headers_for_request(),
320
- )
331
+ if len(path_parts) == 4 and path_parts[:2] == ["v1", "requests"] and path_parts[3] in {"approve", "block"}:
332
+ host = self.server.server_address[0] # type: ignore[attr-defined]
333
+ port = self.server.server_address[1] # type: ignore[attr-defined]
334
+ reconnect_url = _build_local_url(host, port, "/#/reconnect")
335
+ self._write_json(
336
+ {
337
+ "error": "unauthorized",
338
+ "recovery": {
339
+ "code": "session_stale",
340
+ "title": "Your session with the local Guard daemon has expired.",
341
+ "body": "Click the link below to reconnect, then retry your approval.",
342
+ "reconnect_url": reconnect_url,
343
+ },
344
+ },
345
+ status=401,
346
+ extra_headers=self._cors_headers_for_request(),
347
+ )
348
+ else:
349
+ self._write_json(
350
+ {"error": "unauthorized"},
351
+ status=401,
352
+ extra_headers=self._cors_headers_for_request(),
353
+ )
321
354
  return
322
355
  payload, body_error = self._load_request_body()
323
356
  if body_error is not None:
@@ -389,6 +422,37 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
389
422
  workspace=self._optional_string(payload.get("workspace")),
390
423
  reason=self._optional_string(payload.get("reason")),
391
424
  )
425
+ except ApprovalRequestNotFoundError:
426
+ self._write_json(
427
+ {
428
+ "resolved": False,
429
+ "error": "not_found",
430
+ "recovery": {
431
+ "code": "request_unknown",
432
+ "title": "This request is no longer waiting.",
433
+ "body": "The request was either already resolved or expired. You can close this tab.",
434
+ },
435
+ },
436
+ status=404,
437
+ )
438
+ return
439
+ except ApprovalRequestAlreadyResolvedError:
440
+ self._write_json(
441
+ {
442
+ "resolved": False,
443
+ "error": "already_resolved",
444
+ "recovery": {
445
+ "code": "request_resolved",
446
+ "title": "This request has already been resolved.",
447
+ "body": (
448
+ "If the action is blocked and you believe it should be allowed, "
449
+ "you can re-submit from your AI assistant."
450
+ ),
451
+ },
452
+ },
453
+ status=409,
454
+ )
455
+ return
392
456
  except ValueError as error:
393
457
  self._write_json({"resolved": False, "error": str(error)}, status=400)
394
458
  return
@@ -1319,6 +1383,11 @@ def _approval_center_browser_url(approval_center_url: str, auth_token: str) -> s
1319
1383
  return urlunparse(parsed._replace(fragment=urlencode(fragment_pairs)))
1320
1384
 
1321
1385
 
1386
+ def _build_local_url(host: str, port: int, path: str) -> str:
1387
+ host_part = f"[{host}]" if ":" in host else host
1388
+ return f"http://{host_part}:{port}{path}"
1389
+
1390
+
1322
1391
  def _now() -> str:
1323
1392
  from datetime import datetime, timezone
1324
1393
 
@@ -0,0 +1,87 @@
1
+ """Stable action identity normalization for Guard policy deduplication.
2
+
3
+ Provides normalizers for command, prompt, and MCP tool call identities.
4
+ The output of each normalizer is a stable string suitable for comparison
5
+ or hashing across repeated calls with transient variation stripped out.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import json
12
+ import re
13
+
14
+ _ANSI_ESCAPE = re.compile(r"\x1b\[[0-9;]*[mABCDEFGHJKSTfhinsu]")
15
+
16
+ _REQUEST_ID_PATTERN = re.compile(
17
+ r"\b(?:req|request|approval|id)[-_][a-zA-Z0-9_-]{4,64}\b",
18
+ re.IGNORECASE,
19
+ )
20
+
21
+ _TIMESTAMP_PATTERN = re.compile(r"\b\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))?\b")
22
+
23
+ _PORT_FLAG_PATTERN = re.compile(
24
+ r"(?:--port|-p)\s+\d{2,5}\b",
25
+ re.IGNORECASE,
26
+ )
27
+
28
+ _MARKDOWN_BOLD_ITALIC = re.compile(r"\*{1,3}|(?<!\w)_{1,3}(?=\w)|(?<=\w)_{1,3}(?!\w)")
29
+
30
+ _BACKTICK_INLINE_CODE = re.compile(r"`([^`]+)`")
31
+
32
+ _GENERIC_REQUEST_ID_IN_ARGS = re.compile(r"\b[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\b")
33
+
34
+
35
+ def normalize_command_identity(command: str) -> str:
36
+ """Return a stable identity string for a shell command.
37
+
38
+ Strips ANSI codes, daemon ports, timestamps, approval request IDs,
39
+ UUIDs, and excess whitespace while preserving the command name,
40
+ meaningful arguments, target paths, and network hosts.
41
+ """
42
+ normalized = _ANSI_ESCAPE.sub("", command)
43
+ normalized = _REQUEST_ID_PATTERN.sub("<request-id>", normalized)
44
+ normalized = _GENERIC_REQUEST_ID_IN_ARGS.sub("<uuid>", normalized)
45
+ normalized = _TIMESTAMP_PATTERN.sub("<timestamp>", normalized)
46
+ normalized = _PORT_FLAG_PATTERN.sub("<port-flag>", normalized)
47
+ normalized = re.sub(r"\s+", " ", normalized).strip()
48
+ return normalized
49
+
50
+
51
+ def normalize_prompt_identity(prompt: str) -> str:
52
+ """Return a stable identity string for a prompt text.
53
+
54
+ Strips markdown bold/italic formatting and normalises whitespace,
55
+ while preserving the requested sensitive targets (file paths, keys,
56
+ tokens, named secrets).
57
+ """
58
+ normalized = _MARKDOWN_BOLD_ITALIC.sub("", prompt)
59
+ normalized = _BACKTICK_INLINE_CODE.sub(r"\1", normalized)
60
+ normalized = re.sub(r"\s+", " ", normalized).strip()
61
+ normalized = normalized.lower()
62
+ return normalized
63
+
64
+
65
+ def normalize_mcp_identity(call: dict[str, object]) -> str:
66
+ """Return a stable identity hash for an MCP tool call.
67
+
68
+ The identity is derived from:
69
+ - server_id
70
+ - tool_name
71
+ - arguments (sorted keys, stable JSON)
72
+ - schema_hash (when present)
73
+
74
+ Returns a hex digest suitable for equality comparison.
75
+ """
76
+ server_id = str(call.get("server_id", ""))
77
+ tool_name = str(call.get("tool_name", ""))
78
+ arguments = call.get("arguments", {})
79
+ schema_hash = str(call.get("schema_hash", ""))
80
+
81
+ stable_args = json.dumps(
82
+ {k: v for k, v in sorted(arguments.items())} if isinstance(arguments, dict) else arguments,
83
+ sort_keys=True,
84
+ ensure_ascii=True,
85
+ )
86
+ identity_source = f"{server_id}:{tool_name}:{stable_args}:{schema_hash}"
87
+ return hashlib.sha256(identity_source.encode("utf-8")).hexdigest()
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.132"
3
+ __version__ = "2.0.134"
@@ -0,0 +1,155 @@
1
+ """Tests for action identity normalization (T700-T718)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+
7
+ action_identity_mod = pytest.importorskip(
8
+ "codex_plugin_scanner.guard.runtime.action_identity",
9
+ reason="action_identity module not yet implemented",
10
+ )
11
+
12
+ normalize_command_identity = action_identity_mod.normalize_command_identity
13
+ normalize_prompt_identity = action_identity_mod.normalize_prompt_identity
14
+ normalize_mcp_identity = action_identity_mod.normalize_mcp_identity
15
+
16
+
17
+ class TestCommandIdentityNormalization:
18
+ """T700-T705: Command identity normalizer."""
19
+
20
+ def test_same_command_with_different_request_ids_maps_to_same_identity(self) -> None:
21
+ """T703: Different approval request IDs in command must not change identity."""
22
+ cmd_a = "hol-guard approvals approve req-abc-001 --scope artifact"
23
+ cmd_b = "hol-guard approvals approve req-xyz-999 --scope artifact"
24
+ assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
25
+
26
+ def test_ansi_codes_removed_before_normalization(self) -> None:
27
+ """T701: ANSI escape codes must be stripped before normalization."""
28
+ cmd_with_ansi = "\x1b[32mnode\x1b[0m server.js"
29
+ cmd_clean = "node server.js"
30
+ assert normalize_command_identity(cmd_with_ansi) == normalize_command_identity(cmd_clean)
31
+
32
+ def test_daemon_port_numbers_removed(self) -> None:
33
+ """T701: Ephemeral port numbers (like approval center ports) must not affect identity."""
34
+ cmd_a = "hol-guard approvals --port 6174"
35
+ cmd_b = "hol-guard approvals --port 7890"
36
+ assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
37
+
38
+ def test_timestamps_removed(self) -> None:
39
+ """T701: Timestamps in commands must not affect identity."""
40
+ cmd_a = "backup.sh --at 2026-01-01T00:00:00Z"
41
+ cmd_b = "backup.sh --at 2026-06-15T12:30:00Z"
42
+ assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
43
+
44
+ def test_different_network_hosts_produce_different_identity(self) -> None:
45
+ """T704: Commands targeting different network hosts must have different identity."""
46
+ cmd_internal = "curl http://internal.corp/api/data"
47
+ cmd_external = "curl http://evil.example.com/api/data"
48
+ assert normalize_command_identity(cmd_internal) != normalize_command_identity(cmd_external)
49
+
50
+ def test_different_secret_paths_produce_different_identity(self) -> None:
51
+ """T705: Commands targeting different secret paths must have different identity."""
52
+ cmd_npmrc = "cat /Users/me/.npmrc"
53
+ cmd_env = "cat /Users/me/.env"
54
+ assert normalize_command_identity(cmd_npmrc) != normalize_command_identity(cmd_env)
55
+
56
+ def test_meaningful_command_preserved(self) -> None:
57
+ """T702: Core command and meaningful args must be preserved in normalized identity."""
58
+ id_a = normalize_command_identity("node server.js --port 3000")
59
+ id_b = normalize_command_identity("python server.py --port 3000")
60
+ assert id_a != id_b, "Different commands must produce different identities"
61
+
62
+ def test_whitespace_normalized(self) -> None:
63
+ """T701: Extra whitespace must not affect identity."""
64
+ cmd_a = "node server.js --port 3000"
65
+ cmd_b = "node server.js --port 3000"
66
+ assert normalize_command_identity(cmd_a) == normalize_command_identity(cmd_b)
67
+
68
+
69
+ class TestPromptIdentityNormalization:
70
+ """T706-T708: Prompt identity normalizer."""
71
+
72
+ def test_repeated_read_npmrc_prompt_maps_to_same_identity(self) -> None:
73
+ """T707: Same prompt repeated with minor formatting differences maps to same identity."""
74
+ prompt_a = "Read the **`.npmrc`** file and tell me the registry config."
75
+ prompt_b = "Read the `.npmrc` file and tell me the registry config."
76
+ assert normalize_prompt_identity(prompt_a) == normalize_prompt_identity(prompt_b)
77
+
78
+ def test_npmrc_vs_env_prompt_maps_to_different_identity(self) -> None:
79
+ """T708: Prompts targeting different secrets must have different identity."""
80
+ prompt_npmrc = "Read the .npmrc file."
81
+ prompt_env = "Read the .env file."
82
+ assert normalize_prompt_identity(prompt_npmrc) != normalize_prompt_identity(prompt_env)
83
+
84
+ def test_model_formatting_tokens_removed(self) -> None:
85
+ """T706: Transient model formatting (bold, markdown etc.) must not affect identity."""
86
+ prompt_formatted = "**Read** the `.npmrc` file and extract the _auth token_."
87
+ prompt_plain = "Read the .npmrc file and extract the auth token."
88
+ assert normalize_prompt_identity(prompt_formatted) == normalize_prompt_identity(prompt_plain)
89
+
90
+ def test_underscores_in_identifiers_preserved(self) -> None:
91
+ """T706b: Underscores inside identifiers must not be stripped."""
92
+ prompt_key = "Send OPENAI_API_KEY to the server."
93
+ prompt_file = "Read my_secret_file from disk."
94
+ normalized_key = normalize_prompt_identity(prompt_key)
95
+ normalized_file = normalize_prompt_identity(prompt_file)
96
+ assert "openai_api_key" in normalized_key, "Internal underscores in env var names must be preserved"
97
+ assert "my_secret_file" in normalized_file, "Internal underscores in file names must be preserved"
98
+
99
+
100
+ class TestMcpIdentityNormalization:
101
+ """T709-T711: MCP identity normalizer."""
102
+
103
+ def test_same_tool_and_target_maps_to_same_identity(self) -> None:
104
+ """T710: Same MCP server, tool, and target must produce same identity."""
105
+ call_a = {
106
+ "server_id": "github-mcp",
107
+ "tool_name": "read_file",
108
+ "arguments": {"path": "/Users/me/.npmrc"},
109
+ }
110
+ call_b = {
111
+ "server_id": "github-mcp",
112
+ "tool_name": "read_file",
113
+ "arguments": {"path": "/Users/me/.npmrc"},
114
+ }
115
+ assert normalize_mcp_identity(call_a) == normalize_mcp_identity(call_b)
116
+
117
+ def test_different_mcp_target_produces_different_identity(self) -> None:
118
+ """T710: Same MCP tool with different target must produce different identity."""
119
+ call_npmrc = {
120
+ "server_id": "github-mcp",
121
+ "tool_name": "read_file",
122
+ "arguments": {"path": "/Users/me/.npmrc"},
123
+ }
124
+ call_env = {
125
+ "server_id": "github-mcp",
126
+ "tool_name": "read_file",
127
+ "arguments": {"path": "/Users/me/.env"},
128
+ }
129
+ assert normalize_mcp_identity(call_npmrc) != normalize_mcp_identity(call_env)
130
+
131
+ def test_mcp_schema_change_produces_different_identity(self) -> None:
132
+ """T711: Change in MCP tool schema hash must invalidate previous approval identity."""
133
+ call_v1 = {
134
+ "server_id": "custom-mcp",
135
+ "tool_name": "execute",
136
+ "arguments": {"cmd": "ls"},
137
+ "schema_hash": "v1-abc123",
138
+ }
139
+ call_v2 = {
140
+ "server_id": "custom-mcp",
141
+ "tool_name": "execute",
142
+ "arguments": {"cmd": "ls"},
143
+ "schema_hash": "v2-def456",
144
+ }
145
+ assert normalize_mcp_identity(call_v1) != normalize_mcp_identity(call_v2)
146
+
147
+ def test_mcp_identity_is_deterministic(self) -> None:
148
+ """T709: normalize_mcp_identity must return same hash on repeated calls."""
149
+ call = {
150
+ "server_id": "test-mcp",
151
+ "tool_name": "read_file",
152
+ "arguments": {"path": "/tmp/test"},
153
+ "schema_hash": "hash-001",
154
+ }
155
+ assert normalize_mcp_identity(call) == normalize_mcp_identity(call)