plugin-scanner 2.0.79__tar.gz → 2.0.80__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 (333) hide show
  1. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/receipts-workspace.tsx +22 -19
  3. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/pyproject.toml +1 -1
  4. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/pyproject.toml.bak +1 -1
  5. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/commands.py +375 -1
  6. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +3 -3
  7. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/version.py +1 -1
  8. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_runtime.py +988 -0
  9. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.clusterfuzzlite/Dockerfile +0 -0
  10. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.clusterfuzzlite/build.sh +0 -0
  11. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.clusterfuzzlite/project.yaml +0 -0
  12. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  13. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.dockerignore +0 -0
  14. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/CODEOWNERS +0 -0
  15. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  16. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  17. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  18. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/dependabot.yml +0 -0
  19. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/workflows/ci.yml +0 -0
  20. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/workflows/codeql.yml +0 -0
  21. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/workflows/dependabot-uv-lock.yml +0 -0
  22. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/workflows/fuzz.yml +0 -0
  23. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/workflows/harness-smoke.yml +0 -0
  24. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/workflows/publish.yml +0 -0
  25. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.github/workflows/scorecard.yml +0 -0
  26. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.gitignore +0 -0
  27. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/.pre-commit-hooks.yaml +0 -0
  28. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/CONTRIBUTING.md +0 -0
  29. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/Dockerfile +0 -0
  30. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/LICENSE +0 -0
  31. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/README.md +0 -0
  32. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/SECURITY.md +0 -0
  33. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/index.html +0 -0
  34. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/package.json +0 -0
  35. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/pnpm-lock.yaml +0 -0
  36. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/public/apple-touch-icon.png +0 -0
  37. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  38. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/public/brand/Logo_Whole.png +0 -0
  39. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/public/favicon-16x16.png +0 -0
  40. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/public/favicon-32x32.png +0 -0
  41. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/public/favicon.ico +0 -0
  42. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/app.tsx +0 -0
  43. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/approval-center-layout.tsx +0 -0
  44. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/approval-center-primitives.tsx +0 -0
  45. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/approval-center-utils.ts +0 -0
  46. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/fleet-workspace.tsx +0 -0
  47. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/guard-api.ts +0 -0
  48. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/guard-demo.ts +0 -0
  49. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/guard-types.ts +0 -0
  50. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/main.tsx +0 -0
  51. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/runtime-overview.tsx +0 -0
  52. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/settings-workspace.tsx +0 -0
  53. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/styles.css +0 -0
  54. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/src/vite-env.d.ts +0 -0
  55. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/tsconfig.json +0 -0
  56. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/dashboard/vite.config.ts +0 -0
  57. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docker-requirements.txt +0 -0
  58. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/guard/approval-audit.md +0 -0
  59. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/guard/architecture.md +0 -0
  60. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/guard/get-started.md +0 -0
  61. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/guard/harness-support.md +0 -0
  62. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/guard/local-vs-cloud.md +0 -0
  63. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/guard/testing-matrix.md +0 -0
  64. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/trust/mcp-trust-draft.md +0 -0
  65. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/trust/plugin-trust-draft.md +0 -0
  66. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/docs/trust/skill-trust-local.md +0 -0
  67. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/fuzzers/manifest_fuzzer.py +0 -0
  68. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/requirements.txt +0 -0
  69. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/schemas/plugin-quality.v1.json +0 -0
  70. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/schemas/scan-result.v1.json +0 -0
  71. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/schemas/verify-result.v1.json +0 -0
  72. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/__init__.py +0 -0
  73. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/action_runner.py +0 -0
  74. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  75. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  76. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  77. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/claude.py +0 -0
  78. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  79. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  80. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  81. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  82. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  83. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  84. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  85. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  86. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  87. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/security.py +0 -0
  88. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  89. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/cli.py +0 -0
  90. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/cli_ui.py +0 -0
  91. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/config.py +0 -0
  92. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  93. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  94. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  95. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  96. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  97. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  98. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  99. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  100. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  101. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/github_reporting.py +0 -0
  102. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  103. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  104. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  105. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  106. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  107. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  108. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  109. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  110. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  111. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  112. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  113. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  114. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  115. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  116. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  117. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  118. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  119. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  120. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  121. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  122. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  123. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  124. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  125. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  126. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
  127. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  128. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  129. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/config.py +0 -0
  130. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  131. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  132. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  133. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  134. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  135. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  136. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  137. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  138. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  139. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  140. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  141. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  142. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  143. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  144. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  145. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/incident.py +0 -0
  146. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  147. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  148. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/models.py +0 -0
  149. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  150. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  151. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/protect.py +0 -0
  152. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  153. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  154. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  155. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  156. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  157. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  158. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  159. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/risk.py +0 -0
  160. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  161. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  162. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  163. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  164. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  165. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  166. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  167. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  168. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/shims.py +0 -0
  169. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/store.py +0 -0
  170. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  171. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  172. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/guard/types.py +0 -0
  173. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  174. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  175. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  176. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  177. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  178. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/models.py +0 -0
  179. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/path_support.py +0 -0
  180. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/policy.py +0 -0
  181. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  182. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/repo_detect.py +0 -0
  183. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/reporting.py +0 -0
  184. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  185. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/rules/registry.py +0 -0
  186. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/rules/specs.py +0 -0
  187. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/scanner.py +0 -0
  188. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/submission.py +0 -0
  189. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/suppressions.py +0 -0
  190. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  191. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  192. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  193. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_models.py +0 -0
  194. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  195. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  196. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  197. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/trust_specs.py +0 -0
  198. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/src/codex_plugin_scanner/verification.py +0 -0
  199. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/__init__.py +0 -0
  200. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/conftest.py +0 -0
  201. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/__init__.py +0 -0
  202. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  203. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  204. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/bad-plugin/secrets.js +0 -0
  205. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  206. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  207. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/claude-plugin-good/README.md +0 -0
  208. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  209. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  210. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  211. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/code-quality-bad/evil.js +0 -0
  212. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/code-quality-bad/inject.js +0 -0
  213. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  214. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  215. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/gemini-extension-good/README.md +0 -0
  216. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  217. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  218. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  219. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  220. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/.codexignore +0 -0
  221. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/LICENSE +0 -0
  222. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/README.md +0 -0
  223. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  224. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  225. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  226. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  227. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  228. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  229. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  230. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  231. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  232. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  233. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  234. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  235. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  236. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  237. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  238. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  239. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  240. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  241. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  242. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  243. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/mcp-canary-server.py +0 -0
  244. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  245. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  246. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/mit-license/LICENSE +0 -0
  247. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  248. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  249. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  250. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  251. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  252. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  253. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  254. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  255. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  256. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  257. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  258. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  259. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  260. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  261. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  262. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  263. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  264. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  265. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/opencode-good/LICENSE +0 -0
  266. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/opencode-good/README.md +0 -0
  267. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  268. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  269. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  270. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  271. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  272. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  273. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  274. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  275. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test-trust-scoring.py +0 -0
  276. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test-trust-specs.py +0 -0
  277. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_action_runner.py +0 -0
  278. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_best_practices.py +0 -0
  279. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_cisco_install_surfaces.py +0 -0
  280. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_cli.py +0 -0
  281. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_code_quality.py +0 -0
  282. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_config.py +0 -0
  283. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_coverage_remaining.py +0 -0
  284. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_ecosystems.py +0 -0
  285. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_edge_cases.py +0 -0
  286. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_final_coverage.py +0 -0
  287. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_approvals.py +0 -0
  288. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_bootstrap.py +0 -0
  289. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_capabilities.py +0 -0
  290. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_claude_adapter.py +0 -0
  291. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_cli.py +0 -0
  292. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_codex_e2e.py +0 -0
  293. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_codex_install.py +0 -0
  294. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_codex_proxy.py +0 -0
  295. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_config_paths.py +0 -0
  296. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_connect_flow.py +0 -0
  297. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_consumer_mode.py +0 -0
  298. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_copilot_adapter.py +0 -0
  299. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_copilot_proxy.py +0 -0
  300. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_daemon_manager.py +0 -0
  301. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_event_schema_v1.py +0 -0
  302. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_events.py +0 -0
  303. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_launch_env.py +0 -0
  304. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_opencode_proxy.py +0 -0
  305. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_product_flow.py +0 -0
  306. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_protect.py +0 -0
  307. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_render.py +0 -0
  308. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_risk.py +0 -0
  309. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_store_migrations.py +0 -0
  310. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_surface_server.py +0 -0
  311. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_guard_verdicts.py +0 -0
  312. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_hermes_adapter.py +0 -0
  313. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_integration.py +0 -0
  314. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_lint_fixes.py +0 -0
  315. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_live_cisco_smoke.py +0 -0
  316. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_manifest.py +0 -0
  317. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_marketplace.py +0 -0
  318. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_mcp_security.py +0 -0
  319. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_operational_security.py +0 -0
  320. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_policy.py +0 -0
  321. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_quality_artifact.py +0 -0
  322. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_rule_registry.py +0 -0
  323. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_scanner.py +0 -0
  324. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_schema_contracts.py +0 -0
  325. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_security.py +0 -0
  326. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_security_ops.py +0 -0
  327. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_skill_security.py +0 -0
  328. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_submission.py +0 -0
  329. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_trust_scoring.py +0 -0
  330. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_trust_specs.py +0 -0
  331. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_verification.py +0 -0
  332. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/tests/test_versioning.py +0 -0
  333. {plugin_scanner-2.0.79 → plugin_scanner-2.0.80}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.79
