SafeAI-Static-Analyzer 2.2.0__tar.gz → 2.3.0__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 (223) hide show
  1. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/PKG-INFO +28 -5
  2. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/README.md +27 -4
  3. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/PKG-INFO +28 -5
  4. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/SOURCES.txt +10 -0
  5. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/pyproject.toml +4 -0
  6. safeai_static_analyzer-2.3.0/safeai/analyzers/__init__.py +169 -0
  7. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/capability/analyzer.py +3 -0
  8. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/claude_code/analyzer.py +2 -0
  9. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/data_leakage/analyzer.py +3 -0
  10. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/dataflow/analyzer.py +3 -0
  11. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/env_dependency/analyzer.py +3 -0
  12. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/governance/analyzer.py +3 -0
  13. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/analyzer.py +2 -0
  14. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/model_config/analyzer.py +3 -0
  15. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/prompt/analyzer.py +3 -0
  16. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/prompt_file/analyzer.py +3 -0
  17. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/skill/analyzer.py +3 -0
  18. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/tool_def/analyzer.py +2 -0
  19. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/workflow/analyzer.py +3 -0
  20. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/cmd/cli.py +132 -3
  21. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/cmd/postprocess.py +25 -1
  22. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/cmd/registry_cli.py +38 -0
  23. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/engine/file_discovery.py +12 -2
  24. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/engine/orchestrator.py +53 -34
  25. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/__init__.py +26 -1
  26. safeai_static_analyzer-2.3.0/safeai/frameworks/copilot/parser.py +184 -0
  27. safeai_static_analyzer-2.3.0/safeai/frameworks/openclaw/parser.py +172 -0
  28. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/__init__.py +1 -1
  29. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/exporter.py +18 -0
  30. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/importer.py +50 -1
  31. safeai_static_analyzer-2.3.0/safeai/kya/lockfile.py +128 -0
  32. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/manifest.py +21 -2
  33. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/persist.py +8 -2
  34. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/schema.py +11 -0
  35. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/pr_comment.py +9 -0
  36. safeai_static_analyzer-2.3.0/safeai/rules/pack_test.py +152 -0
  37. safeai_static_analyzer-2.3.0/safeai/scoring/__init__.py +0 -0
  38. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/version.py +1 -1
  39. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_capability_diff_v2.py +1 -1
  40. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_compatibility.py +62 -0
  41. safeai_static_analyzer-2.3.0/tests/test_config_adapters.py +192 -0
  42. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_dco.py +8 -0
  43. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_manifest_integrity.py +50 -0
  44. safeai_static_analyzer-2.3.0/tests/test_plugin_sdk.py +468 -0
  45. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_pr_comment.py +51 -0
  46. safeai_static_analyzer-2.3.0/tests/test_release_assets.py +100 -0
  47. safeai_static_analyzer-2.3.0/tests/test_scan_framework.py +374 -0
  48. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/LICENSE +0 -0
  49. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/dependency_links.txt +0 -0
  50. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/entry_points.txt +0 -0
  51. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/requires.txt +0 -0
  52. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/top_level.txt +0 -0
  53. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/__init__.py +0 -0
  54. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/__main__.py +0 -0
  55. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/__init__.py +0 -0
  56. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/aggregation.py +0 -0
  57. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/capabilities.py +0 -0
  58. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/capability_diff.py +0 -0
  59. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/component_diff.py +0 -0
  60. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/component_graph.py +0 -0
  61. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/components.py +0 -0
  62. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/dependency_correlation.py +0 -0
  63. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/escalation.py +0 -0
  64. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/escalation_remediation.py +0 -0
  65. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/import_graph.py +0 -0
  66. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/project_graph.py +0 -0
  67. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/semantic.py +0 -0
  68. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/target_taxonomy.py +0 -0
  69. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/tool_identity.py +0 -0
  70. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/tool_implementation.py +0 -0
  71. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analysis/tool_surface.py +0 -0
  72. {safeai_static_analyzer-2.2.0/safeai/analyzers → safeai_static_analyzer-2.3.0/safeai/analyzers/capability}/__init__.py +0 -0
  73. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/claude_code/__init__.py +0 -0
  74. {safeai_static_analyzer-2.2.0/safeai/analyzers/capability → safeai_static_analyzer-2.3.0/safeai/analyzers/data_leakage}/__init__.py +0 -0
  75. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/dataflow/__init__.py +0 -0
  76. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/env_dependency/__init__.py +0 -0
  77. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/governance/__init__.py +0 -0
  78. {safeai_static_analyzer-2.2.0/safeai/analyzers/data_leakage → safeai_static_analyzer-2.3.0/safeai/analyzers/mcp}/__init__.py +0 -0
  79. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/compatibility.py +0 -0
  80. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/schema.py +0 -0
  81. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/validators.py +0 -0
  82. {safeai_static_analyzer-2.2.0/safeai/analyzers/mcp → safeai_static_analyzer-2.3.0/safeai/analyzers/model_config}/__init__.py +0 -0
  83. {safeai_static_analyzer-2.2.0/safeai/analyzers/model_config → safeai_static_analyzer-2.3.0/safeai/analyzers/prompt}/__init__.py +0 -0
  84. {safeai_static_analyzer-2.2.0/safeai/analyzers/prompt → safeai_static_analyzer-2.3.0/safeai/analyzers/prompt_file}/__init__.py +0 -0
  85. {safeai_static_analyzer-2.2.0/safeai/analyzers/prompt_file → safeai_static_analyzer-2.3.0/safeai/analyzers/skill}/__init__.py +0 -0
  86. {safeai_static_analyzer-2.2.0/safeai/analyzers/skill → safeai_static_analyzer-2.3.0/safeai/analyzers/tool_def}/__init__.py +0 -0
  87. {safeai_static_analyzer-2.2.0/safeai/analyzers/tool_def → safeai_static_analyzer-2.3.0/safeai/analyzers/workflow}/__init__.py +0 -0
  88. {safeai_static_analyzer-2.2.0/safeai/analyzers/workflow → safeai_static_analyzer-2.3.0/safeai/cmd}/__init__.py +0 -0
  89. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/cmd/manifest_cli.py +0 -0
  90. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/controls/__init__.py +0 -0
  91. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/controls/catalogs.py +0 -0
  92. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/controls/mappings.py +0 -0
  93. {safeai_static_analyzer-2.2.0/safeai/cmd → safeai_static_analyzer-2.3.0/safeai/engine}/__init__.py +0 -0
  94. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/engine/metadata.py +0 -0
  95. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/engine/scan.py +0 -0
  96. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/autogen/parser.py +0 -0
  97. {safeai_static_analyzer-2.2.0/safeai/engine → safeai_static_analyzer-2.3.0/safeai/frameworks/azure_foundry}/__init__.py +0 -0
  98. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/azure_foundry/parser.py +0 -0
  99. {safeai_static_analyzer-2.2.0/safeai/frameworks/azure_foundry → safeai_static_analyzer-2.3.0/safeai/frameworks/bedrock_agent}/__init__.py +0 -0
  100. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/bedrock_agent/parser.py +0 -0
  101. {safeai_static_analyzer-2.2.0/safeai/frameworks/bedrock_agent → safeai_static_analyzer-2.3.0/safeai/frameworks/claude_code}/__init__.py +0 -0
  102. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/commands.py +0 -0
  103. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/parser.py +0 -0
  104. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/permissions.py +0 -0
  105. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/settings.py +0 -0
  106. {safeai_static_analyzer-2.2.0/safeai/frameworks/claude_code → safeai_static_analyzer-2.3.0/safeai/frameworks/copilot}/__init__.py +0 -0
  107. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/crewai/__init__.py +0 -0
  108. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/crewai/parser.py +0 -0
  109. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/cursorrules/__init__.py +0 -0
  110. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/cursorrules/parser.py +0 -0
  111. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/dify/__init__.py +0 -0
  112. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/dify/parser.py +0 -0
  113. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/google_adk/__init__.py +0 -0
  114. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/google_adk/parser.py +0 -0
  115. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/haystack/__init__.py +0 -0
  116. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/haystack/parser.py +0 -0
  117. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langchain/__init__.py +0 -0
  118. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langchain/parser.py +0 -0
  119. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langgraph/__init__.py +0 -0
  120. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langgraph/parser.py +0 -0
  121. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/llamaindex/__init__.py +0 -0
  122. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/llamaindex/parser.py +0 -0
  123. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/mastra/__init__.py +0 -0
  124. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/mastra/parser.py +0 -0
  125. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/microsoft_agent/__init__.py +0 -0
  126. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/microsoft_agent/parser.py +0 -0
  127. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/n8n/__init__.py +0 -0
  128. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/n8n/parser.py +0 -0
  129. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/openai_agents/__init__.py +0 -0
  130. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/openai_agents/parser.py +0 -0
  131. {safeai_static_analyzer-2.2.0/safeai/frameworks/semantic_kernel → safeai_static_analyzer-2.3.0/safeai/frameworks/openclaw}/__init__.py +0 -0
  132. {safeai_static_analyzer-2.2.0/safeai/frameworks/windsurf → safeai_static_analyzer-2.3.0/safeai/frameworks/semantic_kernel}/__init__.py +0 -0
  133. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/semantic_kernel/parser.py +0 -0
  134. {safeai_static_analyzer-2.2.0/safeai/report → safeai_static_analyzer-2.3.0/safeai/frameworks/windsurf}/__init__.py +0 -0
  135. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/frameworks/windsurf/parser.py +0 -0
  136. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/assurance.py +0 -0
  137. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/baseline.py +0 -0
  138. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/ci_context.py +0 -0
  139. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/contract.py +0 -0
  140. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/enrich.py +0 -0
  141. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/fingerprints.py +0 -0
  142. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/identity.py +0 -0
  143. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/integrity.py +0 -0
  144. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/policy.py +0 -0
  145. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/__init__.py +0 -0
  146. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/connection.py +0 -0
  147. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/queries.py +0 -0
  148. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/suppressions.py +0 -0
  149. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/kya/util.py +0 -0
  150. {safeai_static_analyzer-2.2.0/safeai/rules → safeai_static_analyzer-2.3.0/safeai/report}/__init__.py +0 -0
  151. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/failure_matrix.py +0 -0
  152. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/html.py +0 -0
  153. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/html_kit.py +0 -0
  154. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/json_report.py +0 -0
  155. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/registry_html.py +0 -0
  156. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/sarif.py +0 -0
  157. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/report/terminal.py +0 -0
  158. {safeai_static_analyzer-2.2.0/safeai/scoring → safeai_static_analyzer-2.3.0/safeai/rules}/__init__.py +0 -0
  159. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/rules/base_rules.yaml +0 -0
  160. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/rules/loader.py +0 -0
  161. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/scorecard.py +0 -0
  162. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/scoring/engine.py +0 -0
  163. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/severity.py +0 -0
  164. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/telemetry/__init__.py +0 -0
  165. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/telemetry/client.py +0 -0
  166. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/telemetry/config.py +0 -0
  167. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/safeai/telemetry/schema.py +0 -0
  168. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/setup.cfg +0 -0
  169. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_adapter_dataflow.py +0 -0
  170. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_assurance.py +0 -0
  171. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_baseline.py +0 -0
  172. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_benchmarks.py +0 -0
  173. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_capability_detection.py +0 -0
  174. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_ci_context.py +0 -0
  175. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_claude_code_deep.py +0 -0
  176. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_component_graph.py +0 -0
  177. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_component_hash.py +0 -0
  178. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_confidence_arbitration.py +0 -0
  179. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_control_mappings.py +0 -0
  180. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_crewai_framework.py +0 -0
  181. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_cursorrules_framework.py +0 -0
  182. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_env_dependency.py +0 -0
  183. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_escalation.py +0 -0
  184. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_escalation_remediation.py +0 -0
  185. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_evidence_type.py +0 -0
  186. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_fingerprints.py +0 -0
  187. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_github_action.py +0 -0
  188. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_governance_analyzer.py +0 -0
  189. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_html_report.py +0 -0
  190. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_init_and_rules.py +0 -0
  191. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_kya_integration.py +0 -0
  192. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_kya_manifest.py +0 -0
  193. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_langgraph_framework.py +0 -0
  194. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_llamaindex_framework.py +0 -0
  195. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_manifest_contract.py +0 -0
  196. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_mcp_tool_poisoning.py +0 -0
  197. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_mcp_validation.py +0 -0
  198. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_n8n_workflow.py +0 -0
  199. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_parser_aggregation.py +0 -0
  200. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_phase15.py +0 -0
  201. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_policy.py +0 -0
  202. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_pr_comment_cli.py +0 -0
  203. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_quality_gates.py +0 -0
  204. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_registry.py +0 -0
  205. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_registry_cli.py +0 -0
  206. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_registry_html.py +0 -0
  207. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_regression_fixtures.py +0 -0
  208. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_release_fixes.py +0 -0
  209. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_release_validation.py +0 -0
  210. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_rules_loader_determinism.py +0 -0
  211. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_sarif.py +0 -0
  212. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_scan_phase11.py +0 -0
  213. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_scorecard.py +0 -0
  214. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_scoring.py +0 -0
  215. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_semantic.py +0 -0
  216. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_severity.py +0 -0
  217. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_suppressions.py +0 -0
  218. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_target_taxonomy_engine.py +0 -0
  219. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_telemetry.py +0 -0
  220. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_tool_identity.py +0 -0
  221. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_tool_implementation.py +0 -0
  222. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_version.py +0 -0
  223. {safeai_static_analyzer-2.2.0 → safeai_static_analyzer-2.3.0}/tests/test_windsurf_framework.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SafeAI-Static-Analyzer
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: The lightweight, open-source Static AI Capability & Risk Analyzer for CI/CD pipelines.
5
5
  Author: IkarusCareer
