plugin-scanner 2.0.130__tar.gz → 2.0.132__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 (422) hide show
  1. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/PKG-INFO +47 -1
  2. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/README.md +46 -0
  3. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/testing-matrix.md +26 -0
  4. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/pyproject.toml +1 -1
  5. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/pyproject.toml.bak +1 -1
  6. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/render.py +10 -1
  7. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/manager.py +110 -0
  8. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/models.py +1 -0
  9. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/store.py +1 -0
  10. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/store_approvals.py +15 -5
  11. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/version.py +1 -1
  12. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/README.md +74 -0
  13. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/benign-docs-fake-token.py +28 -0
  14. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/benign-health-endpoint.py +26 -0
  15. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/benign-nvmrc-fake-creds.py +29 -0
  16. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/benign-source-search.py +34 -0
  17. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/expected-decisions.json +119 -0
  18. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-dockerfile.txt +37 -0
  19. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-encoded-shell-exfil.py +36 -0
  20. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-github-action.yml +46 -0
  21. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-mcp-delete.md +33 -0
  22. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-mcp-secret-read.md +33 -0
  23. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-mcp-skill-exfil.md +39 -0
  24. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-npm-postinstall.js +36 -0
  25. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-prompt-env-read.md +28 -0
  26. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-prompt-guard-bypass.md +30 -0
  27. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-prompt-npmrc-read.md +25 -0
  28. plugin_scanner-2.0.132/tests/fixtures/guard-red-team/malicious-python-setup.py +46 -0
  29. plugin_scanner-2.0.132/tests/test_guard_approval_continuity.py +486 -0
  30. plugin_scanner-2.0.132/tests/test_guard_red_team.py +184 -0
  31. plugin_scanner-2.0.130/tests/fixtures/guard-red-team/README.md +0 -27
  32. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.clusterfuzzlite/Dockerfile +0 -0
  33. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.clusterfuzzlite/build.sh +0 -0
  34. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.clusterfuzzlite/project.yaml +0 -0
  35. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  36. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.dockerignore +0 -0
  37. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/CODEOWNERS +0 -0
  38. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  39. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  40. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  41. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/dependabot.yml +0 -0
  42. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/workflows/ci.yml +0 -0
  43. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/workflows/codeql.yml +0 -0
  44. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/workflows/dependabot-uv-lock.yml +0 -0
  45. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/workflows/fuzz.yml +0 -0
  46. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/workflows/harness-smoke.yml +0 -0
  47. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/workflows/publish.yml +0 -0
  48. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.github/workflows/scorecard.yml +0 -0
  49. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.gitignore +0 -0
  50. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/.pre-commit-hooks.yaml +0 -0
  51. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/CONTRIBUTING.md +0 -0
  52. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/Dockerfile +0 -0
  53. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/LICENSE +0 -0
  54. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/SECURITY.md +0 -0
  55. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/index.html +0 -0
  56. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/package.json +0 -0
  57. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/pnpm-lock.yaml +0 -0
  58. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/public/apple-touch-icon.png +0 -0
  59. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  60. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/public/brand/Logo_Whole.png +0 -0
  61. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/public/favicon-16x16.png +0 -0
  62. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/public/favicon-32x32.png +0 -0
  63. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/public/favicon.ico +0 -0
  64. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/app.tsx +0 -0
  65. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/approval-center-layout.test.ts +0 -0
  66. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/approval-center-layout.tsx +0 -0
  67. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/approval-center-primitives.tsx +0 -0
  68. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/approval-center-review-cards.tsx +0 -0
  69. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/approval-center-utils.ts +0 -0
  70. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/data-flow-evidence-card.tsx +0 -0
  71. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/fleet-workspace.tsx +0 -0
  72. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/guard-api.test.ts +0 -0
  73. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/guard-api.ts +0 -0
  74. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/guard-demo.ts +0 -0
  75. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/guard-types.ts +0 -0
  76. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/main.tsx +0 -0
  77. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/receipts-workspace.test.ts +0 -0
  78. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/receipts-workspace.tsx +0 -0
  79. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/runtime-overview.test.ts +0 -0
  80. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/runtime-overview.tsx +0 -0
  81. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/settings-workspace.test.ts +0 -0
  82. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/settings-workspace.tsx +0 -0
  83. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/styles.css +0 -0
  84. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/src/vite-env.d.ts +0 -0
  85. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/tsconfig.json +0 -0
  86. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/dashboard/vite.config.ts +0 -0
  87. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docker-requirements.txt +0 -0
  88. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/approval-audit.md +0 -0
  89. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/architecture.md +0 -0
  90. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/get-started.md +0 -0
  91. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/harness-support.md +0 -0
  92. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/local-vs-cloud.md +0 -0
  93. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/release-checklist.md +0 -0
  94. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/guard/smoke-tests.md +0 -0
  95. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/trust/mcp-trust-draft.md +0 -0
  96. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/trust/plugin-trust-draft.md +0 -0
  97. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/docs/trust/skill-trust-local.md +0 -0
  98. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/fuzzers/manifest_fuzzer.py +0 -0
  99. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/requirements.txt +0 -0
  100. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/schemas/plugin-quality.v1.json +0 -0
  101. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/schemas/scan-result.v1.json +0 -0
  102. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/schemas/verify-result.v1.json +0 -0
  103. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/__init__.py +0 -0
  104. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/action_runner.py +0 -0
  105. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  106. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  107. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  108. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/claude.py +0 -0
  109. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  110. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  111. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  112. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  113. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  114. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  115. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  116. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  117. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  118. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/security.py +0 -0
  119. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  120. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/cli.py +0 -0
  121. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/cli_ui.py +0 -0
  122. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/config.py +0 -0
  123. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  124. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  125. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  126. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  127. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  128. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  129. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  130. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  131. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  132. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/github_reporting.py +0 -0
  133. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  134. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
  135. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  136. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  137. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  138. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  139. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  140. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  141. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/contracts.py +0 -0
  142. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  143. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  144. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  145. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  146. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  147. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  148. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  149. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  150. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  151. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  152. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  153. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  154. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  155. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  156. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  157. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  158. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  159. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
  160. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  161. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  162. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  163. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  164. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  165. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  166. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/config.py +0 -0
  167. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  168. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  169. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  170. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  171. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  172. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  173. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  174. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  175. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  176. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  177. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  178. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  179. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  180. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  181. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  182. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/incident.py +0 -0
  183. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  184. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  185. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  186. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  187. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/protect.py +0 -0
  188. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  189. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  190. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  191. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  192. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  193. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  194. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  195. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/risk.py +0 -0
  196. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  197. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
  198. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/advisory_escalation.py +0 -0
  199. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/advisory_matchers.py +0 -0
  200. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
  201. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
  202. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
  203. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  204. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
  205. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
  206. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
  207. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  208. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/safe_decode.py +0 -0
  209. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/sandbox.py +0 -0
  210. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  211. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  212. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
  213. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
  214. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  215. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/skill_protection.py +0 -0
  216. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/supply_chain.py +0 -0
  217. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  218. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
  219. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/runtime/threat_intel.py +0 -0
  220. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  221. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  222. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  223. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  224. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/shims.py +0 -0
  225. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  226. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/store_evidence.py +0 -0
  227. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/store_threat_intel.py +0 -0
  228. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/guard/types.py +0 -0
  229. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  230. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  231. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  232. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  233. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  234. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/models.py +0 -0
  235. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/path_support.py +0 -0
  236. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/policy.py +0 -0
  237. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  238. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/repo_detect.py +0 -0
  239. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/reporting.py +0 -0
  240. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  241. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/rules/registry.py +0 -0
  242. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/rules/specs.py +0 -0
  243. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/scanner.py +0 -0
  244. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/submission.py +0 -0
  245. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/suppressions.py +0 -0
  246. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  247. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  248. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  249. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_models.py +0 -0
  250. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  251. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  252. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  253. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/trust_specs.py +0 -0
  254. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/src/codex_plugin_scanner/verification.py +0 -0
  255. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/__init__.py +0 -0
  256. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/conftest.py +0 -0
  257. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/__init__.py +0 -0
  258. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  259. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  260. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/bad-plugin/secrets.js +0 -0
  261. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  262. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  263. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/claude-plugin-good/README.md +0 -0
  264. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  265. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  266. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  267. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/code-quality-bad/evil.js +0 -0
  268. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/code-quality-bad/inject.js +0 -0
  269. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  270. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  271. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/gemini-extension-good/README.md +0 -0
  272. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  273. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  274. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  275. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  276. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/.codexignore +0 -0
  277. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/LICENSE +0 -0
  278. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/README.md +0 -0
  279. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  280. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  281. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  282. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  283. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  284. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  285. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/guard-red-team/canary-exfil-encoded.py +0 -0
  286. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/guard-red-team/canary-exfil.py +0 -0
  287. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/guard-red-team/smoke-evidence-template.json +0 -0
  288. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  289. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  290. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  291. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  292. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  293. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  294. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  295. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  296. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  297. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  298. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  299. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  300. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  301. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  302. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/mcp-canary-server.py +0 -0
  303. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  304. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  305. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/mit-license/LICENSE +0 -0
  306. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  307. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  308. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  309. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  310. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  311. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  312. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  313. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  314. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  315. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  316. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  317. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  318. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  319. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  320. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  321. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  322. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  323. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  324. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/opencode-good/LICENSE +0 -0
  325. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/opencode-good/README.md +0 -0
  326. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  327. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  328. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  329. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  330. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  331. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/supply-chain/benign-npm-package.json +0 -0
  332. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/supply-chain/benign-pnpm-package.json +0 -0
  333. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/supply-chain/benign-pyproject.toml +0 -0
  334. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/supply-chain/malicious-Dockerfile +0 -0
  335. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/supply-chain/malicious-action.yml +0 -0
  336. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/supply-chain/malicious-npm-package.json +0 -0
  337. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/supply-chain/malicious-setup.py +0 -0
  338. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  339. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  340. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  341. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test-trust-scoring.py +0 -0
  342. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test-trust-specs.py +0 -0
  343. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_action_runner.py +0 -0
  344. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_best_practices.py +0 -0
  345. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_cisco_install_surfaces.py +0 -0
  346. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_cli.py +0 -0
  347. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_code_quality.py +0 -0
  348. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_config.py +0 -0
  349. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_coverage_remaining.py +0 -0
  350. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_ecosystems.py +0 -0
  351. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_edge_cases.py +0 -0
  352. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_final_coverage.py +0 -0
  353. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_access_graph.py +0 -0
  354. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_advisory_escalation.py +0 -0
  355. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_approval_store_scale.py +0 -0
  356. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_approvals.py +0 -0
  357. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_bootstrap.py +0 -0
  358. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_canary_fixtures.py +0 -0
  359. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_capabilities.py +0 -0
  360. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_claude_adapter.py +0 -0
  361. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_cli.py +0 -0
  362. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_cloud_local_sync.py +0 -0
  363. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_codex_e2e.py +0 -0
  364. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_codex_install.py +0 -0
  365. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_codex_proxy.py +0 -0
  366. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_config_paths.py +0 -0
  367. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_connect_flow.py +0 -0
  368. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_consumer_mode.py +0 -0
  369. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_copilot_adapter.py +0 -0
  370. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_copilot_proxy.py +0 -0
  371. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_daemon_manager.py +0 -0
  372. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_daemon_perf.py +0 -0
  373. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_data_flow.py +0 -0
  374. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_event_schema_v1.py +0 -0
  375. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_events.py +0 -0
  376. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_evidence_store.py +0 -0
  377. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_harness_contracts.py +0 -0
  378. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_launch_env.py +0 -0
  379. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_mcp_protection.py +0 -0
  380. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_opencode_proxy.py +0 -0
  381. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_product_flow.py +0 -0
  382. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_prompt_injection.py +0 -0
  383. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_protect.py +0 -0
  384. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_render.py +0 -0
  385. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_risk.py +0 -0
  386. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_runtime.py +0 -0
  387. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_runtime_action_harnesses.py +0 -0
  388. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_runtime_actions.py +0 -0
  389. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_runtime_decisions.py +0 -0
  390. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_runtime_detectors.py +0 -0
  391. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_runtime_signals.py +0 -0
  392. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_safe_decode.py +0 -0
  393. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_sandbox.py +0 -0
  394. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_skill_protection.py +0 -0
  395. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_store_migrations.py +0 -0
  396. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_supply_chain.py +0 -0
  397. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_surface_server.py +0 -0
  398. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_threat_intel.py +0 -0
  399. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_guard_verdicts.py +0 -0
  400. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_hermes_adapter.py +0 -0
  401. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_integration.py +0 -0
  402. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_lint_fixes.py +0 -0
  403. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_live_cisco_smoke.py +0 -0
  404. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_manifest.py +0 -0
  405. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_marketplace.py +0 -0
  406. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_mcp_security.py +0 -0
  407. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_openclaw_adapter.py +0 -0
  408. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_operational_security.py +0 -0
  409. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_policy.py +0 -0
  410. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_quality_artifact.py +0 -0
  411. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_rule_registry.py +0 -0
  412. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_scanner.py +0 -0
  413. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_schema_contracts.py +0 -0
  414. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_security.py +0 -0
  415. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_security_ops.py +0 -0
  416. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_skill_security.py +0 -0
  417. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_submission.py +0 -0
  418. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_trust_scoring.py +0 -0
  419. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_trust_specs.py +0 -0
  420. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_verification.py +0 -0
  421. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/tests/test_versioning.py +0 -0
  422. {plugin_scanner-2.0.130 → plugin_scanner-2.0.132}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.130
