plugin-scanner 2.0.91__tar.gz → 2.0.93__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 (339) hide show
  1. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/guard-api.ts +75 -10
  3. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/pyproject.toml +1 -1
  4. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/pyproject.toml.bak +1 -1
  5. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/commands.py +206 -0
  6. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/server.py +70 -14
  7. plugin_scanner-2.0.93/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +9 -0
  8. plugin_scanner-2.0.93/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +1 -0
  9. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/runtime/runner.py +14 -8
  10. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/version.py +1 -1
  11. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_approvals.py +104 -0
  12. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_cli.py +283 -0
  13. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_runtime.py +61 -0
  14. plugin_scanner-2.0.91/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -9
  15. plugin_scanner-2.0.91/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -1
  16. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.clusterfuzzlite/Dockerfile +0 -0
  17. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.clusterfuzzlite/build.sh +0 -0
  18. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.clusterfuzzlite/project.yaml +0 -0
  19. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  20. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.dockerignore +0 -0
  21. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/CODEOWNERS +0 -0
  22. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  23. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  24. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  25. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/dependabot.yml +0 -0
  26. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/workflows/ci.yml +0 -0
  27. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/workflows/codeql.yml +0 -0
  28. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/workflows/dependabot-uv-lock.yml +0 -0
  29. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/workflows/fuzz.yml +0 -0
  30. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/workflows/harness-smoke.yml +0 -0
  31. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/workflows/publish.yml +0 -0
  32. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.github/workflows/scorecard.yml +0 -0
  33. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.gitignore +0 -0
  34. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/.pre-commit-hooks.yaml +0 -0
  35. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/CONTRIBUTING.md +0 -0
  36. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/Dockerfile +0 -0
  37. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/LICENSE +0 -0
  38. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/README.md +0 -0
  39. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/SECURITY.md +0 -0
  40. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/index.html +0 -0
  41. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/package.json +0 -0
  42. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/pnpm-lock.yaml +0 -0
  43. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/public/apple-touch-icon.png +0 -0
  44. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  45. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/public/brand/Logo_Whole.png +0 -0
  46. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/public/favicon-16x16.png +0 -0
  47. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/public/favicon-32x32.png +0 -0
  48. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/public/favicon.ico +0 -0
  49. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/app.tsx +0 -0
  50. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/approval-center-layout.tsx +0 -0
  51. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/approval-center-primitives.tsx +0 -0
  52. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/approval-center-utils.ts +0 -0
  53. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/fleet-workspace.tsx +0 -0
  54. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/guard-demo.ts +0 -0
  55. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/guard-types.ts +0 -0
  56. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/main.tsx +0 -0
  57. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/receipts-workspace.tsx +0 -0
  58. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/runtime-overview.tsx +0 -0
  59. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/settings-workspace.tsx +0 -0
  60. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/styles.css +0 -0
  61. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/src/vite-env.d.ts +0 -0
  62. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/tsconfig.json +0 -0
  63. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/dashboard/vite.config.ts +0 -0
  64. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docker-requirements.txt +0 -0
  65. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/guard/approval-audit.md +0 -0
  66. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/guard/architecture.md +0 -0
  67. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/guard/get-started.md +0 -0
  68. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/guard/harness-support.md +0 -0
  69. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/guard/local-vs-cloud.md +0 -0
  70. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/guard/testing-matrix.md +0 -0
  71. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/trust/mcp-trust-draft.md +0 -0
  72. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/trust/plugin-trust-draft.md +0 -0
  73. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/docs/trust/skill-trust-local.md +0 -0
  74. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/fuzzers/manifest_fuzzer.py +0 -0
  75. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/requirements.txt +0 -0
  76. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/schemas/plugin-quality.v1.json +0 -0
  77. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/schemas/scan-result.v1.json +0 -0
  78. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/schemas/verify-result.v1.json +0 -0
  79. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/__init__.py +0 -0
  80. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/action_runner.py +0 -0
  81. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  82. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  83. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  84. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/claude.py +0 -0
  85. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  86. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  87. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  88. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  89. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  90. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  91. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  92. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  93. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  94. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/security.py +0 -0
  95. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  96. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/cli.py +0 -0
  97. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/cli_ui.py +0 -0
  98. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/config.py +0 -0
  99. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  100. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  101. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  102. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  103. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  104. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  105. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  106. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  107. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  108. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/github_reporting.py +0 -0
  109. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  110. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  111. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  112. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  113. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  114. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  115. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  116. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  117. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  118. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  119. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  120. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  121. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  122. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  123. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  124. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  125. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  126. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  127. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  128. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  129. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  130. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  131. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  132. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  133. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  134. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  135. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  136. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  137. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  138. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  139. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/config.py +0 -0
  140. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  141. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  142. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  143. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  144. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  145. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  146. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  147. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  148. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  149. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  150. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  151. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  152. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  153. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/incident.py +0 -0
  154. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  155. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  156. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/models.py +0 -0
  157. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  158. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  159. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/protect.py +0 -0
  160. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  161. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  162. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  163. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  164. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  165. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  166. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  167. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/risk.py +0 -0
  168. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  169. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  170. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  171. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  172. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  173. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  174. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  175. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/shims.py +0 -0
  176. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/store.py +0 -0
  177. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  178. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  179. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/guard/types.py +0 -0
  180. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  181. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  182. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  183. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  184. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  185. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/models.py +0 -0
  186. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/path_support.py +0 -0
  187. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/policy.py +0 -0
  188. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  189. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/repo_detect.py +0 -0
  190. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/reporting.py +0 -0
  191. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  192. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/rules/registry.py +0 -0
  193. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/rules/specs.py +0 -0
  194. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/scanner.py +0 -0
  195. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/submission.py +0 -0
  196. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/suppressions.py +0 -0
  197. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  198. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  199. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  200. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_models.py +0 -0
  201. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  202. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  203. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  204. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/trust_specs.py +0 -0
  205. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/src/codex_plugin_scanner/verification.py +0 -0
  206. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/__init__.py +0 -0
  207. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/conftest.py +0 -0
  208. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/__init__.py +0 -0
  209. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  210. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  211. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/bad-plugin/secrets.js +0 -0
  212. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  213. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  214. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/claude-plugin-good/README.md +0 -0
  215. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  216. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  217. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  218. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/code-quality-bad/evil.js +0 -0
  219. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/code-quality-bad/inject.js +0 -0
  220. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  221. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  222. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/gemini-extension-good/README.md +0 -0
  223. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  224. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  225. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  226. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  227. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/.codexignore +0 -0
  228. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/LICENSE +0 -0
  229. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/README.md +0 -0
  230. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  231. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  232. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  233. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  234. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  235. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  236. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  237. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  238. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  239. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  240. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  241. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  242. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  243. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  244. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  245. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  246. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  247. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  248. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  249. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  250. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/mcp-canary-server.py +0 -0
  251. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  252. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  253. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/mit-license/LICENSE +0 -0
  254. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  255. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  256. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  257. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  258. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  259. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  260. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  261. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  262. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  263. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  264. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  265. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  266. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  267. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  268. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  269. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  270. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  271. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  272. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/opencode-good/LICENSE +0 -0
  273. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/opencode-good/README.md +0 -0
  274. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  275. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  276. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  277. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  278. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  279. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  280. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  281. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  282. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test-trust-scoring.py +0 -0
  283. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test-trust-specs.py +0 -0
  284. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_action_runner.py +0 -0
  285. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_best_practices.py +0 -0
  286. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_cisco_install_surfaces.py +0 -0
  287. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_cli.py +0 -0
  288. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_code_quality.py +0 -0
  289. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_config.py +0 -0
  290. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_coverage_remaining.py +0 -0
  291. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_ecosystems.py +0 -0
  292. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_edge_cases.py +0 -0
  293. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_final_coverage.py +0 -0
  294. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_bootstrap.py +0 -0
  295. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_capabilities.py +0 -0
  296. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_claude_adapter.py +0 -0
  297. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_codex_e2e.py +0 -0
  298. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_codex_install.py +0 -0
  299. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_codex_proxy.py +0 -0
  300. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_config_paths.py +0 -0
  301. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_connect_flow.py +0 -0
  302. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_consumer_mode.py +0 -0
  303. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_copilot_adapter.py +0 -0
  304. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_copilot_proxy.py +0 -0
  305. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_daemon_manager.py +0 -0
  306. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_event_schema_v1.py +0 -0
  307. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_events.py +0 -0
  308. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_launch_env.py +0 -0
  309. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_opencode_proxy.py +0 -0
  310. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_product_flow.py +0 -0
  311. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_protect.py +0 -0
  312. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_render.py +0 -0
  313. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_risk.py +0 -0
  314. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_store_migrations.py +0 -0
  315. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_surface_server.py +0 -0
  316. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_guard_verdicts.py +0 -0
  317. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_hermes_adapter.py +0 -0
  318. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_integration.py +0 -0
  319. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_lint_fixes.py +0 -0
  320. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_live_cisco_smoke.py +0 -0
  321. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_manifest.py +0 -0
  322. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_marketplace.py +0 -0
  323. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_mcp_security.py +0 -0
  324. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_openclaw_adapter.py +0 -0
  325. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_operational_security.py +0 -0
  326. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_policy.py +0 -0
  327. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_quality_artifact.py +0 -0
  328. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_rule_registry.py +0 -0
  329. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_scanner.py +0 -0
  330. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_schema_contracts.py +0 -0
  331. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_security.py +0 -0
  332. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_security_ops.py +0 -0
  333. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_skill_security.py +0 -0
  334. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_submission.py +0 -0
  335. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_trust_scoring.py +0 -0
  336. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_trust_specs.py +0 -0
  337. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_verification.py +0 -0
  338. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/tests/test_versioning.py +0 -0
  339. {plugin_scanner-2.0.91 → plugin_scanner-2.0.93}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.91
