plugin-scanner 2.0.119__tar.gz → 2.0.121__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 (375) hide show
  1. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/PKG-INFO +1 -1
  2. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/pyproject.toml +1 -1
  3. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/pyproject.toml.bak +1 -1
  4. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/install_commands.py +55 -1
  5. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/render.py +64 -12
  6. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +301 -1
  7. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/actions.py +3 -0
  8. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/detectors.py +47 -1
  9. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/runner.py +1 -0
  10. plugin_scanner-2.0.121/src/codex_plugin_scanner/guard/runtime/skill_protection.py +439 -0
  11. plugin_scanner-2.0.121/src/codex_plugin_scanner/guard/runtime/supply_chain.py +448 -0
  12. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/version.py +1 -1
  13. plugin_scanner-2.0.121/tests/fixtures/supply-chain/benign-npm-package.json +9 -0
  14. plugin_scanner-2.0.121/tests/fixtures/supply-chain/benign-pnpm-package.json +8 -0
  15. plugin_scanner-2.0.121/tests/fixtures/supply-chain/benign-pyproject.toml +8 -0
  16. plugin_scanner-2.0.121/tests/fixtures/supply-chain/malicious-Dockerfile +4 -0
  17. plugin_scanner-2.0.121/tests/fixtures/supply-chain/malicious-action.yml +9 -0
  18. plugin_scanner-2.0.121/tests/fixtures/supply-chain/malicious-npm-package.json +7 -0
  19. plugin_scanner-2.0.121/tests/fixtures/supply-chain/malicious-setup.py +9 -0
  20. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_access_graph.py +2 -1
  21. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_codex_proxy.py +40 -1
  22. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_data_flow.py +1 -0
  23. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_mcp_protection.py +258 -1
  24. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_render.py +21 -0
  25. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_runtime_actions.py +2 -0
  26. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_runtime_detectors.py +24 -0
  27. plugin_scanner-2.0.121/tests/test_guard_skill_protection.py +431 -0
  28. plugin_scanner-2.0.121/tests/test_guard_supply_chain.py +187 -0
  29. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.clusterfuzzlite/Dockerfile +0 -0
  30. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.clusterfuzzlite/build.sh +0 -0
  31. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.clusterfuzzlite/project.yaml +0 -0
  32. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  33. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.dockerignore +0 -0
  34. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/CODEOWNERS +0 -0
  35. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  36. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  37. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  38. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/dependabot.yml +0 -0
  39. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/workflows/ci.yml +0 -0
  40. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/workflows/codeql.yml +0 -0
  41. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/workflows/dependabot-uv-lock.yml +0 -0
  42. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/workflows/fuzz.yml +0 -0
  43. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/workflows/harness-smoke.yml +0 -0
  44. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/workflows/publish.yml +0 -0
  45. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.github/workflows/scorecard.yml +0 -0
  46. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.gitignore +0 -0
  47. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/.pre-commit-hooks.yaml +0 -0
  48. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/CONTRIBUTING.md +0 -0
  49. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/Dockerfile +0 -0
  50. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/LICENSE +0 -0
  51. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/README.md +0 -0
  52. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/SECURITY.md +0 -0
  53. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/index.html +0 -0
  54. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/package.json +0 -0
  55. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/pnpm-lock.yaml +0 -0
  56. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/public/apple-touch-icon.png +0 -0
  57. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  58. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/public/brand/Logo_Whole.png +0 -0
  59. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/public/favicon-16x16.png +0 -0
  60. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/public/favicon-32x32.png +0 -0
  61. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/public/favicon.ico +0 -0
  62. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/app.tsx +0 -0
  63. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/approval-center-layout.tsx +0 -0
  64. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/approval-center-primitives.tsx +0 -0
  65. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/approval-center-utils.ts +0 -0
  66. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/data-flow-evidence-card.tsx +0 -0
  67. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/fleet-workspace.tsx +0 -0
  68. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/guard-api.test.ts +0 -0
  69. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/guard-api.ts +0 -0
  70. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/guard-demo.ts +0 -0
  71. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/guard-types.ts +0 -0
  72. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/main.tsx +0 -0
  73. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/receipts-workspace.tsx +0 -0
  74. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/runtime-overview.tsx +0 -0
  75. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/settings-workspace.tsx +0 -0
  76. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/styles.css +0 -0
  77. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/src/vite-env.d.ts +0 -0
  78. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/tsconfig.json +0 -0
  79. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/dashboard/vite.config.ts +0 -0
  80. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docker-requirements.txt +0 -0
  81. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/guard/approval-audit.md +0 -0
  82. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/guard/architecture.md +0 -0
  83. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/guard/get-started.md +0 -0
  84. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/guard/harness-support.md +0 -0
  85. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/guard/local-vs-cloud.md +0 -0
  86. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/guard/testing-matrix.md +0 -0
  87. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/trust/mcp-trust-draft.md +0 -0
  88. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/trust/plugin-trust-draft.md +0 -0
  89. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/docs/trust/skill-trust-local.md +0 -0
  90. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/fuzzers/manifest_fuzzer.py +0 -0
  91. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/requirements.txt +0 -0
  92. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/schemas/plugin-quality.v1.json +0 -0
  93. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/schemas/scan-result.v1.json +0 -0
  94. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/schemas/verify-result.v1.json +0 -0
  95. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/__init__.py +0 -0
  96. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/action_runner.py +0 -0
  97. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  98. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  99. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  100. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/claude.py +0 -0
  101. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  102. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  103. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  104. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  105. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  106. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  107. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  108. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  109. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  110. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/security.py +0 -0
  111. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  112. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/cli.py +0 -0
  113. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/cli_ui.py +0 -0
  114. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/config.py +0 -0
  115. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  116. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  117. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  118. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  119. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  120. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  121. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  122. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  123. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  124. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/github_reporting.py +0 -0
  125. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  126. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/access_graph_events.py +0 -0
  127. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  128. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  129. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  130. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  131. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
  132. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  133. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  134. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  135. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  136. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  137. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  138. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
  139. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
  140. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
  141. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  142. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  143. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
  144. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  145. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  146. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  147. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  148. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  149. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  150. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
  151. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  152. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  153. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  154. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  155. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  156. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/config.py +0 -0
  157. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  158. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  159. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  160. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  161. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  162. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  163. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  164. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  165. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  166. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  167. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  168. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  169. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  170. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  171. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  172. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  173. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/incident.py +0 -0
  174. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  175. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/models.py +0 -0
  176. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  177. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  178. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/protect.py +0 -0
  179. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  180. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  181. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  182. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  183. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  184. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  185. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/redaction.py +0 -0
  186. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/risk.py +0 -0
  187. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  188. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/data_flow.py +0 -0
  189. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/data_flow_rules.py +0 -0
  190. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/data_flow_variables.py +0 -0
  191. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
  192. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/mcp_protection.py +0 -0
  193. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/prompt_injection.py +0 -0
  194. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  195. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +0 -0
  196. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/secret_sources.py +0 -0
  197. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/shell_commands.py +0 -0
  198. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
  199. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
  200. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/runtime/temp_files.py +0 -0
  201. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  202. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  203. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  204. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  205. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/shims.py +0 -0
  206. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/store.py +0 -0
  207. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  208. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  209. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/guard/types.py +0 -0
  210. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  211. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  212. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  213. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  214. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  215. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/models.py +0 -0
  216. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/path_support.py +0 -0
  217. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/policy.py +0 -0
  218. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  219. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/repo_detect.py +0 -0
  220. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/reporting.py +0 -0
  221. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  222. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/rules/registry.py +0 -0
  223. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/rules/specs.py +0 -0
  224. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/scanner.py +0 -0
  225. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/submission.py +0 -0
  226. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/suppressions.py +0 -0
  227. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  228. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  229. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  230. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_models.py +0 -0
  231. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  232. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  233. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  234. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/trust_specs.py +0 -0
  235. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/src/codex_plugin_scanner/verification.py +0 -0
  236. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/__init__.py +0 -0
  237. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/conftest.py +0 -0
  238. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/__init__.py +0 -0
  239. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  240. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  241. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/bad-plugin/secrets.js +0 -0
  242. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  243. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  244. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/claude-plugin-good/README.md +0 -0
  245. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  246. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  247. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  248. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/code-quality-bad/evil.js +0 -0
  249. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/code-quality-bad/inject.js +0 -0
  250. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  251. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  252. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/gemini-extension-good/README.md +0 -0
  253. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  254. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  255. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  256. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  257. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/.codexignore +0 -0
  258. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/LICENSE +0 -0
  259. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/README.md +0 -0
  260. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  261. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  262. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  263. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  264. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  265. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  266. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  267. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  268. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  269. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  270. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  271. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  272. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  273. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  274. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  275. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  276. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  277. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  278. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  279. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  280. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/mcp-canary-server.py +0 -0
  281. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  282. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  283. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/mit-license/LICENSE +0 -0
  284. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  285. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  286. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  287. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  288. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  289. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  290. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  291. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  292. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  293. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  294. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  295. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  296. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  297. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  298. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  299. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  300. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  301. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  302. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/opencode-good/LICENSE +0 -0
  303. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/opencode-good/README.md +0 -0
  304. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  305. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  306. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  307. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  308. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  309. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  310. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  311. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  312. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test-trust-scoring.py +0 -0
  313. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test-trust-specs.py +0 -0
  314. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_action_runner.py +0 -0
  315. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_best_practices.py +0 -0
  316. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_cisco_install_surfaces.py +0 -0
  317. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_cli.py +0 -0
  318. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_code_quality.py +0 -0
  319. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_config.py +0 -0
  320. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_coverage_remaining.py +0 -0
  321. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_ecosystems.py +0 -0
  322. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_edge_cases.py +0 -0
  323. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_final_coverage.py +0 -0
  324. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_approvals.py +0 -0
  325. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_bootstrap.py +0 -0
  326. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_capabilities.py +0 -0
  327. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_claude_adapter.py +0 -0
  328. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_cli.py +0 -0
  329. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_cloud_local_sync.py +0 -0
  330. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_codex_e2e.py +0 -0
  331. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_codex_install.py +0 -0
  332. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_config_paths.py +0 -0
  333. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_connect_flow.py +0 -0
  334. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_consumer_mode.py +0 -0
  335. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_copilot_adapter.py +0 -0
  336. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_copilot_proxy.py +0 -0
  337. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_daemon_manager.py +0 -0
  338. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_event_schema_v1.py +0 -0
  339. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_events.py +0 -0
  340. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_launch_env.py +0 -0
  341. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_opencode_proxy.py +0 -0
  342. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_product_flow.py +0 -0
  343. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_prompt_injection.py +0 -0
  344. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_protect.py +0 -0
  345. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_risk.py +0 -0
  346. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_runtime.py +0 -0
  347. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_runtime_action_harnesses.py +0 -0
  348. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_runtime_decisions.py +0 -0
  349. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_runtime_signals.py +0 -0
  350. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_store_migrations.py +0 -0
  351. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_surface_server.py +0 -0
  352. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_guard_verdicts.py +0 -0
  353. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_hermes_adapter.py +0 -0
  354. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_integration.py +0 -0
  355. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_lint_fixes.py +0 -0
  356. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_live_cisco_smoke.py +0 -0
  357. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_manifest.py +0 -0
  358. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_marketplace.py +0 -0
  359. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_mcp_security.py +0 -0
  360. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_openclaw_adapter.py +0 -0
  361. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_operational_security.py +0 -0
  362. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_policy.py +0 -0
  363. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_quality_artifact.py +0 -0
  364. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_rule_registry.py +0 -0
  365. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_scanner.py +0 -0
  366. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_schema_contracts.py +0 -0
  367. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_security.py +0 -0
  368. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_security_ops.py +0 -0
  369. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_skill_security.py +0 -0
  370. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_submission.py +0 -0
  371. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_trust_scoring.py +0 -0
  372. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_trust_specs.py +0 -0
  373. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_verification.py +0 -0
  374. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/tests/test_versioning.py +0 -0
  375. {plugin_scanner-2.0.119 → plugin_scanner-2.0.121}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.119
3
+ Version: 2.0.121
4
4
  Summary: Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows.
5
5
  Project-URL: Homepage, https://github.com/hashgraph-online/ai-plugin-scanner
6
6
  Project-URL: Repository, https://github.com/hashgraph-online/ai-plugin-scanner
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.119"
7
+ version = "2.0.121"
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.119"
7
+ version = "2.0.121"
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"
@@ -2,9 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from pathlib import Path
6
+
5
7
  from ..adapters import get_adapter
6
8
  from ..adapters.base import HarnessContext
7
9
  from ..consumer import detect_all
10
+ from ..runtime.skill_protection import build_skill_identity, detect_skill_content_risk
8
11
  from ..store import GuardStore
9
12
 
10
13
 
@@ -34,9 +37,60 @@ def apply_managed_install(
34
37
  }
35
38
  if len(managed_installs) == 1:
36
39
  payload["managed_install"] = managed_installs[0]
40
+ if active and context.workspace_dir is not None:
41
+ skill_scan = scan_workspace_skills(context.workspace_dir, store, now)
42
+ if skill_scan:
43
+ payload["skill_scan"] = skill_scan
37
44
  return payload
38
45
 
39
46
 
47
+ def scan_workspace_skills(
48
+ workspace_dir: Path,
49
+ store: GuardStore,
50
+ now: str,
51
+ ) -> list[dict[str, object]]:
52
+ """Scan SKILL.md files in workspace and return risk summaries for any findings."""
53
+ results: list[dict[str, object]] = []
54
+ skills_dirs = [
55
+ workspace_dir / ".codex" / "skills",
56
+ workspace_dir / ".agents" / "skills",
57
+ workspace_dir / "skills",
58
+ ]
59
+ for skills_dir in skills_dirs:
60
+ if not skills_dir.is_dir():
61
+ continue
62
+ for skill_path in sorted(skills_dir.rglob("SKILL.md")):
63
+ try:
64
+ content = skill_path.read_text(encoding="utf-8", errors="replace")
65
+ except OSError:
66
+ continue
67
+ identity = build_skill_identity(content, skill_path=str(skill_path))
68
+ artifact_id = f"skill-path:{skill_path}"
69
+ stored = store.get_snapshot("skill_scan", artifact_id)
70
+ stored_hash = stored.get("identity_hash") if stored else None
71
+ if stored_hash == identity.identity_hash:
72
+ continue
73
+ signals = detect_skill_content_risk(content, skill_path=str(skill_path))
74
+ store.save_snapshot(
75
+ "skill_scan",
76
+ artifact_id,
77
+ {"identity_hash": identity.identity_hash, "skill_path": str(skill_path)},
78
+ identity.identity_hash,
79
+ now,
80
+ )
81
+ if signals:
82
+ results.append(
83
+ {
84
+ "skill_path": str(skill_path.relative_to(workspace_dir)),
85
+ "identity_hash": identity.identity_hash,
86
+ "risk_count": len(signals),
87
+ "severities": sorted({s.severity for s in signals}),
88
+ "signal_ids": [s.signal_id for s in signals],
89
+ }
90
+ )
91
+ return results
92
+
93
+
40
94
  def _resolve_targets(
41
95
  command: str,
42
96
  requested_harness: str | None,
@@ -74,4 +128,4 @@ def _resolve_targets(
74
128
  )
75
129
 
76
130
 
77
- __all__ = ["apply_managed_install"]
131
+ __all__ = ["apply_managed_install", "scan_workspace_skills"]
@@ -573,25 +573,77 @@ def _render_approvals(console: Console, payload: dict[str, object]) -> None:
573
573
 
574
574
 
575
575
  def _render_managed_install(console: Console, payload: dict[str, object]) -> None:
576
+ skill_scan = _coerce_dict_list(payload.get("skill_scan"))
577
+ supply_chain_risks = _coerce_dict_list(payload.get("supply_chain_risks"))
576
578
  managed_install = payload.get("managed_install")
577
579
  if isinstance(managed_install, dict):
578
580
  _render_single_managed_install(console, managed_install)
579
- return
580
- managed_installs = _coerce_dict_list(payload.get("managed_installs"))
581
- if not managed_installs:
582
- _render_fallback(console, payload)
583
- return
581
+ else:
582
+ managed_installs = _coerce_dict_list(payload.get("managed_installs"))
583
+ if not managed_installs:
584
+ if not skill_scan and not supply_chain_risks:
585
+ _render_fallback(console, payload)
586
+ return
587
+ else:
588
+ console.print(
589
+ Panel(
590
+ _managed_install_batch_summary(payload, managed_installs),
591
+ title="Guard managed harnesses",
592
+ border_style="cyan",
593
+ )
594
+ )
595
+ console.print(_managed_install_batch_table(managed_installs))
596
+ notes = _managed_install_batch_notes(managed_installs)
597
+ if notes:
598
+ console.print(_notes_panel(notes))
599
+ if skill_scan:
600
+ _render_skill_scan_results(console, skill_scan)
601
+ if supply_chain_risks:
602
+ _render_supply_chain_risk_results(console, supply_chain_risks)
603
+
604
+
605
+ def _render_supply_chain_risk_results(console: Console, supply_chain_risks: list[dict[str, object]]) -> None:
606
+ table = Table(box=box.SIMPLE_HEAVY, show_header=True, expand=True)
607
+ table.add_column("Signal", overflow="fold")
608
+ table.add_column("Severity", no_wrap=True)
609
+ table.add_column("Confidence", no_wrap=True)
610
+ table.add_column("Explanation", overflow="fold")
611
+ for entry in supply_chain_risks:
612
+ severity = str(entry.get("severity", "medium"))
613
+ severity_color = {"critical": "red", "high": "yellow", "medium": "cyan", "low": "dim"}.get(severity, "white")
614
+ table.add_row(
615
+ str(entry.get("signal_id", "?")),
616
+ f"[{severity_color}]{severity}[/{severity_color}]",
617
+ str(entry.get("confidence", "?")),
618
+ str(entry.get("plain_reason", "")),
619
+ )
584
620
  console.print(
585
621
  Panel(
586
- _managed_install_batch_summary(payload, managed_installs),
587
- title="Guard managed harnesses",
588
- border_style="cyan",
622
+ table,
623
+ title=f"[bold yellow]Supply chain risks — {len(supply_chain_risks)} signal(s)[/bold yellow]",
624
+ border_style="yellow",
625
+ )
626
+ )
627
+
628
+
629
+ def _render_skill_scan_results(console: Console, skill_scan: list[dict[str, object]]) -> None:
630
+ table = Table(box=box.SIMPLE_HEAVY, show_header=True, expand=True)
631
+ table.add_column("Skill file", overflow="fold")
632
+ table.add_column("Risks", justify="right", no_wrap=True)
633
+ table.add_column("Severities", no_wrap=True)
634
+ table.add_column("Signals", overflow="fold")
635
+ for entry in skill_scan:
636
+ severity_text = ", ".join(_coerce_string_list(entry.get("severities")))
637
+ signal_text = " ".join(_coerce_string_list(entry.get("signal_ids")))
638
+ risk_count = str(entry.get("risk_count", 0))
639
+ table.add_row(str(entry.get("skill_path", "?")), risk_count, severity_text, signal_text)
640
+ console.print(
641
+ Panel(
642
+ table,
643
+ title=f"[bold red]Skill security scan — {len(skill_scan)} file(s) with risks[/bold red]",
644
+ border_style="red",
589
645
  )
590
646
  )
591
- console.print(_managed_install_batch_table(managed_installs))
592
- notes = _managed_install_batch_notes(managed_installs)
593
- if notes:
594
- console.print(_notes_panel(notes))
595
647
 
596
648
 
597
649
  def _render_single_managed_install(console: Console, managed_install: dict[str, object]) -> None:
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import re
7
+ from collections.abc import Mapping
7
8
  from dataclasses import dataclass
8
9
  from hashlib import sha256
9
10
  from pathlib import PurePath
@@ -65,6 +66,10 @@ def build_tool_call_artifact(
65
66
  description=tool_description,
66
67
  )
67
68
  metadata["mcp_tool_identity"] = mcp_tool_identity_metadata(tool_identity)
69
+ if tool_schema is not None:
70
+ metadata["tool_schema"] = tool_schema
71
+ if isinstance(tool_description, str) and tool_description.strip():
72
+ metadata["tool_description"] = tool_description.strip()
68
73
  return GuardArtifact(
69
74
  artifact_id=f"{harness}:runtime:{source_scope}:{server_name}:{tool_name}",
70
75
  name=f"{server_name}:{tool_name}",
@@ -90,7 +95,7 @@ def build_tool_call_hash(artifact: GuardArtifact, arguments: object) -> str:
90
95
  },
91
96
  sort_keys=True,
92
97
  )
93
- return sha256(payload.encode("utf-8")).hexdigest()
98
+ return sha256(payload.encode()).hexdigest()
94
99
 
95
100
 
96
101
  def evaluate_tool_call(
@@ -149,11 +154,13 @@ def evaluate_tool_call(
149
154
 
150
155
  def tool_call_risk_signals(artifact: GuardArtifact, arguments: object) -> tuple[str, ...]:
151
156
  signals_by_category = {
157
+ "filesystem_access": "call shape implies filesystem path access",
152
158
  "destructive_mutation": "tool name implies destructive file or system changes",
153
159
  "command_execution": "tool name implies shell or command execution",
154
160
  "outbound_network": "call arguments imply outbound network activity",
155
161
  "secret_access": "call arguments mention sensitive local files or secrets",
156
162
  "privileged_system_mutation": "call arguments imply privileged system mutation",
163
+ "tool_schema_mismatch": "tool name understates dangerous schema capabilities",
157
164
  }
158
165
  return tuple(signals_by_category[category] for category in tool_call_risk_categories(artifact, arguments))
159
166
 
@@ -163,11 +170,13 @@ def tool_call_risk_categories(artifact: GuardArtifact, arguments: object) -> tup
163
170
 
164
171
  categories = _tool_call_risk_category_set(artifact, arguments)
165
172
  order = (
173
+ "filesystem_access",
166
174
  "command_execution",
167
175
  "destructive_mutation",
168
176
  "outbound_network",
169
177
  "privileged_system_mutation",
170
178
  "secret_access",
179
+ "tool_schema_mismatch",
171
180
  )
172
181
  return tuple(category for category in order if category in categories)
173
182
 
@@ -178,6 +187,9 @@ def _tool_call_risk_category_set(artifact: GuardArtifact, arguments: object) ->
178
187
  combined = _risk_match_text(f"{artifact.name} {serialized_arguments}")
179
188
  tool_name_tokens = set(_tool_name_tokens(tool_name))
180
189
  categories: set[str] = set()
190
+ argument_categories = _argument_key_risk_categories(arguments)
191
+ schema_categories = _schema_risk_categories(artifact.metadata.get("tool_schema"))
192
+ description_categories = _description_risk_categories(artifact.metadata.get("tool_description"))
181
193
 
182
194
  if len(tool_name_tokens.intersection({"delete", "remove", "rm", "destroy", "erase"})) > 0:
183
195
  categories.add("destructive_mutation")
@@ -206,6 +218,11 @@ def _tool_call_risk_category_set(artifact: GuardArtifact, arguments: object) ->
206
218
  (_token_pattern("sudo", "chmod", "chown", "launchctl", "systemctl"),),
207
219
  ):
208
220
  categories.add("privileged_system_mutation")
221
+ categories.update(argument_categories)
222
+ categories.update(schema_categories)
223
+ categories.update(description_categories)
224
+ if _tool_schema_understates_name(tool_name_tokens, schema_categories):
225
+ categories.add("tool_schema_mismatch")
209
226
  return categories
210
227
 
211
228
 
@@ -227,6 +244,289 @@ def _token_pattern(*tokens: str) -> str:
227
244
  return rf"(?<![a-z0-9])({alternatives})(?![a-z0-9])"
228
245
 
229
246
 
247
+ def _argument_key_risk_categories(arguments: object) -> set[str]:
248
+ if not isinstance(arguments, Mapping):
249
+ return set()
250
+ categories: set[str] = set()
251
+ keys = _argument_key_names(arguments)
252
+ if keys.intersection(
253
+ {
254
+ "file",
255
+ "filepath",
256
+ "filepaths",
257
+ "files",
258
+ "path",
259
+ "paths",
260
+ "source",
261
+ "sourcepath",
262
+ "sourcepaths",
263
+ "sources",
264
+ "target",
265
+ "targetpath",
266
+ "targetpaths",
267
+ "targets",
268
+ }
269
+ ):
270
+ categories.add("filesystem_access")
271
+ if keys.intersection({"command", "cmd", "script", "shell"}):
272
+ categories.add("command_execution")
273
+ if keys.intersection({"callback", "endpoint", "uri", "url", "urls", "webhook"}):
274
+ categories.add("outbound_network")
275
+ return categories
276
+
277
+
278
+ def _argument_key_names(value: object) -> set[str]:
279
+ names: set[str] = set()
280
+ pending: list[object] = [value]
281
+ visited_ids: set[int] = set()
282
+ while pending:
283
+ current = pending.pop()
284
+ if isinstance(current, Mapping):
285
+ current_id = id(current)
286
+ if current_id in visited_ids:
287
+ continue
288
+ visited_ids.add(current_id)
289
+ for key, item in current.items():
290
+ names.add(_normalized_argument_key(str(key)))
291
+ pending.append(item)
292
+ elif isinstance(current, list | tuple):
293
+ current_id = id(current)
294
+ if current_id in visited_ids:
295
+ continue
296
+ visited_ids.add(current_id)
297
+ pending.extend(current)
298
+ return names
299
+
300
+
301
+ def _schema_risk_categories(schema: object) -> set[str]:
302
+ keys = _schema_property_key_names(schema)
303
+ categories: set[str] = set()
304
+ if keys.intersection(
305
+ {
306
+ "file",
307
+ "filepath",
308
+ "filepaths",
309
+ "files",
310
+ "path",
311
+ "paths",
312
+ "source",
313
+ "sourcepath",
314
+ "sourcepaths",
315
+ "sources",
316
+ "target",
317
+ "targetpath",
318
+ "targetpaths",
319
+ "targets",
320
+ }
321
+ ):
322
+ categories.add("filesystem_access")
323
+ if keys.intersection({"command", "cmd", "script", "shell"}):
324
+ categories.add("command_execution")
325
+ if keys.intersection({"callback", "endpoint", "uri", "url", "urls", "webhook"}):
326
+ categories.add("outbound_network")
327
+ return categories
328
+
329
+
330
+ def _schema_property_key_names(
331
+ value: object,
332
+ *,
333
+ _root_schema: Mapping[str, object] | None = None,
334
+ _visited_refs: set[str] | None = None,
335
+ _visited_ids: set[int] | None = None,
336
+ ) -> set[str]:
337
+ names: set[str] = set()
338
+ if isinstance(value, Mapping):
339
+ root_schema = value if _root_schema is None else _root_schema
340
+ visited_refs = set() if _visited_refs is None else _visited_refs
341
+ visited_ids = set() if _visited_ids is None else _visited_ids
342
+ val_id = id(value)
343
+ if val_id in visited_ids:
344
+ return names
345
+ visited_ids.add(val_id)
346
+ ref_value = value.get("$ref")
347
+ if isinstance(ref_value, str) and ref_value not in visited_refs:
348
+ visited_refs.add(ref_value)
349
+ resolved = _resolve_local_schema_ref(root_schema, ref_value)
350
+ if resolved is not None:
351
+ names.update(
352
+ _schema_property_key_names(
353
+ resolved,
354
+ _root_schema=root_schema,
355
+ _visited_refs=visited_refs,
356
+ _visited_ids=visited_ids,
357
+ )
358
+ )
359
+ properties = value.get("properties")
360
+ if isinstance(properties, Mapping):
361
+ for key, item in properties.items():
362
+ names.add(_normalized_argument_key(str(key)))
363
+ names.update(
364
+ _schema_property_key_names(
365
+ item,
366
+ _root_schema=root_schema,
367
+ _visited_refs=visited_refs,
368
+ _visited_ids=visited_ids,
369
+ )
370
+ )
371
+ for collection_key in (
372
+ "additionalProperties",
373
+ "allOf",
374
+ "anyOf",
375
+ "contains",
376
+ "else",
377
+ "if",
378
+ "items",
379
+ "oneOf",
380
+ "prefixItems",
381
+ "propertyNames",
382
+ "then",
383
+ "unevaluatedItems",
384
+ "unevaluatedProperties",
385
+ ):
386
+ child = value.get(collection_key)
387
+ names.update(
388
+ _schema_property_key_names(
389
+ child,
390
+ _root_schema=root_schema,
391
+ _visited_refs=visited_refs,
392
+ _visited_ids=visited_ids,
393
+ )
394
+ )
395
+ dependent_schemas = value.get("dependentSchemas")
396
+ if isinstance(dependent_schemas, Mapping):
397
+ for child in dependent_schemas.values():
398
+ names.update(
399
+ _schema_property_key_names(
400
+ child,
401
+ _root_schema=root_schema,
402
+ _visited_refs=visited_refs,
403
+ _visited_ids=visited_ids,
404
+ )
405
+ )
406
+ pattern_properties = value.get("patternProperties")
407
+ if isinstance(pattern_properties, Mapping):
408
+ for child in pattern_properties.values():
409
+ names.update(
410
+ _schema_property_key_names(
411
+ child,
412
+ _root_schema=root_schema,
413
+ _visited_refs=visited_refs,
414
+ _visited_ids=visited_ids,
415
+ )
416
+ )
417
+ return names
418
+ if isinstance(value, list | tuple):
419
+ root_schema = _root_schema
420
+ visited_refs = set() if _visited_refs is None else _visited_refs
421
+ visited_ids = set() if _visited_ids is None else _visited_ids
422
+ for item in value:
423
+ names.update(
424
+ _schema_property_key_names(
425
+ item,
426
+ _root_schema=root_schema,
427
+ _visited_refs=visited_refs,
428
+ _visited_ids=visited_ids,
429
+ )
430
+ )
431
+ return names
432
+
433
+
434
+ def _resolve_local_schema_ref(root_schema: Mapping[str, object], reference: str) -> object | None:
435
+ if reference.startswith("#/"):
436
+ current: object = root_schema
437
+ for part in reference[2:].split("/"):
438
+ token = part.replace("~1", "/").replace("~0", "~")
439
+ if isinstance(current, Mapping):
440
+ if token not in current:
441
+ return None
442
+ current = current[token]
443
+ elif isinstance(current, list | tuple):
444
+ if not token.isdigit():
445
+ return None
446
+ index = int(token)
447
+ if index >= len(current):
448
+ return None
449
+ current = current[index]
450
+ else:
451
+ return None
452
+ return current
453
+ if not reference.startswith("#"):
454
+ return None
455
+ anchor_name = reference[1:]
456
+ if not anchor_name:
457
+ return root_schema
458
+ return _resolve_local_schema_anchor(root_schema, anchor_name)
459
+
460
+
461
+ def _resolve_local_schema_anchor(root_schema: object, anchor_name: str) -> object | None:
462
+ pending: list[object] = [root_schema]
463
+ visited_ids: set[int] = set()
464
+ while pending:
465
+ current = pending.pop()
466
+ current_id = id(current)
467
+ if current_id in visited_ids:
468
+ continue
469
+ visited_ids.add(current_id)
470
+ if not isinstance(current, Mapping):
471
+ if isinstance(current, list | tuple):
472
+ pending.extend(item for item in current if isinstance(item, (Mapping, list, tuple)))
473
+ continue
474
+ anchor = current.get("$anchor")
475
+ dynamic_anchor = current.get("$dynamicAnchor")
476
+ if anchor == anchor_name or dynamic_anchor == anchor_name:
477
+ return current
478
+ pending.extend(item for item in current.values() if isinstance(item, (Mapping, list, tuple)))
479
+ return None
480
+
481
+
482
+ def _description_risk_categories(description: object) -> set[str]:
483
+ if not isinstance(description, str):
484
+ return set()
485
+ normalized = _risk_match_text(description)
486
+ categories: set[str] = set()
487
+ if _matches_any(normalized, (r"\bread files?\b", r"\bopen files?\b", r"\bview files?\b")):
488
+ categories.add("filesystem_access")
489
+ if _matches_any(normalized, (_token_pattern("delete", "remove", "write"),)):
490
+ categories.add("destructive_mutation")
491
+ if _matches_any(normalized, (r"\brun command", _token_pattern("execute", "shell"))):
492
+ categories.add("command_execution")
493
+ return categories
494
+
495
+
496
+ def _tool_schema_understates_name(tool_name_tokens: set[str], schema_categories: set[str]) -> bool:
497
+ dangerous_categories = {"command_execution", "destructive_mutation", "outbound_network"}
498
+ if len(schema_categories.intersection(dangerous_categories)) == 0:
499
+ return False
500
+ name_sounds_dangerous = (
501
+ len(
502
+ tool_name_tokens.intersection(
503
+ {
504
+ "bash",
505
+ "cmd",
506
+ "command",
507
+ "delete",
508
+ "destroy",
509
+ "exec",
510
+ "execute",
511
+ "patch",
512
+ "remove",
513
+ "rm",
514
+ "run",
515
+ "script",
516
+ "shell",
517
+ "write",
518
+ }
519
+ )
520
+ )
521
+ > 0
522
+ )
523
+ return not name_sounds_dangerous
524
+
525
+
526
+ def _normalized_argument_key(value: str) -> str:
527
+ return re.sub(r"[^a-z0-9]", "", _risk_match_text(value))
528
+
529
+
230
530
  def tool_call_risk_summary(artifact: GuardArtifact, arguments: object) -> str:
231
531
  signals = tool_call_risk_signals(artifact, arguments)
232
532
  if len(signals) == 0:
@@ -134,6 +134,7 @@ class GuardActionEnvelope:
134
134
  tool_name: str | None
135
135
  command: str | None
136
136
  prompt_excerpt: str | None
137
+ prompt_text: str | None
137
138
  target_paths: tuple[str, ...]
138
139
  network_hosts: tuple[str, ...]
139
140
  mcp_server: str | None
@@ -190,6 +191,7 @@ class GuardActionEnvelope:
190
191
  tool_name=_string_value(payload.get("tool_name")),
191
192
  command=_string_value(payload.get("command")),
192
193
  prompt_excerpt=_string_value(payload.get("prompt_excerpt")),
194
+ prompt_text=_string_value(payload.get("prompt_text")),
193
195
  target_paths=_string_tuple(payload.get("target_paths")),
194
196
  network_hosts=_string_tuple(payload.get("network_hosts")),
195
197
  mcp_server=_string_value(payload.get("mcp_server")),
@@ -405,6 +407,7 @@ def _normalize_action_payload(
405
407
  tool_name=tool_name,
406
408
  command=command,
407
409
  prompt_excerpt=prompt_excerpt,
410
+ prompt_text=prompt_text,
408
411
  target_paths=target_paths,
409
412
  network_hosts=network_hosts,
410
413
  mcp_server=mcp_server,
@@ -21,6 +21,8 @@ from codex_plugin_scanner.guard.runtime.signals import (
21
21
  confidence_label_from_score,
22
22
  severity_label_from_score,
23
23
  )
24
+ from codex_plugin_scanner.guard.runtime.skill_protection import detect_skill_content_risk, has_skill_structure
25
+ from codex_plugin_scanner.guard.runtime.supply_chain import detect_supply_chain_risk
24
26
  from codex_plugin_scanner.guard.types import PromptRequest
25
27
 
26
28
  DETECTOR_CATEGORY_TAGS: tuple[RiskSignalCategory, ...] = (
@@ -168,8 +170,52 @@ class PromptInjectionDetector:
168
170
  return tuple(_prompt_request_signal(request) for request in requests)
169
171
 
170
172
 
173
+ class SkillRiskDetector:
174
+ detector_id = "skill.content"
175
+ categories: tuple[RiskSignalCategory, ...] = (
176
+ "skill",
177
+ "secret",
178
+ "network",
179
+ "execution",
180
+ "persistence",
181
+ "bypass",
182
+ "encoded",
183
+ )
184
+
185
+ def detect(self, action: GuardActionEnvelope, context: DetectorContext) -> tuple[RiskSignalV2, ...]:
186
+ del context
187
+ if action.action_type != "prompt" or action.prompt_text is None:
188
+ return ()
189
+ if not has_skill_structure(action.prompt_text):
190
+ return ()
191
+ return detect_skill_content_risk(action.prompt_text)
192
+
193
+
194
+ class SupplyChainDetector:
195
+ detector_id = "supply-chain.content"
196
+ categories: tuple[RiskSignalCategory, ...] = (
197
+ "supply_chain",
198
+ "persistence",
199
+ "secret",
200
+ "execution",
201
+ "network",
202
+ )
203
+
204
+ def detect(self, action: GuardActionEnvelope, context: DetectorContext) -> tuple[RiskSignalV2, ...]:
205
+ del context
206
+ if action.prompt_text is None:
207
+ return ()
208
+ return detect_supply_chain_risk(action.prompt_text)
209
+
210
+
171
211
  def register_default_detectors() -> tuple[GuardDetector, ...]:
172
- return (DataFlowExfiltrationDetector(), PromptInjectionDetector(), SecretPathDetector())
212
+ return (
213
+ DataFlowExfiltrationDetector(),
214
+ PromptInjectionDetector(),
215
+ SecretPathDetector(),
216
+ SkillRiskDetector(),
217
+ SupplyChainDetector(),
218
+ )
173
219
 
174
220
 
175
221
  def _prompt_request_signal(request: PromptRequest) -> RiskSignalV2:
@@ -316,6 +316,7 @@ def _guard_run_action_envelope(
316
316
  tool_name=None,
317
317
  command=None,
318
318
  prompt_excerpt=None,
319
+ prompt_text=None,
319
320
  target_paths=(),
320
321
  network_hosts=(),
321
322
  mcp_server=None,