3
+ Version: 2.0.132
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
@@ -132,6 +132,52 @@ See [docs/guard/get-started.md](docs/guard/get-started.md) for the full local fl
132
132
 
133
133
  </details>
134
134
 
135
+ ## Guard: Protection Levels
136
+
137
+ HOL Guard is antivirus for AI harnesses. It intercepts every tool action before your files change or your network is contacted, then decides in milliseconds whether to allow or block.
138
+
139
+ Choose a protection level with `hol-guard settings set security-level <level>`:
140
+
141
+ | Level | Who it's for | What it blocks |
142
+ | :--- | :--- | :--- |
143
+ | **Gentle** | Teams who want minimal friction; experienced users | High-confidence secrets and clear exfil only |
144
+ | **Balanced** | Most users (default) | Secrets, shell exfil, prompt injections, supply-chain hooks |
145
+ | **Strict** | Security-conscious teams | Everything above plus low-confidence signals and untrusted prompts |
146
+ | **Paranoid** | High-security environments | All the above plus any unrecognized MCP server action |
147
+
148
+ If you are unsure, start with **Balanced**. You can promote to **Strict** after reviewing your first week of receipts.
149
+
150
+ ## Guard: Troubleshooting
151
+
152
+ ### Why was my command paused?
153
+
154
+ Guard paused a command because one or more detectors fired. To see exactly what triggered:
155
+
156
+ ```bash
157
+ hol-guard receipts # review recent decisions
158
+ hol-guard doctor # run a probe and see which detectors are active
159
+ hol-guard doctor --perf # include per-detector timing
160
+ ```
161
+
162
+ If the block looks like a false positive, you can approve it from the receipts view or from the dashboard at `http://localhost:6174`.
163
+
164
+ ### How do I clear approvals?
165
+
166
+ From the terminal:
167
+
168
+ ```bash
169
+ hol-guard approvals # list pending approvals
170
+ hol-guard approvals clear # clear all pending approvals (prompts for confirmation)
171
+ ```
172
+
173
+ From the dashboard: open `http://localhost:6174`, go to the **Approval Center**, and use the **Clear all** button. You will be asked to confirm before any approvals are removed.
174
+
175
+ ## Guard: Advisory Sync Privacy
176
+
177
+ Guard's advisory database updates are optional and pull-only. When you run `hol-guard advisories sync`, Guard fetches a signed advisory list from `advisories.hol.org`. No local file paths, harness configs, receipt data, or workspace identifiers are sent to any server during sync.
178
+
179
+ Advisory sync requires a HOL Guard Cloud account. If you have not signed in, sync is skipped and Guard continues using the locally bundled advisory database. Run `hol-guard login` to connect a free account.
180
+
135
181
  ## Scanner Quickstart