3
+ Version: 2.0.80
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
@@ -19,14 +19,32 @@ type ReceiptsState =
19
19
  | { kind: "ready"; items: GuardReceipt[] };
20
20
 
21
21
  const receiptPageSize = 8;
22
- const EMPTY_RECEIPTS: GuardReceipt[] = [];
23
22
 
24
23
  export function ReceiptsWorkspace(props: { receipts: ReceiptsState }) {
24
+ if (props.receipts.kind === "loading") {
25
+ return (
26
+ <div className="space-y-4">
27
+ <div className="guard-skeleton h-8 w-64" />
28
+ <div className="guard-skeleton h-32 w-full" />
29
+ </div>
30
+ );
31
+ }
32
+ if (props.receipts.kind === "error") {
33
+ return (
34
+ <Surface tone="danger">
35
+ <p className="text-sm text-brand-purple">{props.receipts.message}</p>
36
+ </Surface>
37
+ );
38
+ }
39
+ return <ReadyReceiptsWorkspace receiptItems={props.receipts.items} />;
40
+ }
41
+
42
+ function ReadyReceiptsWorkspace(props: { receiptItems: GuardReceipt[] }) {
25
43
  const [searchTerm, setSearchTerm] = useState("");
26
44
  const [harnessFilter, setHarnessFilter] = useState("all");
27
45
  const [decisionFilter, setDecisionFilter] = useState("all");
28
46
  const [page, setPage] = useState(1);
29
- const receiptCount = props.receipts.kind === "ready" ? props.receipts.items.length : 0;
47
+ const receiptCount = props.receiptItems.length;
30
48
 
31
49
  const handleSearchChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
32
50
  setSearchTerm(event.target.value);
@@ -48,7 +66,7 @@ export function ReceiptsWorkspace(props: { receipts: ReceiptsState }) {
48
66
  setPage(1);
49
67
  }, [decisionFilter, harnessFilter, searchTerm, receiptCount]);
