plugin-scanner 2.0.104__tar.gz → 2.0.106__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 (349) hide show
  1. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/commands.py +11 -0
  5. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/render.py +5 -0
  6. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/config.py +2 -0
  7. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/risk.py +2 -6
  8. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/actions.py +32 -2
  9. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/detectors.py +43 -1
  10. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/runner.py +53 -3
  11. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +3 -45
  12. plugin_scanner-2.0.106/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +217 -0
  13. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/version.py +1 -1
  14. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_cli.py +40 -0
  15. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_config_paths.py +2 -0
  16. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_risk.py +60 -0
  17. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime.py +46 -0
  18. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_actions.py +45 -1
  19. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_detectors.py +292 -3
  20. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/Dockerfile +0 -0
  21. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/build.sh +0 -0
  22. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/project.yaml +0 -0
  23. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  24. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.dockerignore +0 -0
  25. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/CODEOWNERS +0 -0
  26. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  27. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  28. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  29. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/dependabot.yml +0 -0
  30. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/ci.yml +0 -0
  31. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/codeql.yml +0 -0
  32. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/dependabot-uv-lock.yml +0 -0
  33. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/fuzz.yml +0 -0
  34. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/harness-smoke.yml +0 -0
  35. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/publish.yml +0 -0
  36. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/scorecard.yml +0 -0
  37. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.gitignore +0 -0
  38. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.pre-commit-hooks.yaml +0 -0
  39. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/CONTRIBUTING.md +0 -0
  40. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/Dockerfile +0 -0
  41. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/LICENSE +0 -0
  42. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/README.md +0 -0
  43. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/SECURITY.md +0 -0
  44. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/index.html +0 -0
  45. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/package.json +0 -0
  46. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/pnpm-lock.yaml +0 -0
  47. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/apple-touch-icon.png +0 -0
  48. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  49. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Whole.png +0 -0
  50. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/favicon-16x16.png +0 -0
  51. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/favicon-32x32.png +0 -0
  52. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/favicon.ico +0 -0
  53. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/app.tsx +0 -0
  54. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/approval-center-layout.tsx +0 -0
  55. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/approval-center-primitives.tsx +0 -0
  56. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/approval-center-utils.ts +0 -0
  57. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/fleet-workspace.tsx +0 -0
  58. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-api.test.ts +0 -0
  59. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-api.ts +0 -0
  60. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-demo.ts +0 -0
  61. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-types.ts +0 -0
  62. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/main.tsx +0 -0
  63. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/receipts-workspace.tsx +0 -0
  64. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/runtime-overview.tsx +0 -0
  65. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/settings-workspace.tsx +0 -0
  66. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/styles.css +0 -0
  67. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/vite-env.d.ts +0 -0
  68. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/tsconfig.json +0 -0
  69. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/vite.config.ts +0 -0
  70. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docker-requirements.txt +0 -0
  71. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/approval-audit.md +0 -0
  72. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/architecture.md +0 -0
  73. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/get-started.md +0 -0
  74. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/harness-support.md +0 -0
  75. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/local-vs-cloud.md +0 -0
  76. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/testing-matrix.md +0 -0
  77. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/trust/mcp-trust-draft.md +0 -0
  78. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/trust/plugin-trust-draft.md +0 -0
  79. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/trust/skill-trust-local.md +0 -0
  80. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/fuzzers/manifest_fuzzer.py +0 -0
  81. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/requirements.txt +0 -0
  82. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/schemas/plugin-quality.v1.json +0 -0
  83. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/schemas/scan-result.v1.json +0 -0
  84. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/schemas/verify-result.v1.json +0 -0
  85. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/__init__.py +0 -0
  86. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/action_runner.py +0 -0
  87. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  88. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  89. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  90. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/claude.py +0 -0
  91. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  92. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  93. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  94. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  95. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  96. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  97. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  98. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  99. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  100. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/security.py +0 -0
  101. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  102. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli.py +0 -0
  103. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli_ui.py +0 -0
  104. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/config.py +0 -0
  105. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  106. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  107. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  108. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  109. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  110. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  111. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  112. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  113. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  114. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/github_reporting.py +0 -0
  115. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  116. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  117. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  118. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  119. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  120. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  121. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  122. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  123. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  124. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  125. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  126. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  127. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  128. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  129. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  130. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  131. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  132. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  133. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  134. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  135. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  136. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  137. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  138. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  139. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  140. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  141. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  142. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  143. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  144. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  145. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  146. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  147. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  148. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  149. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  150. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  151. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  152. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  153. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  154. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  155. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  156. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  157. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  158. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  159. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  160. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  161. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/incident.py +0 -0
  162. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  163. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  164. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/models.py +0 -0
  165. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  166. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  167. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/protect.py +0 -0
  168. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  169. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  170. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  171. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  172. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  173. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  174. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  175. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  176. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  177. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  178. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  179. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  180. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  181. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  182. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  183. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/shims.py +0 -0
  184. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store.py +0 -0
  185. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  186. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  187. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/types.py +0 -0
  188. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  189. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  190. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  191. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  192. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  193. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/models.py +0 -0
  194. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/path_support.py +0 -0
  195. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/policy.py +0 -0
  196. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  197. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/repo_detect.py +0 -0
  198. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/reporting.py +0 -0
  199. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  200. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/registry.py +0 -0
  201. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/specs.py +0 -0
  202. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/scanner.py +0 -0
  203. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/submission.py +0 -0
  204. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/suppressions.py +0 -0
  205. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  206. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  207. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  208. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_models.py +0 -0
  209. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  210. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  211. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  212. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_specs.py +0 -0
  213. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/verification.py +0 -0
  214. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/__init__.py +0 -0
  215. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/conftest.py +0 -0
  216. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/__init__.py +0 -0
  217. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  218. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  219. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/secrets.js +0 -0
  220. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  221. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  222. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/README.md +0 -0
  223. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  224. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  225. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  226. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/evil.js +0 -0
  227. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/inject.js +0 -0
  228. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  229. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  230. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/README.md +0 -0
  231. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  232. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  233. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  234. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  235. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codexignore +0 -0
  236. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/LICENSE +0 -0
  237. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/README.md +0 -0
  238. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  239. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  240. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  241. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  242. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  243. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  244. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  245. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  246. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  247. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  248. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  249. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  250. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  251. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  252. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  253. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  254. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  255. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  256. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  257. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  258. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/mcp-canary-server.py +0 -0
  259. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  260. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  261. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/mit-license/LICENSE +0 -0
  262. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  263. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  264. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  265. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  266. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  267. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  268. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  269. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  270. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  271. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  272. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  273. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  274. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  275. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  276. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  277. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  278. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  279. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  280. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/LICENSE +0 -0
  281. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/README.md +0 -0
  282. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  283. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  284. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  285. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  286. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  287. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  288. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  289. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  290. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test-trust-scoring.py +0 -0
  291. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test-trust-specs.py +0 -0
  292. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_action_runner.py +0 -0
  293. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_best_practices.py +0 -0
  294. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_cisco_install_surfaces.py +0 -0
  295. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_cli.py +0 -0
  296. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_code_quality.py +0 -0
  297. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_config.py +0 -0
  298. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_coverage_remaining.py +0 -0
  299. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_ecosystems.py +0 -0
  300. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_edge_cases.py +0 -0
  301. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_final_coverage.py +0 -0
  302. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_approvals.py +0 -0
  303. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_bootstrap.py +0 -0
  304. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_capabilities.py +0 -0
  305. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_claude_adapter.py +0 -0
  306. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_codex_e2e.py +0 -0
  307. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_codex_install.py +0 -0
  308. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_codex_proxy.py +0 -0
  309. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_connect_flow.py +0 -0
  310. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_consumer_mode.py +0 -0
  311. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_copilot_adapter.py +0 -0
  312. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_copilot_proxy.py +0 -0
  313. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_daemon_manager.py +0 -0
  314. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_event_schema_v1.py +0 -0
  315. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_events.py +0 -0
  316. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_launch_env.py +0 -0
  317. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_opencode_proxy.py +0 -0
  318. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_product_flow.py +0 -0
  319. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_protect.py +0 -0
  320. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_render.py +0 -0
  321. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_action_harnesses.py +0 -0
  322. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_decisions.py +0 -0
  323. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_signals.py +0 -0
  324. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_store_migrations.py +0 -0
  325. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_surface_server.py +0 -0
  326. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_verdicts.py +0 -0
  327. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_hermes_adapter.py +0 -0
  328. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_integration.py +0 -0
  329. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_lint_fixes.py +0 -0
  330. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_live_cisco_smoke.py +0 -0
  331. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_manifest.py +0 -0
  332. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_marketplace.py +0 -0
  333. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_mcp_security.py +0 -0
  334. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_openclaw_adapter.py +0 -0
  335. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_operational_security.py +0 -0
  336. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_policy.py +0 -0
  337. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_quality_artifact.py +0 -0
  338. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_rule_registry.py +0 -0
  339. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_scanner.py +0 -0
  340. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_schema_contracts.py +0 -0
  341. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_security.py +0 -0
  342. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_security_ops.py +0 -0
  343. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_skill_security.py +0 -0
  344. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_submission.py +0 -0
  345. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_trust_scoring.py +0 -0
  346. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_trust_specs.py +0 -0
  347. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_verification.py +0 -0
  348. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_versioning.py +0 -0
  349. {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.104
3
+ Version: 2.0.106
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.104"
7
+ version = "2.0.106"
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.104"
7
+ version = "2.0.106"
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"
@@ -1043,10 +1043,12 @@ def run_guard_command(
1043
1043
  if args.harness:
1044
1044
  adapter = get_adapter(args.harness)
1045
1045
  payload = adapter.diagnostics(context)
1046
+ payload["runtime_detector_registry"] = _runtime_detector_registry_payload(config)
1046
1047
  else:
1047
1048
  payload = {
1048
1049
  "tables": store.list_table_names(),
1049
1050
  "adapters": [detection.to_dict() for detection in detect_all(context)],
1051
+ "runtime_detector_registry": _runtime_detector_registry_payload(config),
1050
1052
  }
1051
1053
  _emit("doctor", payload, getattr(args, "json", False))
1052
1054
  return 0
@@ -3049,6 +3051,15 @@ def _guard_cli_settings_payload(config: GuardConfig) -> dict[str, object]:
3049
3051
  }
3050
3052
 
3051
3053
 
3054
+ def _runtime_detector_registry_payload(config: GuardConfig) -> dict[str, object]:
3055
+ return {
3056
+ "enabled": config.runtime_detector_registry,
3057
+ "debug_trace": config.runtime_detector_debug_trace,
3058
+ "timeout_ms": config.runtime_detector_timeout_ms,
3059
+ "disabled_detector_ids": list(config.runtime_detector_disabled_ids),
3060
+ }
3061
+
3062
+
3052
3063
  def _update_guard_cli_settings(*, args: argparse.Namespace, config: GuardConfig, guard_home: Path) -> GuardConfig:
3053
3064
  settings_command = getattr(args, "settings_set_command", None)
3054
3065
  if settings_command == "security-level":
@@ -293,6 +293,11 @@ def _render_doctor(console: Console, payload: dict[str, object]) -> None:
293
293
  summary.add_row("Installed", _bool_label(bool(payload.get("installed"))))
294
294
  summary.add_row("Command", _bool_label(bool(payload.get("command_available"))))
295
295
  summary.add_row("Artifacts", str(len(_coerce_dict_list(payload.get("artifacts")))))
296
+ registry = payload.get("runtime_detector_registry")
297
+ if isinstance(registry, dict):
298
+ registry_state = "enabled" if bool(registry.get("enabled")) else "disabled"
299
+ timeout_ms = registry.get("timeout_ms")
300
+ summary.add_row("Detector registry", f"{registry_state}, {timeout_ms} ms")
296
301
  summary.add_row("Warnings", str(len(warnings)))
297
302
  console.print(Panel(summary, title="Guard doctor", border_style="cyan"))
298
303
  if warnings:
@@ -178,6 +178,7 @@ class GuardConfig:
178
178
  billing: bool = False
179
179
  runtime_detector_registry: bool = False
180
180
  runtime_detector_timeout_ms: int = 50
181
+ runtime_detector_debug_trace: bool = False
181
182
  runtime_detector_disabled_ids: tuple[str, ...] = ()
182
183
  risk_actions: dict[str, GuardAction] | None = None
183
184
  harness_risk_actions: dict[str, dict[str, GuardAction]] | None = None
@@ -257,6 +258,7 @@ def load_guard_config(guard_home: Path, workspace: Path | None = None) -> GuardC
257
258
  billing=bool(merged.get("billing", False)),
258
259
  runtime_detector_registry=_coerce_loaded_bool(merged.get("runtime_detector_registry", False)),
259
260
  runtime_detector_timeout_ms=_coerce_loaded_positive_int(merged.get("runtime_detector_timeout_ms", 50), 50),
261
+ runtime_detector_debug_trace=_coerce_loaded_bool(merged.get("runtime_detector_debug_trace", False)),
260
262
  runtime_detector_disabled_ids=_coerce_loaded_string_tuple(merged.get("runtime_detector_disabled_ids")),
261
263
  harness_actions=_coerce_action_map(merged.get("harnesses")),
262
264
  publisher_actions=_coerce_action_map(merged.get("publishers")),
@@ -8,6 +8,7 @@ from pathlib import PurePath
8
8
  from urllib.parse import urlsplit
9
9
 
10
10
  from .models import GuardArtifact
11
+ from .runtime.secret_sensitivity import classify_legacy_secret_path_families
11
12
  from .runtime.signals import RiskSignalV2
12
13
  from .types import GuardSignal
13
14
 
@@ -305,12 +306,7 @@ def extract_network_hosts(text: str) -> set[str]:
305
306
  def classify_secret_paths(text: str) -> set[str]:
306
307
  """Classify secret-bearing file families referenced in text."""
307
308
 
308
- lowered = text.lower()
309
- classes: set[str] = set()
310
- for pattern, label in _SECRET_PATH_LABELS:
311
- if pattern in lowered:
312
- classes.add(label)
313
- return classes
309
+ return classify_legacy_secret_path_families(text)
314
310
 
315
311
 
316
312
  def detect_encoded_command(text: str) -> list[GuardSignal]:
@@ -11,6 +11,7 @@ from pathlib import Path, PureWindowsPath
11
11
  from typing import Literal
12
12
 
13
13
  from ..redaction import redact_text
14
+ from .secret_sensitivity import redacted_secret_path_context
14
15
 
15
16
  GuardActionType = Literal[
16
17
  "prompt",
@@ -43,7 +44,23 @@ _SCHEMA_VERSION = 1
43
44
  _SHELL_TOOL_NAMES = frozenset({"bash", "shell", "sh", "zsh", "terminal", "run_command", "run_terminal_command"})
44
45
  _FILE_READ_TOOL_NAMES = frozenset({"read", "read_file", "open_file", "view", "view_file", "cat_file"})
45
46
  _FILE_WRITE_TOOL_NAMES = frozenset({"write", "edit", "multiedit", "write_file", "edit_file"})
46
- _PATH_KEYS = ("path", "file_path", "filePath", "filepath", "file", "filename", "target_path", "targetPath")
47
+ _PATH_KEYS = (
48
+ "path",
49
+ "paths",
50
+ "file_path",
51
+ "file_paths",
52
+ "filePath",
53
+ "filePaths",
54
+ "filepath",
55
+ "file",
56
+ "files",
57
+ "filename",
58
+ "filenames",
59
+ "target_path",
60
+ "target_paths",
61
+ "targetPath",
62
+ "targetPaths",
63
+ )
47
64
  _COMMAND_KEYS = ("command", "cmd", "shell_command", "shellCommand")
48
65
  _SENSITIVE_RAW_KEYS = frozenset(
49
66
  {
@@ -652,6 +669,8 @@ def _target_paths(
652
669
  value = tool_input.get(key)
653
670
  if isinstance(value, str) and value.strip():
654
671
  paths.append(value.strip())
672
+ elif isinstance(value, list):
673
+ paths.extend(item.strip() for item in value if isinstance(item, str) and item.strip())
655
674
  for text in (command, prompt_text):
656
675
  if text is not None:
657
676
  paths.extend(match.group("path") for match in _PROMPT_PATH_PATTERN.finditer(text))
@@ -687,14 +706,25 @@ def _redacted_target_path(path: str, *, home_dir: Path | str | None) -> str | No
687
706
  if stripped == "~" or stripped.startswith("~/"):
688
707
  return redact_text(stripped).text
689
708
  if stripped.startswith("~"):
709
+ secret_context = redacted_secret_path_context(stripped)
710
+ if secret_context is not None:
711
+ return secret_context
690
712
  target_name = Path(stripped).name or "path"
691
713
  return f".../{target_name}"
692
714
  windows_path = PureWindowsPath(stripped)
693
715
  if windows_path.is_absolute():
716
+ secret_context = redacted_secret_path_context(stripped)
717
+ if secret_context is not None:
718
+ return secret_context
694
719
  target_name = windows_path.name or "path"
695
720
  return f".../{target_name}"
696
721
  if _is_absolute_target_path(stripped):
697
- return redacted_workspace_label(stripped, home_dir=home_dir)
722
+ redacted_path = redacted_workspace_label(stripped, home_dir=home_dir)
723
+ if redacted_path.startswith(".../"):
724
+ secret_context = redacted_secret_path_context(stripped)
725
+ if secret_context is not None:
726
+ return secret_context
727
+ return redacted_path
698
728
  return redact_text(stripped).text
699
729
 
700
730
 
@@ -10,6 +10,7 @@ from typing import Literal, Protocol
10
10
 
11
11
  from codex_plugin_scanner.guard.config import GuardConfig
12
12
  from codex_plugin_scanner.guard.runtime.actions import GuardActionEnvelope
13
+ from codex_plugin_scanner.guard.runtime.secret_sensitivity import SecretPathMatch, classify_secret_path
13
14
  from codex_plugin_scanner.guard.runtime.signals import RiskSignalCategory, RiskSignalV2
14
15
 
15
16
  DETECTOR_CATEGORY_TAGS: tuple[RiskSignalCategory, ...] = (
@@ -124,14 +125,55 @@ class DetectorRegistry:
124
125
  return DetectorRunResult(signals=tuple(signals), telemetry=tuple(telemetry))
125
126
 
126
127
 
128
+ class SecretPathDetector:
129
+ detector_id = "secret.path"
130
+ categories: tuple[RiskSignalCategory, ...] = ("secret",)
131
+
132
+ def detect(self, action: GuardActionEnvelope, context: DetectorContext) -> tuple[RiskSignalV2, ...]:
133
+ if action.action_type != "file_read":
134
+ return ()
135
+ matches = tuple(_secret_path_matches(action.target_paths, workspace=context.workspace))
136
+ return tuple(_secret_path_signal(match, index=index) for index, match in enumerate(matches))
137
+
138
+
127
139
  def register_default_detectors() -> tuple[GuardDetector, ...]:
128
- return ()
140
+ return (SecretPathDetector(),)
141
+
142
+
143
+ def _secret_path_signal(match: SecretPathMatch, *, index: int) -> RiskSignalV2:
144
+ return RiskSignalV2(
145
+ signal_id=f"secret:path:{_slug(match.family)}:{index}",
146
+ category="secret",
147
+ severity="high",
148
+ confidence="strong",
149
+ detector="secret.path",
150
+ title=f"Direct access to {match.family}",
151
+ plain_reason=f"Requested direct access to {match.family}.",
152
+ technical_detail=f"matched secret path family: {match.family}",
153
+ evidence_ref="target_paths",
154
+ redaction_level="summary",
155
+ false_positive_hint="Allow only if this tool needs the exact local secret file for the current task.",
156
+ advisory_id=None,
157
+ )
158
+
159
+
160
+ def _secret_path_matches(paths: Sequence[str], *, workspace: Path | None) -> tuple[SecretPathMatch, ...]:
161
+ matches: list[SecretPathMatch] = []
162
+ for path in paths:
163
+ match = classify_secret_path(path, cwd=workspace)
164
+ if match is not None:
165
+ matches.append(match)
166
+ return tuple(matches)
129
167
 
130
168
 
131
169
  def _elapsed_ms(started_at: float, finished_at: float) -> int:
132
170
  return max(0, round((finished_at - started_at) * 1000))
133
171
 
134
172
 
173
+ def _slug(value: str) -> str:
174
+ return "-".join(part for part in value.lower().replace(".", " ").replace("/", " ").split() if part)
175
+
176
+
135
177
  def _telemetry(
136
178
  detector: GuardDetector,
137
179
  status: DetectorRunStatus,
@@ -26,7 +26,7 @@ from ..models import GuardArtifact, HarnessDetection, PolicyDecision
26
26
  from ..store import GuardStore
27
27
  from ..types import PromptRequest, RemediationAction
28
28
  from .actions import GuardActionEnvelope, redacted_workspace_label
29
- from .detectors import DetectorContext, DetectorRegistry, register_default_detectors
29
+ from .detectors import DetectorContext, DetectorRegistry, DetectorRunResult, register_default_detectors
30
30
 
31
31
  _APPROVAL_METADATA_KEYS = (
32
32
  "approval_center_url",
@@ -365,13 +365,63 @@ def _evaluation_with_detector_registry(
365
365
  timeout_ms=config.runtime_detector_timeout_ms,
366
366
  disabled_detector_ids=config.runtime_detector_disabled_ids,
367
367
  )
368
+ trace_error = (
369
+ _write_detector_debug_trace(config, action_envelope, result) if config.runtime_detector_debug_trace else None
370
+ )
368
371
  if not result.signals and not result.telemetry:
369
- return evaluation
370
- return {
372
+ if trace_error is None:
373
+ return evaluation
374
+ return {**evaluation, "runtime_detector_trace_error": trace_error}
375
+ next_evaluation = {
371
376
  **evaluation,
372
377
  "runtime_detector_signals_v2": [signal.to_dict() for signal in result.signals],
373
378
  "runtime_detector_telemetry": [item.to_dict() for item in result.telemetry],
374
379
  }
380
+ if trace_error is not None:
381
+ next_evaluation["runtime_detector_trace_error"] = trace_error
382
+ return next_evaluation
383
+
384
+
385
+ def _write_detector_debug_trace(
386
+ config: GuardConfig,
387
+ action_envelope: GuardActionEnvelope,
388
+ result: DetectorRunResult,
389
+ ) -> dict[str, object] | None:
390
+ created_at = datetime.now(timezone.utc)
391
+ action_payload = action_envelope.to_dict()
392
+ trace_payload = {
393
+ "schema_version": 1,
394
+ "created_at": created_at.isoformat(),
395
+ "action": _redact_detector_debug_payload(action_payload),
396
+ "signals": [signal.to_dict() for signal in result.signals],
397
+ "telemetry": [item.to_dict() for item in result.telemetry],
398
+ }
399
+ trace_dir = config.guard_home / "debug" / "detectors"
400
+ action_digest = hashlib.sha256(
401
+ json.dumps(action_payload, sort_keys=True, default=str).encode("utf-8"),
402
+ ).hexdigest()[:12]
403
+ trace_path = trace_dir / f"{created_at.strftime('%Y%m%dT%H%M%S%fZ')}-{action_digest}.json"
404
+ try:
405
+ trace_dir.mkdir(parents=True, exist_ok=True)
406
+ trace_path.write_text(json.dumps(trace_payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
407
+ except OSError as error:
408
+ return {"error_type": type(error).__name__, "message": str(error)}
409
+ return None
410
+
411
+
412
+ def _redact_detector_debug_payload(value: object) -> object:
413
+ if isinstance(value, dict):
414
+ redacted: dict[str, object] = {}
415
+ for key, item in value.items():
416
+ key_text = str(key)
417
+ if "prompt" in key_text.lower():
418
+ redacted[key_text] = "[redacted]"
419
+ else:
420
+ redacted[key_text] = _redact_detector_debug_payload(item)
421
+ return redacted
422
+ if isinstance(value, (tuple, list)):
423
+ return [_redact_detector_debug_payload(item) for item in value]
424
+ return value
375
425
 
376
426
 
377
427
  def _guard_run_config_paths(
@@ -16,6 +16,8 @@ from dataclasses import dataclass
16
16
  from pathlib import Path
17
17
 
18
18
  from ..models import GuardArtifact
19
+ from .secret_sensitivity import SecretPathMatch as SensitivePathMatch
20
+ from .secret_sensitivity import classify_secret_path
19
21
 
20
22
  _FILE_READ_TOOL_NAMES = frozenset(
21
23
  {
@@ -359,16 +361,6 @@ _SENSITIVE_PATH_REASONS = {
359
361
  }
360
362
 
361
363
 
362
- @dataclass(frozen=True, slots=True)
363
- class SensitivePathMatch:
364
- """A normalized sensitive path classification."""
365
-
366
- requested_path: str
367
- normalized_path: str
368
- path_class: str
369
- reason: str
370
-
371
-
372
364
  @dataclass(frozen=True, slots=True)
373
365
  class FileReadRequestMatch:
374
366
  """A sensitive file-read tool call."""
@@ -405,41 +397,7 @@ def classify_sensitive_path(
405
397
  ) -> SensitivePathMatch | None:
406
398
  """Classify a path if it points at a high-confidence sensitive local file."""
407
399
 
408
- if not isinstance(path, str):
409
- return None
410
- requested_path = path.strip().strip("'").strip('"')
411
- if not requested_path:
412
- return None
413
- expanded_home = _expand_home(requested_path, home_dir)
414
- normalized_path = _normalize_path(expanded_home, cwd)
415
- lowered_segments = tuple(segment for segment in normalized_path.replace("\\", "/").lower().split("/") if segment)
416
- if not lowered_segments:
417
- return None
418
- basename = lowered_segments[-1]
419
- if basename == ".env" or basename.startswith(".env."):
420
- return SensitivePathMatch(
421
- requested_path=requested_path,
422
- normalized_path=normalized_path,
423
- path_class="local .env file",
424
- reason=_SENSITIVE_PATH_REASONS["local .env file"],
425
- )
426
- if basename in _SENSITIVE_BASENAME_LABELS:
427
- path_class = _SENSITIVE_BASENAME_LABELS[basename]
428
- return SensitivePathMatch(
429
- requested_path=requested_path,
430
- normalized_path=normalized_path,
431
- path_class=path_class,
432
- reason=_SENSITIVE_PATH_REASONS[path_class],
433
- )
434
- for suffix, path_class in _SENSITIVE_SUFFIX_LABELS.items():
435
- if lowered_segments[-len(suffix) :] == suffix:
436
- return SensitivePathMatch(
437
- requested_path=requested_path,
438
- normalized_path=normalized_path,
439
- path_class=path_class,
440
- reason=_SENSITIVE_PATH_REASONS[path_class],
441
- )
442
- return None
400
+ return classify_secret_path(path, cwd=cwd, home_dir=home_dir)
443
401
 
444
402
 
445
403
  def extract_sensitive_file_read_request(
@@ -0,0 +1,217 @@
1
+ """Shared secret path family classification for Guard runtime surfaces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Literal
9
+
10
+ SecretSensitivity = Literal["high", "critical"]
11
+
12
+ _AWS_CREDENTIALS_MARKER = "/".join((".aws", "credentials"))
13
+ _DOCKER_CONFIG_MARKER = "/".join((".docker", "config.json"))
14
+ _KUBE_CONFIG_MARKER = "/".join((".kube", "config"))
15
+
16
+ SECRET_PATH_TEXT_MARKERS: tuple[tuple[str, str], ...] = (
17
+ (".env", "local .env file"),
18
+ (".npmrc", "npm registry credentials"),
19
+ (".pypirc", "Python package credentials"),
20
+ (_AWS_CREDENTIALS_MARKER, "AWS shared credentials file"),
21
+ (".ssh/", "SSH private key"),
22
+ (".gnupg/", "GnuPG key material"),
23
+ (_DOCKER_CONFIG_MARKER, "Docker client config"),
24
+ (_KUBE_CONFIG_MARKER, "Kubernetes config"),
25
+ ("terraform.tfvars", "Terraform variable secrets"),
26
+ (".git-credentials", "Git credential store"),
27
+ )
28
+ LEGACY_SECRET_PATH_TEXT_MARKERS: tuple[tuple[str, str], ...] = (
29
+ (".env", "local .env file"),
30
+ (".npmrc", "npm registry credentials"),
31
+ (".pypirc", "python package credentials"),
32
+ (".aws/" + "credentials", "aws shared credentials"),
33
+ (".ssh/", "ssh material"),
34
+ (".gnupg/", "gpg material"),
35
+ (".docker/" + "config.json", "docker credentials"),
36
+ (".kube/config", "kubeconfig"),
37
+ (".git-credentials", "git credential store"),
38
+ )
39
+ _SENSITIVE_BASENAME_LABELS = {
40
+ ".npmrc": "npm registry credentials",
41
+ ".pypirc": "Python package credentials",
42
+ ".netrc": "netrc credentials",
43
+ ".git-credentials": "Git credential store",
44
+ ".terraform.tfvars": "Terraform variable secrets",
45
+ "terraform.tfvars": "Terraform variable secrets",
46
+ }
47
+ _REDACTED_BASENAME_LABELS = {
48
+ "id_rsa": "SSH private key",
49
+ "id_ed25519": "SSH private key",
50
+ "id_ecdsa": "SSH private key",
51
+ }
52
+ _SENSITIVE_SUFFIX_LABELS = {
53
+ (".aws", "credentials"): "AWS shared credentials file",
54
+ (".aws", "config"): "AWS shared config file",
55
+ (".docker", "config.json"): "Docker client config",
56
+ (".kube", "config"): "Kubernetes config",
57
+ (".ssh", "id_rsa"): "SSH private key",
58
+ (".ssh", "id_ed25519"): "SSH private key",
59
+ (".ssh", "id_ecdsa"): "SSH private key",
60
+ (".ssh", "config"): "SSH client config",
61
+ }
62
+ _SENSITIVE_DIRECTORY_LABELS = {
63
+ ".gnupg": "GnuPG key material",
64
+ }
65
+ _SENSITIVE_PATH_REASONS = {
66
+ "local .env file": "Guard treats .env files as sensitive because they commonly store local secrets.",
67
+ "npm registry credentials": "Guard treats .npmrc as sensitive because it may contain registry tokens.",
68
+ "Python package credentials": "Guard treats .pypirc as sensitive because it may contain package credentials.",
69
+ "netrc credentials": "Guard treats .netrc as sensitive because it may contain login secrets.",
70
+ "Git credential store": "Guard treats .git-credentials as sensitive because it may contain repository credentials.",
71
+ "AWS shared credentials file": (
72
+ "Guard treats AWS shared credentials as sensitive because they contain cloud access keys."
73
+ ),
74
+ "AWS shared config file": "Guard treats AWS shared config as sensitive because it may contain credential profiles.",
75
+ "Docker client config": "Guard treats Docker client config as sensitive because it may contain registry auth.",
76
+ "Kubernetes config": "Guard treats Kubernetes config as sensitive because it may include cluster credentials.",
77
+ "SSH private key": "Guard treats SSH private keys as sensitive because they provide direct host access.",
78
+ "SSH client config": "Guard treats SSH config as sensitive because it may reveal or shape host credentials.",
79
+ "GnuPG key material": "Guard treats GnuPG key material as sensitive because it can unlock encrypted assets.",
80
+ "Terraform variable secrets": (
81
+ "Guard treats Terraform variable files as sensitive because they often contain secrets."
82
+ ),
83
+ }
84
+
85
+
86
+ @dataclass(frozen=True, slots=True)
87
+ class SecretPathMatch:
88
+ family: str
89
+ path: str
90
+ sensitivity: SecretSensitivity
91
+ reason: str
92
+ requested_path: str = ""
93
+
94
+ @property
95
+ def normalized_path(self) -> str:
96
+ return self.path
97
+
98
+ @property
99
+ def path_class(self) -> str:
100
+ return self.family
101
+
102
+
103
+ def classify_secret_path(
104
+ path: str | None,
105
+ *,
106
+ cwd: Path | None = None,
107
+ home_dir: Path | None = None,
108
+ ) -> SecretPathMatch | None:
109
+ if not isinstance(path, str):
110
+ return None
111
+ requested_path = path.strip().strip("'").strip('"')
112
+ if not requested_path:
113
+ return None
114
+ expanded_home = _expand_home(requested_path, home_dir)
115
+ normalized_path = _normalize_path(expanded_home, cwd)
116
+ lowered_segments = tuple(segment for segment in normalized_path.replace("\\", "/").lower().split("/") if segment)
117
+ if not lowered_segments:
118
+ return None
119
+ basename = lowered_segments[-1]
120
+ if basename == ".env" or basename.startswith(".env."):
121
+ return _match(
122
+ requested_path=requested_path,
123
+ normalized_path=normalized_path,
124
+ family="local .env file",
125
+ sensitivity="critical",
126
+ )
127
+ if basename in _SENSITIVE_BASENAME_LABELS:
128
+ return _match(
129
+ requested_path=requested_path,
130
+ normalized_path=normalized_path,
131
+ family=_SENSITIVE_BASENAME_LABELS[basename],
132
+ sensitivity="high",
133
+ )
134
+ if "..." in lowered_segments and basename in _REDACTED_BASENAME_LABELS:
135
+ family = _REDACTED_BASENAME_LABELS[basename]
136
+ sensitivity: SecretSensitivity = "critical" if family == "SSH private key" else "high"
137
+ return _match(
138
+ requested_path=requested_path,
139
+ normalized_path=normalized_path,
140
+ family=family,
141
+ sensitivity=sensitivity,
142
+ )
143
+ for directory, family in _SENSITIVE_DIRECTORY_LABELS.items():
144
+ if directory in lowered_segments:
145
+ return _match(
146
+ requested_path=requested_path,
147
+ normalized_path=normalized_path,
148
+ family=family,
149
+ sensitivity="high",
150
+ )
151
+ for suffix, family in _SENSITIVE_SUFFIX_LABELS.items():
152
+ if lowered_segments[-len(suffix) :] == suffix:
153
+ sensitivity: SecretSensitivity = "critical" if family == "SSH private key" else "high"
154
+ return _match(
155
+ requested_path=requested_path,
156
+ normalized_path=normalized_path,
157
+ family=family,
158
+ sensitivity=sensitivity,
159
+ )
160
+ return None
161
+
162
+
163
+ def classify_secret_path_families(text: str) -> set[str]:
164
+ lowered = text.lower()
165
+ return {family for marker, family in SECRET_PATH_TEXT_MARKERS if marker in lowered}
166
+
167
+
168
+ def classify_legacy_secret_path_families(text: str) -> set[str]:
169
+ lowered = text.lower()
170
+ return {family for marker, family in LEGACY_SECRET_PATH_TEXT_MARKERS if marker in lowered}
171
+
172
+
173
+ def redacted_secret_path_context(path: str) -> str | None:
174
+ segments = tuple(segment for segment in path.replace("\\", "/").split("/") if segment)
175
+ lowered_segments = tuple(segment.lower() for segment in segments)
176
+ if not lowered_segments:
177
+ return None
178
+ for suffix in _SENSITIVE_SUFFIX_LABELS:
179
+ if lowered_segments[-len(suffix) :] == suffix:
180
+ return ".../" + "/".join(suffix)
181
+ for directory in _SENSITIVE_DIRECTORY_LABELS:
182
+ if directory in lowered_segments and len(segments) > 1:
183
+ return f".../{directory}/{segments[-1]}"
184
+ return None
185
+
186
+
187
+ def _match(
188
+ *,
189
+ requested_path: str,
190
+ normalized_path: str,
191
+ family: str,
192
+ sensitivity: SecretSensitivity,
193
+ ) -> SecretPathMatch:
194
+ return SecretPathMatch(
195
+ family=family,
196
+ path=normalized_path,
197
+ sensitivity=sensitivity,
198
+ reason=_SENSITIVE_PATH_REASONS[family],
199
+ requested_path=requested_path,
200
+ )
201
+
202
+
203
+ def _expand_home(value: str, home_dir: Path | None) -> str:
204
+ if value == "~":
205
+ return str(home_dir or Path.home())
206
+ if value.startswith("~/") or value.startswith("~\\"):
207
+ base = home_dir or Path.home()
208
+ return str(base / value[2:])
209
+ return value
210
+
211
+
212
+ def _normalize_path(value: str, cwd: Path | None) -> str:
213
+ if os.path.isabs(value):
214
+ return os.path.normpath(value)
215
+ if cwd is not None:
216
+ return os.path.normpath(os.path.join(str(cwd), value))
217
+ return os.path.normpath(value)
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.104"
3
+ __version__ = "2.0.106"
@@ -3962,6 +3962,46 @@ args = ["-lc", "echo hi"]
3962
3962
  assert any("native hooks are disabled" in warning for warning in output["warnings"])
3963
3963
  assert any("managed Codex hooks are missing" in warning for warning in output["warnings"])
3964
3964
 
3965
+ def test_guard_doctor_reports_runtime_detector_registry_state(self, tmp_path, monkeypatch, capsys):
3966
+ home_dir = tmp_path / "home"
3967
+ guard_home = tmp_path / "guard-home"
3968
+ _write_text(
3969
+ guard_home / "config.toml",
3970
+ "\n".join(
3971
+ [
3972
+ "runtime_detector_registry = true",
3973
+ "runtime_detector_timeout_ms = 75",
3974
+ 'runtime_detector_disabled_ids = ["secret.local"]',
3975
+ ]
3976
+ )
3977
+ + "\n",
3978
+ )
3979
+ monkeypatch.setattr("codex_plugin_scanner.guard.adapters.codex._command_available", lambda command: True)
3980
+
3981
+ rc = main(["guard", "doctor", "codex", "--home", str(home_dir), "--guard-home", str(guard_home), "--json"])
3982
+ output = json.loads(capsys.readouterr().out)
3983
+
3984
+ assert rc == 0
3985
+ assert output["runtime_detector_registry"] == {
3986
+ "enabled": True,
3987
+ "debug_trace": False,
3988
+ "timeout_ms": 75,
3989
+ "disabled_detector_ids": ["secret.local"],
3990
+ }
3991
+
3992
+ def test_guard_doctor_human_output_includes_detector_registry_line(self, tmp_path, monkeypatch, capsys):
3993
+ home_dir = tmp_path / "home"
3994
+ guard_home = tmp_path / "guard-home"
3995
+ _write_text(guard_home / "config.toml", "runtime_detector_registry = true\n")
3996
+ monkeypatch.setattr("codex_plugin_scanner.guard.adapters.codex._command_available", lambda command: True)
3997
+
3998
+ rc = main(["guard", "doctor", "codex", "--home", str(home_dir), "--guard-home", str(guard_home)])
3999
+ output = capsys.readouterr().out
4000
+
4001
+ assert rc == 0
4002
+ assert "Detector registry" in output
4003
+ assert "enabled" in output
4004
+
3965
4005
  def test_guard_codex_hook_blocks_shell_file_upload_script(self, tmp_path, capsys):
3966
4006
  home_dir = tmp_path / "home"
3967
4007
  workspace_dir = tmp_path / "workspace"