SafeAI-Static-Analyzer 2.2.1__tar.gz → 2.3.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.2.1 → safeai_static_analyzer-2.3.0}/PKG-INFO +27 -4
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/README.md +26 -3
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/PKG-INFO +27 -4
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/SOURCES.txt +9 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/pyproject.toml +4 -0
- safeai_static_analyzer-2.3.0/safeai/analyzers/__init__.py +169 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/capability/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/claude_code/analyzer.py +2 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/data_leakage/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/dataflow/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/env_dependency/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/governance/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/analyzer.py +2 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/model_config/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/prompt/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/prompt_file/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/skill/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/tool_def/analyzer.py +2 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/workflow/analyzer.py +3 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/cmd/cli.py +126 -1
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/cmd/postprocess.py +25 -1
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/cmd/registry_cli.py +38 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/engine/file_discovery.py +12 -2
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/engine/orchestrator.py +53 -34
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/__init__.py +26 -1
- safeai_static_analyzer-2.3.0/safeai/frameworks/copilot/parser.py +184 -0
- safeai_static_analyzer-2.3.0/safeai/frameworks/openclaw/parser.py +172 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/__init__.py +1 -1
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/exporter.py +18 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/importer.py +50 -1
- safeai_static_analyzer-2.3.0/safeai/kya/lockfile.py +128 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/manifest.py +21 -2
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/persist.py +8 -2
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/schema.py +11 -0
- safeai_static_analyzer-2.3.0/safeai/rules/pack_test.py +152 -0
- safeai_static_analyzer-2.3.0/safeai/scoring/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/version.py +1 -1
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_capability_diff_v2.py +1 -1
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_compatibility.py +62 -0
- safeai_static_analyzer-2.3.0/tests/test_config_adapters.py +192 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_dco.py +8 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_manifest_integrity.py +50 -0
- safeai_static_analyzer-2.3.0/tests/test_plugin_sdk.py +468 -0
- safeai_static_analyzer-2.3.0/tests/test_scan_framework.py +374 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/LICENSE +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/dependency_links.txt +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/entry_points.txt +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/requires.txt +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/SafeAI_Static_Analyzer.egg-info/top_level.txt +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/__main__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/aggregation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/capabilities.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/capability_diff.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/component_diff.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/component_graph.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/components.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/dependency_correlation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/escalation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/escalation_remediation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/import_graph.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/project_graph.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/semantic.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/target_taxonomy.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/tool_identity.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/tool_implementation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analysis/tool_surface.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers → safeai_static_analyzer-2.3.0/safeai/analyzers/capability}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/claude_code/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/capability → safeai_static_analyzer-2.3.0/safeai/analyzers/data_leakage}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/dataflow/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/env_dependency/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/governance/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/data_leakage → safeai_static_analyzer-2.3.0/safeai/analyzers/mcp}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/compatibility.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/schema.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/validators.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/mcp → safeai_static_analyzer-2.3.0/safeai/analyzers/model_config}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/model_config → safeai_static_analyzer-2.3.0/safeai/analyzers/prompt}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/prompt → safeai_static_analyzer-2.3.0/safeai/analyzers/prompt_file}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/prompt_file → safeai_static_analyzer-2.3.0/safeai/analyzers/skill}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/skill → safeai_static_analyzer-2.3.0/safeai/analyzers/tool_def}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/tool_def → safeai_static_analyzer-2.3.0/safeai/analyzers/workflow}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/analyzers/workflow → safeai_static_analyzer-2.3.0/safeai/cmd}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/cmd/manifest_cli.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/controls/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/controls/catalogs.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/controls/mappings.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/cmd → safeai_static_analyzer-2.3.0/safeai/engine}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/engine/metadata.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/engine/scan.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/autogen/parser.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/engine → safeai_static_analyzer-2.3.0/safeai/frameworks/azure_foundry}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/azure_foundry/parser.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/frameworks/azure_foundry → safeai_static_analyzer-2.3.0/safeai/frameworks/bedrock_agent}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/bedrock_agent/parser.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/frameworks/bedrock_agent → safeai_static_analyzer-2.3.0/safeai/frameworks/claude_code}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/commands.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/permissions.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/claude_code/settings.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/frameworks/claude_code → safeai_static_analyzer-2.3.0/safeai/frameworks/copilot}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/crewai/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/crewai/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/cursorrules/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/cursorrules/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/dify/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/dify/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/google_adk/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/google_adk/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/haystack/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/haystack/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langchain/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langchain/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langgraph/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/langgraph/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/llamaindex/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/llamaindex/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/mastra/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/mastra/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/microsoft_agent/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/microsoft_agent/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/n8n/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/n8n/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/openai_agents/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/openai_agents/parser.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/frameworks/semantic_kernel → safeai_static_analyzer-2.3.0/safeai/frameworks/openclaw}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/frameworks/windsurf → safeai_static_analyzer-2.3.0/safeai/frameworks/semantic_kernel}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/semantic_kernel/parser.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/report → safeai_static_analyzer-2.3.0/safeai/frameworks/windsurf}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/frameworks/windsurf/parser.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/assurance.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/baseline.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/ci_context.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/contract.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/enrich.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/fingerprints.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/identity.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/integrity.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/policy.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/connection.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/registry/queries.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/suppressions.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/kya/util.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/rules → safeai_static_analyzer-2.3.0/safeai/report}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/failure_matrix.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/html.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/html_kit.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/json_report.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/pr_comment.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/registry_html.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/sarif.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/report/terminal.py +0 -0
- {safeai_static_analyzer-2.2.1/safeai/scoring → safeai_static_analyzer-2.3.0/safeai/rules}/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/rules/base_rules.yaml +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/rules/loader.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/scorecard.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/scoring/engine.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/severity.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/telemetry/__init__.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/telemetry/client.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/telemetry/config.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/telemetry/schema.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/setup.cfg +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_adapter_dataflow.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_assurance.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_baseline.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_benchmarks.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_capability_detection.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_ci_context.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_claude_code_deep.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_component_graph.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_component_hash.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_confidence_arbitration.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_control_mappings.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_crewai_framework.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_cursorrules_framework.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_env_dependency.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_escalation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_escalation_remediation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_evidence_type.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_fingerprints.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_github_action.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_governance_analyzer.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_html_report.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_init_and_rules.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_kya_integration.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_kya_manifest.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_langgraph_framework.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_llamaindex_framework.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_manifest_contract.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_mcp_tool_poisoning.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_mcp_validation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_n8n_workflow.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_parser_aggregation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_phase15.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_policy.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_pr_comment.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_pr_comment_cli.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_quality_gates.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_registry.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_registry_cli.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_registry_html.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_regression_fixtures.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_release_assets.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_release_fixes.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_release_validation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_rules_loader_determinism.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_sarif.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_scan_phase11.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_scorecard.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_scoring.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_semantic.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_severity.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_suppressions.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_target_taxonomy_engine.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_telemetry.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_tool_identity.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_tool_implementation.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/tests/test_version.py +0 -0
- {safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.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.3.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
|
|
@@ -36,7 +36,7 @@ Dynamic: license-file
|
|
|
36
36
|
[](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
|
|
37
37
|
[](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
|
|
38
38
|
[](https://safeai-analyzer.ikaruscareer.com)
|
|
39
|
-
[](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.3.0)
|
|
40
40
|
[](https://www.bestpractices.dev/en/projects/14126)
|
|
41
41
|
|
|
42
42
|
Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
|
|
@@ -84,7 +84,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
|
|
|
84
84
|
|
|
85
85
|
| Feature | Description |
|
|
86
86
|
|---------|-------------|
|
|
87
|
-
| **Framework Detection** | Detects and parses
|
|
87
|
+
| **Framework Detection** | Detects and parses 19 AI agent frameworks (AST + config + regex, no mutual exclusion) |
|
|
88
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 |
|
|
89
89
|
| **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
|
|
90
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 |
|
|
@@ -170,6 +170,8 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
170
170
|
| n8n | ✔ | Partial | Minimal | Minimal | Experimental |
|
|
171
171
|
| Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
172
172
|
| Windsurf (.windsurfrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
173
|
+
| OpenClaw config | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
174
|
+
| GitHub Copilot instructions | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
173
175
|
|
|
174
176
|
|
|
175
177
|
### Framework Support Details
|
|
@@ -194,6 +196,12 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
194
196
|
- **Cursor (.cursorrules)** — detects declared tools/permissions and
|
|
195
197
|
capability-relevant keywords (shell, filesystem, HTTP, database) in the
|
|
196
198
|
IDE's rules config, JSON, YAML, or free text
|
|
199
|
+
- **Windsurf (.windsurfrules)** — detects declared tools, models, and
|
|
200
|
+
capability-relevant keywords in JSON, YAML, or free-text rules
|
|
201
|
+
- **OpenClaw config** — detects JSON/YAML files under `.openclaw/` and
|
|
202
|
+
`.openclaw.yaml`, including declared tools, models, and MCP servers
|
|
203
|
+
- **GitHub Copilot instructions** — detects repository instruction Markdown,
|
|
204
|
+
YAML frontmatter, and `.copilot/*.yml` tool or permission grants
|
|
197
205
|
|
|
198
206
|
Maturity is on the scale defined in [`docs/reference/FRAMEWORK_SUPPORT.md`](docs/reference/FRAMEWORK_SUPPORT.md):
|
|
199
207
|
**Partial** = reliable detection and discovery with capability/risk analysis over
|
|
@@ -211,6 +219,8 @@ Representative test fixtures and validation tests for framework detection:
|
|
|
211
219
|
| LlamaIndex | `test_llamaindex_framework.py` | `fixtures/llamaindex/representative/agent.py` | @adnqcr7-code [#61] |
|
|
212
220
|
| CrewAI | `test_crewai_framework.py` | `fixtures/crewai/representative/crew.py` | @adnqcr7-code [#62] |
|
|
213
221
|
| Claude Code | `test_claude_code_deep.py` | `fixtures/claude_code/compatibility/` | @adnqcr7-code [#59] |
|
|
222
|
+
| OpenClaw config | `test_config_adapters.py` | `fixtures/openclaw/representative/.openclaw/config.json` | @YaoSong808 [#159] |
|
|
223
|
+
| GitHub Copilot instructions | `test_config_adapters.py` | `fixtures/copilot/representative/.github/copilot-instructions.md` | @YaoSong808 [#159] |
|
|
214
224
|
|
|
215
225
|
---
|
|
216
226
|
|
|
@@ -302,6 +312,16 @@ to a shared path, or use `--no-registry` for ephemeral scans.
|
|
|
302
312
|
- Python 3.11, 3.12, or 3.13
|
|
303
313
|
- PyYAML (for YAML configuration parsing)
|
|
304
314
|
|
|
315
|
+
### Install from PyPI (recommended)
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
pip install SafeAI-Static-Analyzer
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Unpinned on purpose — this always resolves to the latest stable
|
|
322
|
+
release (see [releases](https://github.com/ikaruscareer/SafeAI/releases)).
|
|
323
|
+
Verify the download with [`docs/reference/VERIFICATION.md`](docs/reference/VERIFICATION.md).
|
|
324
|
+
|
|
305
325
|
### Install from source
|
|
306
326
|
|
|
307
327
|
```bash
|
|
@@ -343,6 +363,7 @@ python -m safeai registry <subcommand> [options]
|
|
|
343
363
|
| `--json` | — | JSON output path |
|
|
344
364
|
| `--html` | — | HTML report output path |
|
|
345
365
|
| `--manifest` | — | Canonical KYA manifest output path (`safeai-manifest.json`) |
|
|
366
|
+
| `--digest-file` | — | With `--manifest`: write `<canonical-sha256> <manifest-basename>` to this path. The digest is the one `safeai manifest verify` reports, not a raw file hash, so it is not `sha256sum -c` compatible |
|
|
346
367
|
| `--baseline` | — | Prior manifest/report for new/existing comparison |
|
|
347
368
|
| `--fail-on-new` | off | With `--baseline`: fail only on new/regressed findings |
|
|
348
369
|
| `--policy` | `.safeai/policy.yml` | Policy-as-code YAML file |
|
|
@@ -450,7 +471,7 @@ jobs:
|
|
|
450
471
|
- uses: actions/checkout@v4
|
|
451
472
|
- uses: actions/setup-python@v5
|
|
452
473
|
with: { python-version: '3.12' }
|
|
453
|
-
- uses: ikaruscareer/SafeAI@
|
|
474
|
+
- uses: ikaruscareer/SafeAI@v2
|
|
454
475
|
with: { path: '.', fail-on: critical }
|
|
455
476
|
- uses: github/codeql-action/upload-sarif@v3
|
|
456
477
|
if: always()
|
|
@@ -504,6 +525,8 @@ contributors who have helped make AI safer:
|
|
|
504
525
|
| [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
|
|
505
526
|
| [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
|
|
506
527
|
| [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
|
|
528
|
+
| [@burakeyler](https://github.com/burakeyler) | `--digest-file` manifest digest sidecar (PR #148) |
|
|
529
|
+
| [@YaoSong808](https://github.com/YaoSong808) | OpenClaw and GitHub Copilot config-file adapters (PR #159) |
|
|
507
530
|
| [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
|
|
508
531
|
| [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
|
|
509
532
|
| [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
|
|
4
4
|
[](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
|
|
5
5
|
[](https://safeai-analyzer.ikaruscareer.com)
|
|
6
|
-
[](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.3.0)
|
|
7
7
|
[](https://www.bestpractices.dev/en/projects/14126)
|
|
8
8
|
|
|
9
9
|
Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
|
|
@@ -51,7 +51,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
|
|
|
51
51
|
|
|
52
52
|
| Feature | Description |
|
|
53
53
|
|---------|-------------|
|
|
54
|
-
| **Framework Detection** | Detects and parses
|
|
54
|
+
| **Framework Detection** | Detects and parses 19 AI agent frameworks (AST + config + regex, no mutual exclusion) |
|
|
55
55
|
| **Tool Identity & Access Modes** | Capabilities attributed to named tools (agent / MCP server / skill / tool / workflow node) on an access scale `none < read < write < mutate < execute`; inferred modes are flagged, never overstated |
|
|
56
56
|
| **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
|
|
57
57
|
| **Capability Escalation Detection** | Per-tool authority diffs between scans (new shell, read→write widening, new MCP server, removed approval gate, ...) — 14 rules, including gating-aware subsumption |
|
|
@@ -137,6 +137,8 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
137
137
|
| n8n | ✔ | Partial | Minimal | Minimal | Experimental |
|
|
138
138
|
| Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
139
139
|
| Windsurf (.windsurfrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
140
|
+
| OpenClaw config | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
141
|
+
| GitHub Copilot instructions | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
140
142
|
|
|
141
143
|
|
|
142
144
|
### Framework Support Details
|
|
@@ -161,6 +163,12 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
161
163
|
- **Cursor (.cursorrules)** — detects declared tools/permissions and
|
|
162
164
|
capability-relevant keywords (shell, filesystem, HTTP, database) in the
|
|
163
165
|
IDE's rules config, JSON, YAML, or free text
|
|
166
|
+
- **Windsurf (.windsurfrules)** — detects declared tools, models, and
|
|
167
|
+
capability-relevant keywords in JSON, YAML, or free-text rules
|
|
168
|
+
- **OpenClaw config** — detects JSON/YAML files under `.openclaw/` and
|
|
169
|
+
`.openclaw.yaml`, including declared tools, models, and MCP servers
|
|
170
|
+
- **GitHub Copilot instructions** — detects repository instruction Markdown,
|
|
171
|
+
YAML frontmatter, and `.copilot/*.yml` tool or permission grants
|
|
164
172
|
|
|
165
173
|
Maturity is on the scale defined in [`docs/reference/FRAMEWORK_SUPPORT.md`](docs/reference/FRAMEWORK_SUPPORT.md):
|
|
166
174
|
**Partial** = reliable detection and discovery with capability/risk analysis over
|
|
@@ -178,6 +186,8 @@ Representative test fixtures and validation tests for framework detection:
|
|
|
178
186
|
| LlamaIndex | `test_llamaindex_framework.py` | `fixtures/llamaindex/representative/agent.py` | @adnqcr7-code [#61] |
|
|
179
187
|
| CrewAI | `test_crewai_framework.py` | `fixtures/crewai/representative/crew.py` | @adnqcr7-code [#62] |
|
|
180
188
|
| Claude Code | `test_claude_code_deep.py` | `fixtures/claude_code/compatibility/` | @adnqcr7-code [#59] |
|
|
189
|
+
| OpenClaw config | `test_config_adapters.py` | `fixtures/openclaw/representative/.openclaw/config.json` | @YaoSong808 [#159] |
|
|
190
|
+
| GitHub Copilot instructions | `test_config_adapters.py` | `fixtures/copilot/representative/.github/copilot-instructions.md` | @YaoSong808 [#159] |
|
|
181
191
|
|
|
182
192
|
---
|
|
183
193
|
|
|
@@ -269,6 +279,16 @@ to a shared path, or use `--no-registry` for ephemeral scans.
|
|
|
269
279
|
- Python 3.11, 3.12, or 3.13
|
|
270
280
|
- PyYAML (for YAML configuration parsing)
|
|
271
281
|
|
|
282
|
+
### Install from PyPI (recommended)
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
pip install SafeAI-Static-Analyzer
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Unpinned on purpose — this always resolves to the latest stable
|
|
289
|
+
release (see [releases](https://github.com/ikaruscareer/SafeAI/releases)).
|
|
290
|
+
Verify the download with [`docs/reference/VERIFICATION.md`](docs/reference/VERIFICATION.md).
|
|
291
|
+
|
|
272
292
|
### Install from source
|
|
273
293
|
|
|
274
294
|
```bash
|
|
@@ -310,6 +330,7 @@ python -m safeai registry <subcommand> [options]
|
|
|
310
330
|
| `--json` | — | JSON output path |
|
|
311
331
|
| `--html` | — | HTML report output path |
|
|
312
332
|
| `--manifest` | — | Canonical KYA manifest output path (`safeai-manifest.json`) |
|
|
333
|
+
| `--digest-file` | — | With `--manifest`: write `<canonical-sha256> <manifest-basename>` to this path. The digest is the one `safeai manifest verify` reports, not a raw file hash, so it is not `sha256sum -c` compatible |
|
|
313
334
|
| `--baseline` | — | Prior manifest/report for new/existing comparison |
|
|
314
335
|
| `--fail-on-new` | off | With `--baseline`: fail only on new/regressed findings |
|
|
315
336
|
| `--policy` | `.safeai/policy.yml` | Policy-as-code YAML file |
|
|
@@ -417,7 +438,7 @@ jobs:
|
|
|
417
438
|
- uses: actions/checkout@v4
|
|
418
439
|
- uses: actions/setup-python@v5
|
|
419
440
|
with: { python-version: '3.12' }
|
|
420
|
-
- uses: ikaruscareer/SafeAI@
|
|
441
|
+
- uses: ikaruscareer/SafeAI@v2
|
|
421
442
|
with: { path: '.', fail-on: critical }
|
|
422
443
|
- uses: github/codeql-action/upload-sarif@v3
|
|
423
444
|
if: always()
|
|
@@ -471,6 +492,8 @@ contributors who have helped make AI safer:
|
|
|
471
492
|
| [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
|
|
472
493
|
| [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
|
|
473
494
|
| [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
|
|
495
|
+
| [@burakeyler](https://github.com/burakeyler) | `--digest-file` manifest digest sidecar (PR #148) |
|
|
496
|
+
| [@YaoSong808](https://github.com/YaoSong808) | OpenClaw and GitHub Copilot config-file adapters (PR #159) |
|
|
474
497
|
| [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
|
|
475
498
|
| [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
|
|
476
499
|
| [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SafeAI-Static-Analyzer
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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
|
|
@@ -36,7 +36,7 @@ Dynamic: license-file
|
|
|
36
36
|
[](https://github.com/ikaruscareer/SafeAI/actions/workflows/ci.yml)
|
|
37
37
|
[](https://scorecard.dev/viewer/?uri=github.com/ikaruscareer/SafeAI)
|
|
38
38
|
[](https://safeai-analyzer.ikaruscareer.com)
|
|
39
|
-
[](https://github.com/ikaruscareer/SafeAI/releases/tag/v2.3.0)
|
|
40
40
|
[](https://www.bestpractices.dev/en/projects/14126)
|
|
41
41
|
|
|
42
42
|
Enjoying SafeAI? A ⭐ on [GitHub](https://github.com/ikaruscareer/SafeAI) helps more security teams find it.
|
|
@@ -84,7 +84,7 @@ SafeAI sits before runtime guardrails and red-teaming tools in the security life
|
|
|
84
84
|
|
|
85
85
|
| Feature | Description |
|
|
86
86
|
|---------|-------------|
|
|
87
|
-
| **Framework Detection** | Detects and parses
|
|
87
|
+
| **Framework Detection** | Detects and parses 19 AI agent frameworks (AST + config + regex, no mutual exclusion) |
|
|
88
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 |
|
|
89
89
|
| **Capability Discovery** | Maps 19 capability categories (shell, filesystem, network, database, memory, MCP, ...) with evidence, confidence, and provenance |
|
|
90
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 |
|
|
@@ -170,6 +170,8 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
170
170
|
| n8n | ✔ | Partial | Minimal | Minimal | Experimental |
|
|
171
171
|
| Cursor (.cursorrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
172
172
|
| Windsurf (.windsurfrules) | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
173
|
+
| OpenClaw config | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
174
|
+
| GitHub Copilot instructions | ✔ | Minimal | Minimal | Minimal | Experimental |
|
|
173
175
|
|
|
174
176
|
|
|
175
177
|
### Framework Support Details
|
|
@@ -194,6 +196,12 @@ Registry & Reports — shared SQLite registry; terminal, JSON, SARIF 2.1.0, HTML
|
|
|
194
196
|
- **Cursor (.cursorrules)** — detects declared tools/permissions and
|
|
195
197
|
capability-relevant keywords (shell, filesystem, HTTP, database) in the
|
|
196
198
|
IDE's rules config, JSON, YAML, or free text
|
|
199
|
+
- **Windsurf (.windsurfrules)** — detects declared tools, models, and
|
|
200
|
+
capability-relevant keywords in JSON, YAML, or free-text rules
|
|
201
|
+
- **OpenClaw config** — detects JSON/YAML files under `.openclaw/` and
|
|
202
|
+
`.openclaw.yaml`, including declared tools, models, and MCP servers
|
|
203
|
+
- **GitHub Copilot instructions** — detects repository instruction Markdown,
|
|
204
|
+
YAML frontmatter, and `.copilot/*.yml` tool or permission grants
|
|
197
205
|
|
|
198
206
|
Maturity is on the scale defined in [`docs/reference/FRAMEWORK_SUPPORT.md`](docs/reference/FRAMEWORK_SUPPORT.md):
|
|
199
207
|
**Partial** = reliable detection and discovery with capability/risk analysis over
|
|
@@ -211,6 +219,8 @@ Representative test fixtures and validation tests for framework detection:
|
|
|
211
219
|
| LlamaIndex | `test_llamaindex_framework.py` | `fixtures/llamaindex/representative/agent.py` | @adnqcr7-code [#61] |
|
|
212
220
|
| CrewAI | `test_crewai_framework.py` | `fixtures/crewai/representative/crew.py` | @adnqcr7-code [#62] |
|
|
213
221
|
| Claude Code | `test_claude_code_deep.py` | `fixtures/claude_code/compatibility/` | @adnqcr7-code [#59] |
|
|
222
|
+
| OpenClaw config | `test_config_adapters.py` | `fixtures/openclaw/representative/.openclaw/config.json` | @YaoSong808 [#159] |
|
|
223
|
+
| GitHub Copilot instructions | `test_config_adapters.py` | `fixtures/copilot/representative/.github/copilot-instructions.md` | @YaoSong808 [#159] |
|
|
214
224
|
|
|
215
225
|
---
|
|
216
226
|
|
|
@@ -302,6 +312,16 @@ to a shared path, or use `--no-registry` for ephemeral scans.
|
|
|
302
312
|
- Python 3.11, 3.12, or 3.13
|
|
303
313
|
- PyYAML (for YAML configuration parsing)
|
|
304
314
|
|
|
315
|
+
### Install from PyPI (recommended)
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
pip install SafeAI-Static-Analyzer
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Unpinned on purpose — this always resolves to the latest stable
|
|
322
|
+
release (see [releases](https://github.com/ikaruscareer/SafeAI/releases)).
|
|
323
|
+
Verify the download with [`docs/reference/VERIFICATION.md`](docs/reference/VERIFICATION.md).
|
|
324
|
+
|
|
305
325
|
### Install from source
|
|
306
326
|
|
|
307
327
|
```bash
|
|
@@ -343,6 +363,7 @@ python -m safeai registry <subcommand> [options]
|
|
|
343
363
|
| `--json` | — | JSON output path |
|
|
344
364
|
| `--html` | — | HTML report output path |
|
|
345
365
|
| `--manifest` | — | Canonical KYA manifest output path (`safeai-manifest.json`) |
|
|
366
|
+
| `--digest-file` | — | With `--manifest`: write `<canonical-sha256> <manifest-basename>` to this path. The digest is the one `safeai manifest verify` reports, not a raw file hash, so it is not `sha256sum -c` compatible |
|
|
346
367
|
| `--baseline` | — | Prior manifest/report for new/existing comparison |
|
|
347
368
|
| `--fail-on-new` | off | With `--baseline`: fail only on new/regressed findings |
|
|
348
369
|
| `--policy` | `.safeai/policy.yml` | Policy-as-code YAML file |
|
|
@@ -450,7 +471,7 @@ jobs:
|
|
|
450
471
|
- uses: actions/checkout@v4
|
|
451
472
|
- uses: actions/setup-python@v5
|
|
452
473
|
with: { python-version: '3.12' }
|
|
453
|
-
- uses: ikaruscareer/SafeAI@
|
|
474
|
+
- uses: ikaruscareer/SafeAI@v2
|
|
454
475
|
with: { path: '.', fail-on: critical }
|
|
455
476
|
- uses: github/codeql-action/upload-sarif@v3
|
|
456
477
|
if: always()
|
|
@@ -504,6 +525,8 @@ contributors who have helped make AI safer:
|
|
|
504
525
|
| [@hadbiaghiles](https://github.com/hadbiaghiles) | AutoGen framework documentation |
|
|
505
526
|
| [@D05TL3](https://github.com/D05TL3) | GitHub Actions scanning example |
|
|
506
527
|
| [@mikemikimike](https://github.com/mikemikimike) | Adapter negative detection tests |
|
|
528
|
+
| [@burakeyler](https://github.com/burakeyler) | `--digest-file` manifest digest sidecar (PR #148) |
|
|
529
|
+
| [@YaoSong808](https://github.com/YaoSong808) | OpenClaw and GitHub Copilot config-file adapters (PR #159) |
|
|
507
530
|
| [@mah](https://github.com/mahirhir) | Claude Code deep analysis documentation |
|
|
508
531
|
| [@asarakhatun17-lgtm](https://github.com/asarakhatun17-lgtm) | Supported frameworks consistency fix |
|
|
509
532
|
| [@yugaaank](https://github.com/yugaaank) | Capability detectors (Docker, Kubernetes, Redis, S3, GCP, Slack, Jira, browser) |
|
|
@@ -83,6 +83,8 @@ safeai/frameworks/claude_code/commands.py
|
|
|
83
83
|
safeai/frameworks/claude_code/parser.py
|
|
84
84
|
safeai/frameworks/claude_code/permissions.py
|
|
85
85
|
safeai/frameworks/claude_code/settings.py
|
|
86
|
+
safeai/frameworks/copilot/__init__.py
|
|
87
|
+
safeai/frameworks/copilot/parser.py
|
|
86
88
|
safeai/frameworks/crewai/__init__.py
|
|
87
89
|
safeai/frameworks/crewai/parser.py
|
|
88
90
|
safeai/frameworks/cursorrules/__init__.py
|
|
@@ -107,6 +109,8 @@ safeai/frameworks/n8n/__init__.py
|
|
|
107
109
|
safeai/frameworks/n8n/parser.py
|
|
108
110
|
safeai/frameworks/openai_agents/__init__.py
|
|
109
111
|
safeai/frameworks/openai_agents/parser.py
|
|
112
|
+
safeai/frameworks/openclaw/__init__.py
|
|
113
|
+
safeai/frameworks/openclaw/parser.py
|
|
110
114
|
safeai/frameworks/semantic_kernel/__init__.py
|
|
111
115
|
safeai/frameworks/semantic_kernel/parser.py
|
|
112
116
|
safeai/frameworks/windsurf/__init__.py
|
|
@@ -122,6 +126,7 @@ safeai/kya/fingerprints.py
|
|
|
122
126
|
safeai/kya/identity.py
|
|
123
127
|
safeai/kya/importer.py
|
|
124
128
|
safeai/kya/integrity.py
|
|
129
|
+
safeai/kya/lockfile.py
|
|
125
130
|
safeai/kya/manifest.py
|
|
126
131
|
safeai/kya/policy.py
|
|
127
132
|
safeai/kya/suppressions.py
|
|
@@ -143,6 +148,7 @@ safeai/report/terminal.py
|
|
|
143
148
|
safeai/rules/__init__.py
|
|
144
149
|
safeai/rules/base_rules.yaml
|
|
145
150
|
safeai/rules/loader.py
|
|
151
|
+
safeai/rules/pack_test.py
|
|
146
152
|
safeai/scoring/__init__.py
|
|
147
153
|
safeai/scoring/engine.py
|
|
148
154
|
safeai/telemetry/__init__.py
|
|
@@ -161,6 +167,7 @@ tests/test_compatibility.py
|
|
|
161
167
|
tests/test_component_graph.py
|
|
162
168
|
tests/test_component_hash.py
|
|
163
169
|
tests/test_confidence_arbitration.py
|
|
170
|
+
tests/test_config_adapters.py
|
|
164
171
|
tests/test_control_mappings.py
|
|
165
172
|
tests/test_crewai_framework.py
|
|
166
173
|
tests/test_cursorrules_framework.py
|
|
@@ -185,6 +192,7 @@ tests/test_mcp_validation.py
|
|
|
185
192
|
tests/test_n8n_workflow.py
|
|
186
193
|
tests/test_parser_aggregation.py
|
|
187
194
|
tests/test_phase15.py
|
|
195
|
+
tests/test_plugin_sdk.py
|
|
188
196
|
tests/test_policy.py
|
|
189
197
|
tests/test_pr_comment.py
|
|
190
198
|
tests/test_pr_comment_cli.py
|
|
@@ -198,6 +206,7 @@ tests/test_release_fixes.py
|
|
|
198
206
|
tests/test_release_validation.py
|
|
199
207
|
tests/test_rules_loader_determinism.py
|
|
200
208
|
tests/test_sarif.py
|
|
209
|
+
tests/test_scan_framework.py
|
|
201
210
|
tests/test_scan_phase11.py
|
|
202
211
|
tests/test_scorecard.py
|
|
203
212
|
tests/test_scoring.py
|
|
@@ -55,6 +55,10 @@ safeai = "safeai.cmd.cli:main"
|
|
|
55
55
|
# Built-in parsers are loaded directly by safeai.frameworks.
|
|
56
56
|
[project.entry-points."safeai.parsers"]
|
|
57
57
|
|
|
58
|
+
# Third-party analyzer classes can expose themselves through this group.
|
|
59
|
+
# Built-in analyzers are loaded directly by safeai.analyzers.
|
|
60
|
+
[project.entry-points."safeai.analyzers"]
|
|
61
|
+
|
|
58
62
|
[tool.ruff.lint]
|
|
59
63
|
ignore = ["BLE001", "S110", "S112", "RUF012"]
|
|
60
64
|
extend-per-file-ignores = { "tests/fixtures/action/**" = ["I001", "F841", "PLW1510", "T201"] }
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Analyzer registry and installed analyzer-plugin loader.
|
|
2
|
+
|
|
3
|
+
Analyzers register themselves here so that ``safeai.engine.orchestrator``
|
|
4
|
+
does not need a hard-coded class list. Built-in analyzers self-register
|
|
5
|
+
with a ``phase`` of ``"core"`` (run over file sources) or ``"component"``
|
|
6
|
+
(run over extracted components); the orchestrator preserves that order.
|
|
7
|
+
|
|
8
|
+
External plugins may register through the ``safeai.analyzers`` Python
|
|
9
|
+
entry point group or call ``register_analyzer`` at import time. External
|
|
10
|
+
analyzers run after built-ins in the component phase, must accept
|
|
11
|
+
``components=None``, and never fail a scan: a raising external analyzer
|
|
12
|
+
is skipped with a warning (see orchestrator isolation).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import inspect
|
|
16
|
+
import logging
|
|
17
|
+
from importlib import import_module, metadata
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
CORE = "core"
|
|
22
|
+
COMPONENT = "component"
|
|
23
|
+
_PHASES = (CORE, COMPONENT)
|
|
24
|
+
|
|
25
|
+
# Each entry: {"name", "cls", "phase", "external", "version"}.
|
|
26
|
+
# ``version`` is the installed distribution version for entry-point
|
|
27
|
+
# plugins, else None (resolved to the SafeAI version at record time).
|
|
28
|
+
_ANALYZER_REGISTRY = []
|
|
29
|
+
_ANALYZER_NAMES = set()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def register_analyzer(cls=None, *, phase=CORE, external=False, version=None):
|
|
33
|
+
"""Register an analyzer class.
|
|
34
|
+
|
|
35
|
+
Usable bare (``@register_analyzer``) for core analyzers or with
|
|
36
|
+
keywords (``@register_analyzer(phase="component")``).
|
|
37
|
+
|
|
38
|
+
The class must define a non-empty ``name`` and a ``run()`` method.
|
|
39
|
+
Component-phase analyzers must accept ``components=None``.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def _register(target):
|
|
43
|
+
name = getattr(target, "name", None)
|
|
44
|
+
run = getattr(target, "run", None)
|
|
45
|
+
if not name or not callable(run):
|
|
46
|
+
raise TypeError("Analyzer must define a non-empty name and run()")
|
|
47
|
+
if phase not in _PHASES:
|
|
48
|
+
raise ValueError(f"Analyzer phase must be one of {_PHASES}, got {phase!r}")
|
|
49
|
+
if phase == COMPONENT:
|
|
50
|
+
try:
|
|
51
|
+
params = inspect.signature(run).parameters
|
|
52
|
+
except (TypeError, ValueError):
|
|
53
|
+
params = {}
|
|
54
|
+
accepts_components = (
|
|
55
|
+
"components" in params
|
|
56
|
+
or any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values())
|
|
57
|
+
)
|
|
58
|
+
if not accepts_components:
|
|
59
|
+
raise TypeError(
|
|
60
|
+
f"Component-phase analyzer {name!r} must accept components=None"
|
|
61
|
+
)
|
|
62
|
+
if name not in _ANALYZER_NAMES:
|
|
63
|
+
_ANALYZER_REGISTRY.append(
|
|
64
|
+
{
|
|
65
|
+
"name": name,
|
|
66
|
+
"cls": target,
|
|
67
|
+
"phase": phase,
|
|
68
|
+
"external": external,
|
|
69
|
+
"version": version,
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
_ANALYZER_NAMES.add(name)
|
|
73
|
+
return target
|
|
74
|
+
|
|
75
|
+
if cls is None:
|
|
76
|
+
return _register
|
|
77
|
+
return _register(cls)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _load_external_analyzers():
|
|
81
|
+
"""Load analyzer classes exposed by installed ``safeai.analyzers`` plugins."""
|
|
82
|
+
try:
|
|
83
|
+
entries = metadata.entry_points()
|
|
84
|
+
if hasattr(entries, "select"):
|
|
85
|
+
entries = entries.select(group="safeai.analyzers")
|
|
86
|
+
else:
|
|
87
|
+
entries = entries.get("safeai.analyzers", [])
|
|
88
|
+
except Exception as exc:
|
|
89
|
+
logger.debug("Unable to enumerate analyzer plugins: %s", exc)
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
for entry in entries:
|
|
93
|
+
try:
|
|
94
|
+
dist_version = getattr(entry.dist, "version", None) if hasattr(entry, "dist") else None
|
|
95
|
+
register_analyzer(
|
|
96
|
+
entry.load(), phase=COMPONENT, external=True, version=dist_version
|
|
97
|
+
)
|
|
98
|
+
except Exception as exc:
|
|
99
|
+
logger.warning("Unable to load analyzer plugin %s: %s", entry.name, exc)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Built-in analyzers as (module, class, phase) in legacy run order.
|
|
103
|
+
# The table (not import order) is the source of truth for run order, so
|
|
104
|
+
# ``_import_builtin_analyzers`` is idempotent and order-canonical no
|
|
105
|
+
# matter which modules were already imported.
|
|
106
|
+
_BUILTIN_ANALYZERS = (
|
|
107
|
+
("safeai.analyzers.capability.analyzer", "CapabilityAnalyzer", CORE),
|
|
108
|
+
("safeai.analyzers.prompt.analyzer", "PromptAnalyzer", CORE),
|
|
109
|
+
("safeai.analyzers.data_leakage.analyzer", "DataLeakageAnalyzer", CORE),
|
|
110
|
+
("safeai.analyzers.env_dependency.analyzer", "EnvDependencyAnalyzer", CORE),
|
|
111
|
+
("safeai.analyzers.mcp.analyzer", "MCPAnalyzer", CORE),
|
|
112
|
+
("safeai.analyzers.claude_code.analyzer", "ClaudeCodeAnalyzer", CORE),
|
|
113
|
+
("safeai.analyzers.skill.analyzer", "SkillAnalyzer", COMPONENT),
|
|
114
|
+
("safeai.analyzers.prompt_file.analyzer", "PromptFileAnalyzer", COMPONENT),
|
|
115
|
+
("safeai.analyzers.tool_def.analyzer", "ToolDefAnalyzer", COMPONENT),
|
|
116
|
+
("safeai.analyzers.model_config.analyzer", "ModelConfigAnalyzer", COMPONENT),
|
|
117
|
+
("safeai.analyzers.workflow.analyzer", "WorkflowAnalyzer", COMPONENT),
|
|
118
|
+
("safeai.analyzers.governance.analyzer", "GovernanceAnalyzer", COMPONENT),
|
|
119
|
+
("safeai.analyzers.dataflow.analyzer", "DataFlowAnalyzer", COMPONENT),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _import_builtin_analyzers():
|
|
124
|
+
"""Register built-in analyzers in legacy run order (idempotent)."""
|
|
125
|
+
for module_name, class_name, phase in _BUILTIN_ANALYZERS:
|
|
126
|
+
module = import_module(module_name)
|
|
127
|
+
register_analyzer(getattr(module, class_name), phase=phase)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def discover_analyzers(phase=None, include_external=True):
|
|
131
|
+
"""Return registered analyzer instances, built-ins first, in run order.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
phase : str, optional
|
|
136
|
+
``"core"`` or ``"component"`` to filter; None returns all in phase
|
|
137
|
+
order (core first, then component).
|
|
138
|
+
"""
|
|
139
|
+
_import_builtin_analyzers()
|
|
140
|
+
if include_external:
|
|
141
|
+
_load_external_analyzers()
|
|
142
|
+
entries = list(_ANALYZER_REGISTRY)
|
|
143
|
+
if phase is not None:
|
|
144
|
+
entries = [e for e in entries if e["phase"] == phase]
|
|
145
|
+
else:
|
|
146
|
+
entries = sorted(entries, key=lambda e: (e["phase"] != CORE,))
|
|
147
|
+
instances = []
|
|
148
|
+
for entry in entries:
|
|
149
|
+
instance = entry["cls"]()
|
|
150
|
+
# Marker consumed by the orchestrator for plugin isolation.
|
|
151
|
+
instance._safeai_external = entry["external"]
|
|
152
|
+
instances.append(instance)
|
|
153
|
+
return instances
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def analyzer_records(include_external=True):
|
|
157
|
+
"""Return registry metadata (name/phase/external/version) for recording."""
|
|
158
|
+
_import_builtin_analyzers()
|
|
159
|
+
if include_external:
|
|
160
|
+
_load_external_analyzers()
|
|
161
|
+
return [
|
|
162
|
+
{
|
|
163
|
+
"name": e["name"],
|
|
164
|
+
"phase": e["phase"],
|
|
165
|
+
"external": e["external"],
|
|
166
|
+
"version": e["version"],
|
|
167
|
+
}
|
|
168
|
+
for e in _ANALYZER_REGISTRY
|
|
169
|
+
]
|
|
@@ -10,6 +10,8 @@ Operates in two passes:
|
|
|
10
10
|
|
|
11
11
|
import re
|
|
12
12
|
|
|
13
|
+
from safeai.analyzers import register_analyzer
|
|
14
|
+
|
|
13
15
|
CAP_PATTERNS = {
|
|
14
16
|
"shell": re.compile(r"subprocess|os\.system|popen", re.IGNORECASE),
|
|
15
17
|
"filesystem": re.compile(r"open\(|os\.remove|os\.write|pathlib", re.IGNORECASE),
|
|
@@ -99,6 +101,7 @@ def _iter_evidence(value):
|
|
|
99
101
|
yield str(value)
|
|
100
102
|
|
|
101
103
|
|
|
104
|
+
@register_analyzer
|
|
102
105
|
class CapabilityAnalyzer:
|
|
103
106
|
name = "capability"
|
|
104
107
|
|
|
@@ -12,6 +12,7 @@ scope — reading it would leak a developer's personal environment into a
|
|
|
12
12
|
CI artifact. Multi-scope discovery is deferred to a later release.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
from safeai.analyzers import register_analyzer
|
|
15
16
|
from safeai.analyzers.prompt.analyzer import analyze_prompt_text
|
|
16
17
|
from safeai.frameworks.claude_code import commands as cc_commands
|
|
17
18
|
from safeai.frameworks.claude_code import permissions as cc_permissions
|
|
@@ -116,6 +117,7 @@ def _finding(rule_id, rule_map, message, path, line, evidence, reason, severity=
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
|
|
120
|
+
@register_analyzer
|
|
119
121
|
class ClaudeCodeAnalyzer:
|
|
120
122
|
"""Emit authority findings for Claude Code project configuration."""
|
|
121
123
|
|
|
@@ -11,6 +11,8 @@ full secret values.
|
|
|
11
11
|
|
|
12
12
|
import re
|
|
13
13
|
|
|
14
|
+
from safeai.analyzers import register_analyzer
|
|
15
|
+
|
|
14
16
|
PATTERNS = {
|
|
15
17
|
"API_KEY": re.compile(r"(api[_-]?key)\s*=\s*[\"']?[A-Za-z0-9_-]{16,}" , re.IGNORECASE),
|
|
16
18
|
"TOKEN": re.compile(r"(token)\s*=\s*[\"']?[A-Za-z0-9._-]{16,}", re.IGNORECASE),
|
|
@@ -77,6 +79,7 @@ def mask_secret_evidence(line):
|
|
|
77
79
|
return _SECRET_VALUE_RE.sub(_repl, line.strip())
|
|
78
80
|
|
|
79
81
|
|
|
82
|
+
@register_analyzer
|
|
80
83
|
class DataLeakageAnalyzer:
|
|
81
84
|
name = "data_leakage"
|
|
82
85
|
|
{safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/dataflow/analyzer.py
RENAMED
|
@@ -16,6 +16,8 @@ scope, and are reported as such in each finding's ``limitation`` field.
|
|
|
16
16
|
import ast
|
|
17
17
|
import re
|
|
18
18
|
|
|
19
|
+
from safeai.analyzers import register_analyzer
|
|
20
|
+
|
|
19
21
|
# Untrusted input sources
|
|
20
22
|
SOURCE_PATTERNS = [
|
|
21
23
|
re.compile(r"\b(?:user_input|user_message|user_query|user_prompt)\b", re.IGNORECASE),
|
|
@@ -300,6 +302,7 @@ def _finding(rule_id, rule, message, path, line, evidence=None):
|
|
|
300
302
|
}
|
|
301
303
|
|
|
302
304
|
|
|
305
|
+
@register_analyzer(phase="component")
|
|
303
306
|
class DataFlowAnalyzer:
|
|
304
307
|
"""Detects untrusted input propagation into sensitive sinks.
|
|
305
308
|
|
|
@@ -18,6 +18,8 @@ user-facing value.
|
|
|
18
18
|
|
|
19
19
|
import re
|
|
20
20
|
|
|
21
|
+
from safeai.analyzers import register_analyzer
|
|
22
|
+
|
|
21
23
|
INVENTORY_RULE_ID = "ENV_DEP_INVENTORY"
|
|
22
24
|
|
|
23
25
|
#: Python ``os.getenv(...)`` / ``os.environ[...]`` / ``os.environ.get(...)``.
|
|
@@ -141,6 +143,7 @@ def analyze_file_inventory(path, content):
|
|
|
141
143
|
return refs
|
|
142
144
|
|
|
143
145
|
|
|
146
|
+
@register_analyzer
|
|
144
147
|
class EnvDependencyAnalyzer:
|
|
145
148
|
"""Detects references to external configuration and credentials."""
|
|
146
149
|
|
|
@@ -12,6 +12,8 @@ scoring category and HTML report governance summary section.
|
|
|
12
12
|
|
|
13
13
|
import re
|
|
14
14
|
|
|
15
|
+
from safeai.analyzers import register_analyzer
|
|
16
|
+
|
|
15
17
|
# Patterns for detecting governance controls in code
|
|
16
18
|
_TIMEOUT_RE = re.compile(
|
|
17
19
|
r"\b(?:timeout|time_out|request_timeout|connect_timeout|read_timeout)"
|
|
@@ -157,6 +159,7 @@ def _finding(rule_id, rule, message, path, line, tool_name=None, evidence=None,
|
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
|
|
162
|
+
@register_analyzer(phase="component")
|
|
160
163
|
class GovernanceAnalyzer:
|
|
161
164
|
"""Detects missing operational governance controls on agent tools.
|
|
162
165
|
|
{safeai_static_analyzer-2.2.1 → safeai_static_analyzer-2.3.0}/safeai/analyzers/mcp/analyzer.py
RENAMED
|
@@ -17,6 +17,7 @@ import re
|
|
|
17
17
|
import yaml
|
|
18
18
|
|
|
19
19
|
from safeai.analysis.capabilities import make_capability
|
|
20
|
+
from safeai.analyzers import register_analyzer
|
|
20
21
|
from safeai.analyzers.mcp.compatibility import (
|
|
21
22
|
normalize_mcp_data,
|
|
22
23
|
resolve_mcp_schema_version,
|
|
@@ -156,6 +157,7 @@ def _base_finding(
|
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
|
|
160
|
+
@register_analyzer
|
|
159
161
|
class MCPAnalyzer:
|
|
160
162
|
name = "mcp"
|
|
161
163
|
|