50
68
 
51
- const receiptItems = props.receipts.kind === "ready" ? props.receipts.items : EMPTY_RECEIPTS;
69
+ const receiptItems = props.receiptItems;
52
70
 
53
71
  const harnesses = useMemo(
54
72
  () => Array.from(new Set(receiptItems.map((receipt) => receipt.harness))).sort(),
@@ -94,21 +112,6 @@ export function ReceiptsWorkspace(props: { receipts: ReceiptsState }) {
94
112
  setPage((value) => Math.min(totalPages, value + 1));
95
113
  }, [totalPages]);
96
114
 
97
- if (props.receipts.kind === "loading") {
98
- return (
99
- <div className="space-y-4">
100
- <div className="guard-skeleton h-8 w-64" />
101
- <div className="guard-skeleton h-32 w-full" />
102
- </div>
103
- );
104
- }
105
- if (props.receipts.kind === "error") {
106
- return (
107
- <Surface tone="danger">
108
- <p className="text-sm text-brand-purple">{props.receipts.message}</p>
109
- </Surface>
110
- );
111
- }
112
115
  if (receiptItems.length === 0) {
113
116
  return (
114
117
  <EmptyState
@@ -132,7 +135,7 @@ export function ReceiptsWorkspace(props: { receipts: ReceiptsState }) {
132
135
  </p>
133
136
  </div>
134
137
  <div className="flex flex-wrap gap-2">
135
- <Badge tone="info">{props.receipts.items.length} saved</Badge>
138
+ <Badge tone="info">{props.receiptItems.length} saved</Badge>
136
139
  <Tag tone="slate">{filteredReceipts.length} shown</Tag>
137
140
  </div>
138
141
  </div>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.79"
7
+ version = "2.0.80"
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.79"
7
+ version = "2.0.80"
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"
@@ -3604,6 +3604,7 @@ def _merged_prompt_runtime_artifact(harness: str, artifacts: list[GuardArtifact]
3604
3604
  prompt_signals: list[str] = []
3605
3605
  prompt_matched_texts: list[str] = []
3606
3606
  prompt_request_classes: list[str] = []
3607
+ prompt_display_texts: list[str] = []
3607
3608
  request_identity = "|".join(sorted(artifact.artifact_id for artifact in artifacts))
3608
3609
  for artifact in artifacts:
3609
3610
  metadata = artifact.metadata
@@ -3611,12 +3612,19 @@ def _merged_prompt_runtime_artifact(harness: str, artifacts: list[GuardArtifact]
3611
3612
  matched_text = metadata.get("prompt_matched_text")
3612
3613
  if isinstance(matched_text, str) and matched_text.strip():
3613
3614
  prompt_matched_texts.append(matched_text.strip())
3615
+ display_text = metadata.get("prompt_display_text")
3616
+ if isinstance(display_text, str) and display_text.strip():
3617
+ prompt_display_texts.append(display_text.strip())
3614
3618
  request_class = metadata.get("prompt_request_class")
3615
3619
  if isinstance(request_class, str) and request_class.strip():
3616
3620
  prompt_request_classes.append(request_class.strip())
3617
3621
  deduped_signals = list(dict.fromkeys(prompt_signals))
3618
3622
  deduped_matches = list(dict.fromkeys(prompt_matched_texts))
3619
3623
  deduped_classes = list(dict.fromkeys(prompt_request_classes))
3624
+ deduped_display = list(dict.fromkeys(prompt_display_texts))
3625
+ request_summary = (
3626
+ deduped_display[0] if len(deduped_display) == 1 else "Prompt matches multiple guarded request classes."
3627
+ )
3620
3628
  return GuardArtifact(
3621
3629
  artifact_id=f"{harness}:session:prompt:multi:{hashlib.sha256(request_identity.encode('utf-8')).hexdigest()[:24]}",
3622
3630
  name="prompt multi-signal request",
@@ -3628,7 +3636,10 @@ def _merged_prompt_runtime_artifact(harness: str, artifacts: list[GuardArtifact]
3628
3636
  "prompt_signals": deduped_signals,
3629
3637
  "prompt_summary": "Prompt matches multiple guarded request classes.",
3630
3638
  "prompt_matched_texts": deduped_matches,
3639
+ "prompt_display_text": request_summary,
3631
3640
  "prompt_request_classes": deduped_classes,
3641
+ "request_summary": request_summary,
3642
+ "runtime_request_summary": request_summary,
3632
3643
  },
3633
3644
  )
3634
3645
 
@@ -3648,6 +3659,7 @@ def _hook_runtime_artifact(
3648
3659
  payload=payload,
3649
3660
  config_path=str(_runtime_policy_path(harness, home_dir, workspace)),
3650
3661
  source_scope=_coalesce_string(payload.get("source_scope"), "project"),
3662
+ cwd=workspace,
3651
3663
  )
3652
3664
  if output_artifact is not None:
3653
3665
  return output_artifact
@@ -3675,6 +3687,11 @@ def _hook_runtime_artifact(
3675
3687
  requests=prompt_requests,
3676
3688
  )
3677
3689
  if prompt_artifacts:
3690
+ if harness == "codex":
3691
+ prompt_artifacts = [
3692
+ _with_codex_prompt_display_metadata(artifact, prompt_text=prompt_text)
3693
+ for artifact in prompt_artifacts
3694
+ ]
3678
3695
  return _merged_prompt_runtime_artifact(harness, prompt_artifacts)
3679
3696
  prompt_file_artifact = _codex_prompt_credential_file_artifact(
3680
3697
  prompt_text=prompt_text,
@@ -3718,6 +3735,7 @@ _CODEX_SECRET_OUTPUT_PATTERN = re.compile(
3718
3735
  r"(?i)(?:fake[_-]?credential|fake[_-]?secret|"
3719
3736
  r"(?:api[_-]?key|auth[_-]?token|credential|npm[_-]?token|private[_-]?key|secret|token|password)\s*[:=])"
3720
3737
  )
3738
+ _CODEX_PROMPT_SECRET_KEY_MARKERS = ("TOKEN", "SECRET", "PASSWORD", "PASS", "API_KEY", "API-KEY", "AUTH", "CREDENTIAL")
3721
3739
  _CODEX_TOOL_RESPONSE_MAX_DEPTH = 5
3722
3740
  _CODEX_TOOL_RESPONSE_TEXT_LIMIT = 20000
3723
3741
  _CODEX_PROMPT_FILE_FINGERPRINT_LENGTH = 24
@@ -3728,6 +3746,7 @@ def _codex_post_tool_output_artifact(
3728
3746
  payload: dict[str, object],
3729
3747
  config_path: str,
3730
3748
  source_scope: str,
3749
+ cwd: Path | None,
3731
3750
  ) -> GuardArtifact | None:
3732
3751
  response_text = _collect_codex_tool_response_text(payload.get("tool_response"))
3733
3752
  if not response_text or _CODEX_SECRET_OUTPUT_PATTERN.search(response_text) is None:
@@ -3741,6 +3760,8 @@ def _codex_post_tool_output_artifact(
3741
3760
  command_text = command.strip()
3742
3761
  if not command_text:
3743
3762
  command_text = tool_name
3763
+ if _codex_command_is_read_only_source_search(command_text, cwd=cwd):
3764
+ return None
3744
3765
  fingerprint = hashlib.sha256(
3745
3766
  json.dumps(
3746
3767
  {
@@ -3777,6 +3798,290 @@ def _codex_post_tool_output_artifact(
3777
3798
  )
3778
3799
 
3779
3800
 
3801
+ _CODEX_READ_ONLY_SEARCH_COMMANDS = frozenset({"rg", "grep", "egrep", "fgrep"})
3802
+ _CODEX_READ_ONLY_SEARCH_WRAPPERS = frozenset({"bash", "sh", "zsh"})
3803
+ _CODEX_SEARCH_PATTERN_VALUE_FLAGS = frozenset({"-e", "--regexp", "-f", "--file"})
3804
+ _CODEX_SEARCH_OPTION_VALUE_FLAGS = frozenset(
3805
+ {
3806
+ *_CODEX_SEARCH_PATTERN_VALUE_FLAGS,
3807
+ "-g",
3808
+ "--glob",
3809
+ "--iglob",
3810
+ "--max-depth",
3811
+ "--type",
3812
+ "-t",
3813
+ "--type-not",
3814
+ }
3815
+ )
3816
+ _CODEX_SEARCH_OPTION_VALUE_FLAGS_BY_EXECUTABLE = {
3817
+ "rg": frozenset({"-T"}),
3818
+ }
3819
+ _CODEX_SEARCH_UNSAFE_FLAGS = frozenset({"--pre"})
3820
+ _CODEX_GIT_GLOBAL_VALUE_FLAGS = frozenset(
3821
+ {"-c", "--config-env", "--exec-path", "--git-dir", "--work-tree", "--namespace"}
3822
+ )
3823
+ _CODEX_SOURCE_SEARCH_PREFIXES = (
3824
+ "src/",
3825
+ "app/",
3826
+ "lib/",
3827
+ "tests/",
3828
+ "__tests__/",
3829
+ "workers/",
3830
+ "scripts/",
3831
+ "dashboard/",
3832
+ "packages/",
3833
+ )
3834
+ _CODEX_SOURCE_SEARCH_EXTENSIONS = frozenset(
3835
+ {
3836
+ ".c",
3837
+ ".cc",
3838
+ ".cpp",
3839
+ ".css",
3840
+ ".go",
3841
+ ".h",
3842
+ ".hpp",
3843
+ ".html",
3844
+ ".java",
3845
+ ".js",
3846
+ ".jsx",
3847
+ ".json",
3848
+ ".md",
3849
+ ".mjs",
3850
+ ".py",
3851
+ ".rs",
3852
+ ".sh",
3853
+ ".toml",
3854
+ ".ts",
3855
+ ".tsx",
3856
+ ".yaml",
3857
+ ".yml",
3858
+ }
3859
+ )
3860
+ _CODEX_SENSITIVE_SEARCH_BASENAMES = frozenset(
3861
+ {
3862
+ ".aws",
3863
+ ".docker",
3864
+ ".env",
3865
+ ".git-credentials",
3866
+ ".kube",
3867
+ ".netrc",
3868
+ ".npmrc",
3869
+ ".pypirc",
3870
+ ".ssh",
3871
+ "credentials",
3872
+ "id_rsa",
3873
+ }
3874
+ )
3875
+
3876
+
3877
+ def _codex_command_is_read_only_source_search(command_text: str, *, cwd: Path | None) -> bool:
3878
+ command = command_text.strip()
3879
+ if not command:
3880
+ return False
3881
+ if _codex_command_has_unquoted_shell_control(command):
3882
+ return False
3883
+ try:
3884
+ parts = shlex.split(command)
3885
+ except ValueError:
3886
+ return False
3887
+ if not parts:
3888
+ return False
3889
+ if _codex_command_uses_untrusted_search_binary(parts[0]):
3890
+ return False
3891
+ executable = Path(parts[0]).name
3892
+ if executable in _CODEX_READ_ONLY_SEARCH_COMMANDS:
3893
+ if executable == "rg" and "--no-config" not in parts and os.environ.get("RIPGREP_CONFIG_PATH"):
3894
+ return False
3895
+ return _codex_search_targets_are_source_like(parts[1:], cwd=cwd, executable=executable)
3896
+ git_grep_args = _git_grep_search_args(parts[1:]) if executable == "git" else None
3897
+ if git_grep_args is not None:
3898
+ if _git_grep_uses_external_execution(git_grep_args):
3899
+ return False
3900
+ return _codex_search_targets_are_source_like(git_grep_args, cwd=cwd, executable=executable)
3901
+ script_index = _shell_wrapper_script_index(parts) if executable in _CODEX_READ_ONLY_SEARCH_WRAPPERS else None
3902
+ if script_index is not None and script_index < len(parts):
3903
+ return _codex_command_is_read_only_source_search(parts[script_index], cwd=cwd)
3904
+ return False
3905
+
3906
+
3907
+ def _codex_command_uses_untrusted_search_binary(executable_token: str) -> bool:
3908
+ return executable_token.startswith(".") or "/" in executable_token or "\\" in executable_token
3909
+
3910
+
3911
+ def _git_grep_search_args(args: list[str]) -> list[str] | None:
3912
+ index = 0
3913
+ while index < len(args):
3914
+ arg = args[index]
3915
+ if arg == "grep":
3916
+ return args[index + 1 :]
3917
+ if arg in _CODEX_GIT_GLOBAL_VALUE_FLAGS:
3918
+ index += 2
3919
+ continue
3920
+ if any(arg.startswith(f"{flag}=") for flag in _CODEX_GIT_GLOBAL_VALUE_FLAGS):
3921
+ index += 1
3922
+ continue
3923
+ if arg in {
3924
+ "--no-pager",
3925
+ "--bare",
3926
+ "--literal-pathspecs",
3927
+ "--no-literal-pathspecs",
3928
+ "--glob-pathspecs",
3929
+ "--noglob-pathspecs",
3930
+ }:
3931
+ index += 1
3932
+ continue
3933
+ return None
3934
+ return None
3935
+
3936
+
3937
+ def _git_grep_uses_external_execution(args: list[str]) -> bool:
3938
+ return any(
3939
+ arg == "-O"
3940
+ or (arg.startswith("-O") and len(arg) > 2)
3941
+ or arg == "--open-files-in-pager"
3942
+ or arg.startswith("--open-files-in-pager=")
3943
+ or arg in {"--textconv", "--ext-grep"}
3944
+ for arg in args
3945
+ )
3946
+
3947
+
3948
+ def _shell_wrapper_script_index(parts: list[str]) -> int | None:
3949
+ for index, arg in enumerate(parts[1:], start=1):
3950
+ if arg == "-c":
3951
+ return index + 1
3952
+ if arg.startswith("-") and not arg.startswith("--") and "c" in arg[1:]:
3953
+ return index + 1
3954
+ return None
3955
+
3956
+
3957
+ def _codex_command_has_unquoted_shell_control(command: str) -> bool:
3958
+ quote: str | None = None
3959
+ escaped = False
3960
+ for index, char in enumerate(command):
3961
+ if escaped:
3962
+ escaped = False
3963
+ continue
3964
+ if char == "\\":
3965
+ escaped = True
3966
+ continue
3967
+ if quote is not None:
3968
+ if char == quote:
3969
+ quote = None
3970
+ if quote == '"' and char == "`":
3971
+ return True
3972
+ if quote == '"' and char == "$" and index + 1 < len(command) and command[index + 1] == "(":
3973
+ return True
3974
+ continue
3975
+ if char in {"'", '"'}:
3976
+ quote = char
3977
+ continue
3978
+ if char in {"\n", "\r"}:
3979
+ return True
3980
+ if char in {"|", "&", ";", ">", "<", "`"}:
3981
+ return True
3982
+ if char == "$" and index + 1 < len(command) and command[index + 1] == "(":
3983
+ return True
3984
+ return False
3985
+
3986
+
3987
+ def _codex_search_targets_are_source_like(args: list[str], *, cwd: Path | None, executable: str) -> bool:
3988
+ positional: list[str] = []
3989
+ skip_next = False
3990
+ pattern_from_option = False
3991
+ option_value_flags = _CODEX_SEARCH_OPTION_VALUE_FLAGS | _CODEX_SEARCH_OPTION_VALUE_FLAGS_BY_EXECUTABLE.get(
3992
+ executable, frozenset()
3993
+ )
3994
+ for arg in args:
3995
+ if skip_next:
3996
+ skip_next = False
3997
+ continue
3998
+ if arg in _CODEX_SEARCH_UNSAFE_FLAGS or any(arg.startswith(f"{flag}=") for flag in _CODEX_SEARCH_UNSAFE_FLAGS):
3999
+ return False
4000
+ if arg in _CODEX_SEARCH_PATTERN_VALUE_FLAGS:
4001
+ pattern_from_option = True
4002
+ skip_next = True
4003
+ continue
4004
+ if any(arg.startswith(flag) and len(arg) > len(flag) for flag in ("-e", "-f")):
4005
+ pattern_from_option = True
4006
+ continue
4007
+ if arg in option_value_flags:
4008
+ skip_next = True
4009
+ continue
4010
+ if any(arg.startswith(f"{flag}=") for flag in _CODEX_SEARCH_PATTERN_VALUE_FLAGS):
4011
+ pattern_from_option = True
4012
+ continue
4013
+ if any(arg.startswith(f"{flag}=") for flag in option_value_flags):
4014
+ continue
4015
+ if arg == "--":
4016
+ continue
4017
+ if arg.startswith("-"):
4018
+ continue
4019
+ positional.append(arg)
4020
+ if pattern_from_option:
4021
+ targets = positional
4022
+ elif len(positional) >= 2:
4023
+ targets = positional[1:]
4024
+ else:
4025
+ return False
4026
+ return bool(targets) and all(_codex_search_target_is_source_like(target, cwd=cwd) for target in targets)
4027
+
4028
+
4029
+ def _codex_search_target_is_source_like(target: str, *, cwd: Path | None) -> bool:
4030
+ stripped = target.strip().strip("'\"")
4031
+ if not stripped:
4032
+ return False
4033
+ if stripped.startswith(("~", "/")):
4034
+ return False
4035
+ target_path = Path(stripped)
4036
+ base_dir = (cwd or Path.cwd()).resolve()
4037
+ unresolved_candidate = base_dir / target_path
4038
+ if _path_contains_symlink(unresolved_candidate, base_dir=base_dir):
4039
+ return False
4040
+ try:
4041
+ candidate = unresolved_candidate.resolve(strict=False)
4042
+ except RuntimeError:
4043
+ return False
4044
+ if candidate.exists():
4045
+ try:
4046
+ relative_candidate = candidate.relative_to(base_dir)
4047
+ except ValueError:
4048
+ return False
4049
+ parts = [part for part in relative_candidate.parts if part not in {"", "."}]
4050
+ else:
4051
+ parts = [part for part in target_path.parts if part not in {"", "."}]
4052
+ if not parts:
4053
+ return False
4054
+ lowered_parts = [part.lower() for part in parts]
4055
+ if any(part in _CODEX_SENSITIVE_SEARCH_BASENAMES for part in lowered_parts):
4056
+ return False
4057
+ if any(part.startswith(".") for part in parts):
4058
+ return False
4059
+ normalized = "/".join(parts)
4060
+ if normalized in {prefix.rstrip("/") for prefix in _CODEX_SOURCE_SEARCH_PREFIXES}:
4061
+ return True
4062
+ if any(normalized.startswith(prefix) for prefix in _CODEX_SOURCE_SEARCH_PREFIXES):
4063
+ return True
4064
+ return Path(stripped).suffix.lower() in _CODEX_SOURCE_SEARCH_EXTENSIONS
4065
+
4066
+
4067
+ def _path_contains_symlink(path: Path, *, base_dir: Path) -> bool:
4068
+ candidate = base_dir
4069
+ try:
4070
+ relative_parts = path.relative_to(base_dir).parts
4071
+ except ValueError:
4072
+ return True
4073
+ for part in relative_parts:
4074
+ if part in {"", "."}:
4075
+ continue
4076
+ candidate /= part
4077
+ try:
4078
+ if candidate.is_symlink():
4079
+ return True
4080
+ except OSError:
4081
+ return True
4082
+ return False
4083
+
4084
+
3780
4085
  def _collect_codex_tool_response_text(value: object, *, depth: int = 0) -> str:
3781
4086
  if depth > _CODEX_TOOL_RESPONSE_MAX_DEPTH:
3782
4087
  return ""
@@ -3850,6 +4155,7 @@ def _codex_prompt_credential_file_artifact(
3850
4155
  sort_keys=True,
3851
4156
  ).encode("utf-8")
3852
4157
  ).hexdigest()[:_CODEX_PROMPT_FILE_FINGERPRINT_LENGTH]
4158
+ prompt_display = _codex_prompt_display_text(prompt_text, requested_path=requested_path)
3853
4159
  return GuardArtifact(
3854
4160
  artifact_id=f"codex:project:prompt-file:{fingerprint}",
3855
4161
  name=f"credential-looking local file {path.name}",
@@ -3861,9 +4167,11 @@ def _codex_prompt_credential_file_artifact(
3861
4167
  "prompt_signals": ["requested file content contains credential-looking material"],
3862
4168
  "prompt_summary": "Prompt asks Codex to read a credential-looking local file.",
3863
4169
  "prompt_matched_text": requested_path,
4170
+ "prompt_display_text": prompt_display,
3864
4171
  "prompt_request_class": "secret_read",
3865
4172
  "prompt_request_classes": ["secret_read"],
3866
- "runtime_request_summary": "Prompt requests direct access to a credential-looking local file.",
4173
+ "request_summary": prompt_display,
4174
+ "runtime_request_summary": prompt_display,
3867
4175
  "runtime_request_reason": (
3868
4176
  "Guard scanned a small local dotfile before Codex read it and found credential-looking text."
3869
4177
  ),
@@ -3873,6 +4181,72 @@ def _codex_prompt_credential_file_artifact(
3873
4181
  return None
3874
4182
 
3875
4183
 
4184
+ def _with_codex_prompt_display_metadata(artifact: GuardArtifact, *, prompt_text: str) -> GuardArtifact:
4185
+ matched_text = artifact.metadata.get("prompt_matched_text")
4186
+ display = _codex_prompt_display_text(
4187
+ prompt_text,
4188
+ requested_path=matched_text if isinstance(matched_text, str) else None,
4189
+ )
4190
+ metadata = {
4191
+ **artifact.metadata,
4192
+ "prompt_display_text": display,
4193
+ "request_summary": display,
4194
+ "runtime_request_summary": display,
4195
+ }
4196
+ return replace(artifact, metadata=metadata)
4197
+
4198
+
4199
+ def _codex_prompt_display_text(prompt_text: str, *, requested_path: str | None = None) -> str:
4200
+ sanitized_prompt = _sanitize_codex_display_text(prompt_text)
4201
+ path_suffix = ""
4202
+ if requested_path is not None and requested_path.strip():
4203
+ path_suffix = f" for `{_sanitize_codex_display_text(requested_path.strip())}`"
4204
+ return f"Codex prompt{path_suffix}: {_truncate_codex_display_text(sanitized_prompt, limit=320)}"
4205
+
4206
+
4207
+ def _sanitize_codex_display_text(value: str) -> str:
4208
+ collapsed = " ".join(value.strip().split())
4209
+ redacted = _redact_codex_prompt_secret_assignments(collapsed)
4210
+ sanitized = re.sub(r"/(?:Users|home)/[^/\s]+", "~", redacted)
4211
+ return re.sub(r"[A-Za-z]:\\Users\\[^\\\s]+", "~", sanitized)
4212
+
4213
+
4214
+ def _redact_codex_prompt_secret_assignments(value: str) -> str:
4215
+ output: list[str] = []
4216
+ index = 0
4217
+ while index < len(value):
4218
+ equals_index = value.find("=", index)
4219
+ if equals_index == -1:
4220
+ output.append(value[index:])
4221
+ break
4222
+ key_start = equals_index - 1
4223
+ while key_start >= index and value[key_start] not in {" ", "\t", "\n", "\r", ",", ";"}:
4224
+ key_start -= 1
4225
+ key_start += 1
4226
+ key = value[key_start:equals_index].strip()
4227
+ key_upper = key.upper()
4228
+ if key and any(marker in key_upper for marker in _CODEX_PROMPT_SECRET_KEY_MARKERS):
4229
+ value_start = equals_index + 1
4230
+ while value_start < len(value) and value[value_start].isspace():
4231
+ value_start += 1
4232
+ value_end = value_start
4233
+ while value_end < len(value) and value[value_end] not in {" ", "\t", "\n", "\r", ",", ";"}:
4234
+ value_end += 1
4235
+ output.append(value[index:value_start])
4236
+ output.append("[redacted]")
4237
+ index = value_end
4238
+ continue
4239
+ output.append(value[index : equals_index + 1])
4240
+ index = equals_index + 1
4241
+ return "".join(output)
4242
+
4243
+
4244
+ def _truncate_codex_display_text(value: str, *, limit: int) -> str:
4245
+ if len(value) <= limit:
4246
+ return value
4247
+ return f"{value[: limit - 1].rstrip()}…"
4248
+
4249
+
3876
4250
  def _resolve_prompt_scan_path(requested_path: str, *, cwd: Path | None) -> Path | None:
3877
4251
  stripped = requested_path.strip().strip("'\"").rstrip(".,;:!?)]}")
3878
4252
  if not stripped: