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.
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/PKG-INFO +7 -5
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/README.md +5 -3
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/PKG-INFO +7 -5
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/SOURCES.txt +13 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/requires.txt +1 -1
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/pyproject.toml +1 -1
- safeai_static_analyzer-2.7.0/safeai/analysis/evidence_metrics.py +188 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/import_graph.py +8 -2
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/semantic.py +7 -2
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/capability/analyzer.py +80 -0
- safeai_static_analyzer-2.7.0/safeai/benchmark/__init__.py +21 -0
- safeai_static_analyzer-2.7.0/safeai/benchmark/canonical.py +1603 -0
- safeai_static_analyzer-2.7.0/safeai/benchmark/compare.py +529 -0
- safeai_static_analyzer-2.7.0/safeai/benchmark/metrics.py +540 -0
- safeai_static_analyzer-2.7.0/safeai/benchmark/report.py +546 -0
- safeai_static_analyzer-2.7.0/safeai/benchmark/runner.py +344 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/cli.py +53 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/postprocess.py +25 -3
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/controls/mappings.py +4 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/exceptions.py +28 -2
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/manifest.py +7 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/html.py +239 -2
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/html_kit.py +4 -1
- safeai_static_analyzer-2.7.0/safeai/report/security_brief.py +499 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/base_rules.yaml +5 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/scorecard.py +1 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/version.py +1 -1
- safeai_static_analyzer-2.7.0/tests/test_authority_benchmark.py +607 -0
- safeai_static_analyzer-2.7.0/tests/test_authority_canonical.py +583 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_capability_detection.py +21 -0
- safeai_static_analyzer-2.7.0/tests/test_evidence_metrics.py +187 -0
- safeai_static_analyzer-2.7.0/tests/test_exceptions.py +538 -0
- safeai_static_analyzer-2.7.0/tests/test_html_report.py +257 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_workflow.py +29 -0
- safeai_static_analyzer-2.7.0/tests/test_security_brief.py +248 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_semantic.py +27 -0
- safeai_static_analyzer-2.7.0/tests/test_subprocess_shell_detection.py +85 -0
- safeai_static_analyzer-2.5.0/tests/test_exceptions.py +0 -284
- safeai_static_analyzer-2.5.0/tests/test_html_report.py +0 -135
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/LICENSE +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/dependency_links.txt +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/entry_points.txt +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/SafeAI_Static_Analyzer.egg-info/top_level.txt +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/__main__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/aggregation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/capabilities.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/capability_diff.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/component_diff.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/component_graph.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/components.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/dependency_correlation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/escalation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/escalation_remediation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/iac_correlation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/project_graph.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/target_taxonomy.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/tool_identity.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/tool_implementation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/tool_surface.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/capability/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/claude_code/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/claude_code/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/data_leakage/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/data_leakage/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/dataflow/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/dataflow/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/env_dependency/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/env_dependency/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/governance/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/governance/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/compatibility.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/schema.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/mcp/validators.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/model_config/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/model_config/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt_file/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/prompt_file/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/skill/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/skill/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/tool_def/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/tool_def/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/workflow/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analyzers/workflow/analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/manifest_cli.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/cmd/registry_cli.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/controls/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/controls/catalogs.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/file_discovery.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/metadata.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/orchestrator.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/engine/scan.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/autogen/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/azure_foundry/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/azure_foundry/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/bedrock_agent/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/bedrock_agent/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/commands.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/permissions.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/claude_code/settings.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/copilot/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/copilot/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/crewai/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/crewai/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/cursorrules/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/cursorrules/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/dify/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/dify/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/google_adk/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/google_adk/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/haystack/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/haystack/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langchain/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langchain/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langgraph/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/langgraph/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/llamaindex/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/llamaindex/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/mastra/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/mastra/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/microsoft_agent/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/microsoft_agent/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/n8n/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/n8n/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openai_agents/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openai_agents/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openclaw/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/openclaw/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/semantic_kernel/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/semantic_kernel/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/windsurf/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/frameworks/windsurf/parser.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/k8s.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/linking.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/model.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/semantics.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/iac/terraform.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/assurance.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/baseline.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/ci_context.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/contract.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/enrich.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/exporter.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/fingerprints.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/identity.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/importer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/integrity.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/lockfile.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/policy.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/connection.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/persist.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/queries.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/registry/schema.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/suppressions.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/kya/util.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/failure_matrix.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/json_report.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/pr_comment.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/registry_html.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/sarif.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/report/terminal.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/loader.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/rules/pack_test.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/scoring/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/scoring/engine.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/severity.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/__init__.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/client.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/config.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/telemetry/schema.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/setup.cfg +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_adapter_dataflow.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_architectural_invariants.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_architecture_diagram.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_assurance.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_authority_change.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_baseline.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_baseline_compat.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_benchmarks.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_capability_diff_v2.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_ci_context.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_claude_code_deep.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_compatibility.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_component_graph.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_component_hash.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_confidence_arbitration.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_config_adapters.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_control_mappings.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_crewai_framework.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_cursorrules_framework.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_dco.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_env_dependency.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_escalation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_escalation_remediation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_evidence_contract_v7.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_evidence_type.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_fingerprints.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_github_action.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_governance_analyzer.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_adversarial.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_benchmark.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_correlation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_k8s.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_linking.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_semantics.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_iac_terraform.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_init_and_rules.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_kya_integration.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_kya_manifest.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_langgraph_framework.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_llamaindex_framework.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_manifest_contract.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_manifest_integrity.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_mcp_tool_poisoning.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_mcp_validation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_n8n_workflow.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_parser_aggregation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_phase15.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_plugin_sdk.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_policy.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_pr_comment.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_pr_comment_cli.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_pr_paths.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_quality_gates.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_registry.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_registry_cli.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_registry_html.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_regression_fixtures.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_assets.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_fixes.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_release_validation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_rules_loader_determinism.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_sarif.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scan_framework.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scan_phase11.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scorecard.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_scoring.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_severity.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_suppressions.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_target_taxonomy_engine.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_telemetry.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_terminal_report.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_tool_identity.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_tool_implementation.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_unknown_authority.py +0 -0
- {safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/tests/test_version.py +0 -0
- {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.
|
|
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.
|
|
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
|
[](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
|
|
37
37
|
[](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
|
|
38
|
-
[](https://ikaruslabs.net)
|
|
39
|
+
[](https://ikaruslabs.net/safeai)
|
|
40
|
+
[](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.7.0)
|
|
40
41
|
[](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
|
-
> 🌐 [
|
|
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
|
[](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
|
|
4
4
|
[](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
|
|
5
|
-
[](https://ikaruslabs.net)
|
|
6
|
+
[](https://ikaruslabs.net/safeai)
|
|
7
|
+
[](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.7.0)
|
|
7
8
|
[](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
|
-
> 🌐 [
|
|
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.
|
|
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.
|
|
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
|
[](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
|
|
37
37
|
[](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
|
|
38
|
-
[](https://ikaruslabs.net)
|
|
39
|
+
[](https://ikaruslabs.net/safeai)
|
|
40
|
+
[](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.7.0)
|
|
40
41
|
[](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
|
-
> 🌐 [
|
|
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
|
|
@@ -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
|
{safeai_static_analyzer-2.5.0 → safeai_static_analyzer-2.7.0}/safeai/analysis/import_graph.py
RENAMED
|
@@ -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
|
+
]
|