136
182
 
137
183
  ```bash
@@ -92,6 +92,52 @@ See [docs/guard/get-started.md](docs/guard/get-started.md) for the full local fl
92
92
 
93
93
  </details>
94
94
 
95
+ ## Guard: Protection Levels
96
+
97
+ HOL Guard is antivirus for AI harnesses. It intercepts every tool action before your files change or your network is contacted, then decides in milliseconds whether to allow or block.
98
+
99
+ Choose a protection level with `hol-guard settings set security-level <level>`:
100
+
101
+ | Level | Who it's for | What it blocks |
102
+ | :--- | :--- | :--- |
103
+ | **Gentle** | Teams who want minimal friction; experienced users | High-confidence secrets and clear exfil only |
104
+ | **Balanced** | Most users (default) | Secrets, shell exfil, prompt injections, supply-chain hooks |
105
+ | **Strict** | Security-conscious teams | Everything above plus low-confidence signals and untrusted prompts |
106
+ | **Paranoid** | High-security environments | All the above plus any unrecognized MCP server action |
107
+
108
+ If you are unsure, start with **Balanced**. You can promote to **Strict** after reviewing your first week of receipts.
109
+
110
+ ## Guard: Troubleshooting
111
+
112
+ ### Why was my command paused?
113
+
114
+ Guard paused a command because one or more detectors fired. To see exactly what triggered:
115
+
116
+ ```bash
117
+ hol-guard receipts # review recent decisions
118
+ hol-guard doctor # run a probe and see which detectors are active
119
+ hol-guard doctor --perf # include per-detector timing
120
+ ```
121
+
122
+ If the block looks like a false positive, you can approve it from the receipts view or from the dashboard at `http://localhost:6174`.
123
+
124
+ ### How do I clear approvals?
125
+
126
+ From the terminal:
127
+
128
+ ```bash
129
+ hol-guard approvals # list pending approvals
130
+ hol-guard approvals clear # clear all pending approvals (prompts for confirmation)
131
+ ```
132
+
133
+ From the dashboard: open `http://localhost:6174`, go to the **Approval Center**, and use the **Clear all** button. You will be asked to confirm before any approvals are removed.
134
+
135
+ ## Guard: Advisory Sync Privacy
136
+
137
+ Guard's advisory database updates are optional and pull-only. When you run `hol-guard advisories sync`, Guard fetches a signed advisory list from `advisories.hol.org`. No local file paths, harness configs, receipt data, or workspace identifiers are sent to any server during sync.
138
+
139
+ Advisory sync requires a HOL Guard Cloud account. If you have not signed in, sync is skipped and Guard continues using the locally bundled advisory database. Run `hol-guard login` to connect a free account.
140
+
95
141
  ## Scanner Quickstart
96
142
 
97
143
  ```bash
@@ -81,3 +81,29 @@ Nightly release-bar coverage should include:
81
81
  - Claude Code or Cursor on a self-hosted macOS runner
82
82
  - Gemini or OpenCode on a self-hosted Windows runner
83
83
  - a release gate that only passes when those harness families stay green
84
+
85
+ ## Red-Team Fixture Suite (T646)
86
+
87
+ Run the red-team corpus to validate fixture safety and manifest integrity:
88
+
89
+ ```bash
90
+ pytest tests/test_guard_red_team.py tests/test_guard_canary_fixtures.py -q
91
+ ```
92
+
93
+ This verifies:
94
+ - All malicious fixtures use only `hol-fake-*` sentinel values and route to the canary domain
95
+ - All benign fixtures contain no exfil patterns or real key prefixes
96
+ - Every fixture listed in `expected-decisions.json` exists on disk
97
+ - No local usernames, real paths, or real tokens appear in any fixture
98
+
99
+ To run only the red-team manifest and safety tests:
100
+
101
+ ```bash
102
+ pytest tests/test_guard_red_team.py -q
103
+ ```
104
+
105
+ To run the full test suite including red-team:
106
+
107
+ ```bash
108
+ pytest -q
109
+ ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.130"
7
+ version = "2.0.132"
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.130"
7
+ version = "2.0.132"
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"
@@ -1768,6 +1768,15 @@ def _build_approval_table(items: list[dict[str, object]], *, title: str | None)
1768
1768
  table.add_row("—", "—", "No pending approvals", "—", "—", "—", "—")
1769
1769
  return table
1770
1770
  for item in items:
1771
+ approval_url = item.get("approval_url")
1772
+ fallback_cli = item.get("fallback_cli_command")
1773
+ review_cmd = str(item.get("review_command") or "hol-guard approvals")
1774
+ if approval_url and fallback_cli:
1775
+ resolve_text = f"{approval_url}\n or: {fallback_cli}"
1776
+ elif approval_url:
1777
+ resolve_text = str(approval_url)
1778
+ else:
1779
+ resolve_text = review_cmd
1771
1780
  table.add_row(
1772
1781
  str(item.get("request_id") or "unknown"),
1773
1782
  str(item.get("harness") or "unknown"),
@@ -1775,7 +1784,7 @@ def _build_approval_table(items: list[dict[str, object]], *, title: str | None)
1775
1784
  ", ".join(_coerce_string_list(item.get("changed_fields"))) or "none",
1776
1785
  str(item.get("risk_summary") or "no obvious secret/network signal"),
1777
1786
  _action_text(str(item.get("policy_action") or "warn")),
1778
- str(item.get("review_command") or "hol-guard approvals"),
1787
+ resolve_text,
1779
1788
  )
1780
1789
  return table
1781
1790
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import dataclasses
5
6
  import hashlib
6
7
  import json
7
8
  import os
@@ -33,6 +34,7 @@ _EPHEMERAL_GUARD_DAEMON_STALE_SECONDS = 30.0
33
34
  _EPHEMERAL_GUARD_DAEMON_MAX_STATES = 512
34
35
  _GUARD_DAEMON_PRIVATE_FILE_MODE = 0o600
35
36
  _GUARD_DAEMON_PRIVATE_DIR_MODE = 0o700
37
+ _APPROVAL_CENTER_LOCATOR_FILE = "approval-center-locator.json"
36
38
 
37
39
  _START_LOCKS: dict[str, threading.Lock] = {}
38
40
  _START_LOCKS_GUARD = threading.Lock()
@@ -40,6 +42,18 @@ _LAST_EPHEMERAL_REAP_AT = 0.0
40
42
  _RUNTIME_FINGERPRINT_CACHE: str | None = None
41
43
 
42
44
 
45
+ @dataclasses.dataclass(frozen=True, slots=True)
46
+ class ApprovalCenterLocator:
47
+ """Structured snapshot of where the Guard approval-center daemon is running."""
48
+
49
+ guard_home: Path
50
+ daemon_url: str
51
+ approval_url_base: str
52
+ pid: int
53
+ started_at: str
54
+ state_path: Path
55
+
56
+
43
57
  def _daemon_launcher_env() -> dict[str, str]:
44
58
  env = dict(os.environ)
45
59
  pythonpath_entries: list[str] = []
@@ -173,6 +187,102 @@ def clear_guard_daemon_state(guard_home: Path) -> None:
173
187
  _write_private_text(state_path, "{}")
174
188
 
175
189
 
190
+ def _locator_path(guard_home: Path) -> Path:
191
+ return guard_home / _APPROVAL_CENTER_LOCATOR_FILE
192
+
193
+
194
+ def write_approval_center_locator(guard_home: Path, locator: ApprovalCenterLocator) -> None:
195
+ locator_path = _locator_path(guard_home)
196
+ _ensure_private_directory(locator_path.parent)
197
+ payload = {
198
+ "guard_home": str(locator.guard_home),
199
+ "daemon_url": locator.daemon_url,
200
+ "approval_url_base": locator.approval_url_base,
201
+ "pid": locator.pid,
202
+ "started_at": locator.started_at,
203
+ "state_path": str(locator.state_path),
204
+ }
205
+ _write_private_text(locator_path, json.dumps(payload, indent=2))
206
+
207
+
208
+ def read_approval_center_locator(guard_home: Path) -> ApprovalCenterLocator | None:
209
+ locator_path = _locator_path(guard_home)
210
+ if not locator_path.is_file():
211
+ return None
212
+ try:
213
+ payload = json.loads(locator_path.read_text(encoding="utf-8"))
214
+ except (OSError, json.JSONDecodeError):
215
+ return None
216
+ if not isinstance(payload, dict):
217
+ return None
218
+ pid = payload.get("pid")
219
+ if not isinstance(pid, int) or pid <= 0:
220
+ return None
221
+ if not _guard_daemon_pid_is_running(pid):
222
+ return None
223
+ if not _guard_daemon_pid_matches_command(pid, expected_guard_home=guard_home):
224
+ return None
225
+ daemon_url = payload.get("daemon_url")
226
+ approval_url_base = payload.get("approval_url_base")
227
+ started_at = payload.get("started_at")
228
+ state_path_str = payload.get("state_path")
229
+ guard_home_str = payload.get("guard_home")
230
+ if not all(isinstance(v, str) for v in (daemon_url, approval_url_base, started_at, state_path_str, guard_home_str)):
231
+ return None
232
+ return ApprovalCenterLocator(
233
+ guard_home=Path(guard_home_str),
234
+ daemon_url=daemon_url,
235
+ approval_url_base=approval_url_base,
236
+ pid=pid,
237
+ started_at=started_at,
238
+ state_path=Path(state_path_str),
239
+ )
240
+
241
+
242
+ def _approval_center_daemon_is_healthy(daemon_url: str) -> bool:
243
+ try:
244
+ with urllib.request.urlopen(f"{daemon_url}/healthz", timeout=1) as response:
245
+ if response.status != 200:
246
+ return False
247
+ return _healthz_payload_is_current(response.read().decode("utf-8"))
248
+ except (OSError, ValueError, urllib.error.URLError):
249
+ return False
250
+
251
+
252
+ def _daemon_state_pid_matches_locator(guard_home: Path, locator_pid: int) -> bool:
253
+ state = _load_state(guard_home)
254
+ if not isinstance(state, dict):
255
+ return False
256
+ state_pid = state.get("pid")
257
+ return isinstance(state_pid, int) and state_pid == locator_pid
258
+
259
+
260
+ def ensure_approval_center(guard_home: Path) -> ApprovalCenterLocator:
261
+ existing = read_approval_center_locator(guard_home)
262
+ if (
263
+ existing is not None
264
+ and _approval_center_daemon_is_healthy(existing.daemon_url)
265
+ and _daemon_state_pid_matches_locator(guard_home, existing.pid)
266
+ ):
267
+ return existing
268
+ daemon_url = ensure_guard_daemon(guard_home)
269
+ now = datetime.now(tz=timezone.utc).isoformat()
270
+ state = _load_state(guard_home)
271
+ pid = state.get("pid") if isinstance(state, dict) else None
272
+ if not isinstance(pid, int) or pid <= 0:
273
+ pid = os.getpid()
274
+ locator = ApprovalCenterLocator(
275
+ guard_home=guard_home,
276
+ daemon_url=daemon_url,
277
+ approval_url_base=daemon_url,
278
+ pid=pid,
279
+ started_at=now,
280
+ state_path=_state_path(guard_home),
281
+ )
282
+ write_approval_center_locator(guard_home, locator)
283
+ return locator
284
+
285
+
176
286
  def _load_state(guard_home: Path) -> dict[str, object] | None:
177
287
  state_path = _state_path(guard_home)
178
288
  if not state_path.is_file():
@@ -180,6 +180,7 @@ class GuardApprovalRequest:
180
180
  risk_headline: str | None = None
181
181
  action_envelope_json: dict[str, object] | None = None
182
182
  decision_v2_json: dict[str, object] | None = None
183
+ fallback_cli_command: str | None = None
183
184
 
184
185
  def to_dict(self) -> dict[str, object]:
185
186
  payload = asdict(self)
@@ -686,6 +686,7 @@ class GuardStore:
686
686
  self._ensure_approval_column(connection, "action_envelope_json", "text")
687
687
  self._ensure_approval_column(connection, "decision_v2_json", "text")
688
688
  self._ensure_approval_column(connection, "workspace", "text")
689
+ self._ensure_approval_column(connection, "fallback_cli_command", "text")
689
690
  self._ensure_attachment_column(connection, "lease_id", "text not null default ''")
690
691
  self._ensure_attachment_column(connection, "lease_expires_at", "text")
691
692
  self._ensure_local_device(connection)
@@ -36,6 +36,7 @@ def approval_schema_statement() -> str:
36
36
  risk_headline text,
37
37
  action_envelope_json text,
38
38
  decision_v2_json text,
39
+ fallback_cli_command text,
39
40
  review_command text not null,
40
41
  approval_url text not null,
41
42
  status text not null,
@@ -70,7 +71,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
70
71
  recommended_scope = ?, changed_fields_json = ?, source_scope = ?, config_path = ?, workspace = ?,
71
72
  launch_target = ?, transport = ?, risk_summary = ?, risk_signals_json = ?,
72
73
  artifact_label = ?, source_label = ?, trigger_summary = ?, why_now = ?, launch_summary = ?,
73
- risk_headline = ?, action_envelope_json = ?, decision_v2_json = ?,
74
+ risk_headline = ?, action_envelope_json = ?, decision_v2_json = ?, fallback_cli_command = ?,
74
75
  review_command = ?, approval_url = ?, created_at = ?
75
76
  where request_id = ?
76
77
  """,
@@ -97,6 +98,11 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
97
98
  request.risk_headline,
98
99
  json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
99
100
  json.dumps(request.decision_v2_json) if request.decision_v2_json is not None else None,
101
+ (
102
+ _rewrite_review_command(request.fallback_cli_command, request_id)
103
+ if request.fallback_cli_command
104
+ else None
105
+ ),
100
106
  review_command,
101
107
  approval_url,
102
108
  now,
@@ -111,10 +117,10 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
111
117
  recommended_scope, changed_fields_json, source_scope, config_path, workspace,
112
118
  launch_target, transport, risk_summary,
113
119
  risk_signals_json, artifact_label, source_label, trigger_summary, why_now, launch_summary, risk_headline,
114
- action_envelope_json, decision_v2_json, review_command,
120
+ action_envelope_json, decision_v2_json, fallback_cli_command, review_command,
115
121
  approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
116
122
  )
117
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
123
+ values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
118
124
  """,
