SafeAI-Static-Analyzer 2.0.0__tar.gz → 2.0.1__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.0.0 → safeai_static_analyzer-2.0.1}/PKG-INFO +13 -1
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/README.md +12 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/SafeAI_Static_Analyzer.egg-info/PKG-INFO +13 -1
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/SafeAI_Static_Analyzer.egg-info/SOURCES.txt +1 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/version.py +1 -1
- safeai_static_analyzer-2.0.1/tests/test_compatibility.py +392 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/LICENSE +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/SafeAI_Static_Analyzer.egg-info/dependency_links.txt +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/SafeAI_Static_Analyzer.egg-info/entry_points.txt +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/SafeAI_Static_Analyzer.egg-info/requires.txt +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/SafeAI_Static_Analyzer.egg-info/top_level.txt +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/pyproject.toml +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/__main__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/aggregation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/capabilities.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/capability_diff.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/component_diff.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/component_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/components.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/dependency_correlation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/escalation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/import_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/project_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/semantic.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/target_taxonomy.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/tool_identity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/tool_implementation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/tool_surface.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/capability/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/capability/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/claude_code/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/claude_code/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/data_leakage/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/data_leakage/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/dataflow/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/dataflow/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/env_dependency/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/env_dependency/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/governance/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/governance/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/compatibility.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/schema.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/validators.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/model_config/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/model_config/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/prompt/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/prompt/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/prompt_file/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/prompt_file/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/skill/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/skill/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/tool_def/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/tool_def/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/workflow/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/workflow/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/cmd/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/cmd/cli.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/cmd/postprocess.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/cmd/registry_cli.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/controls/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/controls/catalogs.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/controls/mappings.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/engine/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/engine/orchestrator.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/engine/scan.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/autogen/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/azure_foundry/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/azure_foundry/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/bedrock_agent/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/bedrock_agent/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/claude_code/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/claude_code/commands.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/claude_code/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/claude_code/permissions.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/claude_code/settings.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/crewai/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/crewai/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/cursorrules/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/cursorrules/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/dify/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/dify/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/google_adk/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/google_adk/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/haystack/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/haystack/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/langchain/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/langchain/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/langgraph/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/langgraph/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/llamaindex/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/llamaindex/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/mastra/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/mastra/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/microsoft_agent/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/microsoft_agent/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/n8n/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/n8n/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/openai_agents/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/openai_agents/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/semantic_kernel/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/semantic_kernel/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/windsurf/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/frameworks/windsurf/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/assurance.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/baseline.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/ci_context.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/enrich.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/exporter.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/fingerprints.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/identity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/manifest.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/policy.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/registry/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/registry/connection.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/registry/persist.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/registry/queries.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/registry/schema.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/suppressions.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/kya/util.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/failure_matrix.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/html.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/html_kit.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/json_report.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/pr_comment.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/registry_html.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/sarif.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/report/terminal.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/rules/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/rules/base_rules.yaml +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/rules/loader.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/scorecard.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/scoring/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/scoring/engine.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/severity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/telemetry/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/telemetry/client.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/telemetry/config.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/telemetry/schema.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/setup.cfg +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_adapter_dataflow.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_assurance.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_baseline.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_capability_detection.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_capability_diff_v2.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_ci_context.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_claude_code_deep.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_component_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_component_hash.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_confidence_arbitration.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_control_mappings.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_crewai_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_cursorrules_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_env_dependency.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_escalation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_evidence_type.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_fingerprints.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_github_action.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_governance_analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_html_report.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_init_and_rules.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_kya_integration.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_kya_manifest.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_langgraph_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_llamaindex_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_mcp_tool_poisoning.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_mcp_validation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_n8n_workflow.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_parser_aggregation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_phase15.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_policy.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_pr_comment.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_pr_comment_cli.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_registry.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_registry_cli.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_registry_html.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_release_fixes.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_release_validation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_rules_loader_determinism.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_sarif.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_scan_phase11.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_scorecard.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_scoring.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_semantic.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_severity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_suppressions.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_target_taxonomy_engine.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_telemetry.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_tool_identity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_tool_implementation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/tests/test_version.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/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.0.
|
|
3
|
+
Version: 2.0.1
|
|
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
|
|
@@ -785,6 +785,18 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
|
|
|
785
785
|
|
|
786
786
|
---
|
|
787
787
|
|
|
788
|
+
## Documentation
|
|
789
|
+
|
|
790
|
+
- [Release Notes](./RELEASE_NOTES.md) — changelog for all versions
|
|
791
|
+
- [Upgrade Guide](./UPGRADE.md) — v1.x → v2.0.0 migration
|
|
792
|
+
- [Support Matrix](./SUPPORT_MATRIX.md) — Python versions, platforms, adapters, CI
|
|
793
|
+
- [Release Channels](./RELEASE_CHANNELS.md) — main, stable, prerelease, maintenance
|
|
794
|
+
- [Security Policy](./SECURITY.md) — vulnerability reporting and response targets
|
|
795
|
+
- [Known Limitations](./KNOWN_LIMITATIONS.md) — what SafeAI does and does not do
|
|
796
|
+
- [Roadmap](./ROADMAP.md) — future plans and feature requests
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
788
800
|
## License
|
|
789
801
|
|
|
790
802
|
SafeAI is released under the Apache 2.0 License.
|
|
@@ -754,6 +754,18 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
|
|
|
754
754
|
|
|
755
755
|
---
|
|
756
756
|
|
|
757
|
+
## Documentation
|
|
758
|
+
|
|
759
|
+
- [Release Notes](./RELEASE_NOTES.md) — changelog for all versions
|
|
760
|
+
- [Upgrade Guide](./UPGRADE.md) — v1.x → v2.0.0 migration
|
|
761
|
+
- [Support Matrix](./SUPPORT_MATRIX.md) — Python versions, platforms, adapters, CI
|
|
762
|
+
- [Release Channels](./RELEASE_CHANNELS.md) — main, stable, prerelease, maintenance
|
|
763
|
+
- [Security Policy](./SECURITY.md) — vulnerability reporting and response targets
|
|
764
|
+
- [Known Limitations](./KNOWN_LIMITATIONS.md) — what SafeAI does and does not do
|
|
765
|
+
- [Roadmap](./ROADMAP.md) — future plans and feature requests
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
757
769
|
## License
|
|
758
770
|
|
|
759
771
|
SafeAI is released under the Apache 2.0 License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SafeAI-Static-Analyzer
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
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
|
|
@@ -785,6 +785,18 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
|
|
|
785
785
|
|
|
786
786
|
---
|
|
787
787
|
|
|
788
|
+
## Documentation
|
|
789
|
+
|
|
790
|
+
- [Release Notes](./RELEASE_NOTES.md) — changelog for all versions
|
|
791
|
+
- [Upgrade Guide](./UPGRADE.md) — v1.x → v2.0.0 migration
|
|
792
|
+
- [Support Matrix](./SUPPORT_MATRIX.md) — Python versions, platforms, adapters, CI
|
|
793
|
+
- [Release Channels](./RELEASE_CHANNELS.md) — main, stable, prerelease, maintenance
|
|
794
|
+
- [Security Policy](./SECURITY.md) — vulnerability reporting and response targets
|
|
795
|
+
- [Known Limitations](./KNOWN_LIMITATIONS.md) — what SafeAI does and does not do
|
|
796
|
+
- [Roadmap](./ROADMAP.md) — future plans and feature requests
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
788
800
|
## License
|
|
789
801
|
|
|
790
802
|
SafeAI is released under the Apache 2.0 License.
|
|
@@ -149,6 +149,7 @@ tests/test_capability_detection.py
|
|
|
149
149
|
tests/test_capability_diff_v2.py
|
|
150
150
|
tests/test_ci_context.py
|
|
151
151
|
tests/test_claude_code_deep.py
|
|
152
|
+
tests/test_compatibility.py
|
|
152
153
|
tests/test_component_graph.py
|
|
153
154
|
tests/test_component_hash.py
|
|
154
155
|
tests/test_confidence_arbitration.py
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""Golden fixture integration tests.
|
|
2
|
+
|
|
3
|
+
Each supported framework family has a representative fixture that exercises
|
|
4
|
+
the full scan pipeline: detection, parsing, capability extraction, and
|
|
5
|
+
finding generation. These tests serve as regression anchors — if a fixture
|
|
6
|
+
starts failing, it signals a breaking change in the adapter or engine.
|
|
7
|
+
|
|
8
|
+
Add new fixtures for each framework by:
|
|
9
|
+
1. Creating a directory under tests/fixtures/<framework>/representative/
|
|
10
|
+
2. Adding a representative source file for that framework
|
|
11
|
+
3. Adding a test class below following the existing pattern
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from safeai.engine.scan import run_scan
|
|
18
|
+
from safeai.report.sarif import write_sarif
|
|
19
|
+
|
|
20
|
+
FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _scan(fixture_name):
|
|
24
|
+
return run_scan(os.path.join(FIXTURES, fixture_name, "representative"))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ── Framework golden fixtures ──────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestClaudeCodeGolden:
|
|
31
|
+
"""Claude Code config-file adapter (settings.json + CLAUDE.md + .mcp.json)."""
|
|
32
|
+
|
|
33
|
+
FIXTURE = os.path.join(FIXTURES, "claude_code", "compatibility")
|
|
34
|
+
|
|
35
|
+
def test_detection(self):
|
|
36
|
+
report = run_scan(self.FIXTURE)
|
|
37
|
+
assert "claude_code" in report["detected_frameworks"]
|
|
38
|
+
|
|
39
|
+
def test_artifacts_present(self):
|
|
40
|
+
report = run_scan(self.FIXTURE)
|
|
41
|
+
model = next(
|
|
42
|
+
m for m in report["unified_models"]
|
|
43
|
+
if "claude_code" in m.get("frameworks", [])
|
|
44
|
+
)
|
|
45
|
+
assert model["artifacts"]["tools"]
|
|
46
|
+
|
|
47
|
+
def test_capabilities_detected(self):
|
|
48
|
+
report = run_scan(self.FIXTURE)
|
|
49
|
+
model = next(
|
|
50
|
+
m for m in report["unified_models"]
|
|
51
|
+
if "claude_code" in m.get("frameworks", [])
|
|
52
|
+
)
|
|
53
|
+
cap_names = {c["name"] for c in model["capabilities"]}
|
|
54
|
+
assert "mcp" in cap_names
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TestCrewAIGolden:
|
|
58
|
+
"""CrewAI framework adapter."""
|
|
59
|
+
|
|
60
|
+
def test_detection(self):
|
|
61
|
+
report = _scan("crewai")
|
|
62
|
+
assert "crewai" in report["detected_frameworks"]
|
|
63
|
+
|
|
64
|
+
def test_agents_and_tasks(self):
|
|
65
|
+
report = _scan("crewai")
|
|
66
|
+
model = next(
|
|
67
|
+
m for m in report["unified_models"]
|
|
68
|
+
if "crewai" in m.get("frameworks", [])
|
|
69
|
+
)
|
|
70
|
+
artifacts = model["artifacts"]
|
|
71
|
+
assert any("Agent" in a["name"] for a in artifacts["agents"])
|
|
72
|
+
assert any("Task" in t["name"] for t in artifacts["workflows"])
|
|
73
|
+
|
|
74
|
+
def test_memory_detected(self):
|
|
75
|
+
report = _scan("crewai")
|
|
76
|
+
model = next(
|
|
77
|
+
m for m in report["unified_models"]
|
|
78
|
+
if "crewai" in m.get("frameworks", [])
|
|
79
|
+
)
|
|
80
|
+
assert any("Memory" in mem["name"] for mem in model["artifacts"]["memory"])
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestLangGraphGolden:
|
|
84
|
+
"""LangGraph framework adapter."""
|
|
85
|
+
|
|
86
|
+
def test_detection(self):
|
|
87
|
+
report = _scan("langgraph")
|
|
88
|
+
assert "langgraph" in report["detected_frameworks"]
|
|
89
|
+
|
|
90
|
+
def test_graph_and_tools(self):
|
|
91
|
+
report = _scan("langgraph")
|
|
92
|
+
model = next(
|
|
93
|
+
m for m in report["unified_models"]
|
|
94
|
+
if "langgraph" in m.get("frameworks", [])
|
|
95
|
+
)
|
|
96
|
+
artifacts = model["artifacts"]
|
|
97
|
+
assert any("ToolNode" in t["name"] for t in artifacts["tools"])
|
|
98
|
+
|
|
99
|
+
def test_memory_saver(self):
|
|
100
|
+
report = _scan("langgraph")
|
|
101
|
+
model = next(
|
|
102
|
+
m for m in report["unified_models"]
|
|
103
|
+
if "langgraph" in m.get("frameworks", [])
|
|
104
|
+
)
|
|
105
|
+
assert any(
|
|
106
|
+
mem["name"] == "MemorySaver"
|
|
107
|
+
for mem in model["artifacts"]["memory"]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class TestCursorRulesGolden:
|
|
112
|
+
"""Cursor config-file adapter (.cursorrules)."""
|
|
113
|
+
|
|
114
|
+
def test_detection(self):
|
|
115
|
+
report = _scan("cursorrules")
|
|
116
|
+
assert "cursorrules" in report["detected_frameworks"]
|
|
117
|
+
|
|
118
|
+
def test_tools_and_model(self):
|
|
119
|
+
report = _scan("cursorrules")
|
|
120
|
+
model = next(
|
|
121
|
+
m for m in report["unified_models"]
|
|
122
|
+
if "cursorrules" in m.get("frameworks", [])
|
|
123
|
+
)
|
|
124
|
+
tool_names = {t["name"] for t in model["artifacts"]["tools"]}
|
|
125
|
+
assert "read_file" in tool_names
|
|
126
|
+
model_names = {m["name"] for m in model["artifacts"]["models"]}
|
|
127
|
+
assert "claude-sonnet-4-5" in model_names
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class TestWindsurfGolden:
|
|
131
|
+
"""Windsurf config-file adapter (.windsurfrules)."""
|
|
132
|
+
|
|
133
|
+
def test_detection(self):
|
|
134
|
+
report = _scan("windsurf")
|
|
135
|
+
assert "windsurf" in report["detected_frameworks"]
|
|
136
|
+
|
|
137
|
+
def test_capabilities(self):
|
|
138
|
+
report = _scan("windsurf")
|
|
139
|
+
model = next(
|
|
140
|
+
m for m in report["unified_models"]
|
|
141
|
+
if "windsurf" in m.get("frameworks", [])
|
|
142
|
+
)
|
|
143
|
+
assert model["capabilities"]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class TestN8NGolden:
|
|
147
|
+
"""n8n workflow adapter."""
|
|
148
|
+
|
|
149
|
+
def test_detection(self):
|
|
150
|
+
report = _scan("n8n")
|
|
151
|
+
assert "n8n" in report["detected_frameworks"]
|
|
152
|
+
|
|
153
|
+
def test_workflow_parsed(self):
|
|
154
|
+
report = _scan("n8n")
|
|
155
|
+
model = next(
|
|
156
|
+
m for m in report["unified_models"]
|
|
157
|
+
if "n8n" in m.get("frameworks", [])
|
|
158
|
+
)
|
|
159
|
+
assert model["artifacts"]["workflows"]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class TestLlamaIndexGolden:
|
|
163
|
+
"""LlamaIndex framework adapter."""
|
|
164
|
+
|
|
165
|
+
def test_detection(self):
|
|
166
|
+
report = _scan("llamaindex")
|
|
167
|
+
assert "llamaindex" in report["detected_frameworks"]
|
|
168
|
+
|
|
169
|
+
def test_agent_detected(self):
|
|
170
|
+
report = _scan("llamaindex")
|
|
171
|
+
model = next(
|
|
172
|
+
m for m in report["unified_models"]
|
|
173
|
+
if "llamaindex" in m.get("frameworks", [])
|
|
174
|
+
)
|
|
175
|
+
assert model["artifacts"]["agents"]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ── JSON report contract ───────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class TestJSONReportContract:
|
|
182
|
+
"""Verify the JSON report contains all required top-level keys."""
|
|
183
|
+
|
|
184
|
+
REQUIRED_KEYS = [
|
|
185
|
+
"findings", "counts", "detected_frameworks", "tool_surface",
|
|
186
|
+
"assurance_boundary", "unified_models",
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
def test_required_keys_present(self):
|
|
190
|
+
report = _scan("crewai")
|
|
191
|
+
for key in self.REQUIRED_KEYS:
|
|
192
|
+
assert key in report, f"Missing key: {key}"
|
|
193
|
+
|
|
194
|
+
def test_findings_are_dicts(self):
|
|
195
|
+
report = _scan("crewai")
|
|
196
|
+
for f in report["findings"]:
|
|
197
|
+
assert isinstance(f, dict)
|
|
198
|
+
assert "rule_id" in f
|
|
199
|
+
assert "file" in f
|
|
200
|
+
assert "line" in f
|
|
201
|
+
assert "severity" in f
|
|
202
|
+
|
|
203
|
+
def test_counts_has_severity_keys(self):
|
|
204
|
+
report = _scan("crewai")
|
|
205
|
+
counts = report["counts"]
|
|
206
|
+
assert isinstance(counts, dict)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# ── SARIF output contract ──────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class TestSARIFContract:
|
|
213
|
+
"""Verify SARIF output is valid and contains required fields."""
|
|
214
|
+
|
|
215
|
+
def test_sarif_version(self, tmp_path):
|
|
216
|
+
report = _scan("crewai")
|
|
217
|
+
out = tmp_path / "out.sarif"
|
|
218
|
+
write_sarif(report, str(out))
|
|
219
|
+
data = json.loads(out.read_text())
|
|
220
|
+
assert data["version"] == "2.1.0"
|
|
221
|
+
|
|
222
|
+
def test_sarif_has_runs(self, tmp_path):
|
|
223
|
+
report = _scan("crewai")
|
|
224
|
+
out = tmp_path / "out.sarif"
|
|
225
|
+
write_sarif(report, str(out))
|
|
226
|
+
data = json.loads(out.read_text())
|
|
227
|
+
assert len(data["runs"]) >= 1
|
|
228
|
+
|
|
229
|
+
def test_sarif_run_has_rules_and_results(self, tmp_path):
|
|
230
|
+
report = _scan("claude_code/compatibility")
|
|
231
|
+
out = tmp_path / "out.sarif"
|
|
232
|
+
write_sarif(report, str(out))
|
|
233
|
+
data = json.loads(out.read_text())
|
|
234
|
+
run = data["runs"][0]
|
|
235
|
+
assert "tool" in run
|
|
236
|
+
assert "results" in run
|
|
237
|
+
|
|
238
|
+
def test_sarif_rule_has_required_fields(self, tmp_path):
|
|
239
|
+
report = _scan("claude_code/compatibility")
|
|
240
|
+
out = tmp_path / "out.sarif"
|
|
241
|
+
write_sarif(report, str(out))
|
|
242
|
+
data = json.loads(out.read_text())
|
|
243
|
+
run = data["runs"][0]
|
|
244
|
+
for rule in run["tool"]["driver"]["rules"]:
|
|
245
|
+
assert "id" in rule
|
|
246
|
+
assert "shortDescription" in rule
|
|
247
|
+
|
|
248
|
+
def test_sarif_result_has_locations(self, tmp_path):
|
|
249
|
+
report = _scan("claude_code/compatibility")
|
|
250
|
+
out = tmp_path / "out.sarif"
|
|
251
|
+
write_sarif(report, str(out))
|
|
252
|
+
data = json.loads(out.read_text())
|
|
253
|
+
run = data["runs"][0]
|
|
254
|
+
for result in run.get("results", []):
|
|
255
|
+
assert "ruleId" in result
|
|
256
|
+
assert "locations" in result
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# ── CLI behavior contract ──────────────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class TestCLIContract:
|
|
263
|
+
"""Verify CLI exit codes and output flags work as documented."""
|
|
264
|
+
|
|
265
|
+
def test_zero_exit_on_clean_project(self, tmp_path):
|
|
266
|
+
from safeai.cmd.cli import main
|
|
267
|
+
(tmp_path / "clean.py").write_text("print('hello')\n")
|
|
268
|
+
code = main(["scan", str(tmp_path), "--sarif", "", "--fail-on", "critical"])
|
|
269
|
+
assert code == 0
|
|
270
|
+
|
|
271
|
+
def test_nonzero_exit_on_threshold_breach(self, tmp_path):
|
|
272
|
+
from safeai.cmd.cli import main
|
|
273
|
+
(tmp_path / "risky.py").write_text('api_key = "sk-1234567890abcdef1234"\n')
|
|
274
|
+
code = main(["scan", str(tmp_path), "--sarif", "", "--fail-on", "high"])
|
|
275
|
+
assert code == 1
|
|
276
|
+
|
|
277
|
+
def test_json_output_flag(self, tmp_path):
|
|
278
|
+
from safeai.cmd.cli import main
|
|
279
|
+
(tmp_path / "app.py").write_text("print('hello')\n")
|
|
280
|
+
json_path = str(tmp_path / "out.json")
|
|
281
|
+
code = main(["scan", str(tmp_path), "--json", json_path, "--fail-on", "critical"])
|
|
282
|
+
assert code == 0
|
|
283
|
+
data = json.loads((tmp_path / "out.json").read_text())
|
|
284
|
+
assert "findings" in data
|
|
285
|
+
|
|
286
|
+
def test_sarif_output_flag(self, tmp_path):
|
|
287
|
+
from safeai.cmd.cli import main
|
|
288
|
+
(tmp_path / "app.py").write_text("print('hello')\n")
|
|
289
|
+
sarif_path = str(tmp_path / "out.sarif")
|
|
290
|
+
code = main(["scan", str(tmp_path), "--sarif", sarif_path, "--fail-on", "critical"])
|
|
291
|
+
assert code == 0
|
|
292
|
+
data = json.loads((tmp_path / "out.sarif").read_text())
|
|
293
|
+
assert data["version"] == "2.1.0"
|
|
294
|
+
|
|
295
|
+
def test_html_output_flag(self, tmp_path):
|
|
296
|
+
from safeai.cmd.cli import main
|
|
297
|
+
(tmp_path / "app.py").write_text("print('hello')\n")
|
|
298
|
+
html_path = str(tmp_path / "out.html")
|
|
299
|
+
code = main(["scan", str(tmp_path), "--html", html_path, "--fail-on", "critical"])
|
|
300
|
+
assert code == 0
|
|
301
|
+
assert (tmp_path / "out.html").exists()
|
|
302
|
+
content = (tmp_path / "out.html").read_text()[:100].lower()
|
|
303
|
+
assert "doctype" in content
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# ── Action inputs/outputs contract ─────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class TestActionContract:
|
|
310
|
+
"""Verify GitHub Action input/output mapping is stable."""
|
|
311
|
+
|
|
312
|
+
def test_action_yaml_has_all_inputs(self):
|
|
313
|
+
import yaml
|
|
314
|
+
action_path = os.path.join(
|
|
315
|
+
os.path.dirname(__file__), os.pardir, "action.yml"
|
|
316
|
+
)
|
|
317
|
+
with open(action_path) as f:
|
|
318
|
+
action = yaml.safe_load(f)
|
|
319
|
+
inputs = action["inputs"]
|
|
320
|
+
expected = [
|
|
321
|
+
"path", "version", "fail-on", "sarif", "rules", "baseline",
|
|
322
|
+
"fail-on-new", "fail-on-escalation", "no-registry", "extra-args",
|
|
323
|
+
"scorecard", "scorecard-json", "scorecard-summary", "scorecard-fail-under",
|
|
324
|
+
]
|
|
325
|
+
for name in expected:
|
|
326
|
+
assert name in inputs, f"Missing action input: {name}"
|
|
327
|
+
|
|
328
|
+
def test_action_yaml_has_all_outputs(self):
|
|
329
|
+
import yaml
|
|
330
|
+
action_path = os.path.join(
|
|
331
|
+
os.path.dirname(__file__), os.pardir, "action.yml"
|
|
332
|
+
)
|
|
333
|
+
with open(action_path) as f:
|
|
334
|
+
action = yaml.safe_load(f)
|
|
335
|
+
outputs = action["outputs"]
|
|
336
|
+
expected = ["sarif-path", "scorecard-path", "safeai-version"]
|
|
337
|
+
for name in expected:
|
|
338
|
+
assert name in outputs, f"Missing action output: {name}"
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# ── Adapter contract ───────────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class TestAdapterContract:
|
|
345
|
+
"""Every registered parser must implement detect() and parse().
|
|
346
|
+
Parsers that don't match the file return minimal results — we only
|
|
347
|
+
validate the contract for parsers that do match."""
|
|
348
|
+
|
|
349
|
+
REQUIRED_PARSE_KEYS = {
|
|
350
|
+
"framework", "capabilities", "tools", "models",
|
|
351
|
+
"detection_evidence",
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
def _get_all_parsers(self):
|
|
355
|
+
from safeai.frameworks import discover_parsers
|
|
356
|
+
return discover_parsers()
|
|
357
|
+
|
|
358
|
+
def test_all_parsers_implement_detect(self):
|
|
359
|
+
for parser in self._get_all_parsers():
|
|
360
|
+
assert hasattr(parser, "detect"), (
|
|
361
|
+
f"{type(parser).__name__} missing detect()"
|
|
362
|
+
)
|
|
363
|
+
assert callable(parser.detect)
|
|
364
|
+
|
|
365
|
+
def test_all_parsers_implement_parse(self):
|
|
366
|
+
for parser in self._get_all_parsers():
|
|
367
|
+
assert hasattr(parser, "parse"), (
|
|
368
|
+
f"{type(parser).__name__} missing parse()"
|
|
369
|
+
)
|
|
370
|
+
assert callable(parser.parse)
|
|
371
|
+
|
|
372
|
+
def test_parse_returns_framework_key(self):
|
|
373
|
+
"""Parsers that produce output must include a 'framework' key."""
|
|
374
|
+
for parser in self._get_all_parsers():
|
|
375
|
+
result = parser.parse("test.py", "")
|
|
376
|
+
if result: # only check parsers that return non-empty
|
|
377
|
+
assert "framework" in result, (
|
|
378
|
+
f"{type(parser).__name__}.parse() missing 'framework' key"
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def test_parse_capabilities_are_list(self):
|
|
382
|
+
"""Parsers that detect capabilities return them as a list."""
|
|
383
|
+
for parser in self._get_all_parsers():
|
|
384
|
+
result = parser.parse("test.py", "")
|
|
385
|
+
if not isinstance(result, dict):
|
|
386
|
+
continue
|
|
387
|
+
caps = result.get("capabilities")
|
|
388
|
+
if caps is not None:
|
|
389
|
+
assert isinstance(caps, list)
|
|
390
|
+
for cap in caps:
|
|
391
|
+
assert isinstance(cap, dict)
|
|
392
|
+
assert "name" in cap
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/aggregation.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/capabilities.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/capability_diff.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/component_diff.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/component_graph.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/import_graph.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/project_graph.py
RENAMED
|
File without changes
|
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/target_taxonomy.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/tool_identity.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/tool_implementation.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analysis/tool_surface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/dataflow/__init__.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/dataflow/analyzer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/__init__.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/analyzer.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/compatibility.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/schema.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/mcp/validators.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/prompt/__init__.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/prompt/analyzer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/skill/__init__.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/skill/analyzer.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/tool_def/__init__.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/tool_def/analyzer.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/workflow/__init__.py
RENAMED
|
File without changes
|
{safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.0.1}/safeai/analyzers/workflow/analyzer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|