From b287f9786a96efd62361042a66da95345d5a58fe Mon Sep 17 00:00:00 2001 From: mattft0 Date: Tue, 7 Jul 2026 12:36:05 +0200 Subject: [PATCH 1/2] Fix false "Failed" status for static analysis with no signature hit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit calc_scoring() decided Undetected vs Failed based solely on the presence of behavior.processtree. Static analysis (category "static") never runs the sample in a VM, so processtree is always empty by design — every clean static analysis was wrongly reported as "Failed". Also check raw YARA matches on the target file (not just family-attributed detections) to surface static hits as "Suspicious" instead of silently discarding them. --- lib/cuckoo/common/scoring.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/cuckoo/common/scoring.py b/lib/cuckoo/common/scoring.py index dfa6ea02bf9..63d4671842b 100644 --- a/lib/cuckoo/common/scoring.py +++ b/lib/cuckoo/common/scoring.py @@ -41,7 +41,11 @@ def calc_scoring(results: dict, matched: list): 4. Benign: The file is likely trusted and digitally signed. - Score: 0-3/10 (Benign) 5. Undetected/Failed: The file does not trigger any signatures. - - Score: 0/10 (Undetected/Failed) + - Static analysis: Undetected, or Suspicious if raw YARA rules matched + the target file without being tied to a known family. + - Dynamic analysis: Undetected if a process tree exists, otherwise + Failed (the sample never ran). + - Score: 0/10 (Undetected/Failed), except Suspicious static case above. Parameters: results (dict): The analysis results containing details about the file and its behavior. @@ -156,7 +160,16 @@ def calc_scoring(results: dict, matched: list): # 5. Undetected/Failed else: finalMalscore = 0 - if results.get("behavior", {}).get("processtree", []): + if category == "static": + # Static analysis never executes the sample, so there is never a + # process tree by design. Fall back to raw YARA matches on the + # target file (not tied to a known family) to decide between + # Undetected and Suspicious instead of assuming failure. + yara_hits = results.get("target", {}).get("file", {}).get("yara", []) or results.get( + "target", {} + ).get("file", {}).get("cape_yara", []) + status = "Suspicious" if yara_hits else "Undetected" + elif results.get("behavior", {}).get("processtree", []): status = "Undetected" else: status = "Failed" From 29470b41f97de11145284011802e875900d2107e Mon Sep 17 00:00:00 2001 From: mattft0 Date: Wed, 8 Jul 2026 16:40:27 +0200 Subject: [PATCH 2/2] Address review: fix score for static Suspicious, safer nested .get() - finalMalscore now set to 4.0 when a raw YARA hit marks a static analysis as Suspicious, matching the 4.0-6.0 convention used by the existing Suspicious-Unknown branch instead of leaving it at 0. - Replace chained .get(key, {}) calls with `... or {}` fallbacks so an explicit None on an intermediate key (target/file) can't raise AttributeError, and avoid the redundant double lookup of "target". Addresses gemini-code-assist review on PR #3102. --- lib/cuckoo/common/scoring.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/cuckoo/common/scoring.py b/lib/cuckoo/common/scoring.py index 63d4671842b..428b547d26d 100644 --- a/lib/cuckoo/common/scoring.py +++ b/lib/cuckoo/common/scoring.py @@ -45,7 +45,7 @@ def calc_scoring(results: dict, matched: list): the target file without being tied to a known family. - Dynamic analysis: Undetected if a process tree exists, otherwise Failed (the sample never ran). - - Score: 0/10 (Undetected/Failed), except Suspicious static case above. + - Score: 0/10 (Undetected/Failed), or 4.0/10 for the Suspicious static case above. Parameters: results (dict): The analysis results containing details about the file and its behavior. @@ -165,10 +165,14 @@ def calc_scoring(results: dict, matched: list): # process tree by design. Fall back to raw YARA matches on the # target file (not tied to a known family) to decide between # Undetected and Suspicious instead of assuming failure. - yara_hits = results.get("target", {}).get("file", {}).get("yara", []) or results.get( - "target", {} - ).get("file", {}).get("cape_yara", []) - status = "Suspicious" if yara_hits else "Undetected" + target_file = results.get("target") or {} + file_info = target_file.get("file") or {} + yara_hits = file_info.get("yara") or file_info.get("cape_yara") or [] + if yara_hits: + status = "Suspicious" + finalMalscore = 4.0 + else: + status = "Undetected" elif results.get("behavior", {}).get("processtree", []): status = "Undetected" else: