SafeAI-Static-Analyzer 2.0.0__tar.gz → 2.1.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.0.0 → safeai_static_analyzer-2.1.0}/PKG-INFO +38 -297
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/README.md +35 -296
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/SafeAI_Static_Analyzer.egg-info/PKG-INFO +38 -297
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/SafeAI_Static_Analyzer.egg-info/SOURCES.txt +6 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/SafeAI_Static_Analyzer.egg-info/requires.txt +3 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/pyproject.toml +3 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/mcp/analyzer.py +110 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/cmd/cli.py +24 -2
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/cmd/postprocess.py +54 -2
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/cmd/registry_cli.py +31 -0
- safeai_static_analyzer-2.1.0/safeai/engine/file_discovery.py +185 -0
- safeai_static_analyzer-2.1.0/safeai/engine/metadata.py +91 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/engine/orchestrator.py +7 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/exporter.py +45 -1
- safeai_static_analyzer-2.1.0/safeai/kya/importer.py +481 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/registry/persist.py +2 -1
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/pr_comment.py +102 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/scoring/engine.py +7 -7
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/version.py +1 -1
- safeai_static_analyzer-2.1.0/tests/test_compatibility.py +634 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_github_action.py +1 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_mcp_tool_poisoning.py +105 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_pr_comment.py +21 -0
- safeai_static_analyzer-2.1.0/tests/test_quality_gates.py +51 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_registry_cli.py +164 -1
- safeai_static_analyzer-2.1.0/tests/test_regression_fixtures.py +177 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/LICENSE +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/SafeAI_Static_Analyzer.egg-info/dependency_links.txt +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/SafeAI_Static_Analyzer.egg-info/entry_points.txt +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/SafeAI_Static_Analyzer.egg-info/top_level.txt +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/__main__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/aggregation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/capabilities.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/capability_diff.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/component_diff.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/component_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/components.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/dependency_correlation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/escalation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/import_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/project_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/semantic.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/target_taxonomy.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/tool_identity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/tool_implementation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analysis/tool_surface.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/capability/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/capability/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/claude_code/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/claude_code/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/data_leakage/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/data_leakage/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/dataflow/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/dataflow/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/env_dependency/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/env_dependency/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/governance/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/governance/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/mcp/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/mcp/compatibility.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/mcp/schema.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/mcp/validators.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/model_config/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/model_config/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/prompt/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/prompt/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/prompt_file/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/prompt_file/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/skill/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/skill/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/tool_def/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/tool_def/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/workflow/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/analyzers/workflow/analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/cmd/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/controls/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/controls/catalogs.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/controls/mappings.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/engine/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/engine/scan.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/autogen/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/azure_foundry/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/azure_foundry/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/bedrock_agent/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/bedrock_agent/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/claude_code/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/claude_code/commands.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/claude_code/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/claude_code/permissions.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/claude_code/settings.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/crewai/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/crewai/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/cursorrules/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/cursorrules/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/dify/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/dify/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/google_adk/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/google_adk/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/haystack/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/haystack/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/langchain/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/langchain/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/langgraph/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/langgraph/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/llamaindex/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/llamaindex/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/mastra/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/mastra/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/microsoft_agent/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/microsoft_agent/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/n8n/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/n8n/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/openai_agents/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/openai_agents/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/semantic_kernel/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/semantic_kernel/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/windsurf/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/frameworks/windsurf/parser.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/assurance.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/baseline.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/ci_context.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/enrich.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/fingerprints.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/identity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/manifest.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/policy.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/registry/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/registry/connection.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/registry/queries.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/registry/schema.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/suppressions.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/kya/util.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/failure_matrix.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/html.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/html_kit.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/json_report.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/registry_html.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/sarif.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/report/terminal.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/rules/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/rules/base_rules.yaml +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/rules/loader.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/scorecard.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/scoring/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/severity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/telemetry/__init__.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/telemetry/client.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/telemetry/config.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/safeai/telemetry/schema.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/setup.cfg +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_adapter_dataflow.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_assurance.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_baseline.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_capability_detection.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_capability_diff_v2.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_ci_context.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_claude_code_deep.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_component_graph.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_component_hash.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_confidence_arbitration.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_control_mappings.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_crewai_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_cursorrules_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_env_dependency.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_escalation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_evidence_type.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_fingerprints.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_governance_analyzer.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_html_report.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_init_and_rules.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_kya_integration.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_kya_manifest.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_langgraph_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_llamaindex_framework.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_mcp_validation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_n8n_workflow.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_parser_aggregation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_phase15.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_policy.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_pr_comment_cli.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_registry.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_registry_html.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_release_fixes.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_release_validation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_rules_loader_determinism.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_sarif.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_scan_phase11.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_scorecard.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_scoring.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_semantic.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_severity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_suppressions.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_target_taxonomy_engine.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_telemetry.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_tool_identity.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_tool_implementation.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.0}/tests/test_version.py +0 -0
- {safeai_static_analyzer-2.0.0 → safeai_static_analyzer-2.1.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.1.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
|
|
@@ -27,6 +27,8 @@ Requires-Dist: ruff<0.17,>=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"
|
|
30
|
+
Provides-Extra: standalone
|
|
31
|
+
Requires-Dist: pyinstaller>=6.0; extra == "standalone"
|
|
30
32
|
Dynamic: license-file
|
|
31
33
|
|
|
32
34
|
# SafeAI — Static AI Capability & Risk Analyzer
|
|
@@ -34,7 +36,7 @@ Dynamic: license-file
|
|
|
34
36
|
[](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
|
|
35
37
|
[](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
|
|
36
38
|
[](https://safeai-analyzer.ikaruscareer.com)
|
|
37
|
-
[](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.0.1)
|
|
38
40
|
[](https://www.bestpractices.dev/en/projects/14126)
|
|
39
41
|
|
|
40
42
|
Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
|
|
@@ -82,7 +84,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
|
|
|
82
84
|
|
|
83
85
|
| Feature | Description |
|
|
84
86
|
|---------|-------------|
|
|
85
|
-
| **Framework Detection** | Detects and parses
|
|
87
|
+
| **Framework Detection** | Detects and parses 17 AI agent frameworks (AST + config + regex, no mutual exclusion) |
|
|
86
88
|
| **Tool Identity & Access Modes** | Capabilities attributed to named tools (agent / MCP server / skill / tool / workflow node) on an access scale `none < read < write < mutate < execute`; inferred modes are flagged, never overstated |
|
|
87
89
|
| **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
|
|
88
90
|
| **Capability Escalation Detection** | Per-tool authority diffs between scans (new shell, read→write widening, new MCP server, removed approval gate, ...) — 14 rules, including gating-aware subsumption |
|
|
@@ -153,7 +155,6 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
153
155
|
|-----------|-----------|-----------|-------------------|---------------|--------|
|
|
154
156
|
| LangGraph | ✔ | Partial | Partial | Partial | Partial |
|
|
155
157
|
| CrewAI | ✔ | Partial | Partial | Partial | Partial |
|
|
156
|
-
| AutoGen | ✔ | Partial | Minimal | Minimal | Experimental |
|
|
157
158
|
| LangChain | ✔ | Partial | Partial | Partial | Partial |
|
|
158
159
|
| Semantic Kernel | ✔ | Partial | Partial | Partial | Partial |
|
|
159
160
|
| OpenAI Agents SDK | ✔ | Partial | Partial | Partial | Partial |
|
|
@@ -168,6 +169,7 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
168
169
|
| Dify | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
169
170
|
| n8n | ✔ | Partial | Minimal | Minimal | Experimental |
|
|
170
171
|
| Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
172
|
+
| Windsurf (.windsurfrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
171
173
|
|
|
172
174
|
|
|
173
175
|
### Framework Support Details
|
|
@@ -193,7 +195,7 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
193
195
|
capability-relevant keywords (shell, filesystem, HTTP, database) in the
|
|
194
196
|
IDE's rules config, JSON, YAML, or free text
|
|
195
197
|
|
|
196
|
-
Maturity is on the scale defined in [`FRAMEWORK_SUPPORT.md`](FRAMEWORK_SUPPORT.md):
|
|
198
|
+
Maturity is on the scale defined in [`docs/reference/FRAMEWORK_SUPPORT.md`](docs/reference/FRAMEWORK_SUPPORT.md):
|
|
197
199
|
**Partial** = reliable detection and discovery with capability/risk analysis over
|
|
198
200
|
common patterns; **Experimental** = detection and basic artifact discovery with
|
|
199
201
|
limited framework-specific analysis. No framework is rated fully **Supported**
|
|
@@ -266,6 +268,9 @@ safeai registry list --format html > registry.html # shareable HTML inventory
|
|
|
266
268
|
safeai registry show <agent-id> # latest KYA record
|
|
267
269
|
safeai registry history <agent-id> # all scans for an agent
|
|
268
270
|
safeai registry diff <agent-id> --from previous --to latest
|
|
271
|
+
safeai registry export --output inventory.json
|
|
272
|
+
safeai registry import inventory.json --dry-run
|
|
273
|
+
safeai registry import inventory.json # atomic, idempotent merge
|
|
269
274
|
safeai registry export --format json --output inventory.json
|
|
270
275
|
safeai registry export --format html --output inventory.html
|
|
271
276
|
```
|
|
@@ -281,8 +286,8 @@ What you get on the first run:
|
|
|
281
286
|
|
|
282
287
|
**KYA records static evidence, not runtime truth.** It answers "what does the
|
|
283
288
|
source/configuration say this agent can do?" — never "what is this agent doing
|
|
284
|
-
in production?" See [REGISTRY.md](REGISTRY.md), [KYA_MANIFEST.md](KYA_MANIFEST.md),
|
|
285
|
-
and [LIMITATIONS.md](LIMITATIONS.md).
|
|
289
|
+
in production?" See [docs/reference/REGISTRY.md](docs/reference/REGISTRY.md), [docs/reference/KYA_MANIFEST.md](docs/reference/KYA_MANIFEST.md),
|
|
290
|
+
and [docs/reference/LIMITATIONS.md](docs/reference/LIMITATIONS.md).
|
|
286
291
|
|
|
287
292
|
CI note: registry persistence is auto-disabled for bare CI jobs (the `CI`
|
|
288
293
|
env var). Use `--registry "$RUNNER_TEMP/registry.db"`, set `SAFEAI_REGISTRY`
|
|
@@ -315,7 +320,7 @@ pip install -e ".[dev]"
|
|
|
315
320
|
|
|
316
321
|
## Privacy & Telemetry
|
|
317
322
|
|
|
318
|
-
SafeAI collects no data by default. Usage telemetry is opt-in, anonymous, and fully documented in [`PRIVACY.md`](PRIVACY.md). If you do nothing, nothing is ever sent. See `PRIVACY.md` for the complete data contract, what is never collected, and how to disable telemetry.
|
|
323
|
+
SafeAI collects no data by default. Usage telemetry is opt-in, anonymous, and fully documented in [`PRIVACY.md`](./PRIVACY.md). If you do nothing, nothing is ever sent. See [`PRIVACY.md`](./PRIVACY.md) for the complete data contract, what is never collected, and how to disable telemetry.
|
|
319
324
|
|
|
320
325
|
---
|
|
321
326
|
|
|
@@ -392,7 +397,7 @@ python -m safeai registry export --format json --output safeai-kya-inventory.jso
|
|
|
392
397
|
|
|
393
398
|
## Example Output
|
|
394
399
|
|
|
395
|
-
> See [REPORTING_GUIDE.md](
|
|
400
|
+
> See [docs/guides/REPORTING_GUIDE.md](docs/guides/REPORTING_GUIDE.md) for a complete guide to
|
|
396
401
|
> interpreting each output format (HTML, JSON, SARIF, PR comments, scorecard,
|
|
397
402
|
> registry) and triaging findings.
|
|
398
403
|
|
|
@@ -430,307 +435,29 @@ Findings:
|
|
|
430
435
|
|
|
431
436
|
## CI/CD Integration
|
|
432
437
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
[](https://github.com/marketplace/actions/safeai-static-analysis)
|
|
436
|
-
|
|
437
|
-
SafeAI ships a ready-to-use composite action for GitHub-hosted runners. The
|
|
438
|
-
action is a thin, pure-Python driver: it installs the `SafeAI-Static-Analyzer`
|
|
439
|
-
PyPI distribution into the runner's Python, runs `python -m safeai scan` on
|
|
440
|
-
your repository, preserves the tool's native exit codes, and always writes a
|
|
441
|
-
SARIF 2.1.0 artifact (even when the scan fails). It never evaluates any input
|
|
442
|
-
through a shell, never executes your agent code, and makes no network calls
|
|
443
|
-
beyond installing the PyPI package.
|
|
444
|
-
|
|
445
|
-
Use it with `uses: ikaruscareer/SafeAI@<ref>` (see [Version pinning](#version-pinning)).
|
|
438
|
+
SafeAI ships a [GitHub Actions Marketplace action](https://github.com/marketplace/actions/safeai-static-analysis) and supports all major CI platforms.
|
|
446
439
|
|
|
447
440
|
```yaml
|
|
441
|
+
# GitHub Actions — minimal workflow
|
|
448
442
|
name: safeai-scan
|
|
449
|
-
on:
|
|
450
|
-
push:
|
|
451
|
-
pull_request:
|
|
452
|
-
|
|
453
|
-
permissions:
|
|
454
|
-
contents: read
|
|
455
|
-
|
|
456
|
-
jobs:
|
|
457
|
-
safeai-scan:
|
|
458
|
-
runs-on: ubuntu-latest
|
|
459
|
-
steps:
|
|
460
|
-
- uses: actions/checkout@v4
|
|
461
|
-
|
|
462
|
-
- uses: actions/setup-python@v5
|
|
463
|
-
with:
|
|
464
|
-
python-version: '3.12'
|
|
465
|
-
|
|
466
|
-
- name: Scan with SafeAI
|
|
467
|
-
id: safeai
|
|
468
|
-
uses: ikaruscareer/SafeAI@v1.0.0
|
|
469
|
-
with:
|
|
470
|
-
path: .
|
|
471
|
-
fail-on: critical
|
|
472
|
-
|
|
473
|
-
- name: Upload SARIF to GitHub Advanced Security
|
|
474
|
-
if: always()
|
|
475
|
-
uses: github/codeql-action/upload-sarif@v3
|
|
476
|
-
with:
|
|
477
|
-
sarif_file: ${{ steps.safeai.outputs.sarif-path }}
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
The `if: always()` on the upload step matters: SafeAI fails the job when a
|
|
481
|
-
finding meets the `fail-on` threshold, but the SARIF file is still written so
|
|
482
|
-
code-scanning alerts are still created. `permissions: contents: read` is the
|
|
483
|
-
least privilege the action needs — it only reads your repository and writes
|
|
484
|
-
reports inside the workspace.
|
|
485
|
-
|
|
486
|
-
#### Inputs
|
|
487
|
-
|
|
488
|
-
| Input | Default | Description |
|
|
489
|
-
|-------|---------|-------------|
|
|
490
|
-
| `path` | `.` | Directory or file to scan. Relative paths are resolved against `GITHUB_WORKSPACE`. |
|
|
491
|
-
| `version` | *(latest stable)* | Exact SafeAI PyPI version to install (e.g. `1.5.0`). Empty installs the latest release of `SafeAI-Static-Analyzer`. |
|
|
492
|
-
| `fail-on` | `critical` | Minimum severity that fails the job: `critical`, `high`, or `medium`. |
|
|
493
|
-
| `sarif` | `safeai-results.sarif` | SARIF 2.1.0 output path, relative to the repo root. Empty string disables SARIF. |
|
|
494
|
-
| `rules` | *(none)* | Path to a custom rules directory (`--rules`). |
|
|
495
|
-
| `baseline` | *(none)* | Prior `safeai-manifest.json`/JSON report for new/regressed and escalation comparison (`--baseline`). |
|
|
496
|
-
| `fail-on-new` | `false` | With `baseline`: fail only on NEW or REGRESSED findings at or above `fail-on` (`--fail-on-new`). |
|
|
497
|
-
| `fail-on-escalation` | *(none)* | Minimum capability-escalation severity that fails the job: `critical`, `high`, or `medium`. Requires `baseline`. |
|
|
498
|
-
| `no-registry` | `true` | Skip local KYA registry persistence (`--no-registry`), keeping scans ephemeral. |
|
|
499
|
-
| `extra-args` | `[]` | Extra scan arguments as a JSON array of strings (e.g. `["--verbose"]`). Never shell-evaluated. |
|
|
500
|
-
|
|
501
|
-
#### Outputs
|
|
502
|
-
|
|
503
|
-
| Output | Description |
|
|
504
|
-
|--------|-------------|
|
|
505
|
-
| `sarif-path` | Absolute path to the written SARIF file (empty if SARIF was disabled or the scan did not produce one). |
|
|
506
|
-
|
|
507
|
-
#### Exit behavior
|
|
508
|
-
|
|
509
|
-
The action passes SafeAI's exit code through unchanged:
|
|
510
|
-
|
|
511
|
-
| Code | Condition |
|
|
512
|
-
|------|-----------|
|
|
513
|
-
| 0 | No findings at or above `fail-on`; policy outcome not `deny`. |
|
|
514
|
-
| 1 | Finding at or above threshold, new/regressed finding with `fail-on-new`, escalation with `fail-on-escalation`, or policy `deny`. |
|
|
515
|
-
| 2 | Operational error — missing scan path, invalid input, or missing Python 3.11+. |
|
|
516
|
-
|
|
517
|
-
Even on exit 1, the SARIF file (and any HTML/JSON reports you request via
|
|
518
|
-
`extra-args`) are written so downstream steps can keep working. The action
|
|
519
|
-
prints a GitHub `::warning::` if a scan failed without producing SARIF.
|
|
520
|
-
|
|
521
|
-
#### More examples
|
|
522
|
-
|
|
523
|
-
PR gating on new findings + escalation review:
|
|
524
|
-
|
|
525
|
-
```yaml
|
|
526
|
-
- name: Scan with SafeAI
|
|
527
|
-
uses: ikaruscareer/SafeAI@v1.0.0
|
|
528
|
-
with:
|
|
529
|
-
path: .
|
|
530
|
-
baseline: safeai-manifest.json
|
|
531
|
-
fail-on-new: 'true'
|
|
532
|
-
fail-on-escalation: high
|
|
533
|
-
sarif: safeai-results.sarif
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
Custom rules, verbose logs, and an HTML report (all argv-passed, no shell):
|
|
537
|
-
|
|
538
|
-
```yaml
|
|
539
|
-
- name: Scan with SafeAI
|
|
540
|
-
uses: ikaruscareer/SafeAI@v1.0.0
|
|
541
|
-
with:
|
|
542
|
-
path: ./agents
|
|
543
|
-
rules: .safeai/rules
|
|
544
|
-
extra-args: '["--verbose", "--html", "report.html"]'
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
#### Version pinning
|
|
548
|
-
|
|
549
|
-
Exact versioning is required for Marketplace-verified actions. Pin to a
|
|
550
|
-
release tag:
|
|
551
|
-
|
|
552
|
-
```yaml
|
|
553
|
-
uses: ikaruscareer/SafeAI@v1.0.0
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
and optionally relax to the major tag `@v1` for bugfix updates. For the
|
|
557
|
-
strictest supply-chain posture, pin to a full commit SHA:
|
|
558
|
-
|
|
559
|
-
```yaml
|
|
560
|
-
uses: ikaruscareer/SafeAI@<40-char-commit-sha>
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
The `version` input is independent: it controls the SafeAI **PyPI** package
|
|
564
|
-
installed into the runner, while the `uses:` ref controls which **action**
|
|
565
|
-
release you run.
|
|
566
|
-
|
|
567
|
-
**Recommended posture — pin both.** The `version` input defaults to the
|
|
568
|
-
*latest stable* SafeAI release on PyPI (per the Marketplace design), but
|
|
569
|
-
pinning it makes every scan reproducible and avoids surprising tool/action
|
|
570
|
-
pairings. Set the `version` input to the exact release the action was
|
|
571
|
-
validated against:
|
|
572
|
-
|
|
573
|
-
```yaml
|
|
574
|
-
- uses: ikaruscareer/SafeAI@v1.0.0
|
|
575
|
-
with:
|
|
576
|
-
version: "1.5.0"
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
If you rely on the default, know that SafeAI may upgrade underneath you on a
|
|
580
|
-
future run; combined with `uses: @v1`, that is two moving parts. For
|
|
581
|
-
maximum supply-chain control, pin the action to a commit SHA *and* set an
|
|
582
|
-
explicit `version`.
|
|
583
|
-
|
|
584
|
-
#### Troubleshooting
|
|
585
|
-
|
|
586
|
-
- **`SafeAI requires Python 3.11+`** — the runner's default Python is too old
|
|
587
|
-
or missing. Add `actions/setup-python@v5` with `python-version: '3.12'`
|
|
588
|
-
before this action.
|
|
589
|
-
- **Scan fails but you expected it to pass** — lower the threshold or switch
|
|
590
|
-
to baseline-gated `fail-on-new`, which only fails on regressions.
|
|
591
|
-
- **No SARIF uploaded despite `if: always()`** — confirm the `sarif` input is
|
|
592
|
-
non-empty and that `${{ steps.safeai.outputs.sarif-path }}` is referenced
|
|
593
|
-
with the same `id:` you set on the action step.
|
|
594
|
-
- **Security scanners flag `${{ inputs.* }}` interpolation** — this is safe:
|
|
595
|
-
action inputs are passed to the driver as environment variables with
|
|
596
|
-
`env:`, and the driver hands them to the tool as an argv list. No input is
|
|
597
|
-
ever interpolated into `run:`.
|
|
598
|
-
- **Registry warnings** — `no-registry: true` is the default precisely so
|
|
599
|
-
scans on shared runners stay ephemeral and never touch a shared
|
|
600
|
-
`~/.safeai/registry.db`.
|
|
601
|
-
|
|
602
|
-
The action's own test workflow (`.github/workflows/action-test.yml`) runs the
|
|
603
|
-
action against fixture repositories, builds and inspects the wheel, and
|
|
604
|
-
validates SARIF output on every commit.
|
|
605
|
-
|
|
606
|
-
### GitHub Actions (manual workflow)
|
|
607
|
-
|
|
608
|
-
A self-scan workflow is also included at `.github/workflows/ci.yml`. To use
|
|
609
|
-
SafeAI directly in your project:
|
|
610
|
-
|
|
611
|
-
```yaml
|
|
612
|
-
jobs:
|
|
613
|
-
safeai-scan:
|
|
614
|
-
runs-on: ubuntu-latest
|
|
615
|
-
steps:
|
|
616
|
-
- uses: actions/checkout@v4
|
|
617
|
-
- uses: actions/setup-python@v5
|
|
618
|
-
with:
|
|
619
|
-
python-version: '3.12'
|
|
620
|
-
- name: Install SafeAI
|
|
621
|
-
run: |
|
|
622
|
-
pip install -e .
|
|
623
|
-
- name: Run scan
|
|
624
|
-
run: |
|
|
625
|
-
python -m safeai scan . \
|
|
626
|
-
--sarif results.sarif \
|
|
627
|
-
--html report.html \
|
|
628
|
-
--manifest safeai-manifest.json \
|
|
629
|
-
--no-registry
|
|
630
|
-
- name: Upload SARIF
|
|
631
|
-
uses: github/codeql-action/upload-sarif@v3
|
|
632
|
-
with:
|
|
633
|
-
sarif_file: results.sarif
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
### GitLab CI
|
|
637
|
-
|
|
638
|
-
```yaml
|
|
639
|
-
safeai-scan:
|
|
640
|
-
image: python:3.12
|
|
641
|
-
script:
|
|
642
|
-
- pip install -e .
|
|
643
|
-
- python -m safeai scan . --sarif results.sarif --html report.html --no-registry
|
|
644
|
-
artifacts:
|
|
645
|
-
paths:
|
|
646
|
-
- results.sarif
|
|
647
|
-
- report.html
|
|
648
|
-
```
|
|
649
|
-
|
|
650
|
-
### Azure DevOps
|
|
651
|
-
|
|
652
|
-
```yaml
|
|
653
|
-
- task: PythonScript@0
|
|
654
|
-
inputs:
|
|
655
|
-
scriptSource: 'inline'
|
|
656
|
-
script: |
|
|
657
|
-
import subprocess
|
|
658
|
-
subprocess.run(["pip", "install", "-e", "."])
|
|
659
|
-
subprocess.run(["python", "-m", "safeai", "scan", ".", "--sarif", "$(Build.ArtifactStagingDirectory)/results.sarif", "--no-registry"])
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
### Capability escalation in CI
|
|
663
|
-
|
|
664
|
-
A capability *escalation* is a change between two scans where a tool gains
|
|
665
|
-
more authority than it had before — a new shell capability, a filesystem
|
|
666
|
-
access widening from read to write, a new MCP server, an approval gate
|
|
667
|
-
being removed, and so on (see `RULES_REFERENCE.md` and `KYA_MANIFEST.md`
|
|
668
|
-
for the full rule list). Reviewing these on every pull request is more
|
|
669
|
-
targeted than reviewing every finding, because most findings on a mature
|
|
670
|
-
codebase are pre-existing and already accepted; an escalation is new by
|
|
671
|
-
definition.
|
|
672
|
-
|
|
673
|
-
`--fail-on-escalation` gates the scan on escalation severity, and
|
|
674
|
-
`--pr-comment` writes a short Markdown summary you can post as a PR
|
|
675
|
-
comment. SafeAI itself never posts anything anywhere and makes no network
|
|
676
|
-
calls of any kind — generating the comment and publishing it are two
|
|
677
|
-
separate steps, and the second one is entirely up to your CI workflow.
|
|
678
|
-
|
|
679
|
-
```yaml
|
|
680
|
-
name: safeai-escalation-check
|
|
681
|
-
on:
|
|
682
|
-
pull_request:
|
|
683
|
-
|
|
443
|
+
on: [push, pull_request]
|
|
684
444
|
permissions:
|
|
685
445
|
contents: read
|
|
686
|
-
pull-requests: write
|
|
687
|
-
|
|
688
446
|
jobs:
|
|
689
|
-
|
|
447
|
+
scan:
|
|
690
448
|
runs-on: ubuntu-latest
|
|
691
449
|
steps:
|
|
692
450
|
- uses: actions/checkout@v4
|
|
693
451
|
- uses: actions/setup-python@v5
|
|
694
|
-
with:
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
- name: Fetch baseline manifest from the base branch
|
|
700
|
-
run: |
|
|
701
|
-
git fetch origin "${{ github.event.pull_request.base.ref }}" --depth=1
|
|
702
|
-
git show "origin/${{ github.event.pull_request.base.ref }}:safeai-manifest.json" \
|
|
703
|
-
> safeai-manifest.json || echo '{}' > safeai-manifest.json
|
|
704
|
-
|
|
705
|
-
- name: Run scan
|
|
706
|
-
run: |
|
|
707
|
-
safeai scan . \
|
|
708
|
-
--baseline safeai-manifest.json \
|
|
709
|
-
--pr-comment comment.md \
|
|
710
|
-
--fail-on-escalation high
|
|
711
|
-
|
|
712
|
-
- name: Post or update PR comment
|
|
452
|
+
with: { python-version: '3.12' }
|
|
453
|
+
- uses: ikaruscareer/SafeAI@v1
|
|
454
|
+
with: { path: '.', fail-on: critical }
|
|
455
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
713
456
|
if: always()
|
|
714
|
-
|
|
715
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
716
|
-
run: |
|
|
717
|
-
gh pr comment "${{ github.event.pull_request.number }}" \
|
|
718
|
-
--edit-last --body-file comment.md \
|
|
719
|
-
|| gh pr comment "${{ github.event.pull_request.number }}" \
|
|
720
|
-
--body-file comment.md
|
|
457
|
+
with: { sarif_file: ${{ steps.safeai.outputs.sarif-path }} }
|
|
721
458
|
```
|
|
722
459
|
|
|
723
|
-
|
|
724
|
-
in place on repeat pushes, rather than adding a new one each time; it fails
|
|
725
|
-
when there is no previous comment to edit (for example, on the first push),
|
|
726
|
-
so the fallback plain `gh pr comment` handles that case. The `--fail-on-escalation`
|
|
727
|
-
step runs before the comment step so the workflow's exit code still reflects
|
|
728
|
-
the scan outcome; `if: always()` on the comment step makes sure the comment
|
|
729
|
-
is posted even when the scan step fails the job.
|
|
730
|
-
|
|
731
|
-
### SARIF Integration
|
|
732
|
-
|
|
733
|
-
SafeAI outputs SARIF 2.1.0 format, compatible with GitHub Advanced Security, Azure DevOps, and other SARIF-compliant tools.
|
|
460
|
+
See [CI/CD Integration Guide](./docs/ci-integration.md) for GitHub Actions, GitLab CI, Azure DevOps, and escalation-gating examples.
|
|
734
461
|
|
|
735
462
|
---
|
|
736
463
|
|
|
@@ -785,6 +512,20 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
|
|
|
785
512
|
|
|
786
513
|
---
|
|
787
514
|
|
|
515
|
+
## Documentation
|
|
516
|
+
|
|
517
|
+
| Document | Description |
|
|
518
|
+
|----------|-------------|
|
|
519
|
+
| [Roadmap](./ROADMAP.md) | Future plans and feature requests |
|
|
520
|
+
| [Contributing](./CONTRIBUTING.md) | How to get involved |
|
|
521
|
+
| [Security](./SECURITY.md) | Vulnerability reporting and response |
|
|
522
|
+
| [Privacy](./PRIVACY.md) | Telemetry and data handling |
|
|
523
|
+
| [Release Notes](./docs/release/RELEASE_NOTES.md) | Changelog for all versions |
|
|
524
|
+
| [Upgrade Guide](./docs/release/UPGRADE.md) | v1.x → v2.0.0 migration |
|
|
525
|
+
| [CI/CD Integration](./docs/ci-integration.md) | GitHub Actions, GitLab CI, Azure DevOps examples |
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
788
529
|
## License
|
|
789
530
|
|
|
790
531
|
SafeAI is released under the Apache 2.0 License.
|