119
125
  (
120
126
  request.request_id,
@@ -142,6 +148,7 @@ def add_approval_request(connection: sqlite3.Connection, request: GuardApprovalR
142
148
  request.risk_headline,
143
149
  json.dumps(request.action_envelope_json) if request.action_envelope_json is not None else None,
144
150
  json.dumps(request.decision_v2_json) if request.decision_v2_json is not None else None,
151
+ request.fallback_cli_command,
145
152
  request.review_command,
146
153
  request.approval_url,
147
154
  "pending",
@@ -211,7 +218,8 @@ def list_approval_requests(
211
218
  select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
212
219
  recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
213
220
  risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
214
- launch_summary, risk_headline, action_envelope_json, decision_v2_json, review_command,
221
+ launch_summary, risk_headline, action_envelope_json, decision_v2_json,
222
+ fallback_cli_command, review_command,
215
223
  approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
216
224
  from approval_requests
217
225
  {where_clause}
@@ -230,7 +238,8 @@ def get_approval_request(connection: sqlite3.Connection, request_id: str) -> dic
230
238
  select request_id, harness, artifact_id, artifact_name, artifact_type, artifact_hash, publisher, policy_action,
231
239
  recommended_scope, changed_fields_json, source_scope, config_path, workspace, launch_target, transport,
232
240
  risk_summary, risk_signals_json, artifact_label, source_label, trigger_summary, why_now,
233
- launch_summary, risk_headline, action_envelope_json, decision_v2_json, review_command,
241
+ launch_summary, risk_headline, action_envelope_json, decision_v2_json,
242
+ fallback_cli_command, review_command,
234
243
  approval_url, status, resolution_action, resolution_scope, reason, created_at, resolved_at
235
244
  from approval_requests
236
245
  where request_id = ?
@@ -311,6 +320,7 @@ def _row_to_payload(row: sqlite3.Row) -> dict[str, object]:
311
320
  "risk_headline": row["risk_headline"],
312
321
  "action_envelope_json": _optional_json_object(row["action_envelope_json"]),
313
322
  "decision_v2_json": _optional_json_object(row["decision_v2_json"]),
323
+ "fallback_cli_command": row["fallback_cli_command"],
314
324
  "review_command": str(row["review_command"]),
315
325
  "approval_url": str(row["approval_url"]),
316
326
  "status": str(row["status"]),
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.130"
3
+ __version__ = "2.0.132"
@@ -0,0 +1,74 @@
1
+ # Guard Red-Team Fixtures
2
+
3
+ Fixtures in this directory contain intentionally suspicious-looking content used to test
4
+ that HOL Guard correctly detects and blocks malicious AI agent behaviors.
5
+
6
+ **All credentials, tokens, API keys, and webhook URLs in these fixtures are fake.**
7
+ They use the pattern `hol-fake-*` and route to `https://evil.hol.org/collect`,
8
+ a domain owned by Hashgraph Online solely for testing canary detection.
9
+
10
+ No real secrets or infrastructure are touched by these tests.
11
+
12
+ ## Files
13
+
14
+ ### Canary Fixtures
15
+
16
+ | File | Fixture ID | Purpose |
17
+ |------|-----------|---------|
18
+ | `canary-exfil.py` | T604 | Plain-text canary: reads a fake API key and posts it to the canary endpoint |
19
+ | `canary-exfil-encoded.py` | T605 | Encoded canary: base64-encoded exfil payload to test encoded-execution detection |
20
+
21
+ ### Malicious Prompt Fixtures
22
+
23
+ | File | Fixture ID | Purpose |
24
+ |------|-----------|---------|
25
+ | `malicious-prompt-env-read.md` | T629 | Prompt injection instructing harness to exfiltrate `.env` file contents |
26
+ | `malicious-prompt-npmrc-read.md` | T630 | Prompt injection instructing harness to read npm auth tokens |
27
+ | `malicious-prompt-guard-bypass.md` | T631 | Prompt injection attempting to disable HOL Guard monitoring |
28
+
29
+ ### Malicious MCP Fixtures
30
+
31
+ | File | Fixture ID | Purpose |
32
+ |------|-----------|---------|
33
+ | `malicious-mcp-delete.md` | T632 | MCP tool call attempting destructive file system deletion |
34
+ | `malicious-mcp-secret-read.md` | T633 | MCP tool call reading credential files then posting to external endpoint |
35
+ | `malicious-mcp-skill-exfil.md` | T634 | MCP skill that exfiltrates workspace data with fake bearer token |
36
+
37
+ ### Malicious Package/Infra Fixtures
38
+
39
+ | File | Fixture ID | Purpose |
40
+ |------|-----------|---------|
41
+ | `malicious-npm-postinstall.js` | T635 | npm postinstall hook harvesting local credential files |
42
+ | `malicious-python-setup.py` | T636 | Python setup.py exfiltrating credential files on install |
43
+ | `malicious-dockerfile.txt` | T637 | Dockerfile that exfiltrates secrets during image build |
44
+ | `malicious-github-action.yml` | T638 | GitHub Actions workflow exfiltrating CI secrets |
45
+ | `malicious-encoded-shell-exfil.py` | T639 | Base64-encoded exfil payload to test obfuscation detection |
46
+
47
+ ### Benign Fixtures
48
+
49
+ | File | Fixture ID | Purpose |
50
+ |------|-----------|---------|
51
+ | `benign-source-search.py` | T640 | Legitimate source code search — should not trigger any detectors |
52
+ | `benign-health-endpoint.py` | T641 | Legitimate loopback health check — should not trigger any detectors |
53
+ | `benign-docs-fake-token.py` | T642 | Documentation with explanatory fake tokens — should not be blocked |
54
+ | `benign-nvmrc-fake-creds.py` | T643 | .nvmrc read with version string that looks like a credential |
55
+
56
+ ### Manifests
57
+
58
+ | File | Purpose |
59
+ |------|---------|
60
+ | `expected-decisions.json` | Expected Guard decision for every fixture (used by red-team test runner) |
61
+ | `smoke-evidence-template.json` | Template for recording smoke test evidence during manual harness verification |
62
+
63
+ ## Running Red-Team Tests
64
+
65
+ ```bash
66
+ pytest tests/test_guard_red_team.py tests/test_guard_canary_fixtures.py -q
67
+ ```
68
+
69
+ The red-team test runner (`tests/test_guard_red_team.py`) validates:
70
+ - All malicious fixtures contain only fake key material (no real secrets)
71
+ - All benign fixtures contain no network exfil patterns
72
+ - Fixture structure matches the expected-decisions manifest
73
+ - No local usernames, real paths, or real tokens appear in any fixture
74
+
@@ -0,0 +1,28 @@
1
+ """Benign docs with fake token fixture (T642).
2
+
3
+ This fixture is documentation that happens to contain fake token strings
4
+ that look like real tokens. Guard should NOT block documentation reads
5
+ that only mention tokens in explanatory context.
6
+
7
+ The tokens below are clearly fake and used only for illustrative purposes.
8
+ """
9
+
10
+
11
+ DOCS = """
12
+ # API Authentication Guide
13
+
14
+ To authenticate, pass your API token in the Authorization header:
15
+
16
+ Authorization: Bearer <your-api-token>
17
+
18
+ Example (using a placeholder — replace with your real token):
19
+
20
+ curl -H "Authorization: Bearer hol-fake-docs-token-example-0000" \\
21
+ https://api.example.com/v1/status
22
+
23
+ Note: hol-fake-docs-token-example-0000 is NOT a real token.
24
+ Do not use this value in production.
25
+ """
26
+
27
+ if __name__ == "__main__":
28
+ print(DOCS)
@@ -0,0 +1,26 @@
1
+ """Benign health endpoint fetch fixture (T641).
2
+
3
+ Simulates a legitimate health check against a local service.
4
+ No secret files are read. No external network calls to harmful endpoints.
5
+ The URL is a local loopback address only.
6
+ """
7
+
8
+ from urllib.error import URLError
9
+ from urllib.request import Request, urlopen
10
+
11
+ _HEALTH_URL = "http://127.0.0.1:8080/healthz"
12
+ _TIMEOUT_S = 2.0
13
+
14
+
15
+ def check_health(url: str = _HEALTH_URL, timeout: float = _TIMEOUT_S) -> bool:
16
+ try:
17
+ req = Request(url, method="GET")
18
+ with urlopen(req, timeout=timeout) as response:
19
+ return response.status == 200
20
+ except (URLError, OSError):
21
+ return False
22
+
23
+
24
+ if __name__ == "__main__":
25
+ ok = check_health()
26
+ print("healthy" if ok else "unreachable")
@@ -0,0 +1,29 @@
1
+ """Benign .nvmrc with fake credential string fixture (T643).
2
+
3
+ Simulates reading a .nvmrc file that happens to contain a version string
4
+ that superficially resembles a credential. Guard should NOT block benign
5
+ version-file reads.
6
+
7
+ The strings below are NOT credentials. They are version identifiers used
8
+ only to test that Guard's false-positive rate for benign file reads is low.
9
+ """
10
+
11
+ import os
12
+
13
+ _NVMRC_CONTENT = "v20.11.0\n"
14
+ _FAKE_NVMRC_CRED_LOOKALIKE = "hol-fake-nvmrc-version-token-placeholder-0000"
15
+
16
+
17
+ def read_nvmrc(project_root: str = ".") -> str:
18
+ path = os.path.join(project_root, ".nvmrc")
19
+ try:
20
+ with open(path) as fh:
21
+ return fh.read().strip()
22
+ except OSError:
23
+ return _NVMRC_CONTENT.strip()
24
+
25
+
26
+ if __name__ == "__main__":
27
+ version = read_nvmrc()
28
+ print(f"Node version: {version}")
29
+ print(f"Placeholder note: {_FAKE_NVMRC_CRED_LOOKALIKE} is not a real token")
@@ -0,0 +1,34 @@
1
+ """Benign source search fixture (T640).
2
+
3
+ This fixture simulates a legitimate source code search operation.
4
+ No secret files are read. No external network calls are made.
5
+ All identifiers are benign.
6
+ """
7
+
8
+ import os
9
+ import re
10
+
11
+ _SEARCH_ROOT = "src"
12
+ _PATTERN = re.compile(r"def\s+\w+\(")
13
+
14
+
15
+ def search_functions(root: str) -> list[tuple[str, int, str]]:
16
+ matches: list[tuple[str, int, str]] = []
17
+ for dirpath, _dirs, files in os.walk(root):
18
+ for fname in files:
19
+ if not fname.endswith(".py"):
20
+ continue
21
+ fpath = os.path.join(dirpath, fname)
22
+ try:
23
+ with open(fpath) as fh:
24
+ for lineno, line in enumerate(fh, start=1):
25
+ if _PATTERN.search(line):
26
+ matches.append((fpath, lineno, line.rstrip()))
27
+ except OSError:
28
+ pass
29
+ return matches
30
+
31
+
32
+ if __name__ == "__main__":
33
+ for path, line, text in search_functions(_SEARCH_ROOT):
34
+ print(f"{path}:{line}: {text}")