agentic-radar 0.1.2__py3-none-any.whl

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.
Files changed (41) hide show
  1. agentic_radar/__init__.py +3 -0
  2. agentic_radar/__main__.py +3 -0
  3. agentic_radar/analysis/__init__.py +3 -0
  4. agentic_radar/analysis/analyze.py +8 -0
  5. agentic_radar/analysis/langgraph/__init__.py +3 -0
  6. agentic_radar/analysis/langgraph/analyze.py +148 -0
  7. agentic_radar/analysis/langgraph/custom_tools.py +44 -0
  8. agentic_radar/analysis/langgraph/graph.py +792 -0
  9. agentic_radar/analysis/langgraph/models.py +36 -0
  10. agentic_radar/analysis/langgraph/nodes.py +115 -0
  11. agentic_radar/analysis/langgraph/predefined_tools.json +1 -0
  12. agentic_radar/analysis/langgraph/predefined_tools.py +61 -0
  13. agentic_radar/analysis/langgraph/utils.py +98 -0
  14. agentic_radar/cli.py +93 -0
  15. agentic_radar/graph.py +57 -0
  16. agentic_radar/mapper/__init__.py +3 -0
  17. agentic_radar/mapper/mapper.py +52 -0
  18. agentic_radar/mapper/vulnerabilities.json +1 -0
  19. agentic_radar/report/__init__.py +13 -0
  20. agentic_radar/report/graph/__init__.py +19 -0
  21. agentic_radar/report/graph/assets/agent.svg +12 -0
  22. agentic_radar/report/graph/assets/basic.svg +11 -0
  23. agentic_radar/report/graph/assets/custom_tool.svg +4 -0
  24. agentic_radar/report/graph/assets/tools/code_interpreter.svg +6 -0
  25. agentic_radar/report/graph/assets/tools/database.svg +10 -0
  26. agentic_radar/report/graph/assets/tools/default.svg +4 -0
  27. agentic_radar/report/graph/assets/tools/document_loader.svg +6 -0
  28. agentic_radar/report/graph/assets/tools/llm.svg +13 -0
  29. agentic_radar/report/graph/assets/tools/web_search.svg +9 -0
  30. agentic_radar/report/graph/edge.py +14 -0
  31. agentic_radar/report/graph/graph.py +33 -0
  32. agentic_radar/report/graph/node.py +86 -0
  33. agentic_radar/report/graph/parse.py +60 -0
  34. agentic_radar/report/report.py +81 -0
  35. agentic_radar/report/templates/assets/cover.svg +2719 -0
  36. agentic_radar/report/templates/template.html.jinja +292 -0
  37. agentic_radar-0.1.2.dist-info/LICENSE +661 -0
  38. agentic_radar-0.1.2.dist-info/METADATA +890 -0
  39. agentic_radar-0.1.2.dist-info/RECORD +41 -0
  40. agentic_radar-0.1.2.dist-info/WHEEL +4 -0
  41. agentic_radar-0.1.2.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,3 @@
1
+ import importlib.metadata
2
+
3
+ __version__ = importlib.metadata.version("agentic_radar")
@@ -0,0 +1,3 @@
1
+ from .cli import app
2
+
3
+ app(prog_name="agentic-radar")
@@ -0,0 +1,3 @@
1
+ from .langgraph import LangGraphAnalyzer
2
+
3
+ __all__ = ["LangGraphAnalyzer"]
@@ -0,0 +1,8 @@
1
+ import abc
2
+
3
+ from ..graph import GraphDefinition
4
+
5
+
6
+ class Analyzer(abc.ABC):
7
+ def analyze(self, root_directory: str) -> GraphDefinition:
8
+ raise NotImplementedError()
@@ -0,0 +1,3 @@
1
+ from .analyze import LangGraphAnalyzer
2
+
3
+ __all__ = ["LangGraphAnalyzer"]
@@ -0,0 +1,148 @@
1
+ from pathlib import Path
2
+
3
+ from ...analysis.analyze import Analyzer
4
+ from ...graph import EdgeDefinition as Edge
5
+ from ...graph import GraphDefinition
6
+ from ...graph import NodeDefinition as Node
7
+ from ...graph import NodeType, ToolType
8
+ from .custom_tools import get_all_custom_tools_from_directory
9
+ from .graph import parse_all_graph_instances_in_directory
10
+ from .predefined_tools import get_all_predefined_tools_from_directory
11
+ from .utils import build_global_registry
12
+
13
+ GRAPH_CLASS = "langgraph.graph.StateGraph"
14
+ COMMAND_CLASS = "langgraph.types.Command"
15
+ ADD_CONDITIONAL_EDGES_METHOD_NAME = "add_conditional_edges"
16
+ ADD_NODE_METHOD_NAME = "add_node"
17
+
18
+
19
+ class LangGraphAnalyzer(Analyzer):
20
+ def __init__(self):
21
+ super().__init__()
22
+
23
+ def analyze(self, root_directory: str) -> GraphDefinition:
24
+ global_functions, global_variables = build_global_registry(root_directory)
25
+
26
+ predefined_tools = get_all_predefined_tools_from_directory(root_directory)
27
+
28
+ custom_tools = get_all_custom_tools_from_directory(root_directory)
29
+
30
+ all_graphs = parse_all_graph_instances_in_directory(
31
+ root_directory,
32
+ GRAPH_CLASS,
33
+ COMMAND_CLASS,
34
+ ADD_CONDITIONAL_EDGES_METHOD_NAME,
35
+ ADD_NODE_METHOD_NAME,
36
+ global_functions,
37
+ global_variables,
38
+ )
39
+ nodes = []
40
+ edges = []
41
+
42
+ for graph in all_graphs:
43
+ for i, node in enumerate(graph["graph_info"]["nodes"]):
44
+
45
+ nodes.append(
46
+ Node(type=NodeType.BASIC, name=node["name"], label=node["name"])
47
+ )
48
+
49
+ nodes.append(Node(type=NodeType.BASIC, name="START", label="START"))
50
+
51
+ nodes.append(Node(type=NodeType.BASIC, name="END", label="END"))
52
+
53
+ for basic_edge in graph["graph_info"]["basic_edges"]:
54
+ edges.append(
55
+ Edge(start=basic_edge["start_node"], end=basic_edge["end_node"])
56
+ )
57
+
58
+ for conditional_edge in graph["graph_info"]["conditional_edges"]:
59
+ edges.append(
60
+ Edge(
61
+ start=conditional_edge["start_node"],
62
+ end=conditional_edge["end_node"],
63
+ condition="if else",
64
+ )
65
+ )
66
+
67
+ tools = []
68
+
69
+ for predefined_tool in predefined_tools:
70
+ tools.append(
71
+ Node(
72
+ name=predefined_tool["name"],
73
+ type=NodeType.TOOL,
74
+ category=predefined_tool["category"],
75
+ vulnerabilities=[], # TODO Add vulnerability mapping
76
+ )
77
+ )
78
+
79
+ for custom_tool in custom_tools:
80
+ tools.append(
81
+ Node(
82
+ name=custom_tool["name"],
83
+ description=custom_tool["description"],
84
+ type=NodeType.CUSTOM_TOOL,
85
+ category=ToolType.DEFAULT,
86
+ vulnerabilities=[], # TODO Add vulnerability mapping
87
+ )
88
+ )
89
+
90
+ # This is for report generation testing
91
+ # tools = [
92
+ # Node(
93
+ # name="create_issue",
94
+ # type=NodeType.TOOL,
95
+ # category=ToolType.CODE_INTERPRETER,
96
+ # description="Tool description",
97
+ # vulnerabilities=[
98
+ # VulnerabilityDefinition(
99
+ # name="Improper Output Handling",
100
+ # description="Generated",
101
+ # security_framework_mapping={
102
+ # "CVE": "blabla",
103
+ # "OWASP LLM TOP 10": "blabla",
104
+ # },
105
+ # remediation="Remediate placholder",
106
+ # ),
107
+ # VulnerabilityDefinition(
108
+ # name="Improper Output Handling 2",
109
+ # description="Generated",
110
+ # security_framework_mapping={
111
+ # "CVE": "blabla",
112
+ # "OWASP LLM TOP 10": "blabla",
113
+ # },
114
+ # remediation="Placeholder",
115
+ # ),
116
+ # ],
117
+ # ),
118
+ # Node(
119
+ # name="create_issue 2",
120
+ # type=NodeType.TOOL,
121
+ # category=ToolType.CODE_INTERPRETER,
122
+ # description="Tool description",
123
+ # vulnerabilities=[
124
+ # VulnerabilityDefinition(
125
+ # name="Improper Output Handling",
126
+ # description="Generated",
127
+ # security_framework_mapping={
128
+ # "CVE": '<span style="color: black; font-size: 10px; font-family: Inter; font-weight: 400; text-decoration: underline; line-height: 15px; word-wrap: break-word">CVE-2023-44467</span>',
129
+ # "OWASP LLM TOP 10": "blabla",
130
+ # },
131
+ # remediation="Remediate placholder",
132
+ # ),
133
+ # VulnerabilityDefinition(
134
+ # name="Improper Output Handling 2",
135
+ # description="Generated",
136
+ # security_framework_mapping={
137
+ # "CVE": "blabla",
138
+ # "OWASP LLM TOP 10": "blabla",
139
+ # },
140
+ # remediation="Placeholder",
141
+ # ),
142
+ # ],
143
+ # ),
144
+ # ]
145
+
146
+ return GraphDefinition(
147
+ name=Path(root_directory).name, nodes=nodes, edges=edges, tools=tools
148
+ )
@@ -0,0 +1,44 @@
1
+ import ast
2
+ import os
3
+ from typing import Dict, List
4
+
5
+
6
+ def extract_custom_tools_with_ast(
7
+ file_content: str, file_path: str
8
+ ) -> List[Dict[str, str]]:
9
+ custom_tools = []
10
+ tree = ast.parse(file_content)
11
+
12
+ for node in ast.walk(tree):
13
+ if isinstance(node, ast.FunctionDef):
14
+ decorator_names = []
15
+ for decorator in node.decorator_list:
16
+ if isinstance(decorator, ast.Name):
17
+ decorator_names.append(decorator.id)
18
+ elif isinstance(decorator, ast.Call):
19
+ if isinstance(decorator.func, ast.Name):
20
+ decorator_names.append(decorator.func.id)
21
+ elif isinstance(decorator.func, ast.Attribute):
22
+ decorator_names.append(decorator.func.attr)
23
+
24
+ if "tool" in decorator_names:
25
+ custom_tools.append({"name": node.name, "filepath": file_path, "description": ast.get_docstring(node)})
26
+
27
+ return custom_tools
28
+
29
+
30
+ def get_all_custom_tools_from_directory(directory_path: str) -> List[Dict[str, str]]:
31
+ all_custom_tools = []
32
+ for root, _, files in os.walk(directory_path):
33
+ for file in files:
34
+ if file.endswith(".py"):
35
+ file_path = os.path.join(root, file)
36
+ with open(file_path, "r", encoding="utf-8") as f:
37
+ file_content = f.read()
38
+ custom_tools = extract_custom_tools_with_ast(
39
+ file_content, file_path
40
+ )
41
+ for single_custom_tool in custom_tools:
42
+ all_custom_tools.append(single_custom_tool)
43
+
44
+ return all_custom_tools