diff --git a/lib/cuckoo/common/gcp.py b/lib/cuckoo/common/gcp.py index f3e047c96e4..e7950c78236 100644 --- a/lib/cuckoo/common/gcp.py +++ b/lib/cuckoo/common/gcp.py @@ -175,7 +175,7 @@ def check_exists(self, analysis_id: int) -> bool: GCS_ENABLED = False -def download_from_gcs(gcs_uri: str, destination_path: str, logger: Optional[Any] = None, client: Optional[storage.Client] = None) -> bool: +def download_from_gcs(gcs_uri: str, destination_path: str, logger: Optional[Any] = None, client: Optional["storage.Client"] = None) -> bool: """ Downloads a file from GCS. gcs_uri: gs://bucket_name/object_name diff --git a/tests/test_run_task_adapter.py b/tests/test_run_task_adapter.py index 4e27b6a2091..c7a931f5cc0 100644 --- a/tests/test_run_task_adapter.py +++ b/tests/test_run_task_adapter.py @@ -12,6 +12,9 @@ def fake_process(target=None, sample_sha256=None, task=None, report=False, auto= monkeypatch.setattr(proc, "process", fake_process) monkeypatch.setattr(proc, "db", db) + # Force path_exists to return True so the analysis path check succeeds + monkeypatch.setattr(proc, "path_exists", lambda *args, **kwargs: True) + with db.session.begin(): tid = db.add_path(temp_pe32) task = db.view_task(tid) @@ -20,3 +23,35 @@ def fake_process(target=None, sample_sha256=None, task=None, report=False, auto= assert captured["task_id"] == tid assert captured["auto"] is True and captured["report"] is True + + +def test_run_task_fails_gracefully_when_analysis_dir_missing(monkeypatch, db, temp_pe32): + process_called = False + + def fake_process(*args, **kwargs): + nonlocal process_called + process_called = True + + monkeypatch.setattr(proc, "process", fake_process) + monkeypatch.setattr(proc, "db", db) + + # Force path_exists to return False for the analysis path + original_path_exists = proc.path_exists + def fake_path_exists(path, *args, **kwargs): + if "storage/analyses" in str(path): + return False + return original_path_exists(path, *args, **kwargs) + + monkeypatch.setattr(proc, "path_exists", fake_path_exists) + + with db.session.begin(): + tid = db.add_path(temp_pe32) + task = db.view_task(tid) + + proc.run_task(task) + + assert not process_called, "process should not have been called when analysis dir is missing" + + with db.session.begin(): + updated_task = db.view_task(tid) + assert updated_task.status == proc.TASK_FAILED_PROCESSING diff --git a/utils/dist.py b/utils/dist.py index d135dd0a694..5ea36d1474c 100644 --- a/utils/dist.py +++ b/utils/dist.py @@ -4,7 +4,6 @@ # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org # See the file 'docs/LICENSE' for copying permission. -# https://github.com/cuckoosandbox/cuckoo/pull/1694/files import argparse import distutils.util import hashlib @@ -1228,9 +1227,21 @@ def submit_tasks(self, node_name, pend_tasks_num, options_like=False, force_push # print(t.category, t.target) if t.category in ("file", "pcap", "static"): if not path_exists(t.target): - log.info("Task id: %d - File doesn't exist: %s", t.id, t.target) - main_db.set_status(t.id, TASK_BANNED) - continue + sample_sha256 = None + try: + if t.sample: + sample_sha256 = t.sample.sha256 + except Exception as e: + log.debug("Failed to lazy load sample relation for task %d: %s", t.id, e) + + bin_path = os.path.join(CUCKOO_ROOT, "storage", "binaries", sample_sha256) if sample_sha256 else None + if bin_path and path_exists(bin_path): + log.info("Task id: %d - Target file not found at original path, but found in binaries storage: %s. Updating target path.", t.id, bin_path) + t.target = bin_path + else: + log.info("Task id: %d - File doesn't exist: %s", t.id, t.target) + main_db.set_status(t.id, TASK_BANNED) + continue if not web_conf.general.allow_ignore_size and "ignore_size_check" not in t.options: # We can't upload size bigger than X to our workers. In case we extract archive that contains bigger file. file_size = path_get_size(t.target) diff --git a/utils/process.py b/utils/process.py index 5f58ae7ed29..8096dec8c66 100644 --- a/utils/process.py +++ b/utils/process.py @@ -40,6 +40,7 @@ from lib.cuckoo.common.utils import get_options, option_dict_enabled from lib.cuckoo.core.database import Database, init_database from lib.cuckoo.core.data.task import ( + TASK_FAILED_PROCESSING, TASK_FAILED_REPORTING, TASK_REPORTED ) @@ -190,6 +191,13 @@ def process( def run_task(task, memory_debugging=False, debug=False): """Run exactly one completed task to completion (processing -> report). Extracted from autoprocess so every engine shares identical per-task setup.""" + analysis_path = os.path.join(CUCKOO_ROOT, "storage", "analyses", str(task.id)) + if not path_exists(analysis_path): + log.error("Analysis directory %s does not exist. Marking task %s as failed.", analysis_path, task.id) + with db.session.begin(): + db.set_status(task.id, TASK_FAILED_PROCESSING) + return + sample_hash = "" if task.category != "url": with db.session.begin():