plugin-scanner 2.0.109__tar.gz → 2.0.111__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 (350) hide show
  1. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/commands.py +386 -24
  5. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/render.py +3 -0
  6. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/consumer/service.py +9 -1
  7. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +25 -2
  8. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/version.py +1 -1
  9. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_render.py +23 -5
  10. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_risk.py +20 -0
  11. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime.py +559 -0
  12. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/Dockerfile +0 -0
  13. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/build.sh +0 -0
  14. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/project.yaml +0 -0
  15. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  16. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.dockerignore +0 -0
  17. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/CODEOWNERS +0 -0
  18. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  19. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  20. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  21. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/dependabot.yml +0 -0
  22. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/ci.yml +0 -0
  23. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/codeql.yml +0 -0
  24. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/dependabot-uv-lock.yml +0 -0
  25. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/fuzz.yml +0 -0
  26. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/harness-smoke.yml +0 -0
  27. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/publish.yml +0 -0
  28. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/scorecard.yml +0 -0
  29. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.gitignore +0 -0
  30. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.pre-commit-hooks.yaml +0 -0
  31. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/CONTRIBUTING.md +0 -0
  32. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/Dockerfile +0 -0
  33. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/LICENSE +0 -0
  34. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/README.md +0 -0
  35. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/SECURITY.md +0 -0
  36. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/index.html +0 -0
  37. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/package.json +0 -0
  38. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/pnpm-lock.yaml +0 -0
  39. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/apple-touch-icon.png +0 -0
  40. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  41. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/brand/Logo_Whole.png +0 -0
  42. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/favicon-16x16.png +0 -0
  43. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/favicon-32x32.png +0 -0
  44. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/favicon.ico +0 -0
  45. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/app.tsx +0 -0
  46. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/approval-center-layout.tsx +0 -0
  47. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/approval-center-primitives.tsx +0 -0
  48. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/approval-center-utils.ts +0 -0
  49. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/fleet-workspace.tsx +0 -0
  50. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-api.test.ts +0 -0
  51. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-api.ts +0 -0
  52. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-demo.ts +0 -0
  53. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-types.ts +0 -0
  54. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/main.tsx +0 -0
  55. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/receipts-workspace.tsx +0 -0
  56. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/runtime-overview.tsx +0 -0
  57. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/settings-workspace.tsx +0 -0
  58. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/styles.css +0 -0
  59. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/vite-env.d.ts +0 -0
  60. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/tsconfig.json +0 -0
  61. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/vite.config.ts +0 -0
  62. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docker-requirements.txt +0 -0
  63. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/approval-audit.md +0 -0
  64. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/architecture.md +0 -0
  65. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/get-started.md +0 -0
  66. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/harness-support.md +0 -0
  67. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/local-vs-cloud.md +0 -0
  68. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/testing-matrix.md +0 -0
  69. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/trust/mcp-trust-draft.md +0 -0
  70. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/trust/plugin-trust-draft.md +0 -0
  71. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/trust/skill-trust-local.md +0 -0
  72. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/fuzzers/manifest_fuzzer.py +0 -0
  73. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/requirements.txt +0 -0
  74. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/schemas/plugin-quality.v1.json +0 -0
  75. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/schemas/scan-result.v1.json +0 -0
  76. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/schemas/verify-result.v1.json +0 -0
  77. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/__init__.py +0 -0
  78. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/action_runner.py +0 -0
  79. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  80. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  81. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  82. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/claude.py +0 -0
  83. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  84. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  85. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  86. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  87. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  88. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  89. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  90. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  91. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  92. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/security.py +0 -0
  93. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  94. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/cli.py +0 -0
  95. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/cli_ui.py +0 -0
  96. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/config.py +0 -0
  97. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  98. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  99. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  100. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  101. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  102. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  103. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  104. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  105. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  106. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/github_reporting.py +0 -0
  107. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  108. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  109. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  110. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  111. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  112. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  113. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  114. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  115. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  116. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  117. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  118. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  119. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  120. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  121. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  122. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  123. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  124. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  125. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  126. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  127. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  128. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  129. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  130. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  131. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  132. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  133. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  134. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  135. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  136. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  137. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/config.py +0 -0
  138. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  139. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  140. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  141. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  142. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  143. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  144. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  145. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  146. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  147. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  148. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  149. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  150. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  151. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  152. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  153. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/incident.py +0 -0
  154. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  155. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  156. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/models.py +0 -0
  157. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  158. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  159. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/protect.py +0 -0
  160. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  161. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  162. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  163. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  164. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  165. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  166. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  167. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/risk.py +0 -0
  168. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  169. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  170. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  171. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
  172. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  173. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  174. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  175. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  176. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  177. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  178. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  179. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  180. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/shims.py +0 -0
  181. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/store.py +0 -0
  182. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  183. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  184. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/types.py +0 -0
  185. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  186. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  187. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  188. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  189. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  190. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/models.py +0 -0
  191. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/path_support.py +0 -0
  192. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/policy.py +0 -0
  193. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  194. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/repo_detect.py +0 -0
  195. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/reporting.py +0 -0
  196. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  197. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/rules/registry.py +0 -0
  198. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/rules/specs.py +0 -0
  199. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/scanner.py +0 -0
  200. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/submission.py +0 -0
  201. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/suppressions.py +0 -0
  202. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  203. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  204. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  205. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_models.py +0 -0
  206. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  207. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  208. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  209. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_specs.py +0 -0
  210. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/verification.py +0 -0
  211. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/__init__.py +0 -0
  212. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/conftest.py +0 -0
  213. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/__init__.py +0 -0
  214. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  215. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  216. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/bad-plugin/secrets.js +0 -0
  217. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  218. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  219. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/README.md +0 -0
  220. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  221. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  222. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  223. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/code-quality-bad/evil.js +0 -0
  224. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/code-quality-bad/inject.js +0 -0
  225. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  226. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  227. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/README.md +0 -0
  228. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  229. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  230. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  231. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  232. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/.codexignore +0 -0
  233. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/LICENSE +0 -0
  234. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/README.md +0 -0
  235. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  236. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  237. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  238. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  239. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  240. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  241. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  242. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  243. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  244. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  245. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  246. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  247. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  248. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  249. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  250. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  251. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  252. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  253. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  254. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  255. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/mcp-canary-server.py +0 -0
  256. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  257. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  258. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/mit-license/LICENSE +0 -0
  259. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  260. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  261. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  262. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  263. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  264. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  265. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  266. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  267. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  268. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  269. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  270. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  271. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  272. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  273. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  274. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  275. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  276. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  277. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/LICENSE +0 -0
  278. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/README.md +0 -0
  279. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  280. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  281. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  282. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  283. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  284. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  285. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  286. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  287. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test-trust-scoring.py +0 -0
  288. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test-trust-specs.py +0 -0
  289. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_action_runner.py +0 -0
  290. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_best_practices.py +0 -0
  291. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_cisco_install_surfaces.py +0 -0
  292. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_cli.py +0 -0
  293. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_code_quality.py +0 -0
  294. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_config.py +0 -0
  295. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_coverage_remaining.py +0 -0
  296. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_ecosystems.py +0 -0
  297. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_edge_cases.py +0 -0
  298. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_final_coverage.py +0 -0
  299. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_access_graph.py +0 -0
  300. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_approvals.py +0 -0
  301. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_bootstrap.py +0 -0
  302. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_capabilities.py +0 -0
  303. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_claude_adapter.py +0 -0
  304. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_cli.py +0 -0
  305. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_codex_e2e.py +0 -0
  306. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_codex_install.py +0 -0
  307. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_codex_proxy.py +0 -0
  308. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_config_paths.py +0 -0
  309. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_connect_flow.py +0 -0
  310. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_consumer_mode.py +0 -0
  311. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_copilot_adapter.py +0 -0
  312. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_copilot_proxy.py +0 -0
  313. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_daemon_manager.py +0 -0
  314. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_event_schema_v1.py +0 -0
  315. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_events.py +0 -0
  316. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_launch_env.py +0 -0
  317. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_opencode_proxy.py +0 -0
  318. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_product_flow.py +0 -0
  319. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_protect.py +0 -0
  320. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_action_harnesses.py +0 -0
  321. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_actions.py +0 -0
  322. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_decisions.py +0 -0
  323. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_detectors.py +0 -0
  324. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_signals.py +0 -0
  325. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_store_migrations.py +0 -0
  326. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_surface_server.py +0 -0
  327. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_verdicts.py +0 -0
  328. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_hermes_adapter.py +0 -0
  329. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_integration.py +0 -0
  330. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_lint_fixes.py +0 -0
  331. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_live_cisco_smoke.py +0 -0
  332. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_manifest.py +0 -0
  333. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_marketplace.py +0 -0
  334. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_mcp_security.py +0 -0
  335. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_openclaw_adapter.py +0 -0
  336. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_operational_security.py +0 -0
  337. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_policy.py +0 -0
  338. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_quality_artifact.py +0 -0
  339. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_rule_registry.py +0 -0
  340. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_scanner.py +0 -0
  341. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_schema_contracts.py +0 -0
  342. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_security.py +0 -0
  343. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_security_ops.py +0 -0
  344. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_skill_security.py +0 -0
  345. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_submission.py +0 -0
  346. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_trust_scoring.py +0 -0
  347. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_trust_specs.py +0 -0
  348. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_verification.py +0 -0
  349. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_versioning.py +0 -0
  350. {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.109
3
+ Version: 2.0.111
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.109"
7
+ version = "2.0.111"
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.109"
7
+ version = "2.0.111"
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"
@@ -97,7 +97,12 @@ from ..runtime.secret_file_requests import (
97
97
  extract_sensitive_tool_action_request,
98
98
  is_explicitly_benign_tool_action_request,
99
99
  )
100
- from ..runtime.secret_sensitivity import SecretContentMatch, classify_secret_content
100
+ from ..runtime.secret_sensitivity import (
101
+ SecretContentMatch,
102
+ SecretPathMatch,
103
+ classify_secret_content,
104
+ classify_secret_path,
105
+ )
101
106
  from ..runtime.surface_server import GuardSurfaceRuntime
102
107
  from ..store import GuardStore
103
108
  from .approval_commands import add_approval_parser, run_approval_command
@@ -2999,16 +3004,47 @@ def _runtime_stored_policy_action(
2999
3004
  def _runtime_artifact_policy_action(config: GuardConfig, artifact: GuardArtifact, harness: str) -> str:
3000
3005
  if _prompt_requires_hard_block(artifact):
3001
3006
  return "block"
3002
- risk_actions = [
3003
- resolve_risk_action(config, risk_class, harness=_canonical_harness_name(harness))
3004
- for risk_class in _runtime_artifact_risk_classes(artifact)
3005
- ]
3007
+ canonical_harness = _canonical_harness_name(harness)
3008
+ risk_classes = _runtime_artifact_risk_classes(artifact)
3009
+ has_configured_risk_action = any(
3010
+ _resolve_configured_risk_action(config, risk_class, harness=canonical_harness) for risk_class in risk_classes
3011
+ )
3012
+ if has_configured_risk_action:
3013
+ risk_actions = [
3014
+ _resolve_configured_risk_action(config, risk_class, harness=canonical_harness)
3015
+ or resolve_risk_action(config, risk_class, harness=canonical_harness)
3016
+ for risk_class in risk_classes
3017
+ ]
3018
+ resolved_actions = [action for action in risk_actions if action in VALID_GUARD_ACTIONS]
3019
+ if resolved_actions:
3020
+ return max(resolved_actions, key=_guard_action_severity)
3021
+ guard_default_action = _runtime_artifact_guard_default_action(artifact)
3022
+ if guard_default_action is not None:
3023
+ return guard_default_action
3024
+ risk_actions = [resolve_risk_action(config, risk_class, harness=canonical_harness) for risk_class in risk_classes]
3006
3025
  resolved_actions = [action for action in risk_actions if action in VALID_GUARD_ACTIONS]
3007
3026
  if resolved_actions:
3008
3027
  return max(resolved_actions, key=_guard_action_severity)
3009
3028
  return SAFE_CHANGED_HASH_ACTION
3010
3029
 
3011
3030
 
3031
+ def _resolve_configured_risk_action(config: GuardConfig, risk_class: str, *, harness: str) -> str | None:
3032
+ if config.harness_risk_actions is not None:
3033
+ harness_actions = config.harness_risk_actions.get(harness)
3034
+ if harness_actions is not None and risk_class in harness_actions:
3035
+ return harness_actions[risk_class]
3036
+ if config.risk_actions is not None and risk_class in config.risk_actions:
3037
+ return config.risk_actions[risk_class]
3038
+ return None
3039
+
3040
+
3041
+ def _runtime_artifact_guard_default_action(artifact: GuardArtifact) -> str | None:
3042
+ value = artifact.metadata.get("guard_default_action")
3043
+ if value in VALID_GUARD_ACTIONS:
3044
+ return str(value)
3045
+ return None
3046
+
3047
+
3012
3048
  def _guard_action_severity(action: str) -> int:
3013
3049
  return {
3014
3050
  "allow": 0,
@@ -3205,6 +3241,12 @@ def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: d
3205
3241
  trimmed_summary = risk_summary.strip()
3206
3242
  if len(trimmed_summary) > 180:
3207
3243
  trimmed_summary = f"{trimmed_summary[:177].rstrip()}..."
3244
+ action_class = artifact.metadata.get("action_class")
3245
+ if (
3246
+ action_class == "credential exfiltration shell command"
3247
+ and "credential-looking output" not in trimmed_summary.lower()
3248
+ ):
3249
+ trimmed_summary = f"{trimmed_summary} Guard also detected credential-looking output."
3208
3250
  return f"HOL Guard flagged this request: {trimmed_summary}"
3209
3251
  return "HOL Guard flagged this request for review."
3210
3252
 
@@ -3985,13 +4027,19 @@ def _codex_post_tool_output_artifact(
3985
4027
  cwd: Path | None,
3986
4028
  ) -> GuardArtifact | None:
3987
4029
  response_text = _collect_codex_tool_response_text(payload.get("tool_response"))
3988
- content_matches = classify_secret_content(response_text)
3989
- if not content_matches:
3990
- return None
3991
4030
  tool_name = _coalesce_string(payload.get("tool_name"), "Bash")
3992
4031
  command_text = _codex_post_tool_command_text(payload)
3993
4032
  if not command_text:
3994
4033
  command_text = tool_name
4034
+ local_source_matches = _codex_sensitive_local_source_matches(command_text, cwd=cwd)
4035
+ references_local_content = bool(local_source_matches) or _codex_command_may_read_local_content(
4036
+ command_text, cwd=cwd
4037
+ )
4038
+ content_matches = classify_secret_content(response_text)
4039
+ if not content_matches and references_local_content:
4040
+ content_matches = classify_secret_content(response_text, suppress_samples=False)
4041
+ if not content_matches:
4042
+ return None
3995
4043
  if _codex_source_inspection_can_skip_secret_output(
3996
4044
  command_text=command_text,
3997
4045
  response_text=response_text,
@@ -4009,6 +4057,38 @@ def _codex_post_tool_output_artifact(
4009
4057
  sort_keys=True,
4010
4058
  ).encode("utf-8")
4011
4059
  ).hexdigest()
4060
+ local_secret_source = _codex_local_secret_source_label(
4061
+ local_source_matches,
4062
+ command_text=command_text,
4063
+ )
4064
+ runtime_default_action = "require-reapproval" if references_local_content else "warn"
4065
+ runtime_request_signals = ["tool output contains credential-looking material"]
4066
+ if references_local_content:
4067
+ source_signal = "command references local secrets"
4068
+ if local_secret_source is not None:
4069
+ source_signal = f"command references local secrets from {local_secret_source}"
4070
+ runtime_request_signals.append(source_signal)
4071
+ request_summary = _codex_tool_output_request_summary(
4072
+ tool_name=tool_name,
4073
+ command_text=command_text,
4074
+ local_secret_source=local_secret_source,
4075
+ )
4076
+ runtime_request_summary = _codex_tool_output_runtime_summary(local_secret_source)
4077
+ metadata: dict[str, object] = {
4078
+ "tool_name": tool_name,
4079
+ "command_text": command_text,
4080
+ "action_class": "credential exfiltration shell command",
4081
+ "guard_default_action": runtime_default_action,
4082
+ "request_summary": request_summary,
4083
+ "runtime_request_signals": runtime_request_signals,
4084
+ "runtime_request_summary": runtime_request_summary,
4085
+ "runtime_request_reason": (
4086
+ "Guard inspects supported Codex tool output before Codex uses it, so accidental secret reads can be "
4087
+ "stopped even when the filename was not obviously sensitive."
4088
+ ),
4089
+ }
4090
+ if local_secret_source is not None:
4091
+ metadata["secret_source_family"] = local_secret_source
4012
4092
  return GuardArtifact(
4013
4093
  artifact_id=f"codex:{source_scope}:tool-output:{fingerprint}",
4014
4094
  name=f"{tool_name} credential-looking output",
@@ -4016,25 +4096,307 @@ def _codex_post_tool_output_artifact(
4016
4096
  artifact_type="tool_action_request",
4017
4097
  source_scope=source_scope,
4018
4098
  config_path=config_path,
4019
- metadata={
4020
- "tool_name": tool_name,
4021
- "command_text": command_text,
4022
- "action_class": "credential exfiltration shell command",
4023
- "request_summary": (
4024
- f"Codex tool `{tool_name}` produced credential-looking output while running `{command_text}`."
4025
- ),
4026
- "runtime_request_signals": ["tool output contains credential-looking material"],
4027
- "runtime_request_summary": (
4028
- "Requests a sensitive native tool action: credential-looking output reached Codex."
4029
- ),
4030
- "runtime_request_reason": (
4031
- "Guard inspects supported Codex tool output before Codex uses it, so accidental secret reads can be "
4032
- "stopped even when the filename was not obviously sensitive."
4033
- ),
4034
- },
4099
+ metadata=metadata,
4100
+ )
4101
+
4102
+
4103
+ def _codex_command_references_sensitive_local_source(command_text: str, *, cwd: Path | None) -> bool:
4104
+ return bool(_codex_sensitive_local_source_matches(command_text, cwd=cwd))
4105
+
4106
+
4107
+ def _codex_sensitive_local_source_matches(command_text: str, *, cwd: Path | None) -> list[SecretPathMatch]:
4108
+ matches = _codex_sensitive_path_matches_in_text(command_text, cwd=cwd)
4109
+ try:
4110
+ parts = shlex.split(command_text)
4111
+ except ValueError:
4112
+ return matches
4113
+ for part in parts:
4114
+ stripped = part.strip()
4115
+ if not stripped or stripped.startswith("-") or _codex_token_is_url(stripped):
4116
+ continue
4117
+ path_match = classify_secret_path(stripped, cwd=cwd)
4118
+ if path_match is not None:
4119
+ matches.append(path_match)
4120
+ return _dedupe_codex_secret_path_matches(matches)
4121
+
4122
+
4123
+ def _dedupe_codex_secret_path_matches(matches: list[SecretPathMatch]) -> list[SecretPathMatch]:
4124
+ deduped: list[SecretPathMatch] = []
4125
+ seen: set[tuple[str, str]] = set()
4126
+ for match in matches:
4127
+ key = (match.family, match.requested_path or match.path)
4128
+ if key in seen:
4129
+ continue
4130
+ seen.add(key)
4131
+ deduped.append(match)
4132
+ return deduped
4133
+
4134
+
4135
+ def _codex_token_is_url(token: str) -> bool:
4136
+ parsed = urllib.parse.urlparse(token)
4137
+ return bool(parsed.scheme and parsed.netloc)
4138
+
4139
+
4140
+ def _codex_text_contains_sensitive_path_token(text: str, *, cwd: Path | None) -> bool:
4141
+ return bool(_codex_sensitive_path_matches_in_text(text, cwd=cwd))
4142
+
4143
+
4144
+ def _codex_sensitive_path_matches_in_text(text: str, *, cwd: Path | None) -> list[SecretPathMatch]:
4145
+ matches: list[SecretPathMatch] = []
4146
+ for match in _PROMPT_PATH_TOKEN_PATTERN.finditer(text):
4147
+ if _codex_path_token_is_url_path(text, match.start()):
4148
+ continue
4149
+ path_match = classify_secret_path(match.group(0), cwd=cwd)
4150
+ if path_match is not None:
4151
+ matches.append(path_match)
4152
+ return matches
4153
+
4154
+
4155
+ def _codex_path_token_is_url_path(text: str, start: int) -> bool:
4156
+ prefix = text[:start].lower()
4157
+ last_separator = max(prefix.rfind(separator) for separator in " \t\r\n'\"`<>|;(){}[]")
4158
+ token_prefix = prefix[last_separator + 1 :]
4159
+ if "://" in token_prefix:
4160
+ return True
4161
+ scheme = ""
4162
+ if token_prefix.endswith(":/"):
4163
+ scheme = token_prefix[:-2]
4164
+ elif token_prefix.endswith(":"):
4165
+ scheme = token_prefix[:-1]
4166
+ return _codex_token_prefix_is_url_scheme(scheme)
4167
+
4168
+
4169
+ def _codex_token_prefix_is_url_scheme(scheme: str) -> bool:
4170
+ return bool(scheme) and scheme[0].isalpha() and all(char.isalnum() or char in "+.-" for char in scheme)
4171
+
4172
+
4173
+ def _codex_command_may_read_local_content(command_text: str, *, cwd: Path | None) -> bool:
4174
+ if _codex_command_references_sensitive_local_source(command_text, cwd=cwd):
4175
+ return True
4176
+ if any(marker in command_text for marker in ("$(", "${", "`")):
4177
+ return True
4178
+ pipeline_segments = _split_codex_safe_read_only_pipeline(command_text)
4179
+ if pipeline_segments is not None:
4180
+ return any(
4181
+ _codex_pipeline_segment_may_read_local_content(segment, index=index, cwd=cwd)
4182
+ for index, segment in enumerate(pipeline_segments)
4183
+ )
4184
+ try:
4185
+ parts = _codex_shell_split(command_text)
4186
+ except ValueError:
4187
+ return True
4188
+ return _codex_command_parts_may_read_local_content(parts, cwd=cwd)
4189
+
4190
+
4191
+ def _codex_pipeline_segment_may_read_local_content(segment: str, *, index: int, cwd: Path | None) -> bool:
4192
+ try:
4193
+ parts = _codex_shell_split(segment)
4194
+ except ValueError:
4195
+ return True
4196
+ if not parts:
4197
+ return False
4198
+ if index == 0:
4199
+ return _codex_command_parts_are_environment_dump(parts) or _codex_command_parts_may_read_local_content(
4200
+ parts,
4201
+ cwd=cwd,
4202
+ )
4203
+ return _codex_command_is_read_only_source_search(segment, cwd=cwd) or _codex_command_is_read_only_source_view(
4204
+ segment, cwd=cwd
4205
+ )
4206
+
4207
+
4208
+ def _codex_command_parts_may_read_local_content(parts: list[str], *, cwd: Path | None) -> bool:
4209
+ for start in _codex_command_start_indexes(parts):
4210
+ previous_token = parts[start - 1] if start > 0 else None
4211
+ segment_parts = _codex_command_segment_parts(parts, start)
4212
+ if previous_token == "|":
4213
+ if _codex_command_sequence_is_read_only_source_inspection(segment_parts, cwd=cwd):
4214
+ return True
4215
+ continue
4216
+ if _codex_command_sequence_starts_with_local_reader(segment_parts, cwd=cwd):
4217
+ return True
4218
+ return False
4219
+
4220
+
4221
+ def _codex_command_start_indexes(parts: list[str]) -> list[int]:
4222
+ starts = [0] if parts else []
4223
+ for index, part in enumerate(parts[:-1]):
4224
+ if part in {"&&", "||", ";", "&", "|", "|&"}:
4225
+ starts.append(index + 1)
4226
+ return starts
4227
+
4228
+
4229
+ def _codex_command_segment_parts(parts: list[str], start: int) -> list[str]:
4230
+ end = start
4231
+ while end < len(parts) and parts[end] not in {"&&", "||", ";", "&", "|", "|&"}:
4232
+ end += 1
4233
+ return parts[start:end]
4234
+
4235
+
4236
+ def _codex_command_sequence_is_read_only_source_inspection(parts: list[str], *, cwd: Path | None) -> bool:
4237
+ command_parts = _codex_unwrapped_command_parts(parts)
4238
+ if not command_parts:
4239
+ return False
4240
+ segment = shlex.join(command_parts)
4241
+ return _codex_command_is_read_only_source_search(segment, cwd=cwd) or _codex_command_is_read_only_source_view(
4242
+ segment, cwd=cwd
4035
4243
  )
4036
4244
 
4037
4245
 
4246
+ def _codex_command_sequence_starts_with_local_reader(parts: list[str], *, cwd: Path | None) -> bool:
4247
+ command_parts = _codex_unwrapped_command_parts(parts)
4248
+ if not command_parts:
4249
+ return False
4250
+ if _codex_command_parts_are_git_grep(command_parts):
4251
+ return True
4252
+ return _codex_command_part_is_local_reader(command_parts, 0, cwd=cwd)
4253
+
4254
+
4255
+ def _codex_command_parts_are_git_grep(parts: list[str]) -> bool:
4256
+ return bool(parts) and Path(parts[0]).name.lower() == "git" and _git_grep_search_args(parts[1:]) is not None
4257
+
4258
+
4259
+ def _codex_command_reads_environment_pipeline(command_text: str) -> bool:
4260
+ pipeline_segments = _split_codex_safe_read_only_pipeline(command_text)
4261
+ if pipeline_segments is None:
4262
+ return False
4263
+ try:
4264
+ first_parts = _codex_shell_split(pipeline_segments[0])
4265
+ except ValueError:
4266
+ return False
4267
+ return _codex_command_parts_are_environment_dump(first_parts)
4268
+
4269
+
4270
+ def _codex_command_parts_are_environment_dump(parts: list[str]) -> bool:
4271
+ if not parts:
4272
+ return False
4273
+ executable = Path(parts[0]).name.lower()
4274
+ if executable == "printenv":
4275
+ return True
4276
+ if executable != "env":
4277
+ return False
4278
+ if _codex_env_args_clear_environment(parts[1:]):
4279
+ return False
4280
+ return not _codex_strip_env_wrapper(parts[1:])
4281
+
4282
+
4283
+ def _codex_local_secret_source_label(
4284
+ matches: list[SecretPathMatch],
4285
+ *,
4286
+ command_text: str,
4287
+ ) -> str | None:
4288
+ families: list[str] = []
4289
+ for match in matches:
4290
+ if match.family not in families:
4291
+ families.append(match.family)
4292
+ if families:
4293
+ if len(families) == 1:
4294
+ return families[0]
4295
+ return f"{families[0]} and other local secret files"
4296
+ if _codex_command_reads_environment_pipeline(command_text):
4297
+ return "environment variables"
4298
+ return None
4299
+
4300
+
4301
+ def _codex_tool_output_request_summary(
4302
+ *,
4303
+ tool_name: str,
4304
+ command_text: str,
4305
+ local_secret_source: str | None,
4306
+ ) -> str:
4307
+ if local_secret_source is not None:
4308
+ return f"Codex tool `{tool_name}` read local secrets from {local_secret_source} while running `{command_text}`."
4309
+ return f"Codex tool `{tool_name}` produced credential-looking output while running `{command_text}`."
4310
+
4311
+
4312
+ def _codex_tool_output_runtime_summary(local_secret_source: str | None) -> str:
4313
+ if local_secret_source is not None:
4314
+ return f"Local secrets from {local_secret_source} reached Codex tool output."
4315
+ return "Requests a sensitive native tool action: credential-looking output reached Codex."
4316
+
4317
+
4318
+ def _codex_unwrapped_command_parts(parts: list[str]) -> list[str]:
4319
+ remaining = parts
4320
+ while remaining:
4321
+ executable = Path(remaining[0]).name.lower()
4322
+ if executable == "command":
4323
+ remaining = _codex_strip_command_wrapper(remaining[1:])
4324
+ continue
4325
+ if executable == "env":
4326
+ remaining = _codex_strip_env_wrapper(remaining[1:])
4327
+ continue
4328
+ return remaining
4329
+ return []
4330
+
4331
+
4332
+ def _codex_strip_command_wrapper(parts: list[str]) -> list[str]:
4333
+ index = 0
4334
+ while index < len(parts) and parts[index] in {"-p", "-v", "-V"}:
4335
+ index += 1
4336
+ if index < len(parts) and parts[index] == "--":
4337
+ index += 1
4338
+ return parts[index:]
4339
+
4340
+
4341
+ def _codex_strip_env_wrapper(parts: list[str]) -> list[str]:
4342
+ index = 0
4343
+ while index < len(parts):
4344
+ part = parts[index]
4345
+ if part == "--":
4346
+ return parts[index + 1 :]
4347
+ if part in {"-i", "-0", "--ignore-environment", "--null"}:
4348
+ index += 1
4349
+ continue
4350
+ if part in {"-u", "--unset", "-C", "--chdir", "-S", "--split-string"}:
4351
+ index += 2
4352
+ continue
4353
+ if part.startswith(("--unset=", "--chdir=", "--split-string=")):
4354
+ index += 1
4355
+ continue
4356
+ if part.startswith("-"):
4357
+ index += 1
4358
+ continue
4359
+ if "=" in part and not part.startswith("="):
4360
+ index += 1
4361
+ continue
4362
+ return parts[index:]
4363
+ return []
4364
+
4365
+
4366
+ def _codex_env_args_clear_environment(parts: list[str]) -> bool:
4367
+ for part in parts:
4368
+ if part == "--":
4369
+ return False
4370
+ if part in {"-i", "--ignore-environment"}:
4371
+ return True
4372
+ if part.startswith("-") or ("=" in part and not part.startswith("=")):
4373
+ continue
4374
+ return False
4375
+ return False
4376
+
4377
+
4378
+ def _codex_shell_split(command_text: str) -> list[str]:
4379
+ lexer = shlex.shlex(command_text, posix=True, punctuation_chars=True)
4380
+ lexer.whitespace_split = True
4381
+ lexer.commenters = ""
4382
+ return list(lexer)
4383
+
4384
+
4385
+ def _codex_command_part_is_local_reader(parts: list[str], index: int, *, cwd: Path | None) -> bool:
4386
+ local_read_commands = {"cat", "grep", "head", "rg", "sed", "tail"}
4387
+ executable = Path(parts[index]).name.lower()
4388
+ if executable not in local_read_commands:
4389
+ return False
4390
+ if index == 0:
4391
+ return True
4392
+ if parts[index - 1] == "|":
4393
+ segment = shlex.join(parts[index:])
4394
+ return _codex_command_is_read_only_source_search(segment, cwd=cwd) or _codex_command_is_read_only_source_view(
4395
+ segment, cwd=cwd
4396
+ )
4397
+ return parts[index - 1] in {"&&", "||", ";", "&", "|&"}
4398
+
4399
+
4038
4400
  def _codex_post_tool_command_is_read_only_source_inspection(
4039
4401
  *,
4040
4402
  payload: dict[str, object],
@@ -50,6 +50,7 @@ _SENSITIVE_STRING_PATTERNS: tuple[tuple[re.Pattern[str], str], ...] = (
50
50
  ),
51
51
  (re.compile(r"(?i)(authorization:\s*)(bearer\s+)?[^\s,;]+"), r"\1*****"),
52
52
  (re.compile(r"(?i)(api[-_ ]?key:\s*)[^\s,;]+"), r"\1*****"),
53
+ (re.compile(r"\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b"), "*****"),
53
54
  (re.compile(r"(?i)(bearer\s+)[^\s,;]+"), r"\1*****"),
54
55
  (re.compile(r"(?im)\b(?:_authToken|npm[_ -]?token)\s*[:=]\s*[^\s]+"), "npm token redacted"),
55
56
  (re.compile(r"\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp)://[^\s]+", re.IGNORECASE), "*****"),
@@ -963,6 +964,8 @@ def _render_hook(console: Console, payload: dict[str, object]) -> None:
963
964
  body.add_row("Recorded", _bool_label(bool(payload.get("recorded"))))
964
965
  body.add_row("Artifact", str(payload.get("artifact_name") or payload.get("artifact_id") or "unknown"))
965
966
  body.add_row("Decision", _action_text(str(payload.get("policy_action", "warn"))))
967
+ if payload.get("risk_summary"):
968
+ body.add_row("Why", str(payload.get("risk_summary")))
966
969
  if payload.get("path_summary"):
967
970
  body.add_row("Path", str(payload.get("path_summary")))
968
971
  if payload.get("approval_center_url"):
@@ -16,6 +16,7 @@ from ..config import GuardConfig
16
16
  from ..incident import build_incident_context
17
17
  from ..models import GuardArtifact, HarnessDetection, PolicyDecision
18
18
  from ..policy import build_decision_v2, decide_action
19
+ from ..policy.engine import VALID_GUARD_ACTIONS
19
20
  from ..receipts import build_receipt
20
21
  from ..risk import artifact_risk_signals_typed, artifact_risk_summary, summarize_signals
21
22
  from ..runtime.signals import RiskSignalV2
@@ -102,6 +103,13 @@ def _is_blocking_action(policy_action: str) -> bool:
102
103
  return policy_action in {"block", "sandbox-required", "require-reapproval"}
103
104
 
104
105
 
106
+ def _guard_default_action(artifact: GuardArtifact) -> str | None:
107
+ value = artifact.metadata.get("guard_default_action")
108
+ if value in VALID_GUARD_ACTIONS:
109
+ return str(value)
110
+ return None
111
+
112
+
105
113
  def _build_removed_provenance(previous: dict[str, object]) -> str:
106
114
  scope = previous.get("source_scope")
107
115
  config_path = previous.get("config_path")
@@ -395,7 +403,7 @@ def evaluate_detection(
395
403
  "file_read_request",
396
404
  "tool_action_request",
397
405
  }:
398
- policy_action = "require-reapproval"
406
+ policy_action = _guard_default_action(artifact) or "require-reapproval"
399
407
  elif is_first_seen and configured_action is None and effective_default_action is not None:
400
408
  policy_action = effective_default_action
401
409
  else:
@@ -92,6 +92,8 @@ _SENSITIVE_PATH_REASONS = {
92
92
  }
93
93
  _SECRET_ASSIGNMENT_VALUE_PATTERN = r"(?:\"[^\"\r\n]+\"|'[^'\r\n]+'|[^ \t\r\n\"',}]+)"
94
94
  _HEDERA_PRIVATE_KEY_VALUE_PATTERN = r"(?:\"(?:0x)?[0-9a-f]{64,96}\"|'(?:0x)?[0-9a-f]{64,96}'|(?:0x)?[0-9a-f]{64,96}\b)"
95
+ _SAMPLE_SECRET_VALUE_PATTERN = re.compile(r"(?i)\b(?:example|fake|dummy|invalid|test|canary)\b")
96
+ _SAMPLE_SUPPRESSIBLE_CONTENT_CLASSIFIERS = frozenset({"credential-assignment", "generic-bearer-token"})
95
97
  _SECRET_CONTENT_PATTERNS: tuple[tuple[str, str, SecretContentSensitivity, re.Pattern[str], str], ...] = (
96
98
  (
97
99
  "npm-auth-token",
@@ -148,6 +150,13 @@ _SECRET_CONTENT_PATTERNS: tuple[tuple[str, str, SecretContentSensitivity, re.Pat
148
150
  re.compile(r"-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----", re.MULTILINE),
149
151
  "Guard found a PEM private key header.",
150
152
  ),
153
+ (
154
+ "generic-bearer-token",
155
+ "generic bearer token",
156
+ "medium",
157
+ re.compile(r"(?im)\bbearer\s+[A-Za-z0-9._~+/=-]{16,}\b"),
158
+ "Guard found a bearer token pattern.",
159
+ ),
151
160
  (
152
161
  "credential-marker",
153
162
  "credential assignment",
@@ -272,13 +281,18 @@ def classify_legacy_secret_path_families(text: str) -> set[str]:
272
281
  return {family for marker, family in LEGACY_SECRET_PATH_TEXT_MARKERS if marker in lowered}
273
282
 
274
283
 
275
- def classify_secret_content(text: str | None) -> tuple[SecretContentMatch, ...]:
284
+ def classify_secret_content(text: str | None, *, suppress_samples: bool = True) -> tuple[SecretContentMatch, ...]:
276
285
  if not isinstance(text, str) or not text.strip():
277
286
  return ()
278
287
  matches: list[SecretContentMatch] = []
279
288
  seen: set[str] = set()
280
289
  for classifier, family, sensitivity, pattern, reason in _SECRET_CONTENT_PATTERNS:
281
- if pattern.search(text) is None or classifier in seen:
290
+ if classifier in seen:
291
+ continue
292
+ if not any(
293
+ not _secret_content_match_is_sample(classifier=classifier, text=match.group(0), enabled=suppress_samples)
294
+ for match in pattern.finditer(text)
295
+ ):
282
296
  continue
283
297
  seen.add(classifier)
284
298
  matches.append(
@@ -292,6 +306,15 @@ def classify_secret_content(text: str | None) -> tuple[SecretContentMatch, ...]:
292
306
  return tuple(matches)
293
307
 
294
308
 
309
+ def _secret_content_match_is_sample(*, classifier: str, text: str, enabled: bool) -> bool:
310
+ if not enabled or classifier not in _SAMPLE_SUPPRESSIBLE_CONTENT_CLASSIFIERS:
311
+ return False
312
+ if classifier == "generic-bearer-token":
313
+ token = text.rsplit(None, 1)[-1]
314
+ return _SAMPLE_SECRET_VALUE_PATTERN.search(token) is not None and re.search(r"[A-Za-z0-9]{20,}", token) is None
315
+ return _SAMPLE_SECRET_VALUE_PATTERN.search(text) is not None
316
+
317
+
295
318
  def redacted_secret_path_context(path: str) -> str | None:
296
319
  segments = tuple(segment for segment in path.replace("\\", "/").split("/") if segment)
297
320
  lowered_segments = tuple(segment.lower() for segment in segments)
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.109"
3
+ __version__ = "2.0.111"
@@ -79,11 +79,7 @@ def test_guard_json_render_redacts_after_command_specific_payload_shape(capsys)
79
79
 
80
80
 
81
81
  def test_guard_json_render_redacts_private_key_without_breaking_json(capsys) -> None:
82
- private_key = (
83
- "-----BEGIN PRIVATE KEY-----\n"
84
- "super-secret-material\n"
85
- "-----END PRIVATE KEY-----"
86
- )
82
+ private_key = "-----BEGIN PRIVATE KEY-----\nsuper-secret-material\n-----END PRIVATE KEY-----"
87
83
 
88
84
  emit_guard_payload("status", {"details": private_key}, True)
89
85
 
@@ -159,6 +155,28 @@ def test_guard_settings_json_omits_billing_flag(capsys) -> None:
159
155
  assert "billing" not in output["settings"]
160
156
 
161
157
 
158
+ def test_guard_hook_render_shows_local_secrets_without_internal_jargon(capsys) -> None:
159
+ raw_secret = "sk-" + "A" * 32
160
+
161
+ emit_guard_payload(
162
+ "hook",
163
+ {
164
+ "recorded": True,
165
+ "artifact_name": "Bash local secret output",
166
+ "policy_action": "require-reapproval",
167
+ "risk_summary": f"Local secrets from local .env file. {raw_secret}",
168
+ "path_summary": ".env",
169
+ },
170
+ False,
171
+ )
172
+
173
+ output = _normalize_render_output(capsys.readouterr().out)
174
+ assert "local secrets" in output.lower()
175
+ assert ".env file" in output
176
+ assert raw_secret not in output
177
+ assert "credential-looking output" not in output.lower()
178
+
179
+
162
180
  def test_guard_connect_render_defaults_sync_not_available_to_upgrade_guidance(capsys) -> None:
163
181
  emit_guard_payload(
164
182
  "connect",