brieuc.crosson
commited on
Commit
·
090871d
1
Parent(s):
c8dd313
feat: run ui
Browse files- Dockerfile +2 -2
- agentpimentbleu/app/app.py +33 -33
- agentpimentbleu/config/settings.yaml +2 -2
- agentpimentbleu/core/graphs/sca_impact_graph.py +23 -23
- {utils → agentpimentbleu/utils}/logger.py +0 -0
- flake.nix +0 -2
- main.py +11 -11
Dockerfile
CHANGED
@@ -7,7 +7,7 @@ RUN apt-get update && \
|
|
7 |
git \
|
8 |
nodejs \
|
9 |
npm && \
|
10 |
-
npm install -g npm@
|
11 |
apt-get clean && \
|
12 |
rm -rf /var/lib/apt/lists/*
|
13 |
|
@@ -25,4 +25,4 @@ COPY . .
|
|
25 |
EXPOSE 7860
|
26 |
|
27 |
# Set the command to run the Gradio app
|
28 |
-
CMD ["python", "
|
|
|
7 |
git \
|
8 |
nodejs \
|
9 |
npm && \
|
10 |
+
npm install -g npm@9.6.7 && \
|
11 |
apt-get clean && \
|
12 |
rm -rf /var/lib/apt/lists/*
|
13 |
|
|
|
25 |
EXPOSE 7860
|
26 |
|
27 |
# Set the command to run the Gradio app
|
28 |
+
CMD ["python", "main.py", "ui"]
|
agentpimentbleu/app/app.py
CHANGED
@@ -21,37 +21,37 @@ API_URL = os.environ.get("APB_API_URL", "http://127.0.0.1:8000")
|
|
21 |
def scan_repository(repo_source: str) -> Tuple[str, str]:
|
22 |
"""
|
23 |
Scan a repository for vulnerabilities.
|
24 |
-
|
25 |
Args:
|
26 |
repo_source (str): URL or local path to the repository
|
27 |
-
|
28 |
Returns:
|
29 |
Tuple[str, str]: Formatted results as Markdown and raw JSON
|
30 |
"""
|
31 |
logger.info(f"Scanning repository: {repo_source}")
|
32 |
-
|
33 |
try:
|
34 |
# Prepare the payload
|
35 |
payload = {"repo_source": repo_source}
|
36 |
-
|
37 |
# Make the API request
|
38 |
response = requests.post(f"{API_URL}/scan/", json=payload)
|
39 |
-
|
40 |
# Check if the request was successful
|
41 |
if response.status_code == 200:
|
42 |
# Parse the JSON response
|
43 |
result = response.json()
|
44 |
-
|
45 |
# Format the results as Markdown
|
46 |
markdown_result = format_results_as_markdown(result)
|
47 |
-
|
48 |
# Return both the formatted results and the raw JSON
|
49 |
return markdown_result, json.dumps(result, indent=2)
|
50 |
else:
|
51 |
error_message = f"Error: {response.status_code} - {response.text}"
|
52 |
logger.error(error_message)
|
53 |
return f"## Error\n\n{error_message}", "{}"
|
54 |
-
|
55 |
except Exception as e:
|
56 |
error_message = f"Error scanning repository: {e}"
|
57 |
logger.error(error_message)
|
@@ -61,86 +61,86 @@ def scan_repository(repo_source: str) -> Tuple[str, str]:
|
|
61 |
def format_results_as_markdown(result: dict) -> str:
|
62 |
"""
|
63 |
Format scan results as Markdown.
|
64 |
-
|
65 |
Args:
|
66 |
result (dict): Scan results
|
67 |
-
|
68 |
Returns:
|
69 |
str: Formatted results as Markdown
|
70 |
"""
|
71 |
# Start with a header
|
72 |
markdown = f"# Scan Results for {result.get('repo_source', 'Unknown Repository')}\n\n"
|
73 |
-
|
74 |
# Add scan status
|
75 |
markdown += f"**Status:** {result.get('status', 'Unknown')}\n\n"
|
76 |
-
|
77 |
# Add overall summary if available
|
78 |
if result.get('overall_summary'):
|
79 |
markdown += f"**Summary:** {result['overall_summary']}\n\n"
|
80 |
-
|
81 |
# Add error message if available
|
82 |
if result.get('error_message'):
|
83 |
markdown += f"**Error:** {result['error_message']}\n\n"
|
84 |
return markdown
|
85 |
-
|
86 |
# Add SCA results if available
|
87 |
sca_results = result.get('sca_results')
|
88 |
if sca_results:
|
89 |
markdown += "## Software Composition Analysis (SCA) Results\n\n"
|
90 |
-
|
91 |
# Add dependency file information if available
|
92 |
if sca_results.get('dependency_file_found'):
|
93 |
markdown += f"**Dependency File:** {sca_results['dependency_file_found']}\n\n"
|
94 |
-
|
95 |
# Add issues summary if available
|
96 |
if sca_results.get('issues_summary'):
|
97 |
markdown += f"**Issues Summary:** {sca_results['issues_summary']}\n\n"
|
98 |
-
|
99 |
# Add vulnerabilities if available
|
100 |
vulnerabilities = sca_results.get('vulnerabilities', [])
|
101 |
if vulnerabilities:
|
102 |
markdown += f"### Vulnerabilities ({len(vulnerabilities)})\n\n"
|
103 |
-
|
104 |
for i, vuln in enumerate(vulnerabilities, 1):
|
105 |
markdown += f"#### {i}. {vuln.get('package_name', 'Unknown Package')} - {vuln.get('cve_id', 'Unknown CVE')}\n\n"
|
106 |
-
|
107 |
# Add CVE link if available
|
108 |
if vuln.get('cve_link'):
|
109 |
markdown += f"**CVE Link:** [{vuln['cve_id']}]({vuln['cve_link']})\n\n"
|
110 |
-
|
111 |
# Add CVE description if available
|
112 |
if vuln.get('cve_description'):
|
113 |
markdown += f"**Description:** {vuln['cve_description']}\n\n"
|
114 |
-
|
115 |
# Add version information
|
116 |
markdown += f"**Vulnerable Version Range:** {vuln.get('vulnerable_version_range', 'Unknown')}\n"
|
117 |
markdown += f"**Analyzed Project Version:** {vuln.get('analyzed_project_version', 'Unknown')}\n\n"
|
118 |
-
|
119 |
# Add danger rating
|
120 |
markdown += f"**Danger Rating:** {vuln.get('danger_rating', 'Unknown')}\n\n"
|
121 |
-
|
122 |
# Add impact summary
|
123 |
if vuln.get('impact_in_project_summary'):
|
124 |
markdown += f"**Impact:** {vuln['impact_in_project_summary']}\n\n"
|
125 |
-
|
126 |
# Add evidence if available
|
127 |
if vuln.get('evidence') and vuln['evidence'][0] != 'No evidence available':
|
128 |
markdown += "**Evidence:**\n\n"
|
129 |
for evidence in vuln['evidence']:
|
130 |
markdown += f"```\n{evidence}\n```\n\n"
|
131 |
-
|
132 |
# Add fix information
|
133 |
if vuln.get('proposed_fix_summary'):
|
134 |
markdown += f"**Proposed Fix:** {vuln['proposed_fix_summary']}\n\n"
|
135 |
-
|
136 |
if vuln.get('detailed_fix_guidance'):
|
137 |
markdown += f"**Detailed Guidance:** {vuln['detailed_fix_guidance']}\n\n"
|
138 |
-
|
139 |
# Add separator between vulnerabilities
|
140 |
markdown += "---\n\n"
|
141 |
else:
|
142 |
markdown += "No vulnerabilities found.\n\n"
|
143 |
-
|
144 |
return markdown
|
145 |
|
146 |
|
@@ -149,13 +149,13 @@ with gr.Blocks(title="AgentPimentBleu - Smart Security Scanner") as app:
|
|
149 |
gr.Markdown("# AgentPimentBleu - Smart Security Scanner for Git Repositories")
|
150 |
gr.Markdown("""
|
151 |
Enter a Git repository URL or a local path to an example project to scan for vulnerabilities.
|
152 |
-
|
153 |
Example inputs:
|
154 |
- `https://github.com/username/repository`
|
155 |
- `examples/python_example_vulnerable_project_1`
|
156 |
- `examples/javascript_example_vulnerable_project_1`
|
157 |
""")
|
158 |
-
|
159 |
with gr.Row():
|
160 |
repo_input = gr.Textbox(
|
161 |
label="Repository URL or Local Path",
|
@@ -163,13 +163,13 @@ with gr.Blocks(title="AgentPimentBleu - Smart Security Scanner") as app:
|
|
163 |
lines=1
|
164 |
)
|
165 |
scan_button = gr.Button("Scan Repository", variant="primary")
|
166 |
-
|
167 |
with gr.Row():
|
168 |
with gr.Column():
|
169 |
results_md = gr.Markdown(label="Scan Results")
|
170 |
with gr.Column():
|
171 |
results_json = gr.JSON(label="Raw JSON Output")
|
172 |
-
|
173 |
scan_button.click(
|
174 |
fn=scan_repository,
|
175 |
inputs=repo_input,
|
@@ -178,4 +178,4 @@ with gr.Blocks(title="AgentPimentBleu - Smart Security Scanner") as app:
|
|
178 |
|
179 |
|
180 |
if __name__ == "__main__":
|
181 |
-
app.launch()
|
|
|
21 |
def scan_repository(repo_source: str) -> Tuple[str, str]:
|
22 |
"""
|
23 |
Scan a repository for vulnerabilities.
|
24 |
+
|
25 |
Args:
|
26 |
repo_source (str): URL or local path to the repository
|
27 |
+
|
28 |
Returns:
|
29 |
Tuple[str, str]: Formatted results as Markdown and raw JSON
|
30 |
"""
|
31 |
logger.info(f"Scanning repository: {repo_source}")
|
32 |
+
|
33 |
try:
|
34 |
# Prepare the payload
|
35 |
payload = {"repo_source": repo_source}
|
36 |
+
|
37 |
# Make the API request
|
38 |
response = requests.post(f"{API_URL}/scan/", json=payload)
|
39 |
+
|
40 |
# Check if the request was successful
|
41 |
if response.status_code == 200:
|
42 |
# Parse the JSON response
|
43 |
result = response.json()
|
44 |
+
|
45 |
# Format the results as Markdown
|
46 |
markdown_result = format_results_as_markdown(result)
|
47 |
+
|
48 |
# Return both the formatted results and the raw JSON
|
49 |
return markdown_result, json.dumps(result, indent=2)
|
50 |
else:
|
51 |
error_message = f"Error: {response.status_code} - {response.text}"
|
52 |
logger.error(error_message)
|
53 |
return f"## Error\n\n{error_message}", "{}"
|
54 |
+
|
55 |
except Exception as e:
|
56 |
error_message = f"Error scanning repository: {e}"
|
57 |
logger.error(error_message)
|
|
|
61 |
def format_results_as_markdown(result: dict) -> str:
|
62 |
"""
|
63 |
Format scan results as Markdown.
|
64 |
+
|
65 |
Args:
|
66 |
result (dict): Scan results
|
67 |
+
|
68 |
Returns:
|
69 |
str: Formatted results as Markdown
|
70 |
"""
|
71 |
# Start with a header
|
72 |
markdown = f"# Scan Results for {result.get('repo_source', 'Unknown Repository')}\n\n"
|
73 |
+
|
74 |
# Add scan status
|
75 |
markdown += f"**Status:** {result.get('status', 'Unknown')}\n\n"
|
76 |
+
|
77 |
# Add overall summary if available
|
78 |
if result.get('overall_summary'):
|
79 |
markdown += f"**Summary:** {result['overall_summary']}\n\n"
|
80 |
+
|
81 |
# Add error message if available
|
82 |
if result.get('error_message'):
|
83 |
markdown += f"**Error:** {result['error_message']}\n\n"
|
84 |
return markdown
|
85 |
+
|
86 |
# Add SCA results if available
|
87 |
sca_results = result.get('sca_results')
|
88 |
if sca_results:
|
89 |
markdown += "## Software Composition Analysis (SCA) Results\n\n"
|
90 |
+
|
91 |
# Add dependency file information if available
|
92 |
if sca_results.get('dependency_file_found'):
|
93 |
markdown += f"**Dependency File:** {sca_results['dependency_file_found']}\n\n"
|
94 |
+
|
95 |
# Add issues summary if available
|
96 |
if sca_results.get('issues_summary'):
|
97 |
markdown += f"**Issues Summary:** {sca_results['issues_summary']}\n\n"
|
98 |
+
|
99 |
# Add vulnerabilities if available
|
100 |
vulnerabilities = sca_results.get('vulnerabilities', [])
|
101 |
if vulnerabilities:
|
102 |
markdown += f"### Vulnerabilities ({len(vulnerabilities)})\n\n"
|
103 |
+
|
104 |
for i, vuln in enumerate(vulnerabilities, 1):
|
105 |
markdown += f"#### {i}. {vuln.get('package_name', 'Unknown Package')} - {vuln.get('cve_id', 'Unknown CVE')}\n\n"
|
106 |
+
|
107 |
# Add CVE link if available
|
108 |
if vuln.get('cve_link'):
|
109 |
markdown += f"**CVE Link:** [{vuln['cve_id']}]({vuln['cve_link']})\n\n"
|
110 |
+
|
111 |
# Add CVE description if available
|
112 |
if vuln.get('cve_description'):
|
113 |
markdown += f"**Description:** {vuln['cve_description']}\n\n"
|
114 |
+
|
115 |
# Add version information
|
116 |
markdown += f"**Vulnerable Version Range:** {vuln.get('vulnerable_version_range', 'Unknown')}\n"
|
117 |
markdown += f"**Analyzed Project Version:** {vuln.get('analyzed_project_version', 'Unknown')}\n\n"
|
118 |
+
|
119 |
# Add danger rating
|
120 |
markdown += f"**Danger Rating:** {vuln.get('danger_rating', 'Unknown')}\n\n"
|
121 |
+
|
122 |
# Add impact summary
|
123 |
if vuln.get('impact_in_project_summary'):
|
124 |
markdown += f"**Impact:** {vuln['impact_in_project_summary']}\n\n"
|
125 |
+
|
126 |
# Add evidence if available
|
127 |
if vuln.get('evidence') and vuln['evidence'][0] != 'No evidence available':
|
128 |
markdown += "**Evidence:**\n\n"
|
129 |
for evidence in vuln['evidence']:
|
130 |
markdown += f"```\n{evidence}\n```\n\n"
|
131 |
+
|
132 |
# Add fix information
|
133 |
if vuln.get('proposed_fix_summary'):
|
134 |
markdown += f"**Proposed Fix:** {vuln['proposed_fix_summary']}\n\n"
|
135 |
+
|
136 |
if vuln.get('detailed_fix_guidance'):
|
137 |
markdown += f"**Detailed Guidance:** {vuln['detailed_fix_guidance']}\n\n"
|
138 |
+
|
139 |
# Add separator between vulnerabilities
|
140 |
markdown += "---\n\n"
|
141 |
else:
|
142 |
markdown += "No vulnerabilities found.\n\n"
|
143 |
+
|
144 |
return markdown
|
145 |
|
146 |
|
|
|
149 |
gr.Markdown("# AgentPimentBleu - Smart Security Scanner for Git Repositories")
|
150 |
gr.Markdown("""
|
151 |
Enter a Git repository URL or a local path to an example project to scan for vulnerabilities.
|
152 |
+
|
153 |
Example inputs:
|
154 |
- `https://github.com/username/repository`
|
155 |
- `examples/python_example_vulnerable_project_1`
|
156 |
- `examples/javascript_example_vulnerable_project_1`
|
157 |
""")
|
158 |
+
|
159 |
with gr.Row():
|
160 |
repo_input = gr.Textbox(
|
161 |
label="Repository URL or Local Path",
|
|
|
163 |
lines=1
|
164 |
)
|
165 |
scan_button = gr.Button("Scan Repository", variant="primary")
|
166 |
+
|
167 |
with gr.Row():
|
168 |
with gr.Column():
|
169 |
results_md = gr.Markdown(label="Scan Results")
|
170 |
with gr.Column():
|
171 |
results_json = gr.JSON(label="Raw JSON Output")
|
172 |
+
|
173 |
scan_button.click(
|
174 |
fn=scan_repository,
|
175 |
inputs=repo_input,
|
|
|
178 |
|
179 |
|
180 |
if __name__ == "__main__":
|
181 |
+
app.launch(server_name="0.0.0.0")
|
agentpimentbleu/config/settings.yaml
CHANGED
@@ -4,10 +4,10 @@
|
|
4 |
llm_providers:
|
5 |
gemini:
|
6 |
api_key: 'YOUR_GEMINI_API_KEY'
|
7 |
-
model: 'gemini-
|
8 |
ollama:
|
9 |
base_url: 'http://localhost:11434'
|
10 |
-
model: '
|
11 |
|
12 |
# Dependency Parsers Configuration
|
13 |
dependency_parsers:
|
|
|
4 |
llm_providers:
|
5 |
gemini:
|
6 |
api_key: 'YOUR_GEMINI_API_KEY'
|
7 |
+
model: 'gemini-2.0-flash'
|
8 |
ollama:
|
9 |
base_url: 'http://localhost:11434'
|
10 |
+
model: 'llama3.2:1b'
|
11 |
|
12 |
# Dependency Parsers Configuration
|
13 |
dependency_parsers:
|
agentpimentbleu/core/graphs/sca_impact_graph.py
CHANGED
@@ -4,7 +4,7 @@ AgentPimentBleu - SCA Impact Graph
|
|
4 |
This module defines the LangGraph for SCA and impact analysis.
|
5 |
"""
|
6 |
|
7 |
-
from typing import Dict, List, Optional, TypedDict,
|
8 |
import os
|
9 |
|
10 |
from langgraph.graph import StateGraph, END
|
@@ -37,7 +37,7 @@ class ScaImpactState(TypedDict, total=False):
|
|
37 |
error_message: Optional[str] # Error message if any
|
38 |
|
39 |
|
40 |
-
def prepare_scan_environment(state: ScaImpactState) ->
|
41 |
"""
|
42 |
Prepare the scan environment by cloning the repository.
|
43 |
|
@@ -45,7 +45,7 @@ def prepare_scan_environment(state: ScaImpactState) -> Partial[ScaImpactState]:
|
|
45 |
state (ScaImpactState): Current state
|
46 |
|
47 |
Returns:
|
48 |
-
|
49 |
"""
|
50 |
logger.info(f"Preparing scan environment for {state['repo_source']}")
|
51 |
|
@@ -67,7 +67,7 @@ def prepare_scan_environment(state: ScaImpactState) -> Partial[ScaImpactState]:
|
|
67 |
return {"cloned_repo_path": None, "error_message": error_message}
|
68 |
|
69 |
|
70 |
-
def identify_project_and_run_audit_node(state: ScaImpactState) ->
|
71 |
"""
|
72 |
Identify the project type and run the security audit.
|
73 |
|
@@ -75,7 +75,7 @@ def identify_project_and_run_audit_node(state: ScaImpactState) -> Partial[ScaImp
|
|
75 |
state (ScaImpactState): Current state
|
76 |
|
77 |
Returns:
|
78 |
-
|
79 |
"""
|
80 |
logger.info(f"Identifying project type and running audit for {state['cloned_repo_path']}")
|
81 |
|
@@ -134,7 +134,7 @@ def identify_project_and_run_audit_node(state: ScaImpactState) -> Partial[ScaImp
|
|
134 |
}
|
135 |
|
136 |
|
137 |
-
def build_rag_index_node(state: ScaImpactState) ->
|
138 |
"""
|
139 |
Build a RAG index for the project code.
|
140 |
|
@@ -142,7 +142,7 @@ def build_rag_index_node(state: ScaImpactState) -> Partial[ScaImpactState]:
|
|
142 |
state (ScaImpactState): Current state
|
143 |
|
144 |
Returns:
|
145 |
-
|
146 |
"""
|
147 |
logger.info(f"Building RAG index for {state['cloned_repo_path']}")
|
148 |
|
@@ -180,7 +180,7 @@ def build_rag_index_node(state: ScaImpactState) -> Partial[ScaImpactState]:
|
|
180 |
return {"project_code_index_path": None, "error_message": error_message}
|
181 |
|
182 |
|
183 |
-
def select_next_vulnerability_node(state: ScaImpactState) ->
|
184 |
"""
|
185 |
Select the next vulnerability to process.
|
186 |
|
@@ -188,7 +188,7 @@ def select_next_vulnerability_node(state: ScaImpactState) -> Partial[ScaImpactSt
|
|
188 |
state (ScaImpactState): Current state
|
189 |
|
190 |
Returns:
|
191 |
-
|
192 |
"""
|
193 |
logger.info("Selecting next vulnerability to process")
|
194 |
|
@@ -222,7 +222,7 @@ def select_next_vulnerability_node(state: ScaImpactState) -> Partial[ScaImpactSt
|
|
222 |
}
|
223 |
|
224 |
|
225 |
-
def analyze_cve_description_node(state: ScaImpactState) ->
|
226 |
"""
|
227 |
Analyze the CVE description using LLM.
|
228 |
|
@@ -230,7 +230,7 @@ def analyze_cve_description_node(state: ScaImpactState) -> Partial[ScaImpactStat
|
|
230 |
state (ScaImpactState): Current state
|
231 |
|
232 |
Returns:
|
233 |
-
|
234 |
"""
|
235 |
logger.info("Analyzing CVE description")
|
236 |
|
@@ -317,7 +317,7 @@ def analyze_cve_description_node(state: ScaImpactState) -> Partial[ScaImpactStat
|
|
317 |
return {"current_cve_analysis_results": None, "error_message": error_message}
|
318 |
|
319 |
|
320 |
-
def search_codebase_for_impact_node(state: ScaImpactState) ->
|
321 |
"""
|
322 |
Search the codebase for impact of the vulnerability.
|
323 |
|
@@ -325,7 +325,7 @@ def search_codebase_for_impact_node(state: ScaImpactState) -> Partial[ScaImpactS
|
|
325 |
state (ScaImpactState): Current state
|
326 |
|
327 |
Returns:
|
328 |
-
|
329 |
"""
|
330 |
logger.info("Searching codebase for impact")
|
331 |
|
@@ -499,7 +499,7 @@ def search_codebase_for_impact_node(state: ScaImpactState) -> Partial[ScaImpactS
|
|
499 |
return {"error_message": error_message}
|
500 |
|
501 |
|
502 |
-
def evaluate_impact_and_danger_node(state: ScaImpactState) ->
|
503 |
"""
|
504 |
Evaluate the impact and danger of the vulnerability.
|
505 |
|
@@ -507,7 +507,7 @@ def evaluate_impact_and_danger_node(state: ScaImpactState) -> Partial[ScaImpactS
|
|
507 |
state (ScaImpactState): Current state
|
508 |
|
509 |
Returns:
|
510 |
-
|
511 |
"""
|
512 |
logger.info("Evaluating impact and danger")
|
513 |
|
@@ -611,7 +611,7 @@ def evaluate_impact_and_danger_node(state: ScaImpactState) -> Partial[ScaImpactS
|
|
611 |
return {"error_message": error_message}
|
612 |
|
613 |
|
614 |
-
def propose_fix_node(state: ScaImpactState) ->
|
615 |
"""
|
616 |
Propose a fix for the vulnerability.
|
617 |
|
@@ -619,7 +619,7 @@ def propose_fix_node(state: ScaImpactState) -> Partial[ScaImpactState]:
|
|
619 |
state (ScaImpactState): Current state
|
620 |
|
621 |
Returns:
|
622 |
-
|
623 |
"""
|
624 |
logger.info("Proposing fix")
|
625 |
|
@@ -717,7 +717,7 @@ def propose_fix_node(state: ScaImpactState) -> Partial[ScaImpactState]:
|
|
717 |
return {"error_message": error_message}
|
718 |
|
719 |
|
720 |
-
def aggregate_cve_results_node(state: ScaImpactState) ->
|
721 |
"""
|
722 |
Aggregate the results of the CVE analysis.
|
723 |
|
@@ -725,7 +725,7 @@ def aggregate_cve_results_node(state: ScaImpactState) -> Partial[ScaImpactState]
|
|
725 |
state (ScaImpactState): Current state
|
726 |
|
727 |
Returns:
|
728 |
-
|
729 |
"""
|
730 |
logger.info("Aggregating CVE results")
|
731 |
|
@@ -768,7 +768,7 @@ def aggregate_cve_results_node(state: ScaImpactState) -> Partial[ScaImpactState]
|
|
768 |
return {"error_message": error_message}
|
769 |
|
770 |
|
771 |
-
def cleanup_scan_environment_node(state: ScaImpactState) ->
|
772 |
"""
|
773 |
Clean up the scan environment.
|
774 |
|
@@ -776,7 +776,7 @@ def cleanup_scan_environment_node(state: ScaImpactState) -> Partial[ScaImpactSta
|
|
776 |
state (ScaImpactState): Current state
|
777 |
|
778 |
Returns:
|
779 |
-
|
780 |
"""
|
781 |
logger.info("Cleaning up scan environment")
|
782 |
|
@@ -794,7 +794,7 @@ def cleanup_scan_environment_node(state: ScaImpactState) -> Partial[ScaImpactSta
|
|
794 |
return {"error_message": error_message}
|
795 |
|
796 |
|
797 |
-
def compile_final_report_node(state: ScaImpactState) ->
|
798 |
"""
|
799 |
Compile the final report.
|
800 |
|
@@ -802,7 +802,7 @@ def compile_final_report_node(state: ScaImpactState) -> Partial[ScaImpactState]:
|
|
802 |
state (ScaImpactState): Current state
|
803 |
|
804 |
Returns:
|
805 |
-
|
806 |
"""
|
807 |
logger.info("Compiling final report")
|
808 |
|
|
|
4 |
This module defines the LangGraph for SCA and impact analysis.
|
5 |
"""
|
6 |
|
7 |
+
from typing import Dict, List, Optional, TypedDict, Any, Callable
|
8 |
import os
|
9 |
|
10 |
from langgraph.graph import StateGraph, END
|
|
|
37 |
error_message: Optional[str] # Error message if any
|
38 |
|
39 |
|
40 |
+
def prepare_scan_environment(state: ScaImpactState) -> Dict[str, Any]:
|
41 |
"""
|
42 |
Prepare the scan environment by cloning the repository.
|
43 |
|
|
|
45 |
state (ScaImpactState): Current state
|
46 |
|
47 |
Returns:
|
48 |
+
Dict[str, Any]: Updated state
|
49 |
"""
|
50 |
logger.info(f"Preparing scan environment for {state['repo_source']}")
|
51 |
|
|
|
67 |
return {"cloned_repo_path": None, "error_message": error_message}
|
68 |
|
69 |
|
70 |
+
def identify_project_and_run_audit_node(state: ScaImpactState) -> Dict[str, Any]:
|
71 |
"""
|
72 |
Identify the project type and run the security audit.
|
73 |
|
|
|
75 |
state (ScaImpactState): Current state
|
76 |
|
77 |
Returns:
|
78 |
+
Dict[str, Any]: Updated state
|
79 |
"""
|
80 |
logger.info(f"Identifying project type and running audit for {state['cloned_repo_path']}")
|
81 |
|
|
|
134 |
}
|
135 |
|
136 |
|
137 |
+
def build_rag_index_node(state: ScaImpactState) -> Dict[str, Any]:
|
138 |
"""
|
139 |
Build a RAG index for the project code.
|
140 |
|
|
|
142 |
state (ScaImpactState): Current state
|
143 |
|
144 |
Returns:
|
145 |
+
Dict[str, Any]: Updated state
|
146 |
"""
|
147 |
logger.info(f"Building RAG index for {state['cloned_repo_path']}")
|
148 |
|
|
|
180 |
return {"project_code_index_path": None, "error_message": error_message}
|
181 |
|
182 |
|
183 |
+
def select_next_vulnerability_node(state: ScaImpactState) -> Dict[str, Any]:
|
184 |
"""
|
185 |
Select the next vulnerability to process.
|
186 |
|
|
|
188 |
state (ScaImpactState): Current state
|
189 |
|
190 |
Returns:
|
191 |
+
Dict[str, Any]: Updated state
|
192 |
"""
|
193 |
logger.info("Selecting next vulnerability to process")
|
194 |
|
|
|
222 |
}
|
223 |
|
224 |
|
225 |
+
def analyze_cve_description_node(state: ScaImpactState) -> Dict[str, Any]:
|
226 |
"""
|
227 |
Analyze the CVE description using LLM.
|
228 |
|
|
|
230 |
state (ScaImpactState): Current state
|
231 |
|
232 |
Returns:
|
233 |
+
Dict[str, Any]: Updated state
|
234 |
"""
|
235 |
logger.info("Analyzing CVE description")
|
236 |
|
|
|
317 |
return {"current_cve_analysis_results": None, "error_message": error_message}
|
318 |
|
319 |
|
320 |
+
def search_codebase_for_impact_node(state: ScaImpactState) -> Dict[str, Any]:
|
321 |
"""
|
322 |
Search the codebase for impact of the vulnerability.
|
323 |
|
|
|
325 |
state (ScaImpactState): Current state
|
326 |
|
327 |
Returns:
|
328 |
+
Dict[str, Any]: Updated state
|
329 |
"""
|
330 |
logger.info("Searching codebase for impact")
|
331 |
|
|
|
499 |
return {"error_message": error_message}
|
500 |
|
501 |
|
502 |
+
def evaluate_impact_and_danger_node(state: ScaImpactState) -> Dict[str, Any]:
|
503 |
"""
|
504 |
Evaluate the impact and danger of the vulnerability.
|
505 |
|
|
|
507 |
state (ScaImpactState): Current state
|
508 |
|
509 |
Returns:
|
510 |
+
Dict[str, Any]: Updated state
|
511 |
"""
|
512 |
logger.info("Evaluating impact and danger")
|
513 |
|
|
|
611 |
return {"error_message": error_message}
|
612 |
|
613 |
|
614 |
+
def propose_fix_node(state: ScaImpactState) -> Dict[str, Any]:
|
615 |
"""
|
616 |
Propose a fix for the vulnerability.
|
617 |
|
|
|
619 |
state (ScaImpactState): Current state
|
620 |
|
621 |
Returns:
|
622 |
+
Dict[str, Any]: Updated state
|
623 |
"""
|
624 |
logger.info("Proposing fix")
|
625 |
|
|
|
717 |
return {"error_message": error_message}
|
718 |
|
719 |
|
720 |
+
def aggregate_cve_results_node(state: ScaImpactState) -> Dict[str, Any]:
|
721 |
"""
|
722 |
Aggregate the results of the CVE analysis.
|
723 |
|
|
|
725 |
state (ScaImpactState): Current state
|
726 |
|
727 |
Returns:
|
728 |
+
Dict[str, Any]: Updated state
|
729 |
"""
|
730 |
logger.info("Aggregating CVE results")
|
731 |
|
|
|
768 |
return {"error_message": error_message}
|
769 |
|
770 |
|
771 |
+
def cleanup_scan_environment_node(state: ScaImpactState) -> Dict[str, Any]:
|
772 |
"""
|
773 |
Clean up the scan environment.
|
774 |
|
|
|
776 |
state (ScaImpactState): Current state
|
777 |
|
778 |
Returns:
|
779 |
+
Dict[str, Any]: Updated state
|
780 |
"""
|
781 |
logger.info("Cleaning up scan environment")
|
782 |
|
|
|
794 |
return {"error_message": error_message}
|
795 |
|
796 |
|
797 |
+
def compile_final_report_node(state: ScaImpactState) -> Dict[str, Any]:
|
798 |
"""
|
799 |
Compile the final report.
|
800 |
|
|
|
802 |
state (ScaImpactState): Current state
|
803 |
|
804 |
Returns:
|
805 |
+
Dict[str, Any]: Updated state
|
806 |
"""
|
807 |
logger.info("Compiling final report")
|
808 |
|
{utils → agentpimentbleu/utils}/logger.py
RENAMED
File without changes
|
flake.nix
CHANGED
@@ -38,8 +38,6 @@
|
|
38 |
llama-index-embeddings-huggingface
|
39 |
python-dotenv
|
40 |
pyyaml
|
41 |
-
langchain-google-genai
|
42 |
-
pip-audit
|
43 |
];
|
44 |
|
45 |
# No need for custom postInstall, entry_points in setup.py handles this
|
|
|
38 |
llama-index-embeddings-huggingface
|
39 |
python-dotenv
|
40 |
pyyaml
|
|
|
|
|
41 |
];
|
42 |
|
43 |
# No need for custom postInstall, entry_points in setup.py handles this
|
main.py
CHANGED
@@ -24,7 +24,7 @@ def run_ui():
|
|
24 |
"""Run the Gradio UI."""
|
25 |
logger.info("Starting Gradio UI")
|
26 |
from agentpimentbleu.app.app import app
|
27 |
-
app.launch()
|
28 |
|
29 |
|
30 |
def run_api():
|
@@ -36,18 +36,18 @@ def run_api():
|
|
36 |
def run_scan(repo_source):
|
37 |
"""
|
38 |
Run a direct scan of a repository.
|
39 |
-
|
40 |
Args:
|
41 |
repo_source (str): URL or local path to the repository
|
42 |
"""
|
43 |
logger.info(f"Running scan on {repo_source}")
|
44 |
-
|
45 |
# Get the application configuration
|
46 |
app_config = get_settings()
|
47 |
-
|
48 |
# Run the scan
|
49 |
result = run_sca_scan(repo_source, app_config)
|
50 |
-
|
51 |
# Print the results
|
52 |
print(json.dumps(result, indent=2))
|
53 |
|
@@ -56,20 +56,20 @@ def main():
|
|
56 |
"""Main entry point."""
|
57 |
parser = argparse.ArgumentParser(description="AgentPimentBleu - Smart Security Scanner for Git Repositories")
|
58 |
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
59 |
-
|
60 |
# UI command
|
61 |
ui_parser = subparsers.add_parser("ui", help="Run the Gradio UI")
|
62 |
-
|
63 |
# API command
|
64 |
api_parser = subparsers.add_parser("api", help="Run the FastAPI server")
|
65 |
-
|
66 |
# Scan command
|
67 |
scan_parser = subparsers.add_parser("scan", help="Run a direct scan of a repository")
|
68 |
scan_parser.add_argument("--repo_source", required=True, help="URL or local path to the repository")
|
69 |
-
|
70 |
# Parse arguments
|
71 |
args = parser.parse_args()
|
72 |
-
|
73 |
# Run the appropriate command
|
74 |
if args.command == "ui":
|
75 |
run_ui()
|
@@ -83,4 +83,4 @@ def main():
|
|
83 |
|
84 |
|
85 |
if __name__ == "__main__":
|
86 |
-
main()
|
|
|
24 |
"""Run the Gradio UI."""
|
25 |
logger.info("Starting Gradio UI")
|
26 |
from agentpimentbleu.app.app import app
|
27 |
+
app.launch(server_name="0.0.0.0")
|
28 |
|
29 |
|
30 |
def run_api():
|
|
|
36 |
def run_scan(repo_source):
|
37 |
"""
|
38 |
Run a direct scan of a repository.
|
39 |
+
|
40 |
Args:
|
41 |
repo_source (str): URL or local path to the repository
|
42 |
"""
|
43 |
logger.info(f"Running scan on {repo_source}")
|
44 |
+
|
45 |
# Get the application configuration
|
46 |
app_config = get_settings()
|
47 |
+
|
48 |
# Run the scan
|
49 |
result = run_sca_scan(repo_source, app_config)
|
50 |
+
|
51 |
# Print the results
|
52 |
print(json.dumps(result, indent=2))
|
53 |
|
|
|
56 |
"""Main entry point."""
|
57 |
parser = argparse.ArgumentParser(description="AgentPimentBleu - Smart Security Scanner for Git Repositories")
|
58 |
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
59 |
+
|
60 |
# UI command
|
61 |
ui_parser = subparsers.add_parser("ui", help="Run the Gradio UI")
|
62 |
+
|
63 |
# API command
|
64 |
api_parser = subparsers.add_parser("api", help="Run the FastAPI server")
|
65 |
+
|
66 |
# Scan command
|
67 |
scan_parser = subparsers.add_parser("scan", help="Run a direct scan of a repository")
|
68 |
scan_parser.add_argument("--repo_source", required=True, help="URL or local path to the repository")
|
69 |
+
|
70 |
# Parse arguments
|
71 |
args = parser.parse_args()
|
72 |
+
|
73 |
# Run the appropriate command
|
74 |
if args.command == "ui":
|
75 |
run_ui()
|
|
|
83 |
|
84 |
|
85 |
if __name__ == "__main__":
|
86 |
+
main()
|