SafeAI-Static-Analyzer 2.5.0__tar.gz → 2.7.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 (263) hide show
  1. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/PKG-INFO +7 -5
  2. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/README.md +5 -3
  3. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/PKG-INFO +7 -5
  4. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/SOURCES.txt +13 -0
  5. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/requires.txt +1 -1
  6. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/pyproject.toml +1 -1
  7. safeai_static_analyzer-2.7.0/safeai/analysis/evidence_metrics.py +188 -0
  8. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/import_graph.py +8 -2
  9. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/semantic.py +7 -2
  10. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/capability/analyzer.py +80 -0
  11. safeai_static_analyzer-2.7.0/safeai/benchmark/__init__.py +21 -0
  12. safeai_static_analyzer-2.7.0/safeai/benchmark/canonical.py +1603 -0
  13. safeai_static_analyzer-2.7.0/safeai/benchmark/compare.py +529 -0
  14. safeai_static_analyzer-2.7.0/safeai/benchmark/metrics.py +540 -0
  15. safeai_static_analyzer-2.7.0/safeai/benchmark/report.py +546 -0
  16. safeai_static_analyzer-2.7.0/safeai/benchmark/runner.py +344 -0
  17. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/cli.py +53 -0
  18. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/postprocess.py +25 -3
  19. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/controls/mappings.py +4 -0
  20. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/exceptions.py +28 -2
  21. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/manifest.py +7 -0
  22. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/html.py +239 -2
  23. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/html_kit.py +4 -1
  24. safeai_static_analyzer-2.7.0/safeai/report/security_brief.py +499 -0
  25. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/base_rules.yaml +5 -0
  26. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/scorecard.py +1 -0
  27. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/version.py +1 -1
  28. safeai_static_analyzer-2.7.0/tests/test_authority_benchmark.py +607 -0
  29. safeai_static_analyzer-2.7.0/tests/test_authority_canonical.py +583 -0
  30. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_capability_detection.py +21 -0
  31. safeai_static_analyzer-2.7.0/tests/test_evidence_metrics.py +187 -0
  32. safeai_static_analyzer-2.7.0/tests/test_exceptions.py +538 -0
  33. safeai_static_analyzer-2.7.0/tests/test_html_report.py +257 -0
  34. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_workflow.py +29 -0
  35. safeai_static_analyzer-2.7.0/tests/test_security_brief.py +248 -0
  36. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_semantic.py +27 -0
  37. safeai_static_analyzer-2.7.0/tests/test_subprocess_shell_detection.py +85 -0
  38. safeai_static_analyzer-2.5.0/tests/test_exceptions.py +0 -284
  39. safeai_static_analyzer-2.5.0/tests/test_html_report.py +0 -135
  40. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/LICENSE +0 -0
  41. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/dependency_links.txt +0 -0
  42. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/entry_points.txt +0 -0
  43. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/top_level.txt +0 -0
  44. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/__init__.py +0 -0
  45. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/__main__.py +0 -0
  46. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/__init__.py +0 -0
  47. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/aggregation.py +0 -0
  48. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/capabilities.py +0 -0
  49. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/capability_diff.py +0 -0
  50. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/component_diff.py +0 -0
  51. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/component_graph.py +0 -0
  52. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/components.py +0 -0
  53. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/dependency_correlation.py +0 -0
  54. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/escalation.py +0 -0
  55. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/escalation_remediation.py +0 -0
  56. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/iac_correlation.py +0 -0
  57. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/project_graph.py +0 -0
  58. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/target_taxonomy.py +0 -0
  59. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/tool_identity.py +0 -0
  60. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/tool_implementation.py +0 -0
  61. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/tool_surface.py +0 -0
  62. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/__init__.py +0 -0
  63. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/capability/__init__.py +0 -0
  64. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/claude_code/__init__.py +0 -0
  65. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/claude_code/analyzer.py +0 -0
  66. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/data_leakage/__init__.py +0 -0
  67. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/data_leakage/analyzer.py +0 -0
  68. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/dataflow/__init__.py +0 -0
  69. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/dataflow/analyzer.py +0 -0
  70. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/env_dependency/__init__.py +0 -0
  71. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/env_dependency/analyzer.py +0 -0
  72. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/governance/__init__.py +0 -0
  73. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/governance/analyzer.py +0 -0
  74. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/__init__.py +0 -0
  75. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/analyzer.py +0 -0
  76. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/compatibility.py +0 -0
  77. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/schema.py +0 -0
  78. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/validators.py +0 -0
  79. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/model_config/__init__.py +0 -0
  80. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/model_config/analyzer.py +0 -0
  81. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt/__init__.py +0 -0
  82. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt/analyzer.py +0 -0
  83. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt_file/__init__.py +0 -0
  84. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt_file/analyzer.py +0 -0
  85. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/skill/__init__.py +0 -0
  86. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/skill/analyzer.py +0 -0
  87. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/tool_def/__init__.py +0 -0
  88. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/tool_def/analyzer.py +0 -0
  89. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/workflow/__init__.py +0 -0
  90. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/workflow/analyzer.py +0 -0
  91. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/__init__.py +0 -0
  92. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/manifest_cli.py +0 -0
  93. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/registry_cli.py +0 -0
  94. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/controls/__init__.py +0 -0
  95. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/controls/catalogs.py +0 -0
  96. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/__init__.py +0 -0
  97. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/file_discovery.py +0 -0
  98. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/metadata.py +0 -0
  99. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/orchestrator.py +0 -0
  100. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/scan.py +0 -0
  101. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/__init__.py +0 -0
  102. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/autogen/parser.py +0 -0
  103. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/azure_foundry/__init__.py +0 -0
  104. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/azure_foundry/parser.py +0 -0
  105. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/bedrock_agent/__init__.py +0 -0
  106. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/bedrock_agent/parser.py +0 -0
  107. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/__init__.py +0 -0
  108. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/commands.py +0 -0
  109. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/parser.py +0 -0
  110. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/permissions.py +0 -0
  111. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/settings.py +0 -0
  112. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/copilot/__init__.py +0 -0
  113. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/copilot/parser.py +0 -0
  114. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/crewai/__init__.py +0 -0
  115. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/crewai/parser.py +0 -0
  116. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/cursorrules/__init__.py +0 -0
  117. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/cursorrules/parser.py +0 -0
  118. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/dify/__init__.py +0 -0
  119. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/dify/parser.py +0 -0
  120. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/google_adk/__init__.py +0 -0
  121. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/google_adk/parser.py +0 -0
  122. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/haystack/__init__.py +0 -0
  123. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/haystack/parser.py +0 -0
  124. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langchain/__init__.py +0 -0
  125. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langchain/parser.py +0 -0
  126. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langgraph/__init__.py +0 -0
  127. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langgraph/parser.py +0 -0
  128. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/llamaindex/__init__.py +0 -0
  129. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/llamaindex/parser.py +0 -0
  130. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/mastra/__init__.py +0 -0
  131. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/mastra/parser.py +0 -0
  132. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/microsoft_agent/__init__.py +0 -0
  133. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/microsoft_agent/parser.py +0 -0
  134. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/n8n/__init__.py +0 -0
  135. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/n8n/parser.py +0 -0
  136. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openai_agents/__init__.py +0 -0
  137. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openai_agents/parser.py +0 -0
  138. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openclaw/__init__.py +0 -0
  139. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openclaw/parser.py +0 -0
  140. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/semantic_kernel/__init__.py +0 -0
  141. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/semantic_kernel/parser.py +0 -0
  142. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/windsurf/__init__.py +0 -0
  143. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/windsurf/parser.py +0 -0
  144. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/__init__.py +0 -0
  145. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/k8s.py +0 -0
  146. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/linking.py +0 -0
  147. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/model.py +0 -0
  148. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/semantics.py +0 -0
  149. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/terraform.py +0 -0
  150. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/__init__.py +0 -0
  151. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/assurance.py +0 -0
  152. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/baseline.py +0 -0
  153. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/ci_context.py +0 -0
  154. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/contract.py +0 -0
  155. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/enrich.py +0 -0
  156. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/exporter.py +0 -0
  157. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/fingerprints.py +0 -0
  158. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/identity.py +0 -0
  159. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/importer.py +0 -0
  160. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/integrity.py +0 -0
  161. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/lockfile.py +0 -0
  162. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/policy.py +0 -0
  163. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/__init__.py +0 -0
  164. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/connection.py +0 -0
  165. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/persist.py +0 -0
  166. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/queries.py +0 -0
  167. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/schema.py +0 -0
  168. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/suppressions.py +0 -0
  169. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/util.py +0 -0
  170. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/__init__.py +0 -0
  171. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/failure_matrix.py +0 -0
  172. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/json_report.py +0 -0
  173. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/pr_comment.py +0 -0
  174. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/registry_html.py +0 -0
  175. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/sarif.py +0 -0
  176. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/terminal.py +0 -0
  177. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/__init__.py +0 -0
  178. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/loader.py +0 -0
  179. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/pack_test.py +0 -0
  180. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/scoring/__init__.py +0 -0
  181. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/scoring/engine.py +0 -0
  182. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/severity.py +0 -0
  183. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/__init__.py +0 -0
  184. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/client.py +0 -0
  185. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/config.py +0 -0
  186. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/schema.py +0 -0
  187. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/setup.cfg +0 -0
  188. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_adapter_dataflow.py +0 -0
  189. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_architectural_invariants.py +0 -0
  190. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_architecture_diagram.py +0 -0
  191. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_assurance.py +0 -0
  192. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_authority_change.py +0 -0
  193. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_baseline.py +0 -0
  194. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_baseline_compat.py +0 -0
  195. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_benchmarks.py +0 -0
  196. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_capability_diff_v2.py +0 -0
  197. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_ci_context.py +0 -0
  198. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_claude_code_deep.py +0 -0
  199. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_compatibility.py +0 -0
  200. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_component_graph.py +0 -0
  201. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_component_hash.py +0 -0
  202. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_confidence_arbitration.py +0 -0
  203. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_config_adapters.py +0 -0
  204. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_control_mappings.py +0 -0
  205. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_crewai_framework.py +0 -0
  206. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_cursorrules_framework.py +0 -0
  207. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_dco.py +0 -0
  208. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_env_dependency.py +0 -0
  209. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_escalation.py +0 -0
  210. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_escalation_remediation.py +0 -0
  211. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_evidence_contract_v7.py +0 -0
  212. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_evidence_type.py +0 -0
  213. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_fingerprints.py +0 -0
  214. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_github_action.py +0 -0
  215. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_governance_analyzer.py +0 -0
  216. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_adversarial.py +0 -0
  217. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_benchmark.py +0 -0
  218. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_correlation.py +0 -0
  219. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_k8s.py +0 -0
  220. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_linking.py +0 -0
  221. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_semantics.py +0 -0
  222. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_terraform.py +0 -0
  223. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_init_and_rules.py +0 -0
  224. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_kya_integration.py +0 -0
  225. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_kya_manifest.py +0 -0
  226. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_langgraph_framework.py +0 -0
  227. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_llamaindex_framework.py +0 -0
  228. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_manifest_contract.py +0 -0
  229. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_manifest_integrity.py +0 -0
  230. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_mcp_tool_poisoning.py +0 -0
  231. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_mcp_validation.py +0 -0
  232. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_n8n_workflow.py +0 -0
  233. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_parser_aggregation.py +0 -0
  234. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_phase15.py +0 -0
  235. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_plugin_sdk.py +0 -0
  236. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_policy.py +0 -0
  237. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_pr_comment.py +0 -0
  238. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_pr_comment_cli.py +0 -0
  239. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_pr_paths.py +0 -0
  240. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_quality_gates.py +0 -0
  241. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_registry.py +0 -0
  242. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_registry_cli.py +0 -0
  243. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_registry_html.py +0 -0
  244. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_regression_fixtures.py +0 -0
  245. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_assets.py +0 -0
  246. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_fixes.py +0 -0
  247. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_validation.py +0 -0
  248. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_rules_loader_determinism.py +0 -0
  249. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_sarif.py +0 -0
  250. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scan_framework.py +0 -0
  251. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scan_phase11.py +0 -0
  252. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scorecard.py +0 -0
  253. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scoring.py +0 -0
  254. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_severity.py +0 -0
  255. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_suppressions.py +0 -0
  256. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_target_taxonomy_engine.py +0 -0
  257. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_telemetry.py +0 -0
  258. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_terminal_report.py +0 -0
  259. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_tool_identity.py +0 -0
  260. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_tool_implementation.py +0 -0
  261. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_unknown_authority.py +0 -0
  262. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_version.py +0 -0
  263. {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.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.5.0
3
+ Version: 2.7.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
@@ -23,7 +23,7 @@ Requires-Dist: PyYAML>=6.0.3
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: pytest>=8.0; extra == "dev"
25
25
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
26
- Requires-Dist: ruff<0.17,>=0.16.1; extra == "dev"
26
+ Requires-Dist: ruff<0.18,>=0.16.1; extra == "dev"
27
27
  Requires-Dist: jinja2>=3.1.4; extra == "dev"
28
28
  Requires-Dist: jsonschema>=4.22.0; extra == "dev"
29
29
  Requires-Dist: typing-extensions>=4.4.0; extra == "dev"
@@ -35,8 +35,9 @@ Dynamic: license-file
35
35
 
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
- [![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.4.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.4.0)
38
+ [![Research Hub](https://img.shields.io/badge/research-ikaruslabs.net-0f766e)](https://ikaruslabs.net)
39
+ [![SafeAI](https://img.shields.io/badge/SafeAI-ikaruslabs.net/safeai-0f766e)](https://ikaruslabs.net/safeai)
40
+ [![Latest Release](https://img.shields.io/badge/latest-v2.7.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.7.0)
40
41
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
41
42
 
42
43
  Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
@@ -55,7 +56,7 @@ Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps
55
56
 
56
57
  > SafeAI is not a runtime guardrail and does not certify an agent as safe. It provides source-first, evidence-backed visibility into agent authority and meaningful changes *before deployment*.
57
58
 
58
- > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
59
+ > 🌐 [Ikarus AI Labs Research Hub](https://ikaruslabs.net) — SafeAI: [ikaruslabs.net/safeai](https://ikaruslabs.net/safeai) | OpenPulse: [ikaruslabs.net/openpulse](https://ikaruslabs.net/openpulse) | Research: [ikaruslabs.net/research](https://ikaruslabs.net/research)
59
60
 
60
61
  <img width="1024" height="1024" alt="SafeAI_Agent_Software_Static_Analyzer" src="https://github.com/user-attachments/assets/de40f40b-14b9-4cd6-bc2c-27e81e8253fe" />
61
62
 
@@ -608,6 +609,7 @@ contributors who have helped make AI safer:
608
609
  | [@chenzeyan54-commits](https://github.com/chenzeyan54-commits) | Terminal output sanitization against ANSI/OSC injection (PR #203) |
609
610
  | [@HarshRajSinghania](https://github.com/HarshRajSinghania) | PR comment markdown injection sanitization (PR #204) |
610
611
  | [@thadidaniel-ctrl](https://github.com/thadidaniel-ctrl) | Release workflow supply-chain posture tests (PR #206) |
612
+ | [@Golden007-prog](https://github.com/Golden007-prog) | Authority Evidence Benchmark HTML presentation, history limit (PRs #239, #240) |
611
613
 
612
614
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
613
615
 
@@ -2,8 +2,9 @@
2
2
 
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
- [![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.4.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.4.0)
5
+ [![Research Hub](https://img.shields.io/badge/research-ikaruslabs.net-0f766e)](https://ikaruslabs.net)
6
+ [![SafeAI](https://img.shields.io/badge/SafeAI-ikaruslabs.net/safeai-0f766e)](https://ikaruslabs.net/safeai)
7
+ [![Latest Release](https://img.shields.io/badge/latest-v2.7.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.7.0)
7
8
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
8
9
 
9
10
  Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
@@ -22,7 +23,7 @@ Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps
22
23
 
23
24
  > SafeAI is not a runtime guardrail and does not certify an agent as safe. It provides source-first, evidence-backed visibility into agent authority and meaningful changes *before deployment*.
24
25
 
25
- > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
26
+ > 🌐 [Ikarus AI Labs Research Hub](https://ikaruslabs.net) — SafeAI: [ikaruslabs.net/safeai](https://ikaruslabs.net/safeai) | OpenPulse: [ikaruslabs.net/openpulse](https://ikaruslabs.net/openpulse) | Research: [ikaruslabs.net/research](https://ikaruslabs.net/research)
26
27
 
27
28
  <img width="1024" height="1024" alt="SafeAI_Agent_Software_Static_Analyzer" src="https://github.com/user-attachments/assets/de40f40b-14b9-4cd6-bc2c-27e81e8253fe" />
28
29
 
@@ -575,6 +576,7 @@ contributors who have helped make AI safer:
575
576
  | [@chenzeyan54-commits](https://github.com/chenzeyan54-commits) | Terminal output sanitization against ANSI/OSC injection (PR #203) |
576
577
  | [@HarshRajSinghania](https://github.com/HarshRajSinghania) | PR comment markdown injection sanitization (PR #204) |
577
578
  | [@thadidaniel-ctrl](https://github.com/thadidaniel-ctrl) | Release workflow supply-chain posture tests (PR #206) |
579
+ | [@Golden007-prog](https://github.com/Golden007-prog) | Authority Evidence Benchmark HTML presentation, history limit (PRs #239, #240) |
578
580
 
579
581
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
580
582
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SafeAI-Static-Analyzer
3
- Version: 2.5.0
3
+ Version: 2.7.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
@@ -23,7 +23,7 @@ Requires-Dist: PyYAML>=6.0.3
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: pytest>=8.0; extra == "dev"
25
25
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
26
- Requires-Dist: ruff<0.17,>=0.16.1; extra == "dev"
26
+ Requires-Dist: ruff<0.18,>=0.16.1; extra == "dev"
27
27
  Requires-Dist: jinja2>=3.1.4; extra == "dev"
28
28
  Requires-Dist: jsonschema>=4.22.0; extra == "dev"
29
29
  Requires-Dist: typing-extensions>=4.4.0; extra == "dev"
@@ -35,8 +35,9 @@ Dynamic: license-file
35
35
 
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
- [![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.4.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.4.0)
38
+ [![Research Hub](https://img.shields.io/badge/research-ikaruslabs.net-0f766e)](https://ikaruslabs.net)
39
+ [![SafeAI](https://img.shields.io/badge/SafeAI-ikaruslabs.net/safeai-0f766e)](https://ikaruslabs.net/safeai)
40
+ [![Latest Release](https://img.shields.io/badge/latest-v2.7.0-0f766e)](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.7.0)
40
41
  [![Best Practices](https://bestpractices.dev/projects/14126/badge)](https://www.bestpractices.dev/en/projects/14126)
41
42
 
42
43
  Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
@@ -55,7 +56,7 @@ Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps
55
56
 
56
57
  > SafeAI is not a runtime guardrail and does not certify an agent as safe. It provides source-first, evidence-backed visibility into agent authority and meaningful changes *before deployment*.
57
58
 
58
- > 🌐 [safeai-analyzer.ikaruscareer.com](https://safeai-analyzer.ikaruscareer.com) — project landing page
59
+ > 🌐 [Ikarus AI Labs Research Hub](https://ikaruslabs.net) — SafeAI: [ikaruslabs.net/safeai](https://ikaruslabs.net/safeai) | OpenPulse: [ikaruslabs.net/openpulse](https://ikaruslabs.net/openpulse) | Research: [ikaruslabs.net/research](https://ikaruslabs.net/research)
59
60
 
60
61
  <img width="1024" height="1024" alt="SafeAI_Agent_Software_Static_Analyzer" src="https://github.com/user-attachments/assets/de40f40b-14b9-4cd6-bc2c-27e81e8253fe" />
61
62
 
@@ -608,6 +609,7 @@ contributors who have helped make AI safer:
608
609
  | [@chenzeyan54-commits](https://github.com/chenzeyan54-commits) | Terminal output sanitization against ANSI/OSC injection (PR #203) |
609
610
  | [@HarshRajSinghania](https://github.com/HarshRajSinghania) | PR comment markdown injection sanitization (PR #204) |
610
611
  | [@thadidaniel-ctrl](https://github.com/thadidaniel-ctrl) | Release workflow supply-chain posture tests (PR #206) |
612
+ | [@Golden007-prog](https://github.com/Golden007-prog) | Authority Evidence Benchmark HTML presentation, history limit (PRs #239, #240) |
611
613
 
612
614
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
613
615
 
@@ -22,6 +22,7 @@ safeai/analysis/components.py
22
22
  safeai/analysis/dependency_correlation.py
23
23
  safeai/analysis/escalation.py
24
24
  safeai/analysis/escalation_remediation.py
25
+ safeai/analysis/evidence_metrics.py
25
26
  safeai/analysis/iac_correlation.py
26
27
  safeai/analysis/import_graph.py
27
28
  safeai/analysis/project_graph.py
@@ -60,6 +61,12 @@ safeai/analyzers/tool_def/__init__.py
60
61
  safeai/analyzers/tool_def/analyzer.py
61
62
  safeai/analyzers/workflow/__init__.py
62
63
  safeai/analyzers/workflow/analyzer.py
64
+ safeai/benchmark/__init__.py
65
+ safeai/benchmark/canonical.py
66
+ safeai/benchmark/compare.py
67
+ safeai/benchmark/metrics.py
68
+ safeai/benchmark/report.py
69
+ safeai/benchmark/runner.py
63
70
  safeai/cmd/__init__.py
64
71
  safeai/cmd/cli.py
65
72
  safeai/cmd/manifest_cli.py
@@ -152,6 +159,7 @@ safeai/report/json_report.py
152
159
  safeai/report/pr_comment.py
153
160
  safeai/report/registry_html.py
154
161
  safeai/report/sarif.py
162
+ safeai/report/security_brief.py
155
163
  safeai/report/terminal.py
156
164
  safeai/rules/__init__.py
157
165
  safeai/rules/base_rules.yaml
@@ -167,6 +175,8 @@ tests/test_adapter_dataflow.py
167
175
  tests/test_architectural_invariants.py
168
176
  tests/test_architecture_diagram.py
169
177
  tests/test_assurance.py
178
+ tests/test_authority_benchmark.py
179
+ tests/test_authority_canonical.py
170
180
  tests/test_authority_change.py
171
181
  tests/test_baseline.py
172
182
  tests/test_baseline_compat.py
@@ -188,6 +198,7 @@ tests/test_env_dependency.py
188
198
  tests/test_escalation.py
189
199
  tests/test_escalation_remediation.py
190
200
  tests/test_evidence_contract_v7.py
201
+ tests/test_evidence_metrics.py
191
202
  tests/test_evidence_type.py
192
203
  tests/test_exceptions.py
193
204
  tests/test_fingerprints.py
@@ -233,8 +244,10 @@ tests/test_scan_framework.py
233
244
  tests/test_scan_phase11.py
234
245
  tests/test_scorecard.py
235
246
  tests/test_scoring.py
247
+ tests/test_security_brief.py
236
248
  tests/test_semantic.py
237
249
  tests/test_severity.py
250
+ tests/test_subprocess_shell_detection.py
238
251
  tests/test_suppressions.py
239
252
  tests/test_target_taxonomy_engine.py
240
253
  tests/test_telemetry.py
@@ -3,7 +3,7 @@ PyYAML>=6.0.3
3
3
  [dev]
4
4
  pytest>=8.0
5
5
  pytest-cov>=5.0
6
- ruff<0.17,>=0.16.1
6
+ ruff<0.18,>=0.16.1
7
7
  jinja2>=3.1.4
8
8
  jsonschema>=4.22.0
9
9
  typing-extensions>=4.4.0
@@ -34,7 +34,7 @@ dependencies = [
34
34
  dev = [
35
35
  "pytest>=8.0",
36
36
  "pytest-cov>=5.0",
37
- "ruff>=0.16.1,<0.17",
37
+ "ruff>=0.16.1,<0.18",
38
38
  "jinja2>=3.1.4",
39
39
  "jsonschema>=4.22.0",
40
40
  "typing-extensions>=4.4.0",
@@ -0,0 +1,188 @@
1
+ """Authority evidence metrics: how much of a scan's authority picture is
2
+ evidence-backed, inferred, unattributed, or unknown.
3
+
4
+ ROADMAP Layer 2 research metric and the P0 "unknown-rate surfacing" gap:
5
+ the provenance fields SafeAI already emits are aggregated into a small,
6
+ deterministic block attached to the JSON report and the manifest summary.
7
+
8
+ Rules:
9
+ * Pure and deterministic — identical input yields identical output.
10
+ * Statement and finding metrics are computed from manifest-shaped data
11
+ only (``tool_surface`` entries and finding ``provenance_class``), so an
12
+ independent consumer can recompute them from a manifest without
13
+ SafeAI internals. Authority-dimension metrics need the baseline diff's
14
+ per-tool ``authority`` blocks (attributed tools plus the unattributed
15
+ entry) and are absent without a baseline.
16
+ * Rates follow the benchmark convention: an empty denominator is
17
+ ``insufficient_evidence`` with ``value: None`` — never 0, never 1.
18
+ * Unknown is counted, never reduced or reinterpreted. These metrics
19
+ describe evidence quality; no gate, policy, or score consumes them.
20
+ """
21
+
22
+ from safeai.analysis.tool_identity import UNATTRIBUTED_KEY
23
+ from safeai.kya.contract import PROVENANCE_CLASSES
24
+
25
+ METRICS_VERSION = 1
26
+
27
+ #: Per-tool authority dimensions carried by ``capability_diff`` tools
28
+ #: (``authority_for`` in ``safeai/analysis/capability_diff.py``).
29
+ AUTHORITY_DIMENSIONS = (
30
+ "destinations", "data_scope", "approval", "autonomy",
31
+ "delegation", "credential", "identity",
32
+ )
33
+
34
+ #: Measurements this block deliberately does not make, with the reason.
35
+ NOT_MEASURED = (
36
+ {"metric": "false_attribution_rate",
37
+ "reason": "requires annotated ground truth; measured by `safeai benchmark`"},
38
+ )
39
+
40
+
41
+ def _ratio(numerator, denominator, reason):
42
+ if denominator <= 0:
43
+ return {"value": None, "status": "insufficient_evidence",
44
+ "numerator": 0, "denominator": 0, "reason": reason}
45
+ return {"value": round(numerator / denominator, 4), "status": "measured",
46
+ "numerator": numerator, "denominator": denominator}
47
+
48
+
49
+ def _capability_statements(tool_surface):
50
+ """Yield ``(tool_key, capability)`` for every authority statement."""
51
+ for tool in tool_surface or []:
52
+ if not isinstance(tool, dict):
53
+ continue
54
+ key = str(tool.get("tool_key") or "")
55
+ for capability in tool.get("capabilities") or []:
56
+ if isinstance(capability, dict):
57
+ yield key, capability
58
+
59
+
60
+ def _statement_counts(tool_surface):
61
+ counts = {"total": 0, "evidence_backed": 0, "inferred": 0,
62
+ "unattributed": 0, "with_evidence_refs": 0}
63
+ for key, capability in _capability_statements(tool_surface):
64
+ inferred = bool(capability.get("inferred"))
65
+ unattributed = key == UNATTRIBUTED_KEY
66
+ has_refs = bool(capability.get("evidence"))
67
+ counts["total"] += 1
68
+ counts["inferred"] += inferred
69
+ counts["unattributed"] += unattributed
70
+ counts["with_evidence_refs"] += has_refs
71
+ counts["evidence_backed"] += (not inferred and not unattributed and has_refs)
72
+ return counts
73
+
74
+
75
+ def _finding_provenance(findings):
76
+ buckets = {cls: 0 for cls in PROVENANCE_CLASSES}
77
+ for finding in findings or []:
78
+ if not isinstance(finding, dict):
79
+ continue
80
+ cls = str(finding.get("provenance_class") or "unknown")
81
+ # An out-of-contract class is not evidence of anything: unknown.
82
+ buckets[cls if cls in buckets else "unknown"] += 1
83
+ return dict(sorted(buckets.items()))
84
+
85
+
86
+ def _authority_dimensions(diff_tools):
87
+ """Unknown counts per authority dimension across diffed tools.
88
+
89
+ Returns ``None`` when no diffed tool carries an ``authority`` block
90
+ (no baseline comparison), so the metric is absent rather than empty.
91
+ """
92
+ by_dimension = {dim: {"total": 0, "unknown": 0} for dim in AUTHORITY_DIMENSIONS}
93
+ seen = False
94
+ for tool in diff_tools or []:
95
+ authority = tool.get("authority") if isinstance(tool, dict) else None
96
+ if not isinstance(authority, dict):
97
+ continue
98
+ seen = True
99
+ for dim in AUTHORITY_DIMENSIONS:
100
+ block = authority.get(dim)
101
+ provenance = block.get("provenance_class") if isinstance(block, dict) else None
102
+ by_dimension[dim]["total"] += 1
103
+ by_dimension[dim]["unknown"] += (provenance in (None, "unknown"))
104
+ return by_dimension if seen else None
105
+
106
+
107
+ def _diffed_tools(capability_diff):
108
+ """Attributed tools plus the separately carried unattributed entry."""
109
+ if not isinstance(capability_diff, dict):
110
+ return []
111
+ tools = list(capability_diff.get("tools") or [])
112
+ if isinstance(capability_diff.get("unattributed"), dict):
113
+ tools.append(capability_diff["unattributed"])
114
+ return tools
115
+
116
+
117
+ def compute_evidence_metrics(tool_surface, findings, capability_diff=None):
118
+ """Return the evidence-metrics block for one scan.
119
+
120
+ Parameters
121
+ ----------
122
+ tool_surface : list
123
+ Per-tool capability surface (report or manifest ``tool_surface``).
124
+ findings : list
125
+ Findings carrying ``provenance_class`` (report or manifest).
126
+ capability_diff : dict, optional
127
+ The report's ``capability_diff`` from a baseline comparison.
128
+ """
129
+ statements = _statement_counts(tool_surface)
130
+ provenance = _finding_provenance(findings)
131
+ dimensions = _authority_dimensions(_diffed_tools(capability_diff))
132
+ total = statements["total"]
133
+ no_statements = "no authority statements in this scan"
134
+
135
+ rates = {
136
+ "evidence_backed_authority_rate": _ratio(
137
+ statements["evidence_backed"], total, no_statements),
138
+ "inferred_authority_rate": _ratio(
139
+ statements["inferred"], total, no_statements),
140
+ "unattributed_authority_rate": _ratio(
141
+ statements["unattributed"], total, no_statements),
142
+ "evidence_completeness": _ratio(
143
+ statements["with_evidence_refs"], total, no_statements),
144
+ "unknown_finding_rate": _ratio(
145
+ provenance["unknown"], sum(provenance.values()),
146
+ "no findings in this scan"),
147
+ }
148
+ if dimensions is None:
149
+ rates["unknown_authority_dimension_rate"] = _ratio(
150
+ 0, 0, "no baseline comparison in this scan")
151
+ else:
152
+ rates["unknown_authority_dimension_rate"] = _ratio(
153
+ sum(d["unknown"] for d in dimensions.values()),
154
+ sum(d["total"] for d in dimensions.values()),
155
+ "no diffed tools carry authority dimensions")
156
+
157
+ return {
158
+ "metrics_version": METRICS_VERSION,
159
+ "authority_statements": statements,
160
+ "finding_provenance": provenance,
161
+ "authority_dimensions": dimensions,
162
+ "rates": dict(sorted(rates.items())),
163
+ "not_measured": [dict(item) for item in NOT_MEASURED],
164
+ }
165
+
166
+
167
+ def summary_lines(metrics):
168
+ """Short factual lines for human-facing reports (deterministic)."""
169
+ if not isinstance(metrics, dict):
170
+ return []
171
+ rates = metrics.get("rates") or {}
172
+
173
+ def fmt(name):
174
+ entry = rates.get(name) or {}
175
+ if entry.get("status") != "measured":
176
+ return "insufficient evidence"
177
+ return "{:.0%} ({}/{})".format(
178
+ entry["value"], entry["numerator"], entry["denominator"])
179
+
180
+ lines = [
181
+ "Evidence-backed authority: " + fmt("evidence_backed_authority_rate"),
182
+ "Inferred authority: " + fmt("inferred_authority_rate"),
183
+ "Unattributed authority: " + fmt("unattributed_authority_rate"),
184
+ ]
185
+ if (rates.get("unknown_authority_dimension_rate") or {}).get("status") == "measured":
186
+ lines.append("Unknown authority dimensions: "
187
+ + fmt("unknown_authority_dimension_rate"))
188
+ return lines
@@ -51,7 +51,7 @@ class ImportGraph:
51
51
  def resolve_module(self, module_name):
52
52
  return self.module_to_file.get(module_name)
53
53
 
54
- def resolve_symbol(self, qualified_name):
54
+ def resolve_symbol(self, qualified_name, _visited=None):
55
55
  """Map a fully-qualified symbol to its defining module and file.
56
56
 
57
57
  Tries progressively shorter module prefixes (e.g. ``a.b.c.Var`` ->
@@ -59,6 +59,12 @@ class ImportGraph:
59
59
  chains when a symbol is re-exported through ``__init__.py``.
60
60
  Returns a dict with keys ``module``, ``file``, and ``symbol``, or ``None``.
61
61
  """
62
+ if _visited is None:
63
+ _visited = set()
64
+ if qualified_name in _visited:
65
+ return None
66
+ _visited.add(qualified_name)
67
+
62
68
  parts = qualified_name.split(".")
63
69
  for i in range(len(parts), 0, -1):
64
70
  module = ".".join(parts[:i])
@@ -70,7 +76,7 @@ class ImportGraph:
70
76
  direct = symbol.split(".")[0]
71
77
  target = self.symbol_index[path].get(direct)
72
78
  if isinstance(target, dict) and target.get("kind") == "reexport":
73
- return self.resolve_symbol(target.get("target"))
79
+ return self.resolve_symbol(target.get("target"), _visited)
74
80
  return {
75
81
  "module": module,
76
82
  "file": path,
@@ -78,14 +78,19 @@ def _literal_value(node):
78
78
  return None
79
79
 
80
80
 
81
- def _resolve_from_module(module, level, current_module):
81
+ def _resolve_from_module(module, level, current_module, path=""):
82
82
  """Resolve a relative import to an absolute module name.
83
83
 
84
84
  ``level`` corresponds to the number of leading dots in a ``from ... import``.
85
+ When ``path`` ends with ``__init__.py`` the level is reduced by one because
86
+ a relative import from a package's ``__init__`` is resolved relative to the
87
+ package itself, not one level deeper.
85
88
  """
86
89
  if level == 0:
87
90
  return module or ""
88
91
  parts = current_module.split(".") if current_module else []
92
+ if path.endswith("__init__.py"):
93
+ level = max(0, level - 1)
89
94
  keep = max(0, len(parts) - level)
90
95
  prefix = ".".join(parts[:keep])
91
96
  if module:
@@ -112,7 +117,7 @@ def build_semantic_document(path, content, module_name=""):
112
117
  local = alias.asname or alias.name.split(".")[0]
113
118
  doc.imports[local] = alias.name
114
119
  elif isinstance(node, ast.ImportFrom):
115
- base = _resolve_from_module(node.module, node.level, module_name)
120
+ base = _resolve_from_module(node.module, node.level, module_name, path)
116
121
  for alias in node.names:
117
122
  local = alias.asname or alias.name
118
123
  doc.from_imports[local] = f"{base}.{alias.name}" if base else alias.name
@@ -8,6 +8,7 @@ Operates in two passes:
8
8
  Also detects autonomous agent loop patterns.
9
9
  """
10
10
 
11
+ import ast
11
12
  import re
12
13
 
13
14
  from safeai.analyzers import register_analyzer
@@ -23,6 +24,7 @@ CAP_PATTERNS = {
23
24
  "redis": re.compile(r"import redis|from redis|Redis\(|redis\.StrictRedis|redis\.Redis", re.IGNORECASE),
24
25
  "s3": re.compile(r"import boto3|from boto3|boto3\.client.*s3|boto3\.resource.*s3|S3Client|s3\.put|s3\.get", re.IGNORECASE),
25
26
  "slack": re.compile(r"import slack|from slack|SlackClient|slack_sdk|slack\.WebClient|slack\.SocketModeClient", re.IGNORECASE),
27
+ "ms_teams": re.compile(r"(?:\bimport\s+teams\b|\bfrom\s+teams\s+import\b|\bTeamsClient\b|\bteams_sdk\b|\bimport\s+pymsteams\b|\bfrom\s+pymsteams\b|\bpymsteams\.(?:connectorcard|cardsection)\b)", re.IGNORECASE),
26
28
  "jira": re.compile(r"import jira|from jira|JIRA\(|jira\.Client|jira\.JIRA", re.IGNORECASE),
27
29
  "browser_playwright": re.compile(r"import playwright|from playwright|Playwright", re.IGNORECASE),
28
30
  "browser_selenium": re.compile(r"import selenium|from selenium|webdriver|WebDriver", re.IGNORECASE),
@@ -32,6 +34,60 @@ CAP_PATTERNS = {
32
34
 
33
35
  # Higher-risk variants detected in addition to the base capability patterns.
34
36
  SUBPROCESS_SHELL_RE = re.compile(r"subprocess[\s\S]{0,200}?shell\s*=\s*True", re.IGNORECASE)
37
+
38
+ # subprocess entry points that accept ``shell=``.
39
+ SUBPROCESS_SHELL_FUNCS = {"run", "call", "check_call", "check_output", "Popen"}
40
+
41
+
42
+ def _subprocess_shell_calls(content):
43
+ """Return ``(lineno, evidence)`` for subprocess calls passing ``shell=True``.
44
+
45
+ The per-line regex cannot see a call whose ``shell=True`` sits on a
46
+ later line than ``subprocess.run(`` — the usual black/ruff wrapping of
47
+ long calls. This AST pass resolves ``import subprocess [as x]`` and
48
+ ``from subprocess import run [as y]`` bindings and reports each call at
49
+ its first line. Only a literal ``True`` counts; ``**kwargs`` or
50
+ computed values stay unresolved. Unparseable sources yield nothing.
51
+ """
52
+ try:
53
+ tree = ast.parse(content)
54
+ except (SyntaxError, ValueError):
55
+ return []
56
+ modules, functions = set(), set()
57
+ for node in ast.walk(tree):
58
+ if isinstance(node, ast.Import):
59
+ for alias in node.names:
60
+ if alias.name == "subprocess":
61
+ modules.add(alias.asname or "subprocess")
62
+ elif isinstance(node, ast.ImportFrom) and node.module == "subprocess":
63
+ for alias in node.names:
64
+ if alias.name in SUBPROCESS_SHELL_FUNCS:
65
+ functions.add(alias.asname or alias.name)
66
+ if not modules and not functions:
67
+ return []
68
+
69
+ calls = []
70
+ for node in ast.walk(tree):
71
+ if not isinstance(node, ast.Call):
72
+ continue
73
+ func = node.func
74
+ is_subprocess = (
75
+ isinstance(func, ast.Attribute)
76
+ and func.attr in SUBPROCESS_SHELL_FUNCS
77
+ and isinstance(func.value, ast.Name)
78
+ and func.value.id in modules
79
+ ) or (isinstance(func, ast.Name) and func.id in functions)
80
+ if not is_subprocess:
81
+ continue
82
+ if any(
83
+ kw.arg == "shell"
84
+ and isinstance(kw.value, ast.Constant)
85
+ and kw.value.value is True
86
+ for kw in node.keywords
87
+ ):
88
+ segment = ast.get_source_segment(content, node) or ""
89
+ calls.append((node.lineno, " ".join(segment.split())[:200]))
90
+ return sorted(calls)
35
91
  FILE_WRITE_RE = re.compile(r"\bopen\([^)]*[\"'](?:w|a|x)[bt+]?[\"']", re.IGNORECASE)
36
92
 
37
93
  RULE_BY_CAP = {
@@ -45,6 +101,7 @@ RULE_BY_CAP = {
45
101
  "redis": "CAP_redis",
46
102
  "s3": "CAP_s3",
47
103
  "slack": "CAP_slack",
104
+ "ms_teams": "CAP_ms_teams",
48
105
  "jira": "CAP_jira",
49
106
  "browser_playwright": "CAP_browser_playwright",
50
107
  "browser_selenium": "CAP_browser_selenium",
@@ -63,6 +120,7 @@ CATEGORY_BY_CAP = {
63
120
  "redis": "Databases",
64
121
  "s3": "Cloud",
65
122
  "slack": "Collaboration",
123
+ "ms_teams": "Collaboration",
66
124
  "jira": "Collaboration",
67
125
  "browser_playwright": "Browser",
68
126
  "browser_selenium": "Browser",
@@ -268,6 +326,28 @@ class CapabilityAnalyzer:
268
326
  "score_contribution": 12,
269
327
  "remediation": "Introduce iteration limits and human approval checkpoints.",
270
328
  })
329
+
330
+ # Multi-line subprocess calls the per-line regex missed.
331
+ # Single-line hits are already in ``seen`` (same key), so
332
+ # their line, evidence, and fingerprint are unchanged.
333
+ if path.endswith(".py"):
334
+ for lineno, evidence in _subprocess_shell_calls(content):
335
+ key = (path, "subprocess_shell", lineno)
336
+ if key in seen:
337
+ continue
338
+ seen.add(key)
339
+ rule = rule_map.get("CAP_subprocess_shell", {})
340
+ findings.append(_finding(
341
+ "CAP_subprocess_shell",
342
+ rule,
343
+ "subprocess invoked with shell=True",
344
+ path,
345
+ lineno,
346
+ "Shell",
347
+ evidence=evidence,
348
+ confidence=0.7,
349
+ score_contribution=15,
350
+ ))
271
351
  except Exception:
272
352
  continue
273
353
 
@@ -0,0 +1,21 @@
1
+ """Authority evidence validation benchmark (v2.6).
2
+
3
+ Offline, deterministic corpus evaluation: runs the real ``safeai
4
+ scan`` CLI path against ``tests/benchmarks/authority/`` and compares
5
+ structurally with hand-verified expected truth. The benchmark
6
+ measures; it never gates.
7
+ """
8
+
9
+ from safeai.benchmark.runner import (
10
+ BenchmarkError,
11
+ discover_cases,
12
+ evaluate_case,
13
+ run_corpus,
14
+ )
15
+
16
+ __all__ = [
17
+ "BenchmarkError",
18
+ "discover_cases",
19
+ "evaluate_case",
20
+ "run_corpus",
21
+ ]