6
6
  License-Expression: Apache-2.0
@@ -36,12 +36,12 @@ Dynamic: license-file
36
36
  [![CI](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml/badge.svg)](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
37
37
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ikaruscareer/SafeAI/badge)](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
38
38
  [![Website](https://img.shields.io/badge/web-safeai--analyzer.ikaruscareer.com-0f766e)](https://safeai-analyzer.ikaruscareer.com)
39
- [![Latest Release](https://img.shields.io/badge/latest-v2.0.1-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.0.1)
39
+ [![Latest Release](https://img.shields.io/badge/latest-v2.3.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.3.0)
40
40
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
41
41
 
42
42
  Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
43
43
 
44
- **SafeAI** is a static analysis tool that scans AI application source code for security risks, capability exposure, and governance gaps. It runs entirely offline, never executes agents or calls LLMs, and integrates into CI/CD pipelines.
44
+ **SafeAI** is a static analysis tool that scans AI application source code for security risks, capability exposure, and governance gaps. It is offline and source-private by default — it never executes agents or calls LLMs, and network activity occurs only through explicitly enabled integration commands (currently only `--pr-comment-post`). It integrates into CI/CD pipelines.
45
45
 
46
46
  > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
47
47
 
@@ -84,7 +84,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
84
84
 
85
85
  | Feature | Description |
86
86
  |---------|-------------|
87
- | **Framework Detection** | Detects and parses 17 AI agent frameworks (AST + config + regex, no mutual exclusion) |
87
+ | **Framework Detection** | Detects and parses 19 AI agent frameworks (AST + config + regex, no mutual exclusion) |
88
88
  | **Tool Identity & Access Modes** | Capabilities attributed to named tools (agent / MCP server / skill / tool / workflow node) on an access scale `none < read < write < mutate < execute`; inferred modes are flagged, never overstated |
89
89
  | **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
90
90
  | **Capability Escalation Detection** | Per-tool authority diffs between scans (new shell, read→write widening, new MCP server, removed approval gate, ...) — 14 rules, including gating-aware subsumption |
@@ -170,6 +170,8 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
170
170
  | n8n | ✔ | Partial | Minimal | Minimal | Experimental |
171
171
  | Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
172
172
  | Windsurf (.windsurfrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
173
+ | OpenClaw config | ✔ | Minimal | Minimal | Minimal | Experimental |
174
+ | GitHub Copilot instructions | ✔ | Minimal | Minimal | Minimal | Experimental |
173
175
 
174
176
 
175
177
  ### Framework Support Details
@@ -194,6 +196,12 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
194
196
  - **Cursor (.cursorrules)** — detects declared tools/permissions and
195
197
  capability-relevant keywords (shell, filesystem, HTTP, database) in the
196
198
  IDE's rules config, JSON, YAML, or free text
199
+ - **Windsurf (.windsurfrules)** — detects declared tools, models, and
200
+ capability-relevant keywords in JSON, YAML, or free-text rules
201
+ - **OpenClaw config** — detects JSON/YAML files under `.openclaw/` and
202
+ `.openclaw.yaml`, including declared tools, models, and MCP servers
203
+ - **GitHub Copilot instructions** — detects repository instruction Markdown,
204
+ YAML frontmatter, and `.copilot/*.yml` tool or permission grants
197
205
 
198
206
  Maturity is on the scale defined in [`docs/reference/FRAMEWORK_SUPPORT.md`](docs/reference/FRAMEWORK_SUPPORT.md):
199
207
  **Partial** = reliable detection and discovery with capability/risk analysis over
@@ -211,6 +219,8 @@ Representative test fixtures and validation tests for framework detection:
211
219
  | LlamaIndex | `test_llamaindex_framework.py` | `fixtures/llamaindex/representative/agent.py` | @adnqcr7-code [#61] |
212
220
  | CrewAI | `test_crewai_framework.py` | `fixtures/crewai/representative/crew.py` | @adnqcr7-code [#62] |
213
221
  | Claude Code | `test_claude_code_deep.py` | `fixtures/claude_code/compatibility/` | @adnqcr7-code [#59] |
222
+ | OpenClaw config | `test_config_adapters.py` | `fixtures/openclaw/representative/.openclaw/config.json` | @YaoSong808 [#159] |
223
+ | GitHub Copilot instructions | `test_config_adapters.py` | `fixtures/copilot/representative/.github/copilot-instructions.md` | @YaoSong808 [#159] |
214
224
 
215
225
  ---
216
226
 
@@ -302,6 +312,16 @@ to a shared path, or use `--no-registry` for ephemeral scans.
302
312
  - Python 3.11, 3.12, or 3.13
303
313
  - PyYAML (for YAML configuration parsing)
304
314
 
315
+ ### Install from PyPI (recommended)
316
+
317
+ ```bash
318
+ pip install SafeAI-Static-Analyzer
319
+ ```
320
+
321
+ Unpinned on purpose — this always resolves to the latest stable
322
+ release (see [releases](https://github.com/ikaruscareer/SafeAI/releases)).
323
+ Verify the download with [`docs/reference/VERIFICATION.md`](docs/reference/VERIFICATION.md).
324
+
305
325
  ### Install from source
306
326
 
307
327
  ```bash
@@ -343,6 +363,7 @@ python -m safeai registry <subcommand> [options]
343
363
  | `--json` | — | JSON output path |
344
364
  | `--html` | — | HTML report output path |
345
365
  | `--manifest` | — | Canonical KYA manifest output path (`safeai-manifest.json`) |
366
+ | `--digest-file` | — | With `--manifest`: write `<canonical-sha256> <manifest-basename>` to this path. The digest is the one `safeai manifest verify` reports, not a raw file hash, so it is not `sha256sum -c` compatible |
346
367
  | `--baseline` | — | Prior manifest/report for new/existing comparison |
347
368
  | `--fail-on-new` | off | With `--baseline`: fail only on new/regressed findings |
348
369
  | `--policy` | `.safeai/policy.yml` | Policy-as-code YAML file |
@@ -450,7 +471,7 @@ jobs:
450
471
  - uses: actions/checkout@v4
451
472
  - uses: actions/setup-python@v5
452
473
  with: { python-version: '3.12' }
453
- - uses: ikaruscareer/SafeAI@v1
474
+ - uses: ikaruscareer/SafeAI@v2
454
475
  with: { path: '.', fail-on: critical }
455
476
  - uses: github/codeql-action/upload-sarif@v3
456
477
  if: always()
@@ -504,6 +525,8 @@ contributors who have helped make AI safer:
504
525
  | [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
505
526
  | [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
506
527
  | [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
528
+ | [@burakeyler](https://github.com/burakeyler) | `--digest-file` manifest digest sidecar (PR #148) |
529
+ | [@YaoSong808](https://github.com/YaoSong808) | OpenClaw and GitHub Copilot config-file adapters (PR #159) |
507
530
  | [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
508
531
  | [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
509
532
  | [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
@@ -3,12 +3,12 @@
3
3
  [![CI](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml/badge.svg)](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
4
4
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ikaruscareer/SafeAI/badge)](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
5
5
  [![Website](https://img.shields.io/badge/web-safeai--analyzer.ikaruscareer.com-0f766e)](https://safeai-analyzer.ikaruscareer.com)
6
- [![Latest Release](https://img.shields.io/badge/latest-v2.0.1-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.0.1)
6
+ [![Latest Release](https://img.shields.io/badge/latest-v2.3.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.3.0)
7
7
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
8
8
 
9
9
  Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
10
10
 
11
- **SafeAI** is a static analysis tool that scans AI application source code for security risks, capability exposure, and governance gaps. It runs entirely offline, never executes agents or calls LLMs, and integrates into CI/CD pipelines.
11
+ **SafeAI** is a static analysis tool that scans AI application source code for security risks, capability exposure, and governance gaps. It is offline and source-private by default — it never executes agents or calls LLMs, and network activity occurs only through explicitly enabled integration commands (currently only `--pr-comment-post`). It integrates into CI/CD pipelines.
12
12
 
13
13
  > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
14
14
 
@@ -51,7 +51,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
51
51
 
52
52
  | Feature | Description |
53
53
  |---------|-------------|
54
- | **Framework Detection** | Detects and parses 17 AI agent frameworks (AST + config + regex, no mutual exclusion) |
54
+ | **Framework Detection** | Detects and parses 19 AI agent frameworks (AST + config + regex, no mutual exclusion) |
55
55
  | **Tool Identity & Access Modes** | Capabilities attributed to named tools (agent / MCP server / skill / tool / workflow node) on an access scale `none < read < write < mutate < execute`; inferred modes are flagged, never overstated |
56
56
  | **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
57
57
  | **Capability Escalation Detection** | Per-tool authority diffs between scans (new shell, read→write widening, new MCP server, removed approval gate, ...) — 14 rules, including gating-aware subsumption |
@@ -137,6 +137,8 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
137
137
  | n8n | ✔ | Partial | Minimal | Minimal | Experimental |
138
138
  | Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
139
139
  | Windsurf (.windsurfrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
140
+ | OpenClaw config | ✔ | Minimal | Minimal | Minimal | Experimental |
141
+ | GitHub Copilot instructions | ✔ | Minimal | Minimal | Minimal | Experimental |
140
142
 
141
143
 
142
144
  ### Framework Support Details
@@ -161,6 +163,12 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
161
163
  - **Cursor (.cursorrules)** — detects declared tools/permissions and
162
164
  capability-relevant keywords (shell, filesystem, HTTP, database) in the
163
165
  IDE's rules config, JSON, YAML, or free text
166
+ - **Windsurf (.windsurfrules)** — detects declared tools, models, and
167
+ capability-relevant keywords in JSON, YAML, or free-text rules
168
+ - **OpenClaw config** — detects JSON/YAML files under `.openclaw/` and
169
+ `.openclaw.yaml`, including declared tools, models, and MCP servers
170
+ - **GitHub Copilot instructions** — detects repository instruction Markdown,
171
+ YAML frontmatter, and `.copilot/*.yml` tool or permission grants
164
172
 
165
173
  Maturity is on the scale defined in [`docs/reference/FRAMEWORK_SUPPORT.md`](docs/reference/FRAMEWORK_SUPPORT.md):
166
174
  **Partial** = reliable detection and discovery with capability/risk analysis over
@@ -178,6 +186,8 @@ Representative test fixtures and validation tests for framework detection:
178
186
  | LlamaIndex | `test_llamaindex_framework.py` | `fixtures/llamaindex/representative/agent.py` | @adnqcr7-code [#61] |
179
187
  | CrewAI | `test_crewai_framework.py` | `fixtures/crewai/representative/crew.py` | @adnqcr7-code [#62] |
180
188
  | Claude Code | `test_claude_code_deep.py` | `fixtures/claude_code/compatibility/` | @adnqcr7-code [#59] |
189
+ | OpenClaw config | `test_config_adapters.py` | `fixtures/openclaw/representative/.openclaw/config.json` | @YaoSong808 [#159] |
190
+ | GitHub Copilot instructions | `test_config_adapters.py` | `fixtures/copilot/representative/.github/copilot-instructions.md` | @YaoSong808 [#159] |
181
191
 
182
192
  ---
183
193
 
@@ -269,6 +279,16 @@ to a shared path, or use `--no-registry` for ephemeral scans.
269
279
  - Python 3.11, 3.12, or 3.13
270
280
  - PyYAML (for YAML configuration parsing)
271
281
 
282
+ ### Install from PyPI (recommended)
283
+
284
+ ```bash
285
+ pip install SafeAI-Static-Analyzer
286
+ ```
287
+
288
+ Unpinned on purpose — this always resolves to the latest stable
289
+ release (see [releases](https://github.com/ikaruscareer/SafeAI/releases)).
290
+ Verify the download with [`docs/reference/VERIFICATION.md`](docs/reference/VERIFICATION.md).
291
+
272
292
  ### Install from source
273
293
 
274
294
  ```bash
@@ -310,6 +330,7 @@ python -m safeai registry <subcommand> [options]
310
330
  | `--json` | — | JSON output path |
311
331
  | `--html` | — | HTML report output path |
312
332
  | `--manifest` | — | Canonical KYA manifest output path (`safeai-manifest.json`) |
333
+ | `--digest-file` | — | With `--manifest`: write `<canonical-sha256> <manifest-basename>` to this path. The digest is the one `safeai manifest verify` reports, not a raw file hash, so it is not `sha256sum -c` compatible |
313
334
  | `--baseline` | — | Prior manifest/report for new/existing comparison |
314
335
  | `--fail-on-new` | off | With `--baseline`: fail only on new/regressed findings |
315
336
  | `--policy` | `.safeai/policy.yml` | Policy-as-code YAML file |
@@ -417,7 +438,7 @@ jobs:
417
438
  - uses: actions/checkout@v4
418
439
  - uses: actions/setup-python@v5
419
440
  with: { python-version: '3.12' }
420
- - uses: ikaruscareer/SafeAI@v1
441
+ - uses: ikaruscareer/SafeAI@v2
421
442
  with: { path: '.', fail-on: critical }
422
443
  - uses: github/codeql-action/upload-sarif@v3
423
444
  if: always()
@@ -471,6 +492,8 @@ contributors who have helped make AI safer:
471
492
  | [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
472
493
  | [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
473
494
  | [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
495
+ | [@burakeyler](https://github.com/burakeyler) | `--digest-file` manifest digest sidecar (PR #148) |
496
+ | [@YaoSong808](https://github.com/YaoSong808) | OpenClaw and GitHub Copilot config-file adapters (PR #159) |
474
497
  | [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
475
498
  | [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
476
499
  | [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SafeAI-Static-Analyzer
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: The lightweight, open-source Static AI Capability & Risk Analyzer for CI/CD pipelines.
5
5
  Author: IkarusCareer
6
6
  License-Expression: Apache-2.0
@@ -36,12 +36,12 @@ Dynamic: license-file
36
36
  [![CI](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml/badge.svg)](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
37
37
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ikaruscareer/SafeAI/badge)](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
38
38
  [![Website](https://img.shields.io/badge/web-safeai--analyzer.ikaruscareer.com-0f766e)](https://safeai-analyzer.ikaruscareer.com)
39
- [![Latest Release](https://img.shields.io/badge/latest-v2.0.1-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.0.1)
39
+ [![Latest Release](https://img.shields.io/badge/latest-v2.3.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.3.0)
40
40
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
41
41
 
42
42
  Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
43
43
 
44
- **SafeAI** is a static analysis tool that scans AI application source code for security risks, capability exposure, and governance gaps. It runs entirely offline, never executes agents or calls LLMs, and integrates into CI/CD pipelines.
44
+ **SafeAI** is a static analysis tool that scans AI application source code for security risks, capability exposure, and governance gaps. It is offline and source-private by default — it never executes agents or calls LLMs, and network activity occurs only through explicitly enabled integration commands (currently only `--pr-comment-post`). It integrates into CI/CD pipelines.
45
45
 
46
46
  > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
47
47
 
@@ -84,7 +84,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
84
84
 
85
85
  | Feature | Description |
86
86
  |---------|-------------|
87
- | **Framework Detection** | Detects and parses 17 AI agent frameworks (AST + config + regex, no mutual exclusion) |
87
+ | **Framework Detection** | Detects and parses 19 AI agent frameworks (AST + config + regex, no mutual exclusion) |
88
88
  | **Tool Identity & Access Modes** | Capabilities attributed to named tools (agent / MCP server / skill / tool / workflow node) on an access scale `none < read < write < mutate < execute`; inferred modes are flagged, never overstated |
89
89
  | **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
90
90
  | **Capability Escalation Detection** | Per-tool authority diffs between scans (new shell, read→write widening, new MCP server, removed approval gate, ...) — 14 rules, including gating-aware subsumption |
@@ -170,6 +170,8 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
170
170
  | n8n | ✔ | Partial | Minimal | Minimal | Experimental |
171
171
  | Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
172
172
  | Windsurf (.windsurfrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
173
+ | OpenClaw config | ✔ | Minimal | Minimal | Minimal | Experimental |
174
+ | GitHub Copilot instructions | ✔ | Minimal | Minimal | Minimal | Experimental |
173
175
 
174
176
 
175
177
  ### Framework Support Details
@@ -194,6 +196,12 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
194
196
  - **Cursor (.cursorrules)** — detects declared tools/permissions and
195
197
  capability-relevant keywords (shell, filesystem, HTTP, database) in the
196
198
  IDE's rules config, JSON, YAML, or free text
199
+ - **Windsurf (.windsurfrules)** — detects declared tools, models, and
200
+ capability-relevant keywords in JSON, YAML, or free-text rules
201
+ - **OpenClaw config** — detects JSON/YAML files under `.openclaw/` and
202
+ `.openclaw.yaml`, including declared tools, models, and MCP servers
203
+ - **GitHub Copilot instructions** — detects repository instruction Markdown,
204
+ YAML frontmatter, and `.copilot/*.yml` tool or permission grants
197
205
 
198
206
  Maturity is on the scale defined in [`docs/reference/FRAMEWORK_SUPPORT.md`](docs/reference/FRAMEWORK_SUPPORT.md):
199
207
  **Partial** = reliable detection and discovery with capability/risk analysis over
@@ -211,6 +219,8 @@ Representative test fixtures and validation tests for framework detection:
211
219
  | LlamaIndex | `test_llamaindex_framework.py` | `fixtures/llamaindex/representative/agent.py` | @adnqcr7-code [#61] |
212
220
  | CrewAI | `test_crewai_framework.py` | `fixtures/crewai/representative/crew.py` | @adnqcr7-code [#62] |
213
221
  | Claude Code | `test_claude_code_deep.py` | `fixtures/claude_code/compatibility/` | @adnqcr7-code [#59] |
222
+ | OpenClaw config | `test_config_adapters.py` | `fixtures/openclaw/representative/.openclaw/config.json` | @YaoSong808 [#159] |
223
+ | GitHub Copilot instructions | `test_config_adapters.py` | `fixtures/copilot/representative/.github/copilot-instructions.md` | @YaoSong808 [#159] |
214
224
 
215
225
  ---
216
226
 
@@ -302,6 +312,16 @@ to a shared path, or use `--no-registry` for ephemeral scans.
302
312
  - Python 3.11, 3.12, or 3.13
303
313
  - PyYAML (for YAML configuration parsing)
304
314
 
315
+ ### Install from PyPI (recommended)
316
+
317
+ ```bash
318
+ pip install SafeAI-Static-Analyzer
319
+ ```
320
+
321
+ Unpinned on purpose — this always resolves to the latest stable
322
+ release (see [releases](https://github.com/ikaruscareer/SafeAI/releases)).
323
+ Verify the download with [`docs/reference/VERIFICATION.md`](docs/reference/VERIFICATION.md).
324
+
305
325
  ### Install from source
306
326
 
307
327
  ```bash
@@ -343,6 +363,7 @@ python -m safeai registry <subcommand> [options]
343
363
  | `--json` | — | JSON output path |
344
364
  | `--html` | — | HTML report output path |
345
365
  | `--manifest` | — | Canonical KYA manifest output path (`safeai-manifest.json`) |
366
+ | `--digest-file` | — | With `--manifest`: write `<canonical-sha256> <manifest-basename>` to this path. The digest is the one `safeai manifest verify` reports, not a raw file hash, so it is not `sha256sum -c` compatible |
346
367
  | `--baseline` | — | Prior manifest/report for new/existing comparison |
347
368
  | `--fail-on-new` | off | With `--baseline`: fail only on new/regressed findings |
348
369
  | `--policy` | `.safeai/policy.yml` | Policy-as-code YAML file |
@@ -450,7 +471,7 @@ jobs:
450
471
  - uses: actions/checkout@v4
451
472
  - uses: actions/setup-python@v5
452
473
  with: { python-version: '3.12' }
453
- - uses: ikaruscareer/SafeAI@v1
474
+ - uses: ikaruscareer/SafeAI@v2
454
475
  with: { path: '.', fail-on: critical }
455
476
  - uses: github/codeql-action/upload-sarif@v3
456
477
  if: always()
@@ -504,6 +525,8 @@ contributors who have helped make AI safer:
504
525
  | [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
505
526
  | [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
506
527
  | [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
528
+ | [@burakeyler](https://github.com/burakeyler) | `--digest-file` manifest digest sidecar (PR #148) |
529
+ | [@YaoSong808](https://github.com/YaoSong808) | OpenClaw and GitHub Copilot config-file adapters (PR #159) |
507
530
  | [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
508
531
  | [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
509
532
  | [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
@@ -83,6 +83,8 @@ safeai/frameworks/claude_code/commands.py
83
83
  safeai/frameworks/claude_code/parser.py
84
84
  safeai/frameworks/claude_code/permissions.py
85
85
  safeai/frameworks/claude_code/settings.py
86
+ safeai/frameworks/copilot/__init__.py
87
+ safeai/frameworks/copilot/parser.py
86
88
  safeai/frameworks/crewai/__init__.py
87
89
  safeai/frameworks/crewai/parser.py
88
90
  safeai/frameworks/cursorrules/__init__.py
@@ -107,6 +109,8 @@ safeai/frameworks/n8n/__init__.py
107
109
  safeai/frameworks/n8n/parser.py
108
110
  safeai/frameworks/openai_agents/__init__.py
109
111
  safeai/frameworks/openai_agents/parser.py
112
+ safeai/frameworks/openclaw/__init__.py
113
+ safeai/frameworks/openclaw/parser.py
110
114
  safeai/frameworks/semantic_kernel/__init__.py
111
115
  safeai/frameworks/semantic_kernel/parser.py
112
116
  safeai/frameworks/windsurf/__init__.py
@@ -122,6 +126,7 @@ safeai/kya/fingerprints.py
122
126
  safeai/kya/identity.py
123
127
  safeai/kya/importer.py
124
128
  safeai/kya/integrity.py
129
+ safeai/kya/lockfile.py
125
130
  safeai/kya/manifest.py
126
131
  safeai/kya/policy.py
127
132
  safeai/kya/suppressions.py
@@ -143,6 +148,7 @@ safeai/report/terminal.py
143
148
  safeai/rules/__init__.py
144
149
  safeai/rules/base_rules.yaml
145
150
  safeai/rules/loader.py
151
+ safeai/rules/pack_test.py
146
152
  safeai/scoring/__init__.py
147
153
  safeai/scoring/engine.py
148
154
  safeai/telemetry/__init__.py
@@ -161,6 +167,7 @@ tests/test_compatibility.py
161
167
  tests/test_component_graph.py
162
168
  tests/test_component_hash.py
163
169
  tests/test_confidence_arbitration.py
170
+ tests/test_config_adapters.py
164
171
  tests/test_control_mappings.py
165
172
  tests/test_crewai_framework.py
166
173
  tests/test_cursorrules_framework.py
@@ -185,6 +192,7 @@ tests/test_mcp_validation.py
185
192
  tests/test_n8n_workflow.py
186
193
  tests/test_parser_aggregation.py
187
194
  tests/test_phase15.py
195
+ tests/test_plugin_sdk.py
188
196
  tests/test_policy.py
189
197
  tests/test_pr_comment.py
190
198
  tests/test_pr_comment_cli.py
@@ -193,10 +201,12 @@ tests/test_registry.py
193
201
  tests/test_registry_cli.py
194
202
  tests/test_registry_html.py
195
203
  tests/test_regression_fixtures.py
204
+ tests/test_release_assets.py
196
205
  tests/test_release_fixes.py
197
206
  tests/test_release_validation.py
198
207
  tests/test_rules_loader_determinism.py
199
208
  tests/test_sarif.py
209
+ tests/test_scan_framework.py
200
210
  tests/test_scan_phase11.py
201
211
  tests/test_scorecard.py
202
212
  tests/test_scoring.py
@@ -55,6 +55,10 @@ safeai = "safeai.cmd.cli:main"
55
55
  # Built-in parsers are loaded directly by safeai.frameworks.
56
56
  [project.entry-points."safeai.parsers"]
57
57
 
58
+ # Third-party analyzer classes can expose themselves through this group.
59
+ # Built-in analyzers are loaded directly by safeai.analyzers.
60
+ [project.entry-points."safeai.analyzers"]
61
+
58
62
  [tool.ruff.lint]
59
63
  ignore = ["BLE001", "S110", "S112", "RUF012"]
60
64
  extend-per-file-ignores = { "tests/fixtures/action/**" = ["I001", "F841", "PLW1510", "T201"] }
@@ -0,0 +1,169 @@
1
+ """Analyzer registry and installed analyzer-plugin loader.
2
+
3
+ Analyzers register themselves here so that ``safeai.engine.orchestrator``
4
+ does not need a hard-coded class list. Built-in analyzers self-register
5
+ with a ``phase`` of ``"core"`` (run over file sources) or ``"component"``
6
+ (run over extracted components); the orchestrator preserves that order.
7
+
8
+ External plugins may register through the ``safeai.analyzers`` Python
9
+ entry point group or call ``register_analyzer`` at import time. External
10
+ analyzers run after built-ins in the component phase, must accept
11
+ ``components=None``, and never fail a scan: a raising external analyzer
12
+ is skipped with a warning (see orchestrator isolation).
13
+ """
14
+
15
+ import inspect
16
+ import logging
17
+ from importlib import import_module, metadata
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ CORE = "core"
22
+ COMPONENT = "component"
23
+ _PHASES = (CORE, COMPONENT)
24
+
25
+ # Each entry: {"name", "cls", "phase", "external", "version"}.
26
+ # ``version`` is the installed distribution version for entry-point
27
+ # plugins, else None (resolved to the SafeAI version at record time).
28
+ _ANALYZER_REGISTRY = []
29
+ _ANALYZER_NAMES = set()
30
+
31
+
32
+ def register_analyzer(cls=None, *, phase=CORE, external=False, version=None):
33
+ """Register an analyzer class.
34
+
35
+ Usable bare (``@register_analyzer``) for core analyzers or with
36
+ keywords (``@register_analyzer(phase="component")``).
37
+
38
+ The class must define a non-empty ``name`` and a ``run()`` method.
39
+ Component-phase analyzers must accept ``components=None``.
40
+ """
41
+
42
+ def _register(target):
43
+ name = getattr(target, "name", None)
44
+ run = getattr(target, "run", None)
45
+ if not name or not callable(run):
46
+ raise TypeError("Analyzer must define a non-empty name and run()")
47
+ if phase not in _PHASES:
48
+ raise ValueError(f"Analyzer phase must be one of {_PHASES}, got {phase!r}")
49
+ if phase == COMPONENT:
50
+ try:
51
+ params = inspect.signature(run).parameters
52
+ except (TypeError, ValueError):
53
+ params = {}
54
+ accepts_components = (
55
+ "components" in params
56
+ or any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values())
57
+ )
58
+ if not accepts_components:
59
+ raise TypeError(
60
+ f"Component-phase analyzer {name!r} must accept components=None"
61
+ )
62
+ if name not in _ANALYZER_NAMES:
63
+ _ANALYZER_REGISTRY.append(
64
+ {
65
+ "name": name,
66
+ "cls": target,
67
+ "phase": phase,
68
+ "external": external,
69
+ "version": version,
70
+ }
71
+ )
72
+ _ANALYZER_NAMES.add(name)
73
+ return target
74
+
75
+ if cls is None:
76
+ return _register
77
+ return _register(cls)
78
+
79
+
80
+ def _load_external_analyzers():
81
+ """Load analyzer classes exposed by installed ``safeai.analyzers`` plugins."""
82
+ try:
83
+ entries = metadata.entry_points()
84
+ if hasattr(entries, "select"):
85
+ entries = entries.select(group="safeai.analyzers")
86
+ else:
87
+ entries = entries.get("safeai.analyzers", [])
88
+ except Exception as exc:
89
+ logger.debug("Unable to enumerate analyzer plugins: %s", exc)
90
+ return
91
+
92
+ for entry in entries:
93
+ try:
94
+ dist_version = getattr(entry.dist, "version", None) if hasattr(entry, "dist") else None
95
+ register_analyzer(
96
+ entry.load(), phase=COMPONENT, external=True, version=dist_version
97
+ )
98
+ except Exception as exc:
99
+ logger.warning("Unable to load analyzer plugin %s: %s", entry.name, exc)
100
+
101
+
102
+ # Built-in analyzers as (module, class, phase) in legacy run order.
103
+ # The table (not import order) is the source of truth for run order, so
104
+ # ``_import_builtin_analyzers`` is idempotent and order-canonical no
105
+ # matter which modules were already imported.
106
+ _BUILTIN_ANALYZERS = (
107
+ ("safeai.analyzers.capability.analyzer", "CapabilityAnalyzer", CORE),
108
+ ("safeai.analyzers.prompt.analyzer", "PromptAnalyzer", CORE),
109
+ ("safeai.analyzers.data_leakage.analyzer", "DataLeakageAnalyzer", CORE),
110
+ ("safeai.analyzers.env_dependency.analyzer", "EnvDependencyAnalyzer", CORE),
111
+ ("safeai.analyzers.mcp.analyzer", "MCPAnalyzer", CORE),
112
+ ("safeai.analyzers.claude_code.analyzer", "ClaudeCodeAnalyzer", CORE),
113
+ ("safeai.analyzers.skill.analyzer", "SkillAnalyzer", COMPONENT),
114
+ ("safeai.analyzers.prompt_file.analyzer", "PromptFileAnalyzer", COMPONENT),
115
+ ("safeai.analyzers.tool_def.analyzer", "ToolDefAnalyzer", COMPONENT),
116
+ ("safeai.analyzers.model_config.analyzer", "ModelConfigAnalyzer", COMPONENT),
117
+ ("safeai.analyzers.workflow.analyzer", "WorkflowAnalyzer", COMPONENT),
118
+ ("safeai.analyzers.governance.analyzer", "GovernanceAnalyzer", COMPONENT),
119
+ ("safeai.analyzers.dataflow.analyzer", "DataFlowAnalyzer", COMPONENT),
120
+ )
121
+
122
+
123
+ def _import_builtin_analyzers():
124
+ """Register built-in analyzers in legacy run order (idempotent)."""
125
+ for module_name, class_name, phase in _BUILTIN_ANALYZERS:
126
+ module = import_module(module_name)
127
+ register_analyzer(getattr(module, class_name), phase=phase)
128
+
129
+
130
+ def discover_analyzers(phase=None, include_external=True):
131
+ """Return registered analyzer instances, built-ins first, in run order.
132
+
133
+ Parameters
134
+ ----------
135
+ phase : str, optional
136
+ ``"core"`` or ``"component"`` to filter; None returns all in phase
137
+ order (core first, then component).
138
+ """
139
+ _import_builtin_analyzers()
140
+ if include_external:
141
+ _load_external_analyzers()
142
+ entries = list(_ANALYZER_REGISTRY)
143
+ if phase is not None:
144
+ entries = [e for e in entries if e["phase"] == phase]
145
+ else:
146
+ entries = sorted(entries, key=lambda e: (e["phase"] != CORE,))
147
+ instances = []
148
+ for entry in entries:
149
+ instance = entry["cls"]()
150
+ # Marker consumed by the orchestrator for plugin isolation.
151
+ instance._safeai_external = entry["external"]
152
+ instances.append(instance)
153
+ return instances
154
+
155
+
156
+ def analyzer_records(include_external=True):
157
+ """Return registry metadata (name/phase/external/version) for recording."""
158
+ _import_builtin_analyzers()
159
+ if include_external:
160
+ _load_external_analyzers()
161
+ return [
162
+ {
163
+ "name": e["name"],
164
+ "phase": e["phase"],
165
+ "external": e["external"],
166
+ "version": e["version"],
167
+ }
168
+ for e in _ANALYZER_REGISTRY
169
+ ]
@@ -10,6 +10,8 @@ Operates in two passes:
10
10
 
11
11
  import re
12
12
 
13
+ from safeai.analyzers import register_analyzer
14
+
13
15
  CAP_PATTERNS = {
14
16
  "shell": re.compile(r"subprocess|os\.system|popen", re.IGNORECASE),
15
17
  "filesystem": re.compile(r"open\(|os\.remove|os\.write|pathlib", re.IGNORECASE),
@@ -99,6 +101,7 @@ def _iter_evidence(value):
99
101
  yield str(value)
100
102
 
101
103
 
104
+ @register_analyzer
102
105
  class CapabilityAnalyzer:
103
106
  name = "capability"
104
107
 
@@ -12,6 +12,7 @@ scope — reading it would leak a developer's personal environment into a
12
12
  CI artifact. Multi-scope discovery is deferred to a later release.
13
13
  """
14
14
 
15
+ from safeai.analyzers import register_analyzer
15
16
  from safeai.analyzers.prompt.analyzer import analyze_prompt_text
16
17
  from safeai.frameworks.claude_code import commands as cc_commands
17
18
  from safeai.frameworks.claude_code import permissions as cc_permissions
@@ -116,6 +117,7 @@ def _finding(rule_id, rule_map, message, path, line, evidence, reason, severity=
116
117
  }
117
118
 
118
119
 
120
+ @register_analyzer
119
121
  class ClaudeCodeAnalyzer:
120
122
  """Emit authority findings for Claude Code project configuration."""
121
123
 
@@ -11,6 +11,8 @@ full secret values.
11
11
 
12
12
  import re
13
13
 
14
+ from safeai.analyzers import register_analyzer
15
+
14
16
  PATTERNS = {
15
17
  "API_KEY": re.compile(r"(api[_-]?key)\s*=\s*[\"']?[A-Za-z0-9_-]{16,}" , re.IGNORECASE),
16
18
  "TOKEN": re.compile(r"(token)\s*=\s*[\"']?[A-Za-z0-9._-]{16,}", re.IGNORECASE),
@@ -77,6 +79,7 @@ def mask_secret_evidence(line):
77
79
  return _SECRET_VALUE_RE.sub(_repl, line.strip())
78
80
 
79
81
 
82
+ @register_analyzer
80
83
  class DataLeakageAnalyzer:
81
84
  name = "data_leakage"
82
85
 
@@ -16,6 +16,8 @@ scope, and are reported as such in each finding's ``limitation`` field.
16
16
  import ast
17
17
  import re
18
18
 
19
+ from safeai.analyzers import register_analyzer
20
+
19
21
  # Untrusted input sources
20
22
  SOURCE_PATTERNS = [
21
23
  re.compile(r"\b(?:user_input|user_message|user_query|user_prompt)\b", re.IGNORECASE),
@@ -300,6 +302,7 @@ def _finding(rule_id, rule, message, path, line, evidence=None):
300
302
  }
301
303
 
302
304
 
305
+ @register_analyzer(phase="component")
303
306
  class DataFlowAnalyzer:
304
307
  """Detects untrusted input propagation into sensitive sinks.
305
308
 
@@ -18,6 +18,8 @@ user-facing value.
18
18
 
19
19
  import re
20
20
 
21
+ from safeai.analyzers import register_analyzer
22
+
21
23
  INVENTORY_RULE_ID = "ENV_DEP_INVENTORY"
22
24
 
23
25
  #: Python ``os.getenv(...)`` / ``os.environ[...]`` / ``os.environ.get(...)``.
@@ -141,6 +143,7 @@ def analyze_file_inventory(path, content):
141
143
  return refs
142
144
 
143
145
 
146
+ @register_analyzer
144
147
  class EnvDependencyAnalyzer:
145
148
  """Detects references to external configuration and credentials."""
146
149
 
@@ -12,6 +12,8 @@ scoring category and HTML report governance summary section.
12
12
 
13
13
  import re
14
14
 
15
+ from safeai.analyzers import register_analyzer
16
+
15
17
  # Patterns for detecting governance controls in code
16
18
  _TIMEOUT_RE = re.compile(
17
19
  r"\b(?:timeout|time_out|request_timeout|connect_timeout|read_timeout)"
@@ -157,6 +159,7 @@ def _finding(rule_id, rule, message, path, line, tool_name=None, evidence=None,
157
159
  }
158
160
 
159
161
 
162
+ @register_analyzer(phase="component")
160
163
  class GovernanceAnalyzer:
161
164
  """Detects missing operational governance controls on agent tools.
162
165