mirror of
https://github.com/q3alique/codeflow
synced 2026-06-21 14:06:02 +00:00
acc1b4f3e7
Static taint-analysis and visualization tool for source-code security review. Supports deep analysis for Python, JavaScript/TypeScript, Java, Go, and C#, with structural support for all other languages via the generic extractor. Outputs: interactive HTML report, LLM-ready Markdown review document, and optional Burp Suite JSON export. Self-bootstrapping launcher (run.py) requires no virtual environment.
106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
"""Task 013 tests: Graph builder."""
|
|
import pytest
|
|
from pathlib import Path
|
|
from codeflow.discovery.file_scanner import FileInfo, scan
|
|
from codeflow.analysis.graph_builder import build, _select_extractor, _extract_file
|
|
from codeflow.models.node import NodeType
|
|
from codeflow.models.edge import EdgeType
|
|
from tests.fixtures import FLASK_FIXTURE_DIR
|
|
|
|
|
|
class TestGraphBuilder:
|
|
def test_select_extractor_python(self):
|
|
ext = _select_extractor("python")
|
|
from codeflow.analysis.python_extractor import PythonExtractor
|
|
assert isinstance(ext, PythonExtractor)
|
|
|
|
def test_select_extractor_javascript(self):
|
|
ext = _select_extractor("javascript")
|
|
from codeflow.analysis.js_extractor import JSExtractor
|
|
assert isinstance(ext, JSExtractor)
|
|
|
|
def test_select_extractor_generic(self):
|
|
ext = _select_extractor("java")
|
|
from codeflow.analysis.generic_extractor import GenericExtractor
|
|
assert isinstance(ext, GenericExtractor)
|
|
|
|
def test_build_graph_from_flask_fixture(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
assert graph.node_count() > 0
|
|
assert graph.edge_count() > 0
|
|
|
|
def test_graph_has_module_nodes(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
mods = graph.nodes_by_type(NodeType.MODULE)
|
|
assert len(mods) >= 1
|
|
|
|
def test_graph_has_function_nodes(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
funcs = graph.nodes_by_type(NodeType.FUNCTION)
|
|
assert len(funcs) >= 1
|
|
|
|
def test_graph_has_entry_points(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
eps = graph.nodes_by_type(NodeType.ENTRY_POINT)
|
|
assert len(eps) >= 1
|
|
|
|
def test_graph_has_source_nodes(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
sources = graph.nodes_by_type(NodeType.SOURCE)
|
|
assert len(sources) >= 1
|
|
|
|
def test_graph_has_sink_nodes(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
sinks = graph.nodes_by_type(NodeType.SINK)
|
|
assert len(sinks) >= 1
|
|
|
|
def test_graph_has_calls_edges(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
has_calls = False
|
|
for _, _, edge in graph.edges():
|
|
if edge.edge_type == EdgeType.CALLS:
|
|
has_calls = True
|
|
break
|
|
assert has_calls
|
|
|
|
def test_graph_has_contains_edges(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
has_contains = False
|
|
for _, _, edge in graph.edges():
|
|
if edge.edge_type == EdgeType.CONTAINS:
|
|
has_contains = True
|
|
break
|
|
assert has_contains
|
|
|
|
def test_boundary_nodes_created(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
boundaries = graph.nodes_by_type(NodeType.BOUNDARY)
|
|
assert len(boundaries) >= 1
|
|
|
|
def test_graph_build_empty(self):
|
|
graph = build([])
|
|
assert graph.node_count() == 0
|
|
|
|
def test_cross_file_calls_resolved(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
authenticate_found = any(
|
|
"authenticate" in nid for nid in graph.nodes()
|
|
)
|
|
assert authenticate_found
|
|
|
|
def test_unique_node_ids(self):
|
|
file_infos = scan(FLASK_FIXTURE_DIR)
|
|
graph = build(file_infos)
|
|
ids = list(graph.nodes())
|
|
assert len(ids) == len(set(ids))
|