cycode 3.9.0__tar.gz → 3.9.1.dev1__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 (195) hide show
  1. {cycode-3.9.0 → cycode-3.9.1.dev1}/PKG-INFO +1 -1
  2. cycode-3.9.1.dev1/cycode/__init__.py +1 -0
  3. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/command_utils.py +7 -5
  4. cycode-3.9.1.dev1/cycode/cli/apps/ai_guardrails/consts.py +133 -0
  5. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/hooks_manager.py +29 -3
  6. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/install_command.py +29 -10
  7. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/handlers.py +54 -35
  8. cycode-3.9.1.dev1/cycode/cli/apps/ai_guardrails/scan/payload.py +268 -0
  9. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/response_builders.py +57 -5
  10. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +12 -1
  11. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/types.py +11 -0
  12. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/status_command.py +32 -23
  13. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/uninstall_command.py +29 -10
  14. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/ai_security_manager_client.py +2 -0
  15. {cycode-3.9.0 → cycode-3.9.1.dev1}/pyproject.toml +1 -1
  16. cycode-3.9.0/cycode/__init__.py +0 -1
  17. cycode-3.9.0/cycode/cli/apps/ai_guardrails/consts.py +0 -78
  18. cycode-3.9.0/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -72
  19. {cycode-3.9.0 → cycode-3.9.1.dev1}/LICENCE +0 -0
  20. {cycode-3.9.0 → cycode-3.9.1.dev1}/README.md +0 -0
  21. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/__main__.py +0 -0
  22. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/__init__.py +0 -0
  23. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/app.py +0 -0
  24. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/__init__.py +0 -0
  25. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
  26. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
  27. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
  28. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
  29. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
  30. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
  31. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
  32. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
  33. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
  34. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/__init__.py +0 -0
  35. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/auth_command.py +0 -0
  36. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/auth_common.py +0 -0
  37. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/auth_manager.py +0 -0
  38. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/models.py +0 -0
  39. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/__init__.py +0 -0
  40. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/configure_command.py +0 -0
  41. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/consts.py +0 -0
  42. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/messages.py +0 -0
  43. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/prompts.py +0 -0
  44. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ignore/__init__.py +0 -0
  45. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ignore/ignore_command.py +0 -0
  46. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/mcp/__init__.py +0 -0
  47. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/mcp/mcp_command.py +0 -0
  48. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/__init__.py +0 -0
  49. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/report_command.py +0 -0
  50. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/__init__.py +0 -0
  51. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/common.py +0 -0
  52. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
  53. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
  54. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
  55. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
  56. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
  57. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
  58. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/__init__.py +0 -0
  59. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/report_import_command.py +0 -0
  60. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
  61. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
  62. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/__init__.py +0 -0
  63. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/aggregation_report.py +0 -0
  64. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/code_scanner.py +0 -0
  65. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
  66. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
  67. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
  68. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/detection_excluder.py +0 -0
  69. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/path/__init__.py +0 -0
  70. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/path/path_command.py +0 -0
  71. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
  72. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
  73. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
  74. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
  75. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
  76. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
  77. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
  78. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/repository/__init__.py +0 -0
  79. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
  80. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
  81. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
  82. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
  83. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_command.py +0 -0
  84. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_parameters.py +0 -0
  85. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_result.py +0 -0
  86. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/__init__.py +0 -0
  87. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/get_cli_status.py +0 -0
  88. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/models.py +0 -0
  89. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/status_command.py +0 -0
  90. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/version_command.py +0 -0
  91. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/cli_types.py +0 -0
  92. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/config.py +0 -0
  93. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/console.py +0 -0
  94. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/consts.py +0 -0
  95. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/__init__.py +0 -0
  96. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/custom_exceptions.py +0 -0
  97. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
  98. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
  99. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_errors.py +0 -0
  100. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
  101. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
  102. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/__init__.py +0 -0
  103. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/commit_range_documents.py +0 -0
  104. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
  105. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/file_excluder.py +0 -0
  106. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/iac/__init__.py +0 -0
  107. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
  108. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/models/__init__.py +0 -0
  109. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
  110. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/path_documents.py +0 -0
  111. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/repository_documents.py +0 -0
  112. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/__init__.py +0 -0
  113. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
  114. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
  115. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
  116. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
  117. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
  118. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
  119. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
  120. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
  121. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
  122. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
  123. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
  124. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
  125. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
  126. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
  127. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
  128. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/walk_ignore.py +0 -0
  129. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/zip_documents.py +0 -0
  130. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/logger.py +0 -0
  131. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/main.py +0 -0
  132. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/models.py +0 -0
  133. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/__init__.py +0 -0
  134. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/console_printer.py +0 -0
  135. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/json_printer.py +0 -0
  136. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/printer_base.py +0 -0
  137. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/rich_printer.py +0 -0
  138. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/__init__.py +0 -0
  139. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
  140. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table.py +0 -0
  141. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table_models.py +0 -0
  142. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table_printer.py +0 -0
  143. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table_printer_base.py +0 -0
  144. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/text_printer.py +0 -0
  145. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/__init__.py +0 -0
  146. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
  147. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_data.py +0 -0
  148. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
  149. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
  150. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
  151. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/rich_helpers.py +0 -0
  152. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/__init__.py +0 -0
  153. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/base_file_manager.py +0 -0
  154. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/config_file_manager.py +0 -0
  155. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/configuration_manager.py +0 -0
  156. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/credentials_manager.py +0 -0
  157. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/jwt_creator.py +0 -0
  158. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/__init__.py +0 -0
  159. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/enum_utils.py +0 -0
  160. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/get_api_client.py +0 -0
  161. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/git_proxy.py +0 -0
  162. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/ignore_utils.py +0 -0
  163. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/jwt_utils.py +0 -0
  164. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/path_utils.py +0 -0
  165. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/progress_bar.py +0 -0
  166. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/scan_batch.py +0 -0
  167. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/scan_utils.py +0 -0
  168. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/sentry.py +0 -0
  169. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/shell_executor.py +0 -0
  170. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/string_utils.py +0 -0
  171. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/task_timer.py +0 -0
  172. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/url_utils.py +0 -0
  173. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/version_checker.py +0 -0
  174. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/yaml_utils.py +0 -0
  175. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/config.py +0 -0
  176. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/__init__.py +0 -0
  177. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
  178. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/auth_client.py +0 -0
  179. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/base_token_auth_client.py +0 -0
  180. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/client_creator.py +0 -0
  181. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/config.py +0 -0
  182. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/config_dev.py +0 -0
  183. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_client.py +0 -0
  184. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_client_base.py +0 -0
  185. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_dev_based_client.py +0 -0
  186. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
  187. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_token_based_client.py +0 -0
  188. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/headers.py +0 -0
  189. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/import_sbom_client.py +0 -0
  190. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/logger.py +0 -0
  191. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/models.py +0 -0
  192. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/report_client.py +0 -0
  193. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/scan_client.py +0 -0
  194. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/scan_config_base.py +0 -0
  195. {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/logger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycode
3
- Version: 3.9.0
3
+ Version: 3.9.1.dev1
4
4
  Summary: Boost security in your dev lifecycle via SAST, SCA, Secrets & IaC scanning.
5
5
  License-Expression: MIT
6
6
  License-File: LICENCE
@@ -0,0 +1 @@
1
+ __version__ = '3.9.1.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
@@ -12,24 +12,26 @@ from cycode.cli.apps.ai_guardrails.consts import AIIDEType
12
12
  console = Console()
13
13
 
14
14
 
15
- def validate_and_parse_ide(ide: str) -> AIIDEType:
16
- """Validate IDE parameter and convert to AIIDEType enum.
15
+ def validate_and_parse_ide(ide: str) -> Optional[AIIDEType]:
16
+ """Validate IDE parameter, returning None for 'all'.
17
17
 
18
18
  Args:
19
- ide: IDE name string (e.g., 'cursor')
19
+ ide: IDE name string (e.g., 'cursor', 'claude-code', 'all')
20
20
 
21
21
  Returns:
22
- AIIDEType enum value
22
+ AIIDEType enum value, or None if 'all' was specified
23
23
 
24
24
  Raises:
25
25
  typer.Exit: If IDE is invalid
26
26
  """
27
+ if ide.lower() == 'all':
28
+ return None
27
29
  try:
28
30
  return AIIDEType(ide.lower())
29
31
  except ValueError:
30
32
  valid_ides = ', '.join([ide_type.value for ide_type in AIIDEType])
31
33
  console.print(
32
- f'[red]Error:[/] Invalid IDE "{ide}". Supported IDEs: {valid_ides}',
34
+ f'[red]Error:[/] Invalid IDE "{ide}". Supported IDEs: {valid_ides}, all',
33
35
  style='bold red',
34
36
  )
35
37
  raise typer.Exit(1) from None
@@ -0,0 +1,133 @@
1
+ """Constants for AI guardrails hooks management.
2
+
3
+ Currently supports:
4
+ - Cursor
5
+ - Claude Code
6
+ """
7
+
8
+ import platform
9
+ from enum import Enum
10
+ from pathlib import Path
11
+ from typing import NamedTuple
12
+
13
+
14
+ class AIIDEType(str, Enum):
15
+ """Supported AI IDE types."""
16
+
17
+ CURSOR = 'cursor'
18
+ CLAUDE_CODE = 'claude-code'
19
+
20
+
21
+ class PolicyMode(str, Enum):
22
+ """Policy enforcement mode for global mode and per-feature actions."""
23
+
24
+ BLOCK = 'block'
25
+ WARN = 'warn'
26
+
27
+
28
+ class IDEConfig(NamedTuple):
29
+ """Configuration for an AI IDE."""
30
+
31
+ name: str
32
+ hooks_dir: Path
33
+ repo_hooks_subdir: str # Subdirectory in repo for hooks (e.g., '.cursor')
34
+ hooks_file_name: str
35
+ hook_events: list[str] # List of supported hook event names for this IDE
36
+
37
+
38
+ def _get_cursor_hooks_dir() -> Path:
39
+ """Get Cursor hooks directory based on platform."""
40
+ if platform.system() == 'Darwin':
41
+ return Path.home() / '.cursor'
42
+ if platform.system() == 'Windows':
43
+ return Path.home() / 'AppData' / 'Roaming' / 'Cursor'
44
+ # Linux
45
+ return Path.home() / '.config' / 'Cursor'
46
+
47
+
48
+ def _get_claude_code_hooks_dir() -> Path:
49
+ """Get Claude Code hooks directory.
50
+
51
+ Claude Code uses ~/.claude on all platforms.
52
+ """
53
+ return Path.home() / '.claude'
54
+
55
+
56
+ # IDE-specific configurations
57
+ IDE_CONFIGS: dict[AIIDEType, IDEConfig] = {
58
+ AIIDEType.CURSOR: IDEConfig(
59
+ name='Cursor',
60
+ hooks_dir=_get_cursor_hooks_dir(),
61
+ repo_hooks_subdir='.cursor',
62
+ hooks_file_name='hooks.json',
63
+ hook_events=['beforeSubmitPrompt', 'beforeReadFile', 'beforeMCPExecution'],
64
+ ),
65
+ AIIDEType.CLAUDE_CODE: IDEConfig(
66
+ name='Claude Code',
67
+ hooks_dir=_get_claude_code_hooks_dir(),
68
+ repo_hooks_subdir='.claude',
69
+ hooks_file_name='settings.json',
70
+ hook_events=['UserPromptSubmit', 'PreToolUse:Read', 'PreToolUse:mcp'],
71
+ ),
72
+ }
73
+
74
+ # Default IDE
75
+ DEFAULT_IDE = AIIDEType.CURSOR
76
+
77
+ # Command used in hooks
78
+ CYCODE_SCAN_PROMPT_COMMAND = 'cycode ai-guardrails scan'
79
+
80
+
81
+ def _get_cursor_hooks_config() -> dict:
82
+ """Get Cursor-specific hooks configuration."""
83
+ config = IDE_CONFIGS[AIIDEType.CURSOR]
84
+ hooks = {event: [{'command': CYCODE_SCAN_PROMPT_COMMAND}] for event in config.hook_events}
85
+
86
+ return {
87
+ 'version': 1,
88
+ 'hooks': hooks,
89
+ }
90
+
91
+
92
+ def _get_claude_code_hooks_config() -> dict:
93
+ """Get Claude Code-specific hooks configuration.
94
+
95
+ Claude Code uses a different hook format with nested structure:
96
+ - hooks are arrays of objects with 'hooks' containing command arrays
97
+ - PreToolUse uses 'matcher' field to specify which tools to intercept
98
+ """
99
+ command = f'{CYCODE_SCAN_PROMPT_COMMAND} --ide claude-code'
100
+
101
+ return {
102
+ 'hooks': {
103
+ 'UserPromptSubmit': [
104
+ {
105
+ 'hooks': [{'type': 'command', 'command': command}],
106
+ }
107
+ ],
108
+ 'PreToolUse': [
109
+ {
110
+ 'matcher': 'Read',
111
+ 'hooks': [{'type': 'command', 'command': command}],
112
+ },
113
+ {
114
+ 'matcher': 'mcp__.*',
115
+ 'hooks': [{'type': 'command', 'command': command}],
116
+ },
117
+ ],
118
+ },
119
+ }
120
+
121
+
122
+ def get_hooks_config(ide: AIIDEType) -> dict:
123
+ """Get the hooks configuration for a specific IDE.
124
+
125
+ Args:
126
+ ide: The AI IDE type
127
+
128
+ Returns:
129
+ Dict with hooks configuration for the specified IDE
130
+ """
131
+ if ide == AIIDEType.CLAUDE_CODE:
132
+ return _get_claude_code_hooks_config()
133
+ return _get_cursor_hooks_config()
@@ -59,9 +59,27 @@ def save_hooks_file(hooks_path: Path, hooks_config: dict) -> bool:
59
59
 
60
60
 
61
61
  def is_cycode_hook_entry(entry: dict) -> bool:
62
- """Check if a hook entry is from cycode-cli."""
62
+ """Check if a hook entry is from cycode-cli.
63
+
64
+ Handles both Cursor format (flat) and Claude Code format (nested).
65
+
66
+ Cursor format: {"command": "cycode ai-guardrails scan"}
67
+ Claude Code format: {"hooks": [{"type": "command", "command": "cycode ai-guardrails scan --ide claude-code"}]}
68
+ """
69
+ # Check Cursor format (flat command)
63
70
  command = entry.get('command', '')
64
- return CYCODE_SCAN_PROMPT_COMMAND in command
71
+ if CYCODE_SCAN_PROMPT_COMMAND in command:
72
+ return True
73
+
74
+ # Check Claude Code format (nested hooks array)
75
+ hooks = entry.get('hooks', [])
76
+ for hook in hooks:
77
+ if isinstance(hook, dict):
78
+ hook_command = hook.get('command', '')
79
+ if CYCODE_SCAN_PROMPT_COMMAND in hook_command:
80
+ return True
81
+
82
+ return False
65
83
 
66
84
 
67
85
  def install_hooks(
@@ -185,7 +203,15 @@ def get_hooks_status(scope: str = 'user', repo_path: Optional[Path] = None, ide:
185
203
  ide_config = IDE_CONFIGS[ide]
186
204
  has_cycode_hooks = False
187
205
  for event in ide_config.hook_events:
188
- entries = existing.get('hooks', {}).get(event, [])
206
+ # Handle event:matcher format
207
+ if ':' in event:
208
+ actual_event, matcher_prefix = event.split(':', 1)
209
+ all_entries = existing.get('hooks', {}).get(actual_event, [])
210
+ # Filter entries by matcher
211
+ entries = [e for e in all_entries if e.get('matcher', '').startswith(matcher_prefix)]
212
+ else:
213
+ entries = existing.get('hooks', {}).get(event, [])
214
+
189
215
  cycode_entries = [e for e in entries if is_cycode_hook_entry(e)]
190
216
  if cycode_entries:
191
217
  has_cycode_hooks = True
@@ -11,7 +11,7 @@ from cycode.cli.apps.ai_guardrails.command_utils import (
11
11
  validate_and_parse_ide,
12
12
  validate_scope,
13
13
  )
14
- from cycode.cli.apps.ai_guardrails.consts import IDE_CONFIGS
14
+ from cycode.cli.apps.ai_guardrails.consts import IDE_CONFIGS, AIIDEType
15
15
  from cycode.cli.apps.ai_guardrails.hooks_manager import install_hooks
16
16
  from cycode.cli.utils.sentry import add_breadcrumb
17
17
 
@@ -30,9 +30,9 @@ def install_command(
30
30
  str,
31
31
  typer.Option(
32
32
  '--ide',
33
- help='IDE to install hooks for (e.g., "cursor"). Defaults to cursor.',
33
+ help='IDE to install hooks for (e.g., "cursor", "claude-code", or "all" for all IDEs). Defaults to cursor.',
34
34
  ),
35
- ] = 'cursor',
35
+ ] = AIIDEType.CURSOR,
36
36
  repo_path: Annotated[
37
37
  Optional[Path],
38
38
  typer.Option(
@@ -54,6 +54,7 @@ def install_command(
54
54
  cycode ai-guardrails install # Install for all projects (user scope)
55
55
  cycode ai-guardrails install --scope repo # Install for current repo only
56
56
  cycode ai-guardrails install --ide cursor # Install for Cursor IDE
57
+ cycode ai-guardrails install --ide all # Install for all supported IDEs
57
58
  cycode ai-guardrails install --scope repo --repo-path /path/to/repo
58
59
  """
59
60
  add_breadcrumb('ai-guardrails-install')
@@ -62,17 +63,35 @@ def install_command(
62
63
  validate_scope(scope)
63
64
  repo_path = resolve_repo_path(scope, repo_path)
64
65
  ide_type = validate_and_parse_ide(ide)
65
- ide_name = IDE_CONFIGS[ide_type].name
66
- success, message = install_hooks(scope, repo_path, ide=ide_type)
67
66
 
68
- if success:
69
- console.print(f'[green]✓[/] {message}')
67
+ ides_to_install: list[AIIDEType] = list(AIIDEType) if ide_type is None else [ide_type]
68
+
69
+ results: list[tuple[str, bool, str]] = []
70
+ for current_ide in ides_to_install:
71
+ ide_name = IDE_CONFIGS[current_ide].name
72
+ success, message = install_hooks(scope, repo_path, ide=current_ide)
73
+ results.append((ide_name, success, message))
74
+
75
+ # Report results for each IDE
76
+ any_success = False
77
+ all_success = True
78
+ for _ide_name, success, message in results:
79
+ if success:
80
+ console.print(f'[green]✓[/] {message}')
81
+ any_success = True
82
+ else:
83
+ console.print(f'[red]✗[/] {message}', style='bold red')
84
+ all_success = False
85
+
86
+ if any_success:
70
87
  console.print()
71
88
  console.print('[bold]Next steps:[/]')
72
- console.print(f'1. Restart {ide_name} to activate the hooks')
89
+ successful_ides = [name for name, success, _ in results if success]
90
+ ide_list = ', '.join(successful_ides)
91
+ console.print(f'1. Restart {ide_list} to activate the hooks')
73
92
  console.print('2. (Optional) Customize policy in ~/.cycode/ai-guardrails.yaml')
74
93
  console.print()
75
94
  console.print('[dim]The hooks will scan prompts, file reads, and MCP tool calls for secrets.[/]')
76
- else:
77
- console.print(f'[red]✗[/] {message}', style='bold red')
95
+
96
+ if not all_success:
78
97
  raise typer.Exit(1)
@@ -13,6 +13,7 @@ from typing import Callable, Optional
13
13
 
14
14
  import typer
15
15
 
16
+ from cycode.cli.apps.ai_guardrails.consts import PolicyMode
16
17
  from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload
17
18
  from cycode.cli.apps.ai_guardrails.scan.policy import get_policy_value
18
19
  from cycode.cli.apps.ai_guardrails.scan.response_builders import get_response_builder
@@ -46,7 +47,7 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
46
47
  ai_client.create_event(payload, AiHookEventType.PROMPT, AIHookOutcome.ALLOWED)
47
48
  return response_builder.allow_prompt()
48
49
 
49
- mode = get_policy_value(policy, 'mode', default='block')
50
+ mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
50
51
  prompt = payload.prompt or ''
51
52
  max_bytes = get_policy_value(policy, 'secrets', 'max_bytes', default=200000)
52
53
  timeout_ms = get_policy_value(policy, 'secrets', 'timeout_ms', default=30000)
@@ -55,29 +56,26 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
55
56
  scan_id = None
56
57
  block_reason = None
57
58
  outcome = AIHookOutcome.ALLOWED
59
+ error_message = None
58
60
 
59
61
  try:
60
62
  violation_summary, scan_id = _scan_text_for_secrets(ctx, clipped, timeout_ms)
61
63
 
62
- if (
63
- violation_summary
64
- and get_policy_value(prompt_config, 'action', default='block') == 'block'
65
- and mode == 'block'
66
- ):
67
- outcome = AIHookOutcome.BLOCKED
64
+ if violation_summary:
68
65
  block_reason = BlockReason.SECRETS_IN_PROMPT
69
- user_message = f'{violation_summary}. Remove secrets before sending.'
70
- response = response_builder.deny_prompt(user_message)
71
- else:
72
- if violation_summary:
73
- outcome = AIHookOutcome.WARNED
74
- response = response_builder.allow_prompt()
75
- return response
66
+ action = get_policy_value(prompt_config, 'action', default=PolicyMode.BLOCK)
67
+ if action == PolicyMode.BLOCK and mode == PolicyMode.BLOCK:
68
+ outcome = AIHookOutcome.BLOCKED
69
+ user_message = f'{violation_summary}. Remove secrets before sending.'
70
+ return response_builder.deny_prompt(user_message)
71
+ outcome = AIHookOutcome.WARNED
72
+ return response_builder.allow_prompt()
76
73
  except Exception as e:
77
74
  outcome = (
78
75
  AIHookOutcome.ALLOWED if get_policy_value(policy, 'fail_open', default=True) else AIHookOutcome.BLOCKED
79
76
  )
80
- block_reason = BlockReason.SCAN_FAILURE if outcome == AIHookOutcome.BLOCKED else None
77
+ block_reason = BlockReason.SCAN_FAILURE
78
+ error_message = str(e)
81
79
  raise e
82
80
  finally:
83
81
  ai_client.create_event(
@@ -86,6 +84,7 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
86
84
  outcome,
87
85
  scan_id=scan_id,
88
86
  block_reason=block_reason,
87
+ error_message=error_message,
89
88
  )
90
89
 
91
90
 
@@ -106,38 +105,53 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
106
105
  ai_client.create_event(payload, AiHookEventType.FILE_READ, AIHookOutcome.ALLOWED)
107
106
  return response_builder.allow_permission()
108
107
 
109
- mode = get_policy_value(policy, 'mode', default='block')
108
+ mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
110
109
  file_path = payload.file_path or ''
111
- action = get_policy_value(file_read_config, 'action', default='block')
110
+ action = get_policy_value(file_read_config, 'action', default=PolicyMode.BLOCK)
112
111
 
113
112
  scan_id = None
114
113
  block_reason = None
115
114
  outcome = AIHookOutcome.ALLOWED
115
+ error_message = None
116
116
 
117
117
  try:
118
118
  # Check path-based denylist first
119
- if is_denied_path(file_path, policy) and action == 'block':
120
- outcome = AIHookOutcome.BLOCKED
119
+ if is_denied_path(file_path, policy):
121
120
  block_reason = BlockReason.SENSITIVE_PATH
122
- user_message = f'Cycode blocked sending {file_path} to the AI (sensitive path policy).'
123
- return response_builder.deny_permission(
121
+ if mode == PolicyMode.BLOCK and action == PolicyMode.BLOCK:
122
+ outcome = AIHookOutcome.BLOCKED
123
+ user_message = f'Cycode blocked sending {file_path} to the AI (sensitive path policy).'
124
+ return response_builder.deny_permission(
125
+ user_message,
126
+ 'This file path is classified as sensitive; do not read/send it to the model.',
127
+ )
128
+ # Warn mode - ask user for permission
129
+ outcome = AIHookOutcome.WARNED
130
+ user_message = f'Cycode flagged {file_path} as sensitive. Allow reading?'
131
+ return response_builder.ask_permission(
124
132
  user_message,
125
- 'This file path is classified as sensitive; do not read/send it to the model.',
133
+ 'This file path is classified as sensitive; proceed with caution.',
126
134
  )
127
135
 
128
136
  # Scan file content if enabled
129
137
  if get_policy_value(file_read_config, 'scan_content', default=True):
130
138
  violation_summary, scan_id = _scan_path_for_secrets(ctx, file_path, policy)
131
- if violation_summary and action == 'block' and mode == 'block':
132
- outcome = AIHookOutcome.BLOCKED
139
+ if violation_summary:
133
140
  block_reason = BlockReason.SECRETS_IN_FILE
134
- user_message = f'Cycode blocked reading {file_path}. {violation_summary}'
135
- return response_builder.deny_permission(
141
+ if mode == PolicyMode.BLOCK and action == PolicyMode.BLOCK:
142
+ outcome = AIHookOutcome.BLOCKED
143
+ user_message = f'Cycode blocked reading {file_path}. {violation_summary}'
144
+ return response_builder.deny_permission(
145
+ user_message,
146
+ 'Secrets detected; do not send this file to the model.',
147
+ )
148
+ # Warn mode - ask user for permission
149
+ outcome = AIHookOutcome.WARNED
150
+ user_message = f'Cycode detected secrets in {file_path}. {violation_summary}'
151
+ return response_builder.ask_permission(
136
152
  user_message,
137
- 'Secrets detected; do not send this file to the model.',
153
+ 'Possible secrets detected; proceed with caution.',
138
154
  )
139
- if violation_summary:
140
- outcome = AIHookOutcome.WARNED
141
155
  return response_builder.allow_permission()
142
156
 
143
157
  return response_builder.allow_permission()
@@ -145,7 +159,8 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
145
159
  outcome = (
146
160
  AIHookOutcome.ALLOWED if get_policy_value(policy, 'fail_open', default=True) else AIHookOutcome.BLOCKED
147
161
  )
148
- block_reason = BlockReason.SCAN_FAILURE if outcome == AIHookOutcome.BLOCKED else None
162
+ block_reason = BlockReason.SCAN_FAILURE
163
+ error_message = str(e)
149
164
  raise e
150
165
  finally:
151
166
  ai_client.create_event(
@@ -154,6 +169,7 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
154
169
  outcome,
155
170
  scan_id=scan_id,
156
171
  block_reason=block_reason,
172
+ error_message=error_message,
157
173
  )
158
174
 
159
175
 
@@ -175,26 +191,27 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
175
191
  ai_client.create_event(payload, AiHookEventType.MCP_EXECUTION, AIHookOutcome.ALLOWED)
176
192
  return response_builder.allow_permission()
177
193
 
178
- mode = get_policy_value(policy, 'mode', default='block')
194
+ mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
179
195
  tool = payload.mcp_tool_name or 'unknown'
180
196
  args = payload.mcp_arguments or {}
181
197
  args_text = args if isinstance(args, str) else json.dumps(args)
182
198
  max_bytes = get_policy_value(policy, 'secrets', 'max_bytes', default=200000)
183
199
  timeout_ms = get_policy_value(policy, 'secrets', 'timeout_ms', default=30000)
184
200
  clipped = truncate_utf8(args_text, max_bytes)
185
- action = get_policy_value(mcp_config, 'action', default='block')
201
+ action = get_policy_value(mcp_config, 'action', default=PolicyMode.BLOCK)
186
202
 
187
203
  scan_id = None
188
204
  block_reason = None
189
205
  outcome = AIHookOutcome.ALLOWED
206
+ error_message = None
190
207
 
191
208
  try:
192
209
  if get_policy_value(mcp_config, 'scan_arguments', default=True):
193
210
  violation_summary, scan_id = _scan_text_for_secrets(ctx, clipped, timeout_ms)
194
211
  if violation_summary:
195
- if mode == 'block' and action == 'block':
212
+ block_reason = BlockReason.SECRETS_IN_MCP_ARGS
213
+ if mode == PolicyMode.BLOCK and action == PolicyMode.BLOCK:
196
214
  outcome = AIHookOutcome.BLOCKED
197
- block_reason = BlockReason.SECRETS_IN_MCP_ARGS
198
215
  user_message = f'Cycode blocked MCP tool call "{tool}". {violation_summary}'
199
216
  return response_builder.deny_permission(
200
217
  user_message,
@@ -211,7 +228,8 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
211
228
  outcome = (
212
229
  AIHookOutcome.ALLOWED if get_policy_value(policy, 'fail_open', default=True) else AIHookOutcome.BLOCKED
213
230
  )
214
- block_reason = BlockReason.SCAN_FAILURE if outcome == AIHookOutcome.BLOCKED else None
231
+ block_reason = BlockReason.SCAN_FAILURE
232
+ error_message = str(e)
215
233
  raise e
216
234
  finally:
217
235
  ai_client.create_event(
@@ -220,6 +238,7 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
220
238
  outcome,
221
239
  scan_id=scan_id,
222
240
  block_reason=block_reason,
241
+ error_message=error_message,
223
242
  )
224
243
 
225
244