plugin-scanner 2.0.105__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.105 → plugin_scanner-2.0.106}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/risk.py +2 -6
  5. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/actions.py +32 -2
  6. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/detectors.py +43 -1
  7. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +3 -45
  8. plugin_scanner-2.0.106/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +217 -0
  9. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/version.py +1 -1
  10. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_risk.py +60 -0
  11. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime.py +46 -0
  12. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_actions.py +45 -1
  13. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_detectors.py +156 -3
  14. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/Dockerfile +0 -0
  15. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/build.sh +0 -0
  16. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/project.yaml +0 -0
  17. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  18. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.dockerignore +0 -0
  19. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/CODEOWNERS +0 -0
  20. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  21. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  22. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  23. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/dependabot.yml +0 -0
  24. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/ci.yml +0 -0
  25. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/codeql.yml +0 -0
  26. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/dependabot-uv-lock.yml +0 -0
  27. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/fuzz.yml +0 -0
  28. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/harness-smoke.yml +0 -0
  29. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/publish.yml +0 -0
  30. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/scorecard.yml +0 -0
  31. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.gitignore +0 -0
  32. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.pre-commit-hooks.yaml +0 -0
  33. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/CONTRIBUTING.md +0 -0
  34. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/Dockerfile +0 -0
  35. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/LICENSE +0 -0
  36. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/README.md +0 -0
  37. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/SECURITY.md +0 -0
  38. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/index.html +0 -0
  39. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/package.json +0 -0
  40. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/pnpm-lock.yaml +0 -0
  41. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/apple-touch-icon.png +0 -0
  42. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  43. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Whole.png +0 -0
  44. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/favicon-16x16.png +0 -0
  45. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/favicon-32x32.png +0 -0
  46. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/favicon.ico +0 -0
  47. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/app.tsx +0 -0
  48. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/approval-center-layout.tsx +0 -0
  49. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/approval-center-primitives.tsx +0 -0
  50. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/approval-center-utils.ts +0 -0
  51. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/fleet-workspace.tsx +0 -0
  52. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-api.test.ts +0 -0
  53. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-api.ts +0 -0
  54. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-demo.ts +0 -0
  55. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-types.ts +0 -0
  56. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/main.tsx +0 -0
  57. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/receipts-workspace.tsx +0 -0
  58. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/runtime-overview.tsx +0 -0
  59. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/settings-workspace.tsx +0 -0
  60. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/styles.css +0 -0
  61. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/vite-env.d.ts +0 -0
  62. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/tsconfig.json +0 -0
  63. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/vite.config.ts +0 -0
  64. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docker-requirements.txt +0 -0
  65. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/approval-audit.md +0 -0
  66. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/architecture.md +0 -0
  67. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/get-started.md +0 -0
  68. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/harness-support.md +0 -0
  69. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/local-vs-cloud.md +0 -0
  70. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/testing-matrix.md +0 -0
  71. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/trust/mcp-trust-draft.md +0 -0
  72. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/trust/plugin-trust-draft.md +0 -0
  73. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/trust/skill-trust-local.md +0 -0
  74. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/fuzzers/manifest_fuzzer.py +0 -0
  75. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/requirements.txt +0 -0
  76. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/schemas/plugin-quality.v1.json +0 -0
  77. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/schemas/scan-result.v1.json +0 -0
  78. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/schemas/verify-result.v1.json +0 -0
  79. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/__init__.py +0 -0
  80. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/action_runner.py +0 -0
  81. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  82. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  83. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  84. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/claude.py +0 -0
  85. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  86. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  87. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  88. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  89. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  90. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  91. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  92. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  93. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  94. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/security.py +0 -0
  95. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  96. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli.py +0 -0
  97. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli_ui.py +0 -0
  98. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/config.py +0 -0
  99. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  100. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  101. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  102. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  103. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  104. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  105. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  106. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  107. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  108. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/github_reporting.py +0 -0
  109. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  110. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  111. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  112. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  113. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  114. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  115. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  116. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  117. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  118. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  119. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  120. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  121. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  122. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  123. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  124. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  125. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  126. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  127. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  128. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  129. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  130. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  131. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  132. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  133. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
  134. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  135. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  136. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  137. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  138. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  139. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  140. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  141. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/config.py +0 -0
  142. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  143. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  144. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  145. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  146. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  147. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  148. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  149. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  150. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  151. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  152. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  153. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  154. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  155. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  156. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  157. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  158. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/incident.py +0 -0
  159. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  160. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  161. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/models.py +0 -0
  162. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  163. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  164. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/protect.py +0 -0
  165. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  166. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  167. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  168. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  169. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  170. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  171. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  172. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  173. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  174. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  175. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  176. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  177. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  178. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  179. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  180. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  181. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/shims.py +0 -0
  182. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store.py +0 -0
  183. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  184. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  185. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/types.py +0 -0
  186. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  187. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  188. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  189. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  190. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  191. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/models.py +0 -0
  192. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/path_support.py +0 -0
  193. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/policy.py +0 -0
  194. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  195. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/repo_detect.py +0 -0
  196. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/reporting.py +0 -0
  197. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  198. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/registry.py +0 -0
  199. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/specs.py +0 -0
  200. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/scanner.py +0 -0
  201. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/submission.py +0 -0
  202. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/suppressions.py +0 -0
  203. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  204. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  205. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  206. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_models.py +0 -0
  207. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  208. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  209. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  210. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_specs.py +0 -0
  211. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/verification.py +0 -0
  212. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/__init__.py +0 -0
  213. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/conftest.py +0 -0
  214. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/__init__.py +0 -0
  215. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  216. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  217. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/secrets.js +0 -0
  218. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  219. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  220. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/README.md +0 -0
  221. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  222. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  223. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  224. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/evil.js +0 -0
  225. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/inject.js +0 -0
  226. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  227. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  228. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/README.md +0 -0
  229. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  230. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  231. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  232. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  233. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codexignore +0 -0
  234. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/LICENSE +0 -0
  235. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/README.md +0 -0
  236. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  237. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  238. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  239. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  240. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  241. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  242. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  243. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  244. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  245. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  246. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  247. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  248. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  249. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  250. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  251. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  252. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  253. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  254. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  255. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  256. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/mcp-canary-server.py +0 -0
  257. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  258. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  259. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/mit-license/LICENSE +0 -0
  260. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  261. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  262. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  263. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  264. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  265. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  266. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  267. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  268. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  269. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  270. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  271. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  272. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  273. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  274. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  275. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  276. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  277. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  278. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/LICENSE +0 -0
  279. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/README.md +0 -0
  280. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  281. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  282. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  283. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  284. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  285. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  286. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  287. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  288. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test-trust-scoring.py +0 -0
  289. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test-trust-specs.py +0 -0
  290. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_action_runner.py +0 -0
  291. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_best_practices.py +0 -0
  292. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_cisco_install_surfaces.py +0 -0
  293. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_cli.py +0 -0
  294. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_code_quality.py +0 -0
  295. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_config.py +0 -0
  296. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_coverage_remaining.py +0 -0
  297. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_ecosystems.py +0 -0
  298. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_edge_cases.py +0 -0
  299. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_final_coverage.py +0 -0
  300. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_approvals.py +0 -0
  301. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_bootstrap.py +0 -0
  302. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_capabilities.py +0 -0
  303. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_claude_adapter.py +0 -0
  304. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_cli.py +0 -0
  305. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_codex_e2e.py +0 -0
  306. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_codex_install.py +0 -0
  307. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_codex_proxy.py +0 -0
  308. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_config_paths.py +0 -0
  309. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_connect_flow.py +0 -0
  310. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_consumer_mode.py +0 -0
  311. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_copilot_adapter.py +0 -0
  312. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_copilot_proxy.py +0 -0
  313. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_daemon_manager.py +0 -0
  314. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_event_schema_v1.py +0 -0
  315. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_events.py +0 -0
  316. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_launch_env.py +0 -0
  317. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_opencode_proxy.py +0 -0
  318. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_product_flow.py +0 -0
  319. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_protect.py +0 -0
  320. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_render.py +0 -0
  321. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_action_harnesses.py +0 -0
  322. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_decisions.py +0 -0
  323. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_signals.py +0 -0
  324. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_store_migrations.py +0 -0
  325. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_surface_server.py +0 -0
  326. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_verdicts.py +0 -0
  327. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_hermes_adapter.py +0 -0
  328. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_integration.py +0 -0
  329. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_lint_fixes.py +0 -0
  330. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_live_cisco_smoke.py +0 -0
  331. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_manifest.py +0 -0
  332. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_marketplace.py +0 -0
  333. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_mcp_security.py +0 -0
  334. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_openclaw_adapter.py +0 -0
  335. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_operational_security.py +0 -0
  336. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_policy.py +0 -0
  337. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_quality_artifact.py +0 -0
  338. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_rule_registry.py +0 -0
  339. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_scanner.py +0 -0
  340. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_schema_contracts.py +0 -0
  341. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_security.py +0 -0
  342. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_security_ops.py +0 -0
  343. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_skill_security.py +0 -0
  344. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_submission.py +0 -0
  345. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_trust_scoring.py +0 -0
  346. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_trust_specs.py +0 -0
  347. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_verification.py +0 -0
  348. {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_versioning.py +0 -0
  349. {plugin_scanner-2.0.105 → 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.105
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.105"
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.105"
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"
@@ -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,
@@ -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.105"
3
+ __version__ = "2.0.106"
@@ -24,6 +24,7 @@ from codex_plugin_scanner.guard.risk import (
24
24
  artifact_risk_signals_typed,
25
25
  artifact_risk_signals_v2,
26
26
  artifact_risk_summary,
27
+ classify_secret_paths,
27
28
  detect_encoded_command,
28
29
  detect_guard_bypass,
29
30
  detect_staged_download,
@@ -43,6 +44,7 @@ from codex_plugin_scanner.guard.runtime.secret_file_requests import (
43
44
  is_explicitly_benign_tool_action_request,
44
45
  is_file_read_tool_name,
45
46
  )
47
+ from codex_plugin_scanner.guard.runtime.secret_sensitivity import SecretPathMatch, classify_secret_path
46
48
  from codex_plugin_scanner.guard.store import GuardStore
47
49
 
48
50
 
@@ -109,6 +111,16 @@ def test_artifact_risk_signals_legacy_strings_remain_stable():
109
111
  )
110
112
 
111
113
 
114
+ def test_classify_secret_paths_preserves_legacy_labels():
115
+ classes = classify_secret_paths(".pypirc ~/.aws/" + "credentials ~/.docker/" + "config.json")
116
+
117
+ assert classes == {
118
+ "python package credentials",
119
+ "aws shared credentials",
120
+ "docker credentials",
121
+ }
122
+
123
+
112
124
  def test_artifact_risk_signals_v2_adapts_existing_signal_metadata():
113
125
  artifact = GuardArtifact(
114
126
  artifact_id="codex:project:secret_probe",
@@ -346,6 +358,32 @@ def test_secret_file_path_classifier_stays_precise(tmp_path):
346
358
  assert classify_sensitive_path(".envrc") is None
347
359
 
348
360
 
361
+ @pytest.mark.parametrize(
362
+ ("path", "family"),
363
+ [
364
+ (".env", "local .env file"),
365
+ (".npmrc", "npm registry credentials"),
366
+ (".pypirc", "Python package credentials"),
367
+ ("~/.aws/" + "credentials", "AWS shared credentials file"),
368
+ ("~/.ssh/id_rsa", "SSH private key"),
369
+ ("~/.ssh/id_ed25519", "SSH private key"),
370
+ ("~/.gnupg/private-keys-v1.d/example.key", "GnuPG key material"),
371
+ ("~/.docker/" + "config.json", "Docker client config"),
372
+ ("~/.kube/config", "Kubernetes config"),
373
+ (".terraform.tfvars", "Terraform variable secrets"),
374
+ ],
375
+ )
376
+ def test_secret_sensitivity_module_classifies_planned_secret_path_families(tmp_path, path, family):
377
+ match = classify_secret_path(path, home_dir=tmp_path)
378
+
379
+ assert isinstance(match, SecretPathMatch)
380
+ assert match.family == family
381
+ assert match.path_class == family
382
+ assert match.path == match.normalized_path
383
+ assert match.sensitivity in {"high", "critical"}
384
+ assert match.reason
385
+
386
+
349
387
  def test_file_read_request_classifier_is_argument_aware(tmp_path):
350
388
  env_request = extract_sensitive_file_read_request("read_file", {"path": ".env.local"})
351
389
  claude_request = extract_sensitive_file_read_request("Read", {"file_path": "~/.ssh/config"}, home_dir=tmp_path)
@@ -365,6 +403,28 @@ def test_file_read_request_classifier_is_argument_aware(tmp_path):
365
403
  assert extract_sensitive_file_read_request("write_file", {"path": ".env"}) is None
366
404
 
367
405
 
406
+ @pytest.mark.parametrize(
407
+ ("path", "family"),
408
+ [
409
+ (".env", "local .env file"),
410
+ (".npmrc", "npm registry credentials"),
411
+ (".pypirc", "Python package credentials"),
412
+ ("~/.aws/" + "credentials", "AWS shared credentials file"),
413
+ ("~/.ssh/id_rsa", "SSH private key"),
414
+ ("~/.ssh/id_ed25519", "SSH private key"),
415
+ ("~/.gnupg/private-keys-v1.d/example.key", "GnuPG key material"),
416
+ ("~/.docker/" + "config.json", "Docker client config"),
417
+ ("~/.kube/config", "Kubernetes config"),
418
+ (".terraform.tfvars", "Terraform variable secrets"),
419
+ ],
420
+ )
421
+ def test_file_read_request_classifier_covers_planned_secret_paths(tmp_path, path, family):
422
+ request = extract_sensitive_file_read_request("Read", {"file_path": path}, home_dir=tmp_path)
423
+
424
+ assert request is not None
425
+ assert request.path_match.family == family
426
+
427
+
368
428
  def test_file_read_request_artifact_hash_is_exact_to_tool_and_path():
369
429
  first_request = extract_sensitive_file_read_request("read_file", {"path": ".env"})
370
430
  same_request = extract_sensitive_file_read_request("read_file", {"path": ".env"})
@@ -2400,6 +2400,52 @@ clearer UX and an implementation plan with technical references.
2400
2400
  assert output["policy_action"] == "require-reapproval"
2401
2401
  assert output["path_summary"] == str(home_dir / ".env")
2402
2402
 
2403
+ @pytest.mark.parametrize(
2404
+ "path",
2405
+ [
2406
+ ".env",
2407
+ ".npmrc",
2408
+ ".pypirc",
2409
+ "~/.aws/" + "credentials",
2410
+ "~/.ssh/id_rsa",
2411
+ "~/.ssh/id_ed25519",
2412
+ "~/.gnupg/private-keys-v1.d/example.key",
2413
+ "~/.docker/" + "config.json",
2414
+ "~/.kube/config",
2415
+ ".terraform.tfvars",
2416
+ ],
2417
+ )
2418
+ def test_guard_hook_asks_for_planned_secret_file_reads(self, tmp_path, capsys, monkeypatch, path):
2419
+ home_dir = tmp_path / "home"
2420
+ workspace_dir = tmp_path / "workspace"
2421
+ _build_guard_fixture(home_dir, workspace_dir)
2422
+ event = {
2423
+ "tool_name": "read_file",
2424
+ "tool_input": {"path": path},
2425
+ "source_scope": "project",
2426
+ }
2427
+ monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps(event)))
2428
+ monkeypatch.setattr(guard_commands_module, "ensure_guard_daemon", lambda _guard_home: "http://127.0.0.1:4455")
2429
+
2430
+ rc = main(
2431
+ [
2432
+ "guard",
2433
+ "hook",
2434
+ "--home",
2435
+ str(home_dir),
2436
+ "--workspace",
2437
+ str(workspace_dir),
2438
+ "--harness",
2439
+ "copilot",
2440
+ "--json",
2441
+ ]
2442
+ )
2443
+ output = json.loads(capsys.readouterr().out)
2444
+
2445
+ assert rc == 1
2446
+ assert output["artifact_type"] == "file_read_request"
2447
+ assert output["policy_action"] == "require-reapproval"
2448
+
2403
2449
 
2404
2450
  def test_guard_hook_emits_copilot_native_ask_response(tmp_path, capsys, monkeypatch):
2405
2451
  home_dir = tmp_path / "home"