3
+ Version: 2.0.93
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
@@ -18,24 +18,31 @@ import {
18
18
  } from "./guard-demo";
19
19
 
20
20
  const GUARD_TOKEN_PARAM = "guard-token";
21
+ const GUARD_DAEMON_PARAM = "guardDaemon";
21
22
 
22
23
  async function readJson<T>(input: RequestInfo, init?: RequestInit): Promise<T> {
23
- const response = await fetch(input, init);
24
+ const response = await fetch(guardApiInput(input), withGuardAuth(init));
24
25
  if (!response.ok) {
25
26
  throw new Error(`Request failed with ${response.status}`);
26
27
  }
27
28
  return (await response.json()) as T;
28
29
  }
29
30
 
30
- function guardTokenFromHash(): string | null {
31
- const fragment = window.location.hash.startsWith("#")
32
- ? window.location.hash.slice(1)
33
- : window.location.hash;
34
- return new URLSearchParams(fragment).get(GUARD_TOKEN_PARAM);
31
+ function guardParams(): URLSearchParams {
32
+ const params = new URLSearchParams(window.location.search);
33
+ const fragment = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
34
+ for (const [key, value] of new URLSearchParams(fragment)) {
35
+ params.set(key, value);
36
+ }
37
+ return params;
38
+ }
39
+
40
+ function guardParam(name: string): string | null {
41
+ return guardParams().get(name);
35
42
  }
36
43
 
37
44
  function readGuardToken(): string | null {
38
- const guardToken = guardTokenFromHash();
45
+ const guardToken = guardParam(GUARD_TOKEN_PARAM);
39
46
  if (guardToken) {
40
47
  window.sessionStorage.setItem(GUARD_TOKEN_PARAM, guardToken);
41
48
  return guardToken;
@@ -43,6 +50,55 @@ function readGuardToken(): string | null {
43
50
  return window.sessionStorage.getItem(GUARD_TOKEN_PARAM);
44
51
  }
45
52
 
53
+ function readGuardDaemonOrigin(): string | null {
54
+ const rawDaemonUrl = guardParam(GUARD_DAEMON_PARAM);
55
+ if (rawDaemonUrl) {
56
+ const daemonOrigin = localGuardDaemonOrigin(rawDaemonUrl);
57
+ if (daemonOrigin) {
58
+ window.sessionStorage.setItem(GUARD_DAEMON_PARAM, daemonOrigin);
59
+ return daemonOrigin;
60
+ }
61
+ }
62
+ const storedDaemonUrl = window.sessionStorage.getItem(GUARD_DAEMON_PARAM);
63
+ return storedDaemonUrl ? localGuardDaemonOrigin(storedDaemonUrl) : null;
64
+ }
65
+
66
+ function localGuardDaemonOrigin(rawUrl: string): string | null {
67
+ try {
68
+ const url = new URL(rawUrl);
69
+ if (url.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]", "::1"].includes(url.hostname)) {
70
+ return null;
71
+ }
72
+ if (url.username || url.password || (url.pathname && url.pathname !== "/") || url.search || url.hash) {
73
+ return null;
74
+ }
75
+ return url.origin;
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ function guardApiInput(input: RequestInfo): RequestInfo {
82
+ const daemonOrigin = readGuardDaemonOrigin();
83
+ if (!daemonOrigin || typeof input !== "string" || !input.startsWith("/")) {
84
+ return input;
85
+ }
86
+ return `${daemonOrigin}${input}`;
87
+ }
88
+
89
+ function withGuardAuth(init?: RequestInit): RequestInit | undefined {
90
+ const guardToken = readGuardToken();
91
+ if (!guardToken) {
92
+ return init;
93
+ }
94
+ const headers = new Headers(init?.headers);
95
+ headers.set("X-Guard-Token", guardToken);
96
+ return {
97
+ ...init,
98
+ headers
99
+ };
100
+ }
101
+
46
102
  function guardAuthHeaders(): HeadersInit {
47
103
  const guardToken = readGuardToken();
48
104
  return guardToken ? { "X-Guard-Token": guardToken } : {};
@@ -59,7 +115,12 @@ export function guardAwareHref(href: string): string {
59
115
  return href;
60
116
  }
61
117
 
62
- url.hash = new URLSearchParams([[GUARD_TOKEN_PARAM, guardToken]]).toString();
118
+ const fragmentPairs = [[GUARD_TOKEN_PARAM, guardToken]];
119
+ const daemonOrigin = readGuardDaemonOrigin();
120
+ if (daemonOrigin) {
121
+ fragmentPairs.push([GUARD_DAEMON_PARAM, daemonOrigin]);
122
+ }
123
+ url.hash = new URLSearchParams(fragmentPairs).toString();
63
124
  if (href.startsWith("http://") || href.startsWith("https://")) {
64
125
  return url.toString();
65
126
  }
@@ -193,7 +254,7 @@ export async function fetchLatestReceipt(
193
254
  if (isGuardDemoMode()) {
194
255
  return getDemoReceipts().find((entry) => entry.artifact_id === artifactId) ?? null;
195
256
  }
196
- const response = await fetch(
257
+ const response = await fetchGuardApi(
197
258
  `/v1/receipts/latest?harness=${encodeURIComponent(harness)}&artifact_id=${encodeURIComponent(artifactId)}`
198
259
  );
199
260
  if (response.status === 404) {
@@ -252,7 +313,7 @@ export async function fetchDiff(
252
313
  if (isGuardDemoMode()) {
253
314
  return getDemoDiff(artifactId, harness);
254
315
  }
255
- const response = await fetch(
316
+ const response = await fetchGuardApi(
256
317
  `/v1/artifacts/${encodeURIComponent(artifactId)}/diff?harness=${encodeURIComponent(harness)}`
257
318
  );
258
319
  if (response.status === 404) {
@@ -264,6 +325,10 @@ export async function fetchDiff(
264
325
  return (await response.json()) as GuardArtifactDiff;
265
326
  }
266
327
 
328
+ function fetchGuardApi(input: RequestInfo, init?: RequestInit): Promise<Response> {
329
+ return fetch(guardApiInput(input), withGuardAuth(init));
330
+ }
331
+
267
332
  export async function resolveRequest(input: {
268
333
  requestId: string;
269
334
  action: "allow" | "block";
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.91"
7
+ version = "2.0.93"
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.91"
7
+ version = "2.0.93"
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"
@@ -86,6 +86,7 @@ from ..runtime.runner import (
86
86
  guard_run,
87
87
  prompt_requests_to_artifacts,
88
88
  sync_receipts,
89
+ sync_runtime_session,
89
90
  )
90
91
  from ..runtime.secret_file_requests import (
91
92
  build_file_read_request_artifact,
@@ -108,6 +109,9 @@ from .product import build_guard_start_payload, build_guard_status_payload
108
109
  from .update_commands import run_guard_update
109
110
 
110
111
  _GUARD_CLIENT_VERSION = "2.0.0"
112
+ _SERVICE_RUNTIME_PROFILE_STATE_KEY = "service_runtime_profile"
113
+ _SERVICE_RUNTIME_CHOICES = ("hermes", "openclaw", "custom")
114
+ _SERVICE_RUNTIME_SURFACE = "agent-sdk"
111
115
  _GUARD_HELP_GROUPS = (
112
116
  "Everyday protection:\n"
113
117
  " start First-run setup and the Guard operating loop\n"
@@ -121,6 +125,7 @@ _GUARD_HELP_GROUPS = (
121
125
  " connect Pair this machine to Guard Cloud\n"
122
126
  " login Compatibility alias for browser pairing\n"
123
127
  " sync Send local decisions to Guard Cloud\n"
128
+ " service Manage hosted-runtime Guard Cloud login and sync\n"
124
129
  " device Inspect or rotate this machine identity\n"
125
130
  " bridge Forward Guard signals to external channels\n"
126
131
  "\n"
@@ -437,6 +442,41 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
437
442
  sync_parser.add_argument("--guard-home")
438
443
  sync_parser.add_argument("--json", action="store_true")
439
444
 
445
+ service_parser = guard_subparsers.add_parser(
446
+ "service",
447
+ help="Manage headless hosted-runtime Guard Cloud login, sync, and status",
448
+ )
449
+ service_subparsers = service_parser.add_subparsers(
450
+ dest="service_command",
451
+ required=True,
452
+ parser_class=FriendlyArgumentParser,
453
+ )
454
+
455
+ service_login_parser = service_subparsers.add_parser(
456
+ "login",
457
+ help="Save a hosted-runtime Guard Cloud token and runtime profile",
458
+ )
459
+ _add_guard_common_args(service_login_parser)
460
+ service_login_parser.add_argument("--runtime", choices=_SERVICE_RUNTIME_CHOICES, required=True)
461
+ service_login_parser.add_argument("--label", required=True)
462
+ service_login_parser.add_argument("--sync-url", required=True, type=_guard_http_url)
463
+ service_login_parser.add_argument("--token", required=True)
464
+ service_login_parser.add_argument("--json", action="store_true")
465
+
466
+ service_sync_parser = service_subparsers.add_parser(
467
+ "sync",
468
+ help="Publish the hosted runtime session, then sync receipts",
469
+ )
470
+ _add_guard_common_args(service_sync_parser)
471
+ service_sync_parser.add_argument("--json", action="store_true")
472
+
473
+ service_status_parser = service_subparsers.add_parser(
474
+ "status",
475
+ help="Show hosted-runtime Guard Cloud login state and latest sync summaries",
476
+ )
477
+ _add_guard_common_args(service_status_parser)
478
+ service_status_parser.add_argument("--json", action="store_true")
479
+
440
480
  device_parser = guard_subparsers.add_parser("device", help="Manage local Guard installation identity")
441
481
  _add_guard_common_args(device_parser)
442
482
  device_parser.add_argument("--json", action="store_true")
@@ -1093,6 +1133,35 @@ def run_guard_command(
1093
1133
  _emit("sync", payload, getattr(args, "json", False))
1094
1134
  return 0
1095
1135
 
1136
+ if args.guard_command == "service":
1137
+ service_command = getattr(args, "service_command", None)
1138
+ if service_command == "login":
1139
+ payload, exit_code = _guard_service_login_payload(args=args, store=store)
1140
+ _emit("service-login", payload, getattr(args, "json", False))
1141
+ return exit_code
1142
+ if service_command == "sync":
1143
+ try:
1144
+ payload = _guard_service_sync_payload(store)
1145
+ except (GuardSyncNotConfiguredError, RuntimeError) as error:
1146
+ message = (
1147
+ _guard_service_sync_prerequisite_message()
1148
+ if isinstance(error, GuardSyncNotConfiguredError)
1149
+ else str(error)
1150
+ )
1151
+ if getattr(args, "json", False):
1152
+ _emit("service-sync", {"synced": False, "error": message}, True)
1153
+ else:
1154
+ print(message, file=sys.stderr)
1155
+ return 1
1156
+ _emit("service-sync", payload, getattr(args, "json", False))
1157
+ return 0
1158
+ if service_command == "status":
1159
+ payload = _guard_service_status_payload(store)
1160
+ _emit("service-status", payload, getattr(args, "json", False))
1161
+ return 0
1162
+ print("service subcommand is required", file=sys.stderr)
1163
+ return 2
1164
+
1096
1165
  if args.guard_command == "device":
1097
1166
  command = getattr(args, "device_command", None)
1098
1167
  now = _now()
@@ -5218,6 +5287,143 @@ def _manual_guard_login_payload(
5218
5287
  return {"logged_in": True, "sync_url": manual_sync_url}, 0
5219
5288
 
5220
5289
 
5290
+ def _guard_service_runtime_profile(
5291
+ store: GuardStore,
5292
+ ) -> dict[str, str] | None:
5293
+ payload = store.get_sync_payload(_SERVICE_RUNTIME_PROFILE_STATE_KEY)
5294
+ if not isinstance(payload, dict):
5295
+ return None
5296
+ runtime = _optional_string(payload.get("runtime"))
5297
+ label = _optional_string(payload.get("label"))
5298
+ surface = _optional_string(payload.get("surface"))
5299
+ client_name = _optional_string(payload.get("client_name"))
5300
+ client_title = _optional_string(payload.get("client_title"))
5301
+ client_version = _optional_string(payload.get("client_version"))
5302
+ if (
5303
+ runtime not in _SERVICE_RUNTIME_CHOICES
5304
+ or label is None
5305
+ or surface is None
5306
+ or client_name is None
5307
+ or client_title is None
5308
+ or client_version is None
5309
+ ):
5310
+ return None
5311
+ return {
5312
+ "runtime": runtime,
5313
+ "label": label,
5314
+ "workspace": _optional_string(payload.get("workspace")) or "",
5315
+ "surface": surface,
5316
+ "client_name": client_name,
5317
+ "client_title": client_title,
5318
+ "client_version": client_version,
5319
+ }
5320
+
5321
+
5322
+ def _guard_service_login_payload(
5323
+ *,
5324
+ args: argparse.Namespace,
5325
+ store: GuardStore,
5326
+ ) -> tuple[dict[str, object], int]:
5327
+ now = _now()
5328
+ label = str(args.label).strip()
5329
+ workspace = _optional_string(args.workspace) or ""
5330
+ sync_url = str(args.sync_url).strip()
5331
+ token = str(args.token).strip()
5332
+ if not token:
5333
+ return {
5334
+ "logged_in": False,
5335
+ "error": "Hosted Guard runtime token cannot be empty.",
5336
+ }, 2
5337
+ runtime = str(args.runtime)
5338
+ service_profile = {
5339
+ "runtime": runtime,
5340
+ "label": label,
5341
+ "workspace": workspace,
5342
+ "surface": _SERVICE_RUNTIME_SURFACE,
5343
+ "client_name": "hol-guard",
5344
+ "client_title": label,
5345
+ "client_version": _GUARD_CLIENT_VERSION,
5346
+ }
5347
+ store.set_sync_credentials(sync_url, token, now)
5348
+ store.set_sync_payload(_SERVICE_RUNTIME_PROFILE_STATE_KEY, service_profile, now)
5349
+ device = store.set_device_label(label, now)
5350
+ store.add_event(
5351
+ "service_sign_in",
5352
+ {
5353
+ "runtime": runtime,
5354
+ "label": label,
5355
+ "workspace": workspace or None,
5356
+ "sync_url": sync_url,
5357
+ "source": "hosted-runtime-cli",
5358
+ },
5359
+ now,
5360
+ )
5361
+ return {
5362
+ "logged_in": True,
5363
+ "sync_url": sync_url,
5364
+ "service": service_profile,
5365
+ "device": device,
5366
+ }, 0
5367
+
5368
+
5369
+ def _guard_service_sync_prerequisite_message() -> str:
5370
+ return (
5371
+ "Hosted Guard runtime is not configured yet. Run `hol-guard service login --runtime <runtime> "
5372
+ '--label "<label>" --sync-url "<url>" --token "<token>"` first.'
5373
+ )
5374
+
5375
+
5376
+ def _guard_service_status_payload(store: GuardStore) -> dict[str, object]:
5377
+ credentials = store.get_sync_credentials()
5378
+ service_profile = _guard_service_runtime_profile(store)
5379
+ return {
5380
+ "configured": credentials is not None and service_profile is not None,
5381
+ "connection": {
5382
+ "configured": credentials is not None,
5383
+ "sync_url": credentials["sync_url"] if credentials is not None else None,
5384
+ },
5385
+ "service": service_profile,
5386
+ "runtime": store.get_sync_payload("runtime_session_summary") or {},
5387
+ "receipts": store.get_sync_payload("sync_summary") or {},
5388
+ }
5389
+
5390
+
5391
+ def _guard_service_sync_payload(store: GuardStore) -> dict[str, object]:
5392
+ service_profile = _guard_service_runtime_profile(store)
5393
+ if service_profile is None:
5394
+ raise GuardSyncNotConfiguredError(_guard_service_sync_prerequisite_message())
5395
+ runtime_summary = sync_runtime_session(
5396
+ store,
5397
+ session={
5398
+ "harness": service_profile["runtime"],
5399
+ "surface": service_profile["surface"],
5400
+ "status": "active",
5401
+ "client_name": service_profile["client_name"],
5402
+ "client_title": service_profile["client_title"],
5403
+ "client_version": service_profile["client_version"],
5404
+ "workspace": service_profile["workspace"],
5405
+ "capabilities": ["hosted-runtime", "guard-cloud-sync"],
5406
+ },
5407
+ )
5408
+ receipts_summary = sync_receipts(store)
5409
+ store.add_event(
5410
+ "service_sync",
5411
+ {
5412
+ "runtime": service_profile["runtime"],
5413
+ "workspace": service_profile["workspace"] or None,
5414
+ "runtime_session_id": runtime_summary.get("runtime_session_id"),
5415
+ "synced_at": receipts_summary.get("synced_at"),
5416
+ },
5417
+ _now(),
5418
+ )
5419
+ return {
5420
+ "synced": True,
5421
+ "service": service_profile,
5422
+ "runtime": runtime_summary,
5423
+ "receipts": receipts_summary,
5424
+ }
5425
+
5426
+
5221
5427
  def _guard_sync_prerequisite_message() -> str:
5222
5428
  return (
5223
5429
  "Guard Cloud is not connected yet. Run `hol-guard connect` to sign in and pair this machine, "
@@ -54,6 +54,7 @@ _CLAUDE_HOOK_EXECUTION_LOCK = threading.Lock()
54
54
  _DEFAULT_GUARD_DAEMON_IDLE_TIMEOUT_SECONDS = 30 * 60
55
55
  _EPHEMERAL_GUARD_DAEMON_IDLE_TIMEOUT_SECONDS = 5
56
56
  _GUARD_DAEMON_IDLE_POLL_INTERVAL_SECONDS = 0.5
57
+ _HOSTED_GUARD_DASHBOARD_ORIGINS = frozenset({"https://hol.org", "https://www.hol.org"})
57
58
 
58
59
 
59
60
  class _GuardDaemonHandler(BaseHTTPRequestHandler):
@@ -64,21 +65,31 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
64
65
  if origin is None:
65
66
  self._write_empty(status=400)
66
67
  return
67
- if not self._origin_is_allowed():
68
+ headers = self._cors_headers_for_request(
69
+ allow_methods="GET, POST, OPTIONS",
70
+ allow_headers="Content-Type, X-Guard-Token",
71
+ )
72
+ if headers is None:
68
73
  self._write_empty(status=403)
69
74
  return
70
- self._write_empty(
71
- status=200,
72
- extra_headers=self._cors_headers(
73
- origin, allow_methods="GET, POST, OPTIONS", allow_headers="Content-Type, X-Guard-Token"
74
- ),
75
- )
75
+ self._write_empty(status=200, extra_headers=headers)
76
76
 
77
77
  def do_GET(self) -> None:
78
78
  store = self.server.store # type: ignore[attr-defined]
79
79
  parsed = urlparse(self.path)
80
80
  self._touch_runtime_heartbeat(parsed.path)
81
81
  path_parts = [part for part in parsed.path.split("/") if part]
82
+ if not self._origin_is_allowed_for_request(parsed.path, path_parts):
83
+ self._write_json({"error": "forbidden_origin"}, status=403)
84
+ return
85
+ if (
86
+ self._is_hosted_dashboard_origin()
87
+ and self._is_hosted_dashboard_api_path(parsed.path, path_parts)
88
+ and parsed.path != "/v1/connect/state"
89
+ and not self._header_token_is_valid()
90
+ ):
91
+ self._write_json({"error": "unauthorized"}, status=401)
92
+ return
82
93
  if parsed.path == "/healthz":
83
94
  self._write_json(
84
95
  {
@@ -204,16 +215,15 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
204
215
  def do_POST(self) -> None:
205
216
  parsed = urlparse(self.path)
206
217
  self._touch_runtime_heartbeat(parsed.path)
207
- if parsed.path != "/v1/connect/complete" and not self._origin_is_allowed():
218
+ path_parts = [part for part in parsed.path.split("/") if part]
219
+ if parsed.path != "/v1/connect/complete" and not self._origin_is_allowed_for_request(parsed.path, path_parts):
208
220
  self._write_json({"error": "forbidden_origin"}, status=403)
209
221
  return
210
- path_parts = [part for part in parsed.path.split("/") if part]
211
222
  if self._requires_header_token(parsed.path, path_parts) and not self._header_token_is_valid():
212
- origin = self._normalize_origin(self.headers.get("Origin"))
213
223
  self._write_json(
214
224
  {"error": "unauthorized"},
215
225
  status=401,
216
- extra_headers=self._cors_headers(origin) if origin is not None else None,
226
+ extra_headers=self._cors_headers_for_request(),
217
227
  )
218
228
  return
219
229
  payload, body_error = self._load_request_body()
@@ -801,7 +811,7 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
801
811
  finally:
802
812
  self._decrement_active_stream_clients()
803
813
 
804
- def _origin_is_allowed(self) -> bool:
814
+ def _origin_is_allowed_for_request(self, path: str, path_parts: list[str]) -> bool:
805
815
  origin = self.headers.get("Origin")
806
816
  if origin is None:
807
817
  return True
@@ -809,7 +819,36 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
809
819
  if normalized_origin is None:
810
820
  return False
811
821
  parsed = urlparse(normalized_origin)
812
- return parsed.hostname in {"127.0.0.1", "localhost", "::1"}
822
+ local_origin = parsed.hostname in {"127.0.0.1", "localhost", "::1"}
823
+ if local_origin:
824
+ return True
825
+ return normalized_origin in _HOSTED_GUARD_DASHBOARD_ORIGINS and self._is_hosted_dashboard_api_path(
826
+ path, path_parts
827
+ )
828
+
829
+ @staticmethod
830
+ def _is_hosted_dashboard_api_path(path: str, path_parts: list[str]) -> bool:
831
+ if path in {
832
+ "/v1/inventory",
833
+ "/v1/connect/state",
834
+ "/v1/policy",
835
+ "/v1/policy/clear",
836
+ "/v1/receipts",
837
+ "/v1/receipts/latest",
838
+ "/v1/requests",
839
+ "/v1/runtime",
840
+ "/v1/settings",
841
+ }:
842
+ return True
843
+ if len(path_parts) == 3 and path_parts[:2] in (["v1", "requests"], ["v1", "receipts"]):
844
+ return True
845
+ if len(path_parts) == 4 and path_parts[:2] == ["v1", "requests"] and path_parts[3] in {"approve", "block"}:
846
+ return True
847
+ return len(path_parts) == 4 and path_parts[:2] == ["v1", "artifacts"] and path_parts[3] == "diff"
848
+
849
+ def _is_hosted_dashboard_origin(self) -> bool:
850
+ origin = self._normalize_origin(self.headers.get("Origin"))
851
+ return origin in _HOSTED_GUARD_DASHBOARD_ORIGINS
813
852
 
814
853
  @staticmethod
815
854
  def _normalize_origin(origin: str | None) -> str | None:
@@ -852,6 +891,19 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
852
891
  "Vary": "Origin",
853
892
  }
854
893
 
894
+ def _cors_headers_for_request(
895
+ self,
896
+ *,
897
+ allow_methods: str = "POST, OPTIONS",
898
+ allow_headers: str = "Content-Type, X-Guard-Token",
899
+ ) -> dict[str, str] | None:
900
+ parsed = urlparse(self.path)
901
+ path_parts = [part for part in parsed.path.split("/") if part]
902
+ origin = self._normalize_origin(self.headers.get("Origin"))
903
+ if origin is None or not self._origin_is_allowed_for_request(parsed.path, path_parts):
904
+ return None
905
+ return self._cors_headers(origin, allow_methods=allow_methods, allow_headers=allow_headers)
906
+
855
907
  def _handle_policy_upsert(self, payload: dict[str, object]) -> None:
856
908
  harness = payload.get("harness")
857
909
  scope = payload.get("scope")
@@ -964,10 +1016,14 @@ class _GuardDaemonHandler(BaseHTTPRequestHandler):
964
1016
  extra_headers: dict[str, str] | None = None,
965
1017
  ) -> None:
966
1018
  body = json.dumps(payload).encode("utf-8")
1019
+ headers = dict(extra_headers or {})
1020
+ cors_headers = self._cors_headers_for_request(allow_methods="GET, POST, OPTIONS")
1021
+ if cors_headers is not None:
1022
+ headers = {**cors_headers, **headers}
967
1023
  self.send_response(status)
968
1024
  self.send_header("Content-Type", "application/json")
969
1025
  self.send_header("Content-Length", str(len(body)))
970
- for key, value in self._validated_headers(extra_headers).items():
1026
+ for key, value in self._validated_headers(headers).items():
971
1027
  self.send_header(key, value)
972
1028
  self.end_headers()
973
1029
  self.wfile.write(body)