Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
30 changes: 30 additions & 0 deletions nettacker/core/lib/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
reverse_and_regex_condition,
get_http_header_key,
get_http_header_value,
version_matches_dsl,
extract_version_from_content,
)

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
Expand Down Expand Up @@ -90,6 +92,34 @@ def response_conditions_matched(sub_step, response):
)
else:
condition_results["responsetime"] = []
# DSL version matching support
if condition == "version_match":
version_config = conditions[condition]
# Extract version from response using patterns
version_patterns = version_config.get("patterns", [])
source = version_config.get("source", "content")
dsl_expression = version_config.get("expression", "")

# Get the content to search from
search_content = ""
if source == "content":
search_content = response.get("content", "")
elif source == "headers":
header_name = version_config.get("header", "server")
search_content = response.get("headers", {}).get(header_name.lower(), "")

# Extract version
detected_version = extract_version_from_content(search_content, version_patterns)

# Match against DSL expression
if detected_version and dsl_expression:
match_result = version_matches_dsl(detected_version, dsl_expression)
if match_result:
condition_results["version_match"] = [detected_version]
else:
condition_results["version_match"] = []
else:
condition_results["version_match"] = []
Comment on lines +95 to +122
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Review the config structure documentation.

The implementation assumes a specific structure for version_config with keys like "patterns", "source", "expression", and "header". However, the YAML file uses "expressions" (plural) at line 54, not "expression" (singular) at line 101.

At line 101, the code reads:

dsl_expression = version_config.get("expression", "")

But the YAML at line 54-55 uses:

expressions:
  - "< 8.18.5"

This mismatch means dsl_expression will always be an empty string. You need to decide on singular vs plural and handle accordingly:

-            dsl_expression = version_config.get("expression", "")
+            # Handle both single expression and list of expressions
+            expressions = version_config.get("expressions", [])
+            if not expressions:
+                # Fallback for singular
+                single_expr = version_config.get("expression", "")
+                expressions = [single_expr] if single_expr else []
             
             # Get the content to search from
             search_content = ""
@@ -112,11 +117,13 @@
             detected_version = extract_version_from_content(search_content, version_patterns)
             
             # Match against DSL expression
-            if detected_version and dsl_expression:
-                match_result = version_matches_dsl(detected_version, dsl_expression)
-                if match_result:
-                    condition_results["version_match"] = [detected_version]
-                else:
+            if detected_version and expressions:
+                # Check if version matches ANY expression (OR logic)
+                matched = any(version_matches_dsl(detected_version, expr) for expr in expressions)
+                if matched:
+                    condition_results["version_match"] = [detected_version]
+                else:
                     condition_results["version_match"] = []
             else:
                 condition_results["version_match"] = []
🤖 Prompt for AI Agents
In nettacker/core/lib/http.py around lines 95 to 122, the code reads
version_config.get("expression", "") but the YAML uses "expressions" (plural),
so dsl_expression is always empty; update the code to read the correct key
(decide on singular or plural to match project config) — either change to use
"expressions" and treat it as a list (e.g., use the first expression or iterate
all and succeed on any match) or normalize both keys by checking for
"expression" then "expressions"; then pass the chosen expression string(s) into
version_matches_dsl and set condition_results["version_match"] accordingly
(non-empty when any expression matches), ensuring header lookup still lowercases
header names when source == "headers".

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Critical: Condition name mismatch between implementation and usage.

The code checks for condition == "version_match" (line 96), but the YAML module at line 51 uses version_dsl as the condition key. This mismatch will cause the version matching feature to fail silently.

Looking at the YAML file nettacker/modules/vuln/aria_operations_cve_2025_41244.yaml at line 51, the condition is named version_dsl, but this code expects version_match. You need to align the naming:

Option 1 (Recommended): Change the Python code to match the YAML:

-        if condition == "version_match":
+        if condition == "version_dsl":

Option 2: Change the YAML to use version_match instead of version_dsl.

Additionally, verify this alignment:


🏁 Script executed:

#!/bin/bash
# Search for all usages of version_dsl and version_match in YAML and Python files
echo "=== Checking for version_dsl usage ==="
rg -n "version_dsl" --type yaml --type py

echo "=== Checking for version_match usage ==="
rg -n "version_match" --type yaml --type py

Length of output: 1345


Based on my verification of the codebase, the critical issue identified in the original review comment is confirmed: there is a definitive condition name mismatch that will cause the version matching feature to fail silently.

Critical: Condition name mismatch — YAML uses version_dsl but Python code expects version_match.

The search results confirm:

  • version_dsl appears only in the YAML file at line 51 of nettacker/modules/vuln/aria_operations_cve_2025_41244.yaml
  • version_match appears in the Python condition handler at line 96 of nettacker/core/lib/http.py, where the code checks if condition == "version_match"
  • When the YAML conditions dictionary is iterated, the key will be "version_dsl", which will never match the string "version_match" in the comparison

This mismatch causes the entire version matching feature to be silently skipped—no error is raised, the condition simply doesn't execute.

Fix: Change the YAML condition key from version_dsl to version_match to align with the Python implementation:

-            version_dsl:
+            version_match:

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In nettacker/core/lib/http.py around lines 95 to 122 there is a condition
handler checking for "version_match" but the YAML uses the key "version_dsl",
causing the branch to never execute; fix by aligning the keys so the YAML
condition key is renamed from "version_dsl" to "version_match" (or alternatively
change the Python check to "version_dsl") across the codebase so the iteration
over conditions will trigger the version extraction and DSL matching logic.

if condition_type.lower() == "or":
# if one of the values are matched, it will be a string or float object in the array
# we count False in the array and if it's not all []; then we know one of the conditions
Expand Down
41 changes: 41 additions & 0 deletions nettacker/core/utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from itertools import product

from nettacker import logger
from nettacker.core.utils.dsl_matcher import dsl_matcher

log = logger.get_logger()

Expand Down Expand Up @@ -450,3 +451,43 @@ def generate_compare_filepath(scan_id):
date_time=now(format="%Y_%m_%d_%H_%M_%S"),
scan_id=scan_id,
)


def version_matches_dsl(detected_version, dsl_expression):
"""
Check if a detected version matches a DSL expression.

Supports various version comparison operators:
- Range operators: >=, <=, >, <, ==, !=
- Semantic operators: ~ (tilde), ^ (caret)
- Logical operators: ||, &&, or, and
- Range syntax: "1.0 to 2.0", "1.0 - 2.0"
- Wildcards: *, ?

Args:
detected_version: Version string detected from target
dsl_expression: DSL expression to match against

Returns:
bool: True if version matches the expression

Examples:
version_matches_dsl("2.4.51", ">=2.4.0, <2.4.54") # True
version_matches_dsl("1.2.3", "~1.2.0") # True (patch-level match)
version_matches_dsl("8.18.0", ">=8.0 && <9.0") # True
"""
return dsl_matcher.parse_dsl_expression(dsl_expression, detected_version)


def extract_version_from_content(content, patterns):
"""
Extract version from content using regex patterns.

Args:
content: String content to search
patterns: List of regex patterns to try

Returns:
str: Extracted version or None
"""
return dsl_matcher.extract_version_from_response(content, patterns)
Loading