Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/cuckoo/core/data/guests.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,20 @@ def __init__(self, name, label, platform, manager, task_id):
class GuestsMixIn:
def guest_get_status(self, task_id: int):
"""Gets the status for a given guest."""
try:
task_id = int(task_id)
except (TypeError, ValueError):
return None
stmt = select(Guest).where(Guest.task_id == task_id)
guest = self.session.scalar(stmt)
return guest.status if guest else None

def guest_set_status(self, task_id: int, status: str):
"""Sets the status for a given guest."""
try:
task_id = int(task_id)
except (TypeError, ValueError):
return
stmt = select(Guest).where(Guest.task_id == task_id)
guest = self.session.scalar(stmt)
if guest is not None:
Expand Down
4 changes: 4 additions & 0 deletions lib/cuckoo/core/data/samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,10 @@ def get_source_url(self, sample_id: int = None) -> Optional[str]:

def get_parent_sample_from_task(self, task_id: int) -> Optional[Sample]:
"""Finds the Parent Sample using the ID of the child's Task."""
try:
task_id = int(task_id)
except (TypeError, ValueError):
return None

# This query joins the Sample table (as the parent) to the
# association object and filters by the task_id.
Expand Down
12 changes: 12 additions & 0 deletions lib/cuckoo/core/data/tasking.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,10 @@ def add_url(
)

def set_vnc_port(self, task_id: int, port: int):
try:
task_id = int(task_id)
except (TypeError, ValueError):
return
stmt = select(Task).where(Task.id == task_id)
task = self.session.scalar(stmt)

Expand Down Expand Up @@ -1680,6 +1684,10 @@ def view_task(self, task_id, details=False) -> Optional[Task]:
@param task_id: ID of the task to query.
@return: details on the task.
"""
try:
task_id = int(task_id)
except (TypeError, ValueError):
return None
query = select(Task).where(Task.id == task_id)
if details:
query = query.options(
Expand Down Expand Up @@ -1755,6 +1763,10 @@ def tasks_reprocess(self, task_id: int):

def view_errors(self, task_id: int) -> List[Error]:
"""Gets all errors related to a task."""
try:
task_id = int(task_id)
except (TypeError, ValueError):
return []
stmt = select(Error).where(Error.task_id == task_id)
return self.session.scalars(stmt).all()

Expand Down
95 changes: 46 additions & 49 deletions modules/processing/behavior.py
Original file line number Diff line number Diff line change
Expand Up @@ -1153,29 +1153,6 @@ class ProcessTree:

def __init__(self):
self.processes = []
self.tree = []

def add_node(self, node, tree):
"""Add a node to a process tree.
@param node: node to add.
@param tree: processes tree.
@return: boolean with operation success status.
"""
# Walk through the existing tree.
ret = False
for process in tree:
# If the current process has the same ID of the parent process of
# the provided one, append it the children.
if process["pid"] == node["parent_id"]:
process["children"].append(node)
ret = True
break
# Otherwise try with the children of the current process.
else:
if self.add_node(node, process["children"]):
ret = True
break
return ret

def event_apicall(self, call, process):
for entry in self.processes:
Expand All @@ -1195,34 +1172,54 @@ def event_apicall(self, call, process):
)

def run(self):
children = []

# Walk through the generated list of processes.
for process in self.processes:
has_parent = False
# Walk through the list again.
for process_again in self.processes:
if process_again == process:
continue
# If we find a parent for the first process, we mark it as
# as a child.
if process_again["pid"] == process["parent_id"]:
has_parent = True
break

# If the process has a parent, add it to the children list.
if has_parent:
children.append(process)
# Otherwise it's an orphan and we add it to the tree root.
# Index processes by PID.
# This implementation uses an iterative approach to build the tree and detects cycles
# to prevent infinite recursion or excessive depth that causes JSON serialization issues.
node_lookup = {p["pid"]: p for p in self.processes}
roots = []

# Initialize children list for all processes
for p in self.processes:
p["children"] = []

for p in self.processes:
parent_pid = p.get("parent_id")

# Check if parent exists and is not self (self-parenting treated as root)
if parent_pid in node_lookup and parent_pid != p["pid"]:
parent = node_lookup[parent_pid]

# Cycle Detection: Traverse ancestry to ensure 'p' is not an ancestor of 'parent'
curr = parent
is_cycle = False
# Use a simple counter or set to avoid infinite checks if the map has internal loops
depth = 0
max_depth = 100

while depth < max_depth:
if curr is p:
is_cycle = True
break

# Move up to the next parent
curr_parent_pid = curr.get("parent_id")
if curr_parent_pid in node_lookup and curr_parent_pid != curr["pid"]:
curr = node_lookup[curr_parent_pid]
depth += 1
else:
# Reached a root or unknown parent
break

if not is_cycle:
parent["children"].append(p)
else:
# Cycle detected or depth limit hit, treat as root to avoid breaking JSON
log.warning("Cycle or deep nesting detected for process %s (parent %s). treating as root.", p["pid"], parent_pid)
roots.append(p)
else:
self.tree.append(process)

# Now we loop over the remaining child processes.
for process in children:
if not self.add_node(process, self.tree):
self.tree.append(process)
roots.append(p)

return self.tree
return roots


class NetworkMap:
Expand Down
30 changes: 30 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -1591,3 +1591,33 @@ def test_filter_machines_to_task(self, task, machines, expected_result, db: _Dat
assert len(output_machines) == expected_result
else:
assert output_machines.count() == expected_result

def test_id_coercion_robustness(self, db: _Database, temp_filename: str):
with db.session.begin():
t1 = db.add_path(temp_filename)

with db.session.begin():
# Test view_task with string task_id
task = db.view_task(str(t1))
assert task is not None
assert task.id == t1

# Test view_task with invalid task_id
assert db.view_task("not-an-int") is None

# Test view_errors with invalid task_id
assert db.view_errors("not-an-int") == []

# Test set_vnc_port with invalid task_id (should not raise exception)
db.set_vnc_port("not-an-int", 5901)

with db.session.begin():
# Test guest_get_status/set_status with string and invalid task_id
assert db.guest_get_status(str(t1)) is None
assert db.guest_get_status("not-an-int") is None
db.guest_set_status("not-an-int", "running")

with db.session.begin():
assert db.get_parent_sample_from_task(str(t1)) is None
assert db.get_parent_sample_from_task("not-an-int") is None

28 changes: 14 additions & 14 deletions web/audit/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file "docs/LICENSE" for copying permission.

from django.urls import re_path, path
from django.urls import path
from audit import views

urlpatterns = [
re_path(r"^$", views.audit_index, name="audit_index"),
re_path(r"^page/(?P<page>\d+)/$", views.audit_index, name="audit_index"),
re_path(r"^session/(?P<session_id>\d+)/$", views.session_index, name="test_session"),
re_path(r"^session/(?P<session_id>\d+)/status$", views.session_status, name="session_status"),
re_path(r"^session/(?P<session_id>\d+)/run_update/<int:testrun_id>/", views.get_run_update, name="get_run_update"),
re_path(r"^reload_available_tests/", views.reload_available_tests, name="reload_available_tests"),
re_path(r"^create_test_session/$", views.create_test_session, name="create_test_session"),
re_path(r"^delete_test_session/(?P<session_id>\d+)/$", views.delete_test_session, name="delete_test_session"),
path(r"session/<int:session_id>/queue_tests/", views.queue_all_tests, name="queue_all_tests"),
path(r"session/<int:session_id>/unqueue_tests/", views.unqueue_all_tests, name="unqueue_all_tests"),
path(r"session/<int:session_id>/queue_tests/<int:testrun_id>/", views.queue_test, name="queue_test"),
path(r"session/<int:session_id>/unqueue_tests/<int:testrun_id>/", views.unqueue_test, name="unqueue_test"),
re_path(r"^update_task_config/(?P<availabletest_id>\d+)/$", views.update_task_config, name="update_task_config")
path("", views.audit_index, name="audit_index"),
path("page/<int:page>/", views.audit_index, name="audit_index"),
path("session/<int:session_id>/", views.session_index, name="test_session"),
path("session/<int:session_id>/status", views.session_status, name="session_status"),
path("session/<int:session_id>/run_update/<int:testrun_id>/", views.get_run_update, name="get_run_update"),
path("reload_available_tests/", views.reload_available_tests, name="reload_available_tests"),
path("create_test_session/", views.create_test_session, name="create_test_session"),
path("delete_test_session/<int:session_id>/", views.delete_test_session, name="delete_test_session"),
path("session/<int:session_id>/queue_tests/", views.queue_all_tests, name="queue_all_tests"),
path("session/<int:session_id>/unqueue_tests/", views.unqueue_all_tests, name="unqueue_all_tests"),
path("session/<int:session_id>/queue_tests/<int:testrun_id>/", views.queue_test, name="queue_test"),
path("session/<int:session_id>/unqueue_tests/<int:testrun_id>/", views.unqueue_test, name="unqueue_test"),
path("update_task_config/<int:availabletest_id>/", views.update_task_config, name="update_task_config")
]
12 changes: 6 additions & 6 deletions web/compare/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file "docs/LICENSE" for copying permission.

from django.urls import re_path
from django.urls import path

from compare import views

urlpatterns = [
re_path(r"^(?P<left_id>\d+)/$", views.left, name="compare_left"),
re_path(r"^(?P<left_id>\d+)/(?P<right_id>\d+)/$", views.both, name="compare_both"),
re_path(r"^(?P<left_id>\d+)/(?P<right_id>\d+)/diff/$", views.diff, name="compare_diff"),
re_path(r"^(?P<left_id>\d+)/(?P<right_id>\d+)/diff/data/$", views.diff_data, name="compare_diff_data"),
re_path(r"^(?P<left_id>\d+)/(?P<right_hash>\w+)/$", views.hash, name="compare_hash"),
path("<int:left_id>/", views.left, name="compare_left"),
path("<int:left_id>/<int:right_id>/", views.both, name="compare_both"),
path("<int:left_id>/<int:right_id>/diff/", views.diff, name="compare_diff"),
path("<int:left_id>/<int:right_id>/diff/data/", views.diff_data, name="compare_diff_data"),
path("<int:left_id>/<str:right_hash>/", views.hash, name="compare_hash"),
]
4 changes: 2 additions & 2 deletions web/guac/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from django.urls import path, re_path
from django.urls import path

from guac import views

urlpatterns = [
re_path(r"^(?P<task_id>\d+)/(?P<session_data>[\w=]+)/$", views.index, name="index"),
path("<int:task_id>/<str:session_data>/", views.index, name="index"),
path("direct/vnc/<str:host>/<int:port>/", views.direct_vnc_host_port, name="direct_vnc_host_port"),
path("direct/vnc/<str:vm_name>/", views.direct_vnc_vm, name="direct_vnc_vm"),
path("direct/vnc/<str:vm_name>/start/", views.direct_vnc_vm_start, name="direct_vnc_vm_start"),
Expand Down
10 changes: 5 additions & 5 deletions web/submission/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

from django.urls import re_path
from django.urls import path

from submission import views

urlpatterns = [
re_path(r"^$", views.index, name="submission"),
re_path(r"^resubmit/(?P<task_id>\d+)/(?P<resubmit_hash>[\w\d]{64})/$", views.index, name="submission"),
re_path(r"status/(?P<task_id>\d+)/$", views.status, name="submission_status"),
re_path(r"remote_session/(?P<task_id>\d+)/$", views.remote_session, name="remote_session"),
path("", views.index, name="submission"),
path("resubmit/<int:task_id>/<str:resubmit_hash>/", views.index, name="submission"),
path("status/<int:task_id>/", views.status, name="submission_status"),
path("remote_session/<int:task_id>/", views.remote_session, name="remote_session"),
]
2 changes: 1 addition & 1 deletion web/templates/analysis/behavior/_tree.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h5 class="mb-0 text-white"><i class="fas fa-sitemap me-2 text-info"></i> Proces
<strong><a href="javascript:show_tab('process_{{process.pid}}');" class="text-white">{{process.name}}</a></strong>
<span class="text-white-50 ms-1">({{process.pid}})</span>
{% if process.commandline %}
<span class="text-light small ms-2">{{ process.commandline }}</span>
<span class="small ms-2">{{ process.commandline }}</span>
{% endif %}
{% if detections2pid|get_detection_by_pid:process.pid %}
<span class="badge bg-danger ms-2">{{ detections2pid|get_detection_by_pid:process.pid }}</span>
Expand Down
Loading