SafeAI-Static-Analyzer 1.9.1__tar.gz → 2.0.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 (199) hide show
  1. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/PKG-INFO +23 -8
  2. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/README.md +22 -7
  3. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/SafeAI_Static_Analyzer.egg-info/PKG-INFO +23 -8
  4. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/SafeAI_Static_Analyzer.egg-info/SOURCES.txt +10 -1
  5. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/capability/analyzer.py +3 -0
  6. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/claude_code/analyzer.py +31 -10
  7. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/data_leakage/analyzer.py +1 -0
  8. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/dataflow/analyzer.py +155 -6
  9. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/env_dependency/analyzer.py +1 -0
  10. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/governance/analyzer.py +21 -1
  11. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/mcp/analyzer.py +75 -0
  12. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/model_config/analyzer.py +1 -0
  13. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/prompt/analyzer.py +9 -0
  14. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/prompt_file/analyzer.py +1 -0
  15. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/skill/analyzer.py +1 -0
  16. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/tool_def/analyzer.py +1 -0
  17. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/workflow/analyzer.py +1 -0
  18. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/cmd/postprocess.py +4 -0
  19. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/controls/mappings.py +54 -0
  20. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/engine/orchestrator.py +3 -1
  21. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/__init__.py +2 -0
  22. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/claude_code/permissions.py +130 -4
  23. safeai_static_analyzer-2.0.0/safeai/frameworks/cursorrules/parser.py +156 -0
  24. safeai_static_analyzer-2.0.0/safeai/frameworks/windsurf/parser.py +143 -0
  25. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/assurance.py +26 -0
  26. safeai_static_analyzer-2.0.0/safeai/report/failure_matrix.py +76 -0
  27. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/report/html.py +16 -0
  28. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/report/sarif.py +1 -0
  29. safeai_static_analyzer-2.0.0/safeai/rules/__init__.py +0 -0
  30. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/rules/base_rules.yaml +15 -0
  31. safeai_static_analyzer-2.0.0/safeai/scoring/__init__.py +0 -0
  32. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/version.py +1 -1
  33. safeai_static_analyzer-2.0.0/tests/test_adapter_dataflow.py +378 -0
  34. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_claude_code_deep.py +127 -5
  35. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_control_mappings.py +43 -0
  36. safeai_static_analyzer-2.0.0/tests/test_cursorrules_framework.py +112 -0
  37. safeai_static_analyzer-2.0.0/tests/test_evidence_type.py +86 -0
  38. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_governance_analyzer.py +80 -1
  39. safeai_static_analyzer-2.0.0/tests/test_mcp_tool_poisoning.py +147 -0
  40. safeai_static_analyzer-2.0.0/tests/test_windsurf_framework.py +112 -0
  41. safeai_static_analyzer-1.9.1/tests/test_adapter_dataflow.py +0 -183
  42. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/LICENSE +0 -0
  43. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/SafeAI_Static_Analyzer.egg-info/dependency_links.txt +0 -0
  44. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/SafeAI_Static_Analyzer.egg-info/entry_points.txt +0 -0
  45. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/SafeAI_Static_Analyzer.egg-info/requires.txt +0 -0
  46. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/SafeAI_Static_Analyzer.egg-info/top_level.txt +0 -0
  47. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/pyproject.toml +0 -0
  48. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/__init__.py +0 -0
  49. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/__main__.py +0 -0
  50. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/__init__.py +0 -0
  51. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/aggregation.py +0 -0
  52. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/capabilities.py +0 -0
  53. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/capability_diff.py +0 -0
  54. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/component_diff.py +0 -0
  55. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/component_graph.py +0 -0
  56. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/components.py +0 -0
  57. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/dependency_correlation.py +0 -0
  58. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/escalation.py +0 -0
  59. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/import_graph.py +0 -0
  60. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/project_graph.py +0 -0
  61. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/semantic.py +0 -0
  62. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/target_taxonomy.py +0 -0
  63. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/tool_identity.py +0 -0
  64. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/tool_implementation.py +0 -0
  65. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analysis/tool_surface.py +0 -0
  66. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/__init__.py +0 -0
  67. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/capability/__init__.py +0 -0
  68. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/claude_code/__init__.py +0 -0
  69. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/data_leakage/__init__.py +0 -0
  70. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/dataflow/__init__.py +0 -0
  71. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/env_dependency/__init__.py +0 -0
  72. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/governance/__init__.py +0 -0
  73. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/mcp/__init__.py +0 -0
  74. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/mcp/compatibility.py +0 -0
  75. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/mcp/schema.py +0 -0
  76. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/mcp/validators.py +0 -0
  77. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/model_config/__init__.py +0 -0
  78. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/prompt/__init__.py +0 -0
  79. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/prompt_file/__init__.py +0 -0
  80. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/skill/__init__.py +0 -0
  81. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/tool_def/__init__.py +0 -0
  82. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/analyzers/workflow/__init__.py +0 -0
  83. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/cmd/__init__.py +0 -0
  84. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/cmd/cli.py +0 -0
  85. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/cmd/registry_cli.py +0 -0
  86. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/controls/__init__.py +0 -0
  87. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/controls/catalogs.py +0 -0
  88. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/engine/__init__.py +0 -0
  89. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/engine/scan.py +0 -0
  90. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/autogen/parser.py +0 -0
  91. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/azure_foundry/__init__.py +0 -0
  92. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/azure_foundry/parser.py +0 -0
  93. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/bedrock_agent/__init__.py +0 -0
  94. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/bedrock_agent/parser.py +0 -0
  95. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/claude_code/__init__.py +0 -0
  96. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/claude_code/commands.py +0 -0
  97. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/claude_code/parser.py +0 -0
  98. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/claude_code/settings.py +0 -0
  99. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/crewai/__init__.py +0 -0
  100. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/crewai/parser.py +0 -0
  101. {safeai_static_analyzer-1.9.1/safeai/frameworks/dify → safeai_static_analyzer-2.0.0/safeai/frameworks/cursorrules}/__init__.py +0 -0
  102. {safeai_static_analyzer-1.9.1/safeai/frameworks/google_adk → safeai_static_analyzer-2.0.0/safeai/frameworks/dify}/__init__.py +0 -0
  103. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/dify/parser.py +0 -0
  104. {safeai_static_analyzer-1.9.1/safeai/frameworks/haystack → safeai_static_analyzer-2.0.0/safeai/frameworks/google_adk}/__init__.py +0 -0
  105. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/google_adk/parser.py +0 -0
  106. {safeai_static_analyzer-1.9.1/safeai/frameworks/langchain → safeai_static_analyzer-2.0.0/safeai/frameworks/haystack}/__init__.py +0 -0
  107. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/haystack/parser.py +0 -0
  108. {safeai_static_analyzer-1.9.1/safeai/frameworks/langgraph → safeai_static_analyzer-2.0.0/safeai/frameworks/langchain}/__init__.py +0 -0
  109. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/langchain/parser.py +0 -0
  110. {safeai_static_analyzer-1.9.1/safeai/frameworks/llamaindex → safeai_static_analyzer-2.0.0/safeai/frameworks/langgraph}/__init__.py +0 -0
  111. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/langgraph/parser.py +0 -0
  112. {safeai_static_analyzer-1.9.1/safeai/frameworks/mastra → safeai_static_analyzer-2.0.0/safeai/frameworks/llamaindex}/__init__.py +0 -0
  113. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/llamaindex/parser.py +0 -0
  114. {safeai_static_analyzer-1.9.1/safeai/frameworks/microsoft_agent → safeai_static_analyzer-2.0.0/safeai/frameworks/mastra}/__init__.py +0 -0
  115. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/mastra/parser.py +0 -0
  116. {safeai_static_analyzer-1.9.1/safeai/frameworks/n8n → safeai_static_analyzer-2.0.0/safeai/frameworks/microsoft_agent}/__init__.py +0 -0
  117. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/microsoft_agent/parser.py +0 -0
  118. {safeai_static_analyzer-1.9.1/safeai/frameworks/openai_agents → safeai_static_analyzer-2.0.0/safeai/frameworks/n8n}/__init__.py +0 -0
  119. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/n8n/parser.py +0 -0
  120. {safeai_static_analyzer-1.9.1/safeai/frameworks/semantic_kernel → safeai_static_analyzer-2.0.0/safeai/frameworks/openai_agents}/__init__.py +0 -0
  121. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/openai_agents/parser.py +0 -0
  122. {safeai_static_analyzer-1.9.1/safeai/report → safeai_static_analyzer-2.0.0/safeai/frameworks/semantic_kernel}/__init__.py +0 -0
  123. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/frameworks/semantic_kernel/parser.py +0 -0
  124. {safeai_static_analyzer-1.9.1/safeai/rules → safeai_static_analyzer-2.0.0/safeai/frameworks/windsurf}/__init__.py +0 -0
  125. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/__init__.py +0 -0
  126. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/baseline.py +0 -0
  127. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/ci_context.py +0 -0
  128. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/enrich.py +0 -0
  129. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/exporter.py +0 -0
  130. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/fingerprints.py +0 -0
  131. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/identity.py +0 -0
  132. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/manifest.py +0 -0
  133. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/policy.py +0 -0
  134. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/registry/__init__.py +0 -0
  135. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/registry/connection.py +0 -0
  136. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/registry/persist.py +0 -0
  137. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/registry/queries.py +0 -0
  138. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/registry/schema.py +0 -0
  139. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/suppressions.py +0 -0
  140. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/kya/util.py +0 -0
  141. {safeai_static_analyzer-1.9.1/safeai/scoring → safeai_static_analyzer-2.0.0/safeai/report}/__init__.py +0 -0
  142. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/report/html_kit.py +0 -0
  143. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/report/json_report.py +0 -0
  144. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/report/pr_comment.py +0 -0
  145. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/report/registry_html.py +0 -0
  146. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/report/terminal.py +0 -0
  147. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/rules/loader.py +0 -0
  148. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/scorecard.py +0 -0
  149. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/scoring/engine.py +0 -0
  150. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/severity.py +0 -0
  151. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/telemetry/__init__.py +0 -0
  152. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/telemetry/client.py +0 -0
  153. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/telemetry/config.py +0 -0
  154. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/safeai/telemetry/schema.py +0 -0
  155. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/setup.cfg +0 -0
  156. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_assurance.py +0 -0
  157. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_baseline.py +0 -0
  158. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_capability_detection.py +0 -0
  159. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_capability_diff_v2.py +0 -0
  160. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_ci_context.py +0 -0
  161. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_component_graph.py +0 -0
  162. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_component_hash.py +0 -0
  163. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_confidence_arbitration.py +0 -0
  164. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_crewai_framework.py +0 -0
  165. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_env_dependency.py +0 -0
  166. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_escalation.py +0 -0
  167. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_fingerprints.py +0 -0
  168. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_github_action.py +0 -0
  169. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_html_report.py +0 -0
  170. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_init_and_rules.py +0 -0
  171. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_kya_integration.py +0 -0
  172. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_kya_manifest.py +0 -0
  173. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_langgraph_framework.py +0 -0
  174. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_llamaindex_framework.py +0 -0
  175. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_mcp_validation.py +0 -0
  176. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_n8n_workflow.py +0 -0
  177. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_parser_aggregation.py +0 -0
  178. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_phase15.py +0 -0
  179. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_policy.py +0 -0
  180. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_pr_comment.py +0 -0
  181. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_pr_comment_cli.py +0 -0
  182. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_registry.py +0 -0
  183. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_registry_cli.py +0 -0
  184. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_registry_html.py +0 -0
  185. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_release_fixes.py +0 -0
  186. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_release_validation.py +0 -0
  187. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_rules_loader_determinism.py +0 -0
  188. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_sarif.py +0 -0
  189. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_scan_phase11.py +0 -0
  190. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_scorecard.py +0 -0
  191. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_scoring.py +0 -0
  192. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_semantic.py +0 -0
  193. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_severity.py +0 -0
  194. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_suppressions.py +0 -0
  195. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_target_taxonomy_engine.py +0 -0
  196. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_telemetry.py +0 -0
  197. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_tool_identity.py +0 -0
  198. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_tool_implementation.py +0 -0
  199. {safeai_static_analyzer-1.9.1 → safeai_static_analyzer-2.0.0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SafeAI-Static-Analyzer
3
- Version: 1.9.1
3
+ Version: 2.0.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
@@ -37,6 +37,8 @@ Dynamic: license-file
37
37
  [![Latest Release](https://img.shields.io/badge/latest-v1.9.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v1.9.0)
38
38
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
39
39
 
40
+ Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
41
+
40
42
  **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.
41
43
 
42
44
  > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
@@ -80,7 +82,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
80
82
 
81
83
  | Feature | Description |
82
84
  |---------|-------------|
83
- | **Framework Detection** | Detects and parses 15 AI agent frameworks (AST + config + regex, no mutual exclusion) |
85
+ | **Framework Detection** | Detects and parses 16 AI agent frameworks (AST + config + regex, no mutual exclusion) |
84
86
  | **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 |
85
87
  | **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
86
88
  | **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 |
@@ -116,7 +118,7 @@ File Collection — Python, YAML, JSON, .prompt, and .claude configs;
116
118
  prunes VCS, caches, oversized files, and SafeAI's own artifacts
117
119
  │
118
120
  ▼
119
- Framework Detection — 15 parsers (AST + config + regex), all run on all files;
121
+ Framework Detection — 16 parsers (AST + config + regex), all run on all files;
120
122
  import graph and dependency manifests
121
123
  │
122
124
  ▼
@@ -165,13 +167,14 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
165
167
  | LlamaIndex | ✔ | Partial | Minimal | Minimal | Experimental |
166
168
  | Dify | ✔ | Minimal | Minimal | Minimal | Experimental |
167
169
  | n8n | ✔ | Partial | Minimal | Minimal | Experimental |
170
+ | Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
168
171
 
169
172
 
170
173
  ### Framework Support Details
171
174
 
172
175
  - **LangGraph** — detects `StateGraph`, `add_edge`, `bind_tools`, nodes, models
173
176
  - **CrewAI** — detects `Agent`, `Task`, tools, models
174
- - **AutoGen** — detects `AssistantAgent`, `UserProxyAgent`, `register_for_llm`, agent delegation
177
+ - **AutoGen** — detects `AssistantAgent`, `UserProxyAgent`, `register_for_llm`, `register_function`, models
175
178
  - **LangChain** — detects `AgentExecutor`, `Chain`, `Tool`, `PromptTemplate`, models
176
179
  - **Semantic Kernel** — detects `Kernel.invoke`, plugins, functions, skills, memory
177
180
  - **OpenAI Agents SDK** — detects `Agent`, tools, handoffs, MCP references
@@ -186,6 +189,9 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
186
189
  - **LlamaIndex** — detects agents, tools, indexes, and model references
187
190
  - **Dify** — detects Dify workflow and agent configuration files
188
191
  - **n8n** — detects n8n workflow exports, nodes, and connections
192
+ - **Cursor (.cursorrules)** — detects declared tools/permissions and
193
+ capability-relevant keywords (shell, filesystem, HTTP, database) in the
194
+ IDE's rules config, JSON, YAML, or free text
189
195
 
190
196
  Maturity is on the scale defined in [`FRAMEWORK_SUPPORT.md`](FRAMEWORK_SUPPORT.md):
191
197
  **Partial** = reliable detection and discovery with capability/risk analysis over
@@ -761,10 +767,19 @@ See [ROADMAP.md](./ROADMAP.md) for the detailed roadmap.
761
767
  SafeAI is built by and for the AI security community. Thank you to all
762
768
  contributors who have helped make AI safer:
763
769
 
764
- - **[@Aming9303](https://github.com/Aming9303)** — `safeai registry components`
765
- CLI ([#82](https://github.com/ikaruscareer/SafeAI/pull/82)),
766
- `safeai init` command ([#83](https://github.com/ikaruscareer/SafeAI/pull/83)),
767
- GitHub Actions workflow example ([#84](https://github.com/ikaruscareer/SafeAI/pull/84)).
770
+ | Contributor | Key Contributions |
771
+ |-------------|-------------------|
772
+ | [@i-safonoff](https://github.com/i-safonoff) | `.cursorrules` framework adapter, `rule_coverage_summary()`, RULES_REFERENCE.md, dataflow casing fix |
773
+ | [@ARAVIND281](https://github.com/ARAVIND281) | Claude Code permission evaluation order, interprocedural data-flow tracking |
774
+ | [@Solarthis](https://github.com/Solarthis) | MCP tool description injection detection |
775
+ | [@Aming9303](https://github.com/Aming9303) | `safeai registry components` CLI, `safeai init` command, GitHub Actions example |
776
+ | [@adnqcr7-code](https://github.com/adnqcr7-code) | Framework detection tests (LangGraph, CrewAI, LlamaIndex, n8n, Claude Code), CI/SARIF docs |
777
+ | [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
778
+ | [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
779
+ | [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
780
+ | [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
781
+ | [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
782
+ | [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
768
783
 
769
784
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
770
785
 
@@ -6,6 +6,8 @@
6
6
  [![Latest Release](https://img.shields.io/badge/latest-v1.9.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v1.9.0)
7
7
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
8
8
 
9
+ Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
10
+
9
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.
10
12
 
11
13
  > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
@@ -49,7 +51,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
49
51
 
50
52
  | Feature | Description |
51
53
  |---------|-------------|
52
- | **Framework Detection** | Detects and parses 15 AI agent frameworks (AST + config + regex, no mutual exclusion) |
54
+ | **Framework Detection** | Detects and parses 16 AI agent frameworks (AST + config + regex, no mutual exclusion) |
53
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 |
54
56
  | **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
55
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 |
@@ -85,7 +87,7 @@ File Collection — Python, YAML, JSON, .prompt, and .claude configs;
85
87
  prunes VCS, caches, oversized files, and SafeAI's own artifacts
86
88
  │
87
89
  ▼
88
- Framework Detection — 15 parsers (AST + config + regex), all run on all files;
90
+ Framework Detection — 16 parsers (AST + config + regex), all run on all files;
89
91
  import graph and dependency manifests
90
92
  │
91
93
  ▼
@@ -134,13 +136,14 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
134
136
  | LlamaIndex | ✔ | Partial | Minimal | Minimal | Experimental |
135
137
  | Dify | ✔ | Minimal | Minimal | Minimal | Experimental |
136
138
  | n8n | ✔ | Partial | Minimal | Minimal | Experimental |
139
+ | Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
137
140
 
138
141
 
139
142
  ### Framework Support Details
140
143
 
141
144
  - **LangGraph** — detects `StateGraph`, `add_edge`, `bind_tools`, nodes, models
142
145
  - **CrewAI** — detects `Agent`, `Task`, tools, models
143
- - **AutoGen** — detects `AssistantAgent`, `UserProxyAgent`, `register_for_llm`, agent delegation
146
+ - **AutoGen** — detects `AssistantAgent`, `UserProxyAgent`, `register_for_llm`, `register_function`, models
144
147
  - **LangChain** — detects `AgentExecutor`, `Chain`, `Tool`, `PromptTemplate`, models
145
148
  - **Semantic Kernel** — detects `Kernel.invoke`, plugins, functions, skills, memory
146
149
  - **OpenAI Agents SDK** — detects `Agent`, tools, handoffs, MCP references
@@ -155,6 +158,9 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
155
158
  - **LlamaIndex** — detects agents, tools, indexes, and model references
156
159
  - **Dify** — detects Dify workflow and agent configuration files
157
160
  - **n8n** — detects n8n workflow exports, nodes, and connections
161
+ - **Cursor (.cursorrules)** — detects declared tools/permissions and
162
+ capability-relevant keywords (shell, filesystem, HTTP, database) in the
163
+ IDE's rules config, JSON, YAML, or free text
158
164
 
159
165
  Maturity is on the scale defined in [`FRAMEWORK_SUPPORT.md`](FRAMEWORK_SUPPORT.md):
160
166
  **Partial** = reliable detection and discovery with capability/risk analysis over
@@ -730,10 +736,19 @@ See [ROADMAP.md](./ROADMAP.md) for the detailed roadmap.
730
736
  SafeAI is built by and for the AI security community. Thank you to all
731
737
  contributors who have helped make AI safer:
732
738
 
733
- - **[@Aming9303](https://github.com/Aming9303)** — `safeai registry components`
734
- CLI ([#82](https://github.com/ikaruscareer/SafeAI/pull/82)),
735
- `safeai init` command ([#83](https://github.com/ikaruscareer/SafeAI/pull/83)),
736
- GitHub Actions workflow example ([#84](https://github.com/ikaruscareer/SafeAI/pull/84)).
739
+ | Contributor | Key Contributions |
740
+ |-------------|-------------------|
741
+ | [@i-safonoff](https://github.com/i-safonoff) | `.cursorrules` framework adapter, `rule_coverage_summary()`, RULES_REFERENCE.md, dataflow casing fix |
742
+ | [@ARAVIND281](https://github.com/ARAVIND281) | Claude Code permission evaluation order, interprocedural data-flow tracking |
743
+ | [@Solarthis](https://github.com/Solarthis) | MCP tool description injection detection |
744
+ | [@Aming9303](https://github.com/Aming9303) | `safeai registry components` CLI, `safeai init` command, GitHub Actions example |
745
+ | [@adnqcr7-code](https://github.com/adnqcr7-code) | Framework detection tests (LangGraph, CrewAI, LlamaIndex, n8n, Claude Code), CI/SARIF docs |
746
+ | [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
747
+ | [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
748
+ | [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
749
+ | [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
750
+ | [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
751
+ | [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
737
752
 
738
753
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
739
754
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SafeAI-Static-Analyzer
3
- Version: 1.9.1
3
+ Version: 2.0.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
@@ -37,6 +37,8 @@ Dynamic: license-file
37
37
  [![Latest Release](https://img.shields.io/badge/latest-v1.9.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v1.9.0)
38
38
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
39
39
 
40
+ Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
41
+
40
42
  **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.
41
43
 
42
44
  > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
@@ -80,7 +82,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
80
82
 
81
83
  | Feature | Description |
82
84
  |---------|-------------|
83
- | **Framework Detection** | Detects and parses 15 AI agent frameworks (AST + config + regex, no mutual exclusion) |
85
+ | **Framework Detection** | Detects and parses 16 AI agent frameworks (AST + config + regex, no mutual exclusion) |
84
86
  | **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 |
85
87
  | **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
86
88
  | **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 |
@@ -116,7 +118,7 @@ File Collection — Python, YAML, JSON, .prompt, and .claude configs;
116
118
  prunes VCS, caches, oversized files, and SafeAI's own artifacts
117
119
  │
118
120
  ▼
119
- Framework Detection — 15 parsers (AST + config + regex), all run on all files;
121
+ Framework Detection — 16 parsers (AST + config + regex), all run on all files;
120
122
  import graph and dependency manifests
121
123
  │
122
124
  ▼
@@ -165,13 +167,14 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
165
167
  | LlamaIndex | ✔ | Partial | Minimal | Minimal | Experimental |
166
168
  | Dify | ✔ | Minimal | Minimal | Minimal | Experimental |
167
169
  | n8n | ✔ | Partial | Minimal | Minimal | Experimental |
170
+ | Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
168
171
 
169
172
 
170
173
  ### Framework Support Details
171
174
 
172
175
  - **LangGraph** — detects `StateGraph`, `add_edge`, `bind_tools`, nodes, models
173
176
  - **CrewAI** — detects `Agent`, `Task`, tools, models
174
- - **AutoGen** — detects `AssistantAgent`, `UserProxyAgent`, `register_for_llm`, agent delegation
177
+ - **AutoGen** — detects `AssistantAgent`, `UserProxyAgent`, `register_for_llm`, `register_function`, models
175
178
  - **LangChain** — detects `AgentExecutor`, `Chain`, `Tool`, `PromptTemplate`, models
176
179
  - **Semantic Kernel** — detects `Kernel.invoke`, plugins, functions, skills, memory
177
180
  - **OpenAI Agents SDK** — detects `Agent`, tools, handoffs, MCP references
@@ -186,6 +189,9 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
186
189
  - **LlamaIndex** — detects agents, tools, indexes, and model references
187
190
  - **Dify** — detects Dify workflow and agent configuration files
188
191
  - **n8n** — detects n8n workflow exports, nodes, and connections
192
+ - **Cursor (.cursorrules)** — detects declared tools/permissions and
193
+ capability-relevant keywords (shell, filesystem, HTTP, database) in the
194
+ IDE's rules config, JSON, YAML, or free text
189
195
 
190
196
  Maturity is on the scale defined in [`FRAMEWORK_SUPPORT.md`](FRAMEWORK_SUPPORT.md):
191
197
  **Partial** = reliable detection and discovery with capability/risk analysis over
@@ -761,10 +767,19 @@ See [ROADMAP.md](./ROADMAP.md) for the detailed roadmap.
761
767
  SafeAI is built by and for the AI security community. Thank you to all
762
768
  contributors who have helped make AI safer:
763
769
 
764
- - **[@Aming9303](https://github.com/Aming9303)** — `safeai registry components`
765
- CLI ([#82](https://github.com/ikaruscareer/SafeAI/pull/82)),
766
- `safeai init` command ([#83](https://github.com/ikaruscareer/SafeAI/pull/83)),
767
- GitHub Actions workflow example ([#84](https://github.com/ikaruscareer/SafeAI/pull/84)).
770
+ | Contributor | Key Contributions |
771
+ |-------------|-------------------|
772
+ | [@i-safonoff](https://github.com/i-safonoff) | `.cursorrules` framework adapter, `rule_coverage_summary()`, RULES_REFERENCE.md, dataflow casing fix |
773
+ | [@ARAVIND281](https://github.com/ARAVIND281) | Claude Code permission evaluation order, interprocedural data-flow tracking |
774
+ | [@Solarthis](https://github.com/Solarthis) | MCP tool description injection detection |
775
+ | [@Aming9303](https://github.com/Aming9303) | `safeai registry components` CLI, `safeai init` command, GitHub Actions example |
776
+ | [@adnqcr7-code](https://github.com/adnqcr7-code) | Framework detection tests (LangGraph, CrewAI, LlamaIndex, n8n, Claude Code), CI/SARIF docs |
777
+ | [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
778
+ | [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
779
+ | [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
780
+ | [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
781
+ | [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
782
+ | [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
768
783
 
769
784
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
770
785
 
@@ -81,6 +81,8 @@ safeai/frameworks/claude_code/permissions.py
81
81
  safeai/frameworks/claude_code/settings.py
82
82
  safeai/frameworks/crewai/__init__.py
83
83
  safeai/frameworks/crewai/parser.py
84
+ safeai/frameworks/cursorrules/__init__.py
85
+ safeai/frameworks/cursorrules/parser.py
84
86
  safeai/frameworks/dify/__init__.py
85
87
  safeai/frameworks/dify/parser.py
86
88
  safeai/frameworks/google_adk/__init__.py
@@ -103,6 +105,8 @@ safeai/frameworks/openai_agents/__init__.py
103
105
  safeai/frameworks/openai_agents/parser.py
104
106
  safeai/frameworks/semantic_kernel/__init__.py
105
107
  safeai/frameworks/semantic_kernel/parser.py
108
+ safeai/frameworks/windsurf/__init__.py
109
+ safeai/frameworks/windsurf/parser.py
106
110
  safeai/kya/__init__.py
107
111
  safeai/kya/assurance.py
108
112
  safeai/kya/baseline.py
@@ -121,6 +125,7 @@ safeai/kya/registry/persist.py
121
125
  safeai/kya/registry/queries.py
122
126
  safeai/kya/registry/schema.py
123
127
  safeai/report/__init__.py
128
+ safeai/report/failure_matrix.py
124
129
  safeai/report/html.py
125
130
  safeai/report/html_kit.py
126
131
  safeai/report/json_report.py
@@ -149,8 +154,10 @@ tests/test_component_hash.py
149
154
  tests/test_confidence_arbitration.py
150
155
  tests/test_control_mappings.py
151
156
  tests/test_crewai_framework.py
157
+ tests/test_cursorrules_framework.py
152
158
  tests/test_env_dependency.py
153
159
  tests/test_escalation.py
160
+ tests/test_evidence_type.py
154
161
  tests/test_fingerprints.py
155
162
  tests/test_github_action.py
156
163
  tests/test_governance_analyzer.py
@@ -160,6 +167,7 @@ tests/test_kya_integration.py
160
167
  tests/test_kya_manifest.py
161
168
  tests/test_langgraph_framework.py
162
169
  tests/test_llamaindex_framework.py
170
+ tests/test_mcp_tool_poisoning.py
163
171
  tests/test_mcp_validation.py
164
172
  tests/test_n8n_workflow.py
165
173
  tests/test_parser_aggregation.py
@@ -184,4 +192,5 @@ tests/test_target_taxonomy_engine.py
184
192
  tests/test_telemetry.py
185
193
  tests/test_tool_identity.py
186
194
  tests/test_tool_implementation.py
187
- tests/test_version.py
195
+ tests/test_version.py
196
+ tests/test_windsurf_framework.py
@@ -72,6 +72,7 @@ CATEGORY_BY_CAP = {
72
72
  def _finding(rule_id, rule, message, path, line, capability, framework="generic", evidence=None, confidence=0.6, score_contribution=8):
73
73
  return {
74
74
  "rule_id": rule_id,
75
+ "evidence_type": "static-pattern", # #94 - regex fallback pass over raw file content
75
76
  "severity": rule.get("severity", "medium"),
76
77
  "message": message,
77
78
  "file": path,
@@ -160,6 +161,7 @@ class CapabilityAnalyzer:
160
161
 
161
162
  findings.append({
162
163
  "rule_id": rule_id,
164
+ "evidence_type": "static-pattern", # #94 - regex fallback pass over raw file content
163
165
  "severity": rule.get("severity", "medium"),
164
166
  "message": f"Capability discovered: {cap_name}",
165
167
  "file": path,
@@ -249,6 +251,7 @@ class CapabilityAnalyzer:
249
251
  rule = rule_map.get("CAP_AUTONOMY", {})
250
252
  findings.append({
251
253
  "rule_id": "CAP_AUTONOMY",
254
+ "evidence_type": "static-pattern", # #94 - regex fallback pass over raw file content
252
255
  "severity": rule.get("severity", "high"),
253
256
  "message": "Potential autonomous agent loop detected",
254
257
  "file": path,
@@ -22,8 +22,9 @@ _REMEDIATION = {
22
22
  "command or path pattern the workflow needs.",
23
23
  "CC_BYPASS_PERMISSIONS": "Remove the bypass/auto-approve setting and keep the "
24
24
  "human approval gate enabled in committed settings.",
25
- "CC_DENY_SHADOWED": "Narrow the allow entry so the deny entry can take effect, "
26
- "or remove the deny entry that cannot apply.",
25
+ "CC_DENY_SHADOWED": "Remove the allow entry that cannot apply, or narrow the "
26
+ "deny entry if the allow was the intended behaviour. The "
27
+ "deny is what takes effect.",
27
28
  "CC_FS_WRITE_OUTSIDE_ROOT": "Constrain write grants to paths inside the project root.",
28
29
  "CC_SLASH_COMMAND_SHELL": "Pin the command, avoid inline shell in stored "
29
30
  "instructions, and review who can invoke it.",
@@ -98,6 +99,7 @@ def _finding(rule_id, rule_map, message, path, line, evidence, reason, severity=
98
99
  resolved = severity or rule.get("severity") or _DEFAULT_SEVERITY.get(rule_id, "medium")
99
100
  return {
100
101
  "rule_id": rule_id,
102
+ "evidence_type": "static-config", # #94 - reads declared permission/hook/settings entries
101
103
  "severity": resolved,
102
104
  "message": message,
103
105
  "file": path,
@@ -204,15 +206,27 @@ class ClaudeCodeAnalyzer:
204
206
 
205
207
  def _permission_findings(self, records, rule_map):
206
208
  findings = []
209
+ effective = cc_permissions.resolve_effective_rules(records)
207
210
  for record in records:
208
211
  if record["decision"] == "allow" and record["wildcard"] and record["tool"]:
212
+ # #93 - a wildcard allow grants nothing the tool's denies do not
213
+ # already take back, at any scope. Report it, but not as though
214
+ # the surface were unbounded when it demonstrably is not.
215
+ constrained = effective.get(str(record["tool"]).lower(), {}).get("decision") == "deny"
209
216
  findings.append(_finding(
210
217
  "CC_WILDCARD_PERMISSION", rule_map,
211
218
  f"Unconstrained Claude Code permission: {record['entry']}",
212
219
  record["path"], record["line"],
213
220
  evidence=record["entry"],
214
- reason=f"{record['tool']} is allowed with no argument constraint, granting "
215
- f"{record['access_mode']} authority over its entire surface.",
221
+ reason=(
222
+ f"{record['tool']} is allowed with no argument constraint, but a deny "
223
+ f"rule for the same tool is evaluated first, so the granted surface is "
224
+ f"bounded by that deny."
225
+ if constrained else
226
+ f"{record['tool']} is allowed with no argument constraint, granting "
227
+ f"{record['access_mode']} authority over its entire surface."
228
+ ),
229
+ severity="medium" if constrained else None,
216
230
  ))
217
231
  if cc_permissions.writes_outside_root(record):
218
232
  findings.append(_finding(
@@ -225,14 +239,21 @@ class ClaudeCodeAnalyzer:
225
239
  capability="Filesystem",
226
240
  ))
227
241
 
228
- for deny, allow in cc_permissions.shadowed_denials(records):
242
+ # #93 - this finding used to be stated the wrong way round. Claude Code
243
+ # evaluates deny, then ask, then allow, and "rule specificity doesn't
244
+ # change the order", so a matching deny ALWAYS decides the call. It is
245
+ # the allow that is dead configuration, not the deny, and the severity
246
+ # follows: contradictory rules are a maintenance problem, not a hole.
247
+ for allow, deny in cc_permissions.ineffective_allows(records):
229
248
  findings.append(_finding(
230
249
  "CC_DENY_SHADOWED", rule_map,
231
- f"Deny entry is shadowed by a broader allow: {deny['entry']}",
232
- deny["path"], deny["line"],
233
- evidence=f"deny {deny['entry']} vs allow {allow['entry']}",
234
- reason="The deny entry cannot take effect because a broader allow entry covers "
235
- "it, so the configuration reads as safer than it behaves.",
250
+ f"Allow entry can never apply, a deny covers it: {allow['entry']}",
251
+ allow["path"], allow["line"],
252
+ evidence=f"allow {allow['entry']} overridden by deny {deny['entry']}",
253
+ reason="Claude Code evaluates deny before allow and specificity does not change "
254
+ "that, so the deny decides every matching call and this allow widens "
255
+ "nothing. The deny is effective; the allow is dead configuration.",
256
+ severity="low",
236
257
  ))
237
258
  return findings
238
259
 
@@ -94,6 +94,7 @@ class DataLeakageAnalyzer:
94
94
  )
95
95
  findings.append({
96
96
  "rule_id": "DATA_LEAKAGE",
97
+ "evidence_type": "static-pattern", # #94 - regex patterns over source text
97
98
  "severity": severity,
98
99
  "message": f"Potential secret exposure: {key}",
99
100
  "file": path,
@@ -6,8 +6,14 @@ file operations). Uses line-level proxy heuristics for propagation tracking.
6
6
 
7
7
  This is a heuristic analysis — not a full interprocedural data-flow solver.
8
8
  It provides a best-effort detection of common taint patterns in agent code.
9
+
10
+ Propagation is tracked within a function, and across ONE direct call to a
11
+ function defined in the same file (#96). Cross-file flows, recursion, dynamic
12
+ dispatch and calls made through an attribute or a variable remain out of
13
+ scope, and are reported as such in each finding's ``limitation`` field.
9
14
  """
10
15
 
16
+ import ast
11
17
  import re
12
18
 
13
19
  # Untrusted input sources
@@ -26,7 +32,12 @@ SINK_PATTERNS = {
26
32
  "prompt": re.compile(r"\b(?:prompt|system_prompt|user_prompt|chat_history|messages\.append)\b", re.IGNORECASE),
27
33
  "tool_call": re.compile(r"\b(?:tool_call|invoke_tool|call_tool|execute_tool)\b", re.IGNORECASE),
28
34
  "shell": re.compile(r"\b(?:subprocess|os\.system|popen|exec\(|eval\()\b", re.IGNORECASE),
29
- "file_write": re.compile(r"\bopen\([^)]*[\"'](?:w|a|x)[bt+]?[\"']\b", re.IGNORECASE),
35
+ # The trailing \b this pattern used to carry could never match: it sat
36
+ # after the closing quote of the mode string, and the next character is
37
+ # always ')' — two non-word characters, so no boundary exists. The rule
38
+ # was unreachable, which is why #96's acceptance criterion could not be
39
+ # met without this. Found while wiring interprocedural tracking.
40
+ "file_write": re.compile(r"\bopen\([^)]*[\"'](?:w|a|x)[bt+]*[\"']", re.IGNORECASE),
30
41
  "http_request": re.compile(r"\b(?:requests\.(?:get|post|put|delete)|httpx\.)\b", re.IGNORECASE),
31
42
  "database": re.compile(r"\b(?:execute|cursor\.execute|query)\b", re.IGNORECASE),
32
43
  }
@@ -134,10 +145,135 @@ def _track_propagation(content, sources, sinks):
134
145
  return findings
135
146
 
136
147
 
148
+ def _same_file_call_graph(content):
149
+ """Map same-file functions and the direct calls into them (#96).
150
+
151
+ Returns ``(functions, calls)`` where ``functions`` maps a function name to
152
+ its positional parameter names and body line span, and ``calls`` is a list
153
+ of direct call sites with the plain-name arguments they were given.
154
+
155
+ Deliberately shallow. Only ``def``/``async def`` at any nesting level, only
156
+ calls written as a bare name, and only arguments that are bare names are
157
+ considered — a call through an attribute (``obj.method(x)``), a variable
158
+ holding a function, or a computed argument is not resolved. That keeps the
159
+ pass conservative in the direction that matters: it can miss a real flow,
160
+ but it will not invent a callee.
161
+
162
+ Returns ``(None, None)`` when the file does not parse. The analyzer is run
163
+ over whatever is on disk, which includes files mid-edit and Python 2, and a
164
+ syntax error must not take the whole analyzer down.
165
+ """
166
+ try:
167
+ tree = ast.parse(content)
168
+ except (SyntaxError, ValueError, RecursionError):
169
+ return None, None
170
+
171
+ functions = {}
172
+ for node in ast.walk(tree):
173
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
174
+ end = getattr(node, "end_lineno", None)
175
+ if end is None: # pragma: no cover - Python < 3.8 shape
176
+ continue
177
+ functions[node.name] = {
178
+ "params": [a.arg for a in node.args.args],
179
+ "start": node.lineno,
180
+ "end": end,
181
+ }
182
+
183
+ calls = []
184
+ for node in ast.walk(tree):
185
+ if not isinstance(node, ast.Call):
186
+ continue
187
+ if not isinstance(node.func, ast.Name):
188
+ continue
189
+ positional = [
190
+ arg.id if isinstance(arg, ast.Name) else None for arg in node.args
191
+ ]
192
+ keywords = {
193
+ kw.arg: kw.value.id
194
+ for kw in node.keywords
195
+ if kw.arg and isinstance(kw.value, ast.Name)
196
+ }
197
+ calls.append({
198
+ "callee": node.func.id,
199
+ "line": node.lineno,
200
+ "positional": positional,
201
+ "keywords": keywords,
202
+ })
203
+
204
+ return functions, calls
205
+
206
+
207
+ def _track_interprocedural(content, sources, sinks):
208
+ """Follow taint one call deep, within the same file (#96).
209
+
210
+ The intraprocedural pass requires the sink to appear *after* the source
211
+ line. That is wrong across a call boundary and not by a little: a helper is
212
+ usually defined ABOVE the code that calls it, so its sinks sit at lower line
213
+ numbers than the tainted argument. This pass therefore does not apply that
214
+ ordering rule to the callee body — the ordering that matters is
215
+ source-before-CALL, which is checked here.
216
+
217
+ One level only, and no recursion: a call inside the callee is not followed.
218
+ """
219
+ functions, calls = _same_file_call_graph(content)
220
+ if not functions or not calls:
221
+ return []
222
+
223
+ findings = []
224
+ for source in sources:
225
+ base_var = source["variable"].split(".")[0].split("(")[0]
226
+ if not base_var.isidentifier():
227
+ continue
228
+
229
+ for call in calls:
230
+ # The taint has to exist before it can be passed.
231
+ if call["line"] < source["line"]:
232
+ continue
233
+ callee = functions.get(call["callee"])
234
+ if callee is None:
235
+ continue
236
+
237
+ # Which parameter did the tainted variable land on?
238
+ tainted_params = [
239
+ callee["params"][index]
240
+ for index, arg in enumerate(call["positional"])
241
+ if arg == base_var and index < len(callee["params"])
242
+ ]
243
+ tainted_params += [
244
+ name for name, value in call["keywords"].items()
245
+ if value == base_var and name in callee["params"]
246
+ ]
247
+ if not tainted_params:
248
+ continue
249
+
250
+ for sink in sinks:
251
+ if not (callee["start"] <= sink["line"] <= callee["end"]):
252
+ continue
253
+ if not any(
254
+ re.search(rf"\b{re.escape(param)}\b", sink["sink_text"])
255
+ for param in tainted_params
256
+ ):
257
+ continue
258
+ findings.append({
259
+ "source_line": source["line"],
260
+ "source_var": base_var,
261
+ "sink_line": sink["line"],
262
+ "sink_type": sink["sink_type"],
263
+ "source_text": source["source_text"],
264
+ "sink_text": sink["sink_text"],
265
+ "via_call": call["callee"],
266
+ "call_line": call["line"],
267
+ })
268
+
269
+ return findings
270
+
271
+
137
272
  def _finding(rule_id, rule, message, path, line, evidence=None):
138
273
  """Create a data-flow finding dict, deriving severity/owasp from the rule."""
139
274
  return {
140
275
  "rule_id": rule_id,
276
+ "evidence_type": "static-pattern", # #94 - line-level taint heuristic over source text
141
277
  "severity": rule.get("severity", "high"),
142
278
  "message": message,
143
279
  "file": path,
@@ -155,9 +291,11 @@ def _finding(rule_id, rule, message, path, line, evidence=None):
155
291
  "confidence": "heuristic",
156
292
  "scope": "static-analysis",
157
293
  "limitation": (
158
- "Line-level proxy heuristic; not a full interprocedural data-flow "
159
- "solver. May miss indirect propagation or produce false positives "
160
- "on dynamic constructs."
294
+ "Line-level proxy heuristic. Follows taint one direct call deep "
295
+ "within a single file (#96); does NOT cross files, follow "
296
+ "recursion, resolve dynamic dispatch, or resolve a call made "
297
+ "through an attribute or a variable. May miss indirect "
298
+ "propagation or produce false positives on dynamic constructs."
161
299
  ),
162
300
  }
163
301
 
@@ -193,6 +331,9 @@ class DataFlowAnalyzer:
193
331
  continue
194
332
 
195
333
  propagations = _track_propagation(content, sources, sinks)
334
+ # #96 - one call deep, same file. Appended rather than merged: the
335
+ # dedupe key below already collapses a flow both passes find.
336
+ propagations += _track_interprocedural(content, sources, sinks)
196
337
 
197
338
  for prop in propagations:
198
339
  key = (path, prop["source_line"], prop["sink_line"], prop["sink_type"])
@@ -200,7 +341,7 @@ class DataFlowAnalyzer:
200
341
  continue
201
342
  seen.add(key)
202
343
 
203
- rule_id = f"DATAFLOW_{prop['sink_type'].upper()}"
344
+ rule_id = f"DATAFLOW_{prop['sink_type']}"
204
345
  rule = rule_map.get(rule_id, {})
205
346
  findings.append(_finding(
206
347
  rule_id=rule_id,
@@ -213,7 +354,15 @@ class DataFlowAnalyzer:
213
354
  ),
214
355
  path=path,
215
356
  line=prop["sink_line"],
216
- evidence=f"source:{prop['source_var']}@L{prop['source_line']} -> sink:{prop['sink_type']}@L{prop['sink_line']}",
357
+ evidence=(
358
+ f"source:{prop['source_var']}@L{prop['source_line']} -> "
359
+ + (
360
+ f"call:{prop['via_call']}()@L{prop['call_line']} -> "
361
+ if prop.get("via_call")
362
+ else ""
363
+ )
364
+ + f"sink:{prop['sink_type']}@L{prop['sink_line']}"
365
+ ),
217
366
  ))
218
367
 
219
368
  return findings
@@ -186,6 +186,7 @@ def _wrap_inventory(inventory, rules):
186
186
  rule = rule_map.get(INVENTORY_RULE_ID, {})
187
187
  return [{
188
188
  "rule_id": INVENTORY_RULE_ID,
189
+ "evidence_type": "static-config", # #94 - inventories declared config references, names only
189
190
  "severity": rule.get("severity", "info"),
190
191
  "message": f"Referenced {len(inventory)} external configuration/credential names",
191
192
  "file": "<scan>",