mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-08-23 17:52:29 +02:00
fix: Resolve critical bugs - file handle leaks and IndexError issues
Fixed multiple critical bugs identified during comprehensive code audit: **Critical Fixes:** - Fix file handle leaks in SDK client upload methods (sync and async) - Use context managers to ensure file handles are properly closed - Affects: sdk/src/fuzzforge_sdk/client.py lines 397, 484 **High Priority Fixes:** - Fix IndexError in OSS-Fuzz stats parsing when accessing array elements - Add bounds checking before accessing parts[i+1] - Affects: workers/ossfuzz/activities.py lines 372-376 - Fix IndexError in exception handling URL parsing - Add empty string validation before splitting URL segments - Prevents crash when parsing malformed URLs - Affects: sdk/src/fuzzforge_sdk/exceptions.py lines 419-426 **Medium Priority Fixes:** - Fix IndexError in Android workflow SARIF report parsing - Check if runs list is empty before accessing first element - Affects: backend/toolbox/workflows/android_static_analysis/workflow.py line 270 All fixes follow defensive programming practices with proper bounds checking and resource management using context managers.
This commit is contained in:
@@ -267,7 +267,8 @@ class AndroidStaticAnalysisWorkflow:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Calculate summary
|
# Calculate summary
|
||||||
total_findings = len(sarif_report.get("runs", [{}])[0].get("results", []))
|
runs = sarif_report.get("runs", [])
|
||||||
|
total_findings = len(runs[0].get("results", [])) if runs else 0
|
||||||
|
|
||||||
summary = {
|
summary = {
|
||||||
"workflow": "android_static_analysis",
|
"workflow": "android_static_analysis",
|
||||||
|
|||||||
@@ -393,10 +393,6 @@ class FuzzForgeClient:
|
|||||||
# Prepare multipart form data
|
# Prepare multipart form data
|
||||||
url = urljoin(self.base_url, f"/workflows/{workflow_name}/upload-and-submit")
|
url = urljoin(self.base_url, f"/workflows/{workflow_name}/upload-and-submit")
|
||||||
|
|
||||||
files = {
|
|
||||||
"file": (filename, open(upload_file, "rb"), "application/gzip")
|
|
||||||
}
|
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
if parameters:
|
if parameters:
|
||||||
@@ -418,13 +414,15 @@ class FuzzForgeClient:
|
|||||||
# This is a placeholder - real implementation would need custom approach
|
# This is a placeholder - real implementation would need custom approach
|
||||||
pass
|
pass
|
||||||
|
|
||||||
response = self._client.post(url, files=files, data=data)
|
# Use context manager to ensure file handle is closed
|
||||||
|
with open(upload_file, "rb") as f:
|
||||||
|
files = {
|
||||||
|
"file": (filename, f, "application/gzip")
|
||||||
|
}
|
||||||
|
response = self._client.post(url, files=files, data=data)
|
||||||
|
|
||||||
# Close file handle
|
response_data = self._handle_response(response)
|
||||||
files["file"][1].close()
|
return RunSubmissionResponse(**response_data)
|
||||||
|
|
||||||
data = self._handle_response(response)
|
|
||||||
return RunSubmissionResponse(**data)
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Cleanup temporary tarball
|
# Cleanup temporary tarball
|
||||||
@@ -480,10 +478,6 @@ class FuzzForgeClient:
|
|||||||
# Prepare multipart form data
|
# Prepare multipart form data
|
||||||
url = urljoin(self.base_url, f"/workflows/{workflow_name}/upload-and-submit")
|
url = urljoin(self.base_url, f"/workflows/{workflow_name}/upload-and-submit")
|
||||||
|
|
||||||
files = {
|
|
||||||
"file": (filename, open(upload_file, "rb"), "application/gzip")
|
|
||||||
}
|
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
if parameters:
|
if parameters:
|
||||||
@@ -494,10 +488,12 @@ class FuzzForgeClient:
|
|||||||
|
|
||||||
logger.info(f"Uploading {filename} to {workflow_name}...")
|
logger.info(f"Uploading {filename} to {workflow_name}...")
|
||||||
|
|
||||||
response = await self._async_client.post(url, files=files, data=data)
|
# Use context manager to ensure file handle is closed
|
||||||
|
with open(upload_file, "rb") as f:
|
||||||
# Close file handle
|
files = {
|
||||||
files["file"][1].close()
|
"file": (filename, f, "application/gzip")
|
||||||
|
}
|
||||||
|
response = await self._async_client.post(url, files=files, data=data)
|
||||||
|
|
||||||
response_data = await self._ahandle_response(response)
|
response_data = await self._ahandle_response(response)
|
||||||
return RunSubmissionResponse(**response_data)
|
return RunSubmissionResponse(**response_data)
|
||||||
|
|||||||
@@ -415,16 +415,20 @@ def from_http_error(status_code: int, response_text: str, url: str) -> FuzzForge
|
|||||||
if "/workflows/" in url and "/submit" not in url:
|
if "/workflows/" in url and "/submit" not in url:
|
||||||
# Extract workflow name from URL
|
# Extract workflow name from URL
|
||||||
parts = url.split("/workflows/")
|
parts = url.split("/workflows/")
|
||||||
if len(parts) > 1:
|
if len(parts) > 1 and parts[1]:
|
||||||
workflow_name = parts[1].split("/")[0]
|
workflow_segments = parts[1].split("/")
|
||||||
return WorkflowNotFoundError(workflow_name, context=context)
|
if workflow_segments and workflow_segments[0]:
|
||||||
|
workflow_name = workflow_segments[0]
|
||||||
|
return WorkflowNotFoundError(workflow_name, context=context)
|
||||||
|
|
||||||
elif "/runs/" in url:
|
elif "/runs/" in url:
|
||||||
# Extract run ID from URL
|
# Extract run ID from URL
|
||||||
parts = url.split("/runs/")
|
parts = url.split("/runs/")
|
||||||
if len(parts) > 1:
|
if len(parts) > 1 and parts[1]:
|
||||||
run_id = parts[1].split("/")[0]
|
run_segments = parts[1].split("/")
|
||||||
return RunNotFoundError(run_id, context)
|
if run_segments and run_segments[0]:
|
||||||
|
run_id = run_segments[0]
|
||||||
|
return RunNotFoundError(run_id, context)
|
||||||
|
|
||||||
elif status_code == 400:
|
elif status_code == 400:
|
||||||
# Check for specific error patterns in response
|
# Check for specific error patterns in response
|
||||||
|
|||||||
@@ -368,11 +368,11 @@ def parse_fuzzing_stats(stdout: str, stderr: str, engine: str) -> Dict[str, Any]
|
|||||||
# Example: #8192 NEW cov: 1234 ft: 5678 corp: 89/10KB
|
# Example: #8192 NEW cov: 1234 ft: 5678 corp: 89/10KB
|
||||||
parts = line.split()
|
parts = line.split()
|
||||||
for i, part in enumerate(parts):
|
for i, part in enumerate(parts):
|
||||||
if part.startswith("cov:"):
|
if part.startswith("cov:") and i+1 < len(parts):
|
||||||
stats["coverage"] = int(parts[i+1])
|
stats["coverage"] = int(parts[i+1])
|
||||||
elif part.startswith("corp:"):
|
elif part.startswith("corp:") and i+1 < len(parts):
|
||||||
stats["corpus_entries"] = int(parts[i+1].split('/')[0])
|
stats["corpus_entries"] = int(parts[i+1].split('/')[0])
|
||||||
elif part.startswith("exec/s:"):
|
elif part.startswith("exec/s:") and i+1 < len(parts):
|
||||||
stats["executions_per_sec"] = float(parts[i+1])
|
stats["executions_per_sec"] = float(parts[i+1])
|
||||||
elif part.startswith("#"):
|
elif part.startswith("#"):
|
||||||
stats["total_executions"] = int(part[1:])
|
stats["total_executions"] = int(part[1:])
|
||||||
|
|||||||
Reference in New Issue
Block a user