Skip to content

Conversation

rxerium
Copy link

@rxerium rxerium commented Oct 4, 2025

Proposed change

Your PR description goes here.

Type of change

  • New core framework functionality
  • Bugfix (non-breaking change which fixes an issue)
  • Code refactoring without any functionality changes
  • New or existing module/payload change
  • Documentation/localization improvement
  • Test coverage improvement
  • Dependency upgrade
  • Other improvement (best practice, cleanup, optimization, etc)

Checklist

  • I've followed the contributing guidelines
  • I've run make pre-commit, it didn't generate any changes
  • I've run make test, all tests passed locally

Summary by CodeRabbit

  • New Features

    • Added DSL-based version matching for HTTP response checks, including ranges, wildcards and semantic operators, plus a CVE-version validation check.
    • Added a VMware Aria Operations vulnerability probe that extracts and evaluates product versions.
  • Documentation

    • Minor README formatting cleanup.
  • Style

    • Minor whitespace/formatting tweak in an existing vulnerability definition; no behavior change.
  • Chores

    • Added dependencies for improved version parsing and comparison.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 4, 2025

Walkthrough

Adds a new DSLMatcher for parsing/evaluating version DSLs, integrates it into HTTP response condition checks (version_dsl and cve_version_match), introduces a VMware Aria Operations vulnerability module using the DSL, updates packaging dependencies, and applies minor README/YAML formatting tweaks.

Changes

Cohort / File(s) Change summary
Docs formatting
README.md
Removed an empty line after the "Stargazers over time" badge; formatting-only.
DSL version engine
core/dsl_matcher.py
New DSLMatcher class implementing DSL parsing, version cleaning/extraction, wildcard/range/multi-expression handling, semantic ~/^ support, comparison fallbacks, and CVE range matching; public methods for parsing expressions, CVE-range matching, and extracting versions from responses.
HTTP protocol integration
core/module_protocols/core_http.py
Integrated DSLMatcher; added version_dsl and cve_version_match response condition checks, extraction from headers/body, normalization of inputs, and reverse handling; preserved existing condition flow.
Vulnerability modules
modules/vuln/aria_operations_cve_2025_41244.yaml, modules/vuln/apache_cve_2021_41773.yaml
Added new VMware Aria Operations CVE module using version_dsl; minor whitespace/format alignment in Apache CVE YAML.
Dependencies
requirements.txt
Added packaging>=21.0 and semver>=3.0.0; PyYAML==6.0.1 retained.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Module as Vulnerability Module
  participant CoreHTTP as core.module_protocols.core_http
  participant DSL as core.dsl_matcher.DSLMatcher
  participant Target as Target Server

  User->>Module: run payload
  Module->>CoreHTTP: build and send HTTP request
  CoreHTTP->>Target: HTTP request
  Target-->>CoreHTTP: HTTP response (status, headers, body)
  Note over CoreHTTP: assemble searchable content (headers + body)

  alt version_dsl condition
    CoreHTTP->>DSL: extract_version_from_response(content, patterns)
    DSL-->>CoreHTTP: extracted_version
    CoreHTTP->>DSL: parse_dsl_expression(expression, extracted_version)
    DSL-->>CoreHTTP: match boolean
    CoreHTTP-->>Module: condition result (apply reverse if set)
  else cve_version_match condition
    CoreHTTP->>DSL: extract_version_from_response(content, patterns)
    DSL-->>CoreHTTP: extracted_version
    CoreHTTP->>DSL: match_cve_version_range(extracted_version, affected_versions)
    DSL-->>CoreHTTP: vulnerable? (boolean)
    CoreHTTP-->>Module: condition result (apply reverse if set)
  end

  Module-->>User: final match/outcome
Loading
sequenceDiagram
  participant DSL as DSLMatcher
  participant Expr as DSL Expression
  participant Ver as Target Version

  DSL->>DSL: _clean_version(Ver)
  alt comma-separated / multiple
    DSL->>DSL: _evaluate_multiple(Expr, Ver)
  else range ("to" or "-")
    DSL->>DSL: _evaluate_range(Expr, Ver)
  else wildcard
    DSL->>DSL: _evaluate_wildcard(Expr, Ver)
  else comparator (~ ^ >= <= > < == !=)
    DSL->>DSL: _compare_version(v1, v2, op)
    Note over DSL: uses packaging, semver, or numeric-part fallback\n_tilde_/_caret_ handled by helpers
  end
  DSL-->>Expr: boolean result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

Thump-thump, I hop through headers bright,
I sniff versions in the pale response light.
Ranges, wildcards, tilde, caret play—
I match and guard the ports today.
New matcher tuned, I bound away. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly summarizes the primary change by indicating the integration of the new DSL-based engine, directly reflecting the PR’s main objective without unnecessary detail or ambiguity. It is concise, clear, and specific enough for reviewers to understand the core enhancement at a glance.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like a good addition to nettacker. I would suggest making a separate PR for this and removing this file from here.

Copy link
Contributor

@pUrGe12 pUrGe12 left a comment

Choose a reason for hiding this comment

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

please clean this PR and give a better explanation of what you're trying to do. It seems like you're created a new folder and file core_http.py based of http.py. It would be better if you integrate your solution within the existing framework.

@rxerium
Copy link
Author

rxerium commented Oct 6, 2025

Thank you @pUrGe12!

It seems like you're created a new folder and file core_http.py based of http.py

I've modified core_http.py as opposed to creating it but I think I have spotted a few issues with the integration. I will take a look this week and update accordingly.

I will also remove CVE_2025_41244.yaml‎ before merging - I thought it would be useful to include as a showcase for what this integration would look like in a template.

@rxerium
Copy link
Author

rxerium commented Oct 9, 2025

@pUrGe12, with your familiarity with the project I invite you test these changes.

I've:

  • Created modules/vuln/aria_operations_cve_2025_41244.yaml - I've left this file in the PR for now for the purpose of testing (feel free to test against any target here) DSL functionality - as noted above we can remove it before merging

  • Modified core/module_protocols/core_http.py - Two new condition handlers added:
    version_dsl (lines 57-90): Extracts version numbers from HTTP responses using regex patterns and validates them against DSL expressions for flexible version comparisons.
    cve_version_match (lines 92-125): Similar to version_dsl but specifically designed for CVE vulnerability detection, checking if extracted versions fall within specified affected version ranges.

  • Created core/dsl_matcher.py - A new version comparison engine that evaluates version matching expressions for vulnerabilities. It allows CVE modules to specify version ranges and conditions (like >=8.16.0 or <8.18.2 etc.) to determine if a detected software version is vulnerable, supporting multiple comparison operators, wildcards, semantic versioning, and both AND/OR logic for combining conditions.

  • Added new modules to requirements.txt:
packaging>=21.0 # library_name=packaging # For version parsing and comparison
semver>=3.0.0 # library_name=semver # For semantic version comparison

Purpose of this PR:
The DSL integration enables version-aware vulnerability detection by extracting version numbers from HTTP responses and evaluating them against flexible expressions (like >= 2.0.0 && < 3.5.1) or CVE-affected version ranges. This allows scanning modules to automatically determine if a detected software version is vulnerable without hardcoding every possible version number.

Thanks!

@securestep9
Copy link
Collaborator

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91f5472 and 43d8696.

📒 Files selected for processing (6)
  • README.md (0 hunks)
  • core/dsl_matcher.py (1 hunks)
  • core/module_protocols/core_http.py (3 hunks)
  • modules/vuln/apache_cve_2021_41773.yaml (1 hunks)
  • modules/vuln/aria_operations_cve_2025_41244.yaml (1 hunks)
  • requirements.txt (1 hunks)
💤 Files with no reviewable changes (1)
  • README.md
🧰 Additional context used
🪛 Ruff (0.13.3)
core/dsl_matcher.py

1-1: Shebang is present but file is not executable

(EXE001)


49-49: Do not catch blind exception: Exception

(BLE001)


125-125: Do not catch blind exception: Exception

(BLE001)


182-182: Do not catch blind exception: Exception

(BLE001)


215-215: Consider moving this statement to an else block

(TRY300)


217-217: Do not catch blind exception: Exception

(BLE001)


247-247: Consider moving this statement to an else block

(TRY300)


249-249: Do not catch blind exception: Exception

(BLE001)


271-271: Do not catch blind exception: Exception

(BLE001)


307-307: Do not catch blind exception: Exception

(BLE001)


322-322: Do not catch blind exception: Exception

(BLE001)


361-361: Do not catch blind exception: Exception

(BLE001)


434-435: try-except-continue detected, consider logging the exception

(S112)


434-434: Do not catch blind exception: Exception

(BLE001)


479-479: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test

@rxerium
Copy link
Author

rxerium commented Oct 10, 2025

amazing! I will work on these changes today/this weekend!

@rxerium
Copy link
Author

rxerium commented Oct 11, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 11, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
core/dsl_matcher.py (1)

204-266: Consider extracting shared logic between tilde and caret operators.

_tilde_compare and _caret_compare share significant code structure (version splitting, padding, part comparison, boundary calculation). The primary difference is which version component gets incremented (minor vs. major). Extracting common logic into a helper method would improve maintainability.

Example refactor:

def _semantic_range_compare(self, version1, version2, boundary_index):
    """
    Helper for tilde/caret comparison.
    Args:
        boundary_index: 0 for major (caret), 1 for minor (tilde)
    """
    try:
        v1_parts = [int(x) for x in version1.split('.')]
        v2_parts = [int(x) for x in version2.split('.')]
        
        max_len = max(len(v1_parts), len(v2_parts))
        v1_parts.extend([0] * (max_len - len(v1_parts)))
        v2_parts.extend([0] * (max_len - len(v2_parts)))
        
        if not self._compare_version_parts(v1_parts, v2_parts, '>='):
            return False
        
        if len(v2_parts) > boundary_index:
            next_version = v2_parts.copy()
            next_version[boundary_index] += 1
            for i in range(boundary_index + 1, len(next_version)):
                next_version[i] = 0
            return self._compare_version_parts(v1_parts, next_version, '<')
        
        return True
    except Exception:
        return False

def _tilde_compare(self, version1, version2):
    return self._semantic_range_compare(version1, version2, boundary_index=1)

def _caret_compare(self, version1, version2):
    return self._semantic_range_compare(version1, version2, boundary_index=0)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43d8696 and e6b5cd6.

📒 Files selected for processing (1)
  • core/dsl_matcher.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.13.3)
core/dsl_matcher.py

1-1: Shebang is present but file is not executable

(EXE001)


49-49: Do not catch blind exception: Exception

(BLE001)


141-141: Do not catch blind exception: Exception

(BLE001)


198-198: Do not catch blind exception: Exception

(BLE001)


231-231: Consider moving this statement to an else block

(TRY300)


233-233: Do not catch blind exception: Exception

(BLE001)


263-263: Consider moving this statement to an else block

(TRY300)


265-265: Do not catch blind exception: Exception

(BLE001)


298-298: Do not catch blind exception: Exception

(BLE001)


334-334: Do not catch blind exception: Exception

(BLE001)


349-349: Do not catch blind exception: Exception

(BLE001)


388-388: Do not catch blind exception: Exception

(BLE001)


461-462: try-except-continue detected, consider logging the exception

(S112)


461-461: Do not catch blind exception: Exception

(BLE001)


506-506: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (2)
core/dsl_matcher.py (2)

100-111: Logical connector parsing successfully implemented.

The previous critical issue regarding unparsed logical connectors has been addressed. The code now correctly handles OR (||, or) and AND (&&, and) operators with case-insensitive matching for word-based operators, splitting expressions before operator dispatch.


268-297: Range parsing correctly handles prerelease versions.

The previous major issue regarding hyphenated prerelease versions being misdetected as ranges has been fixed. The code now requires spaces around the hyphen (-) to treat it as a range separator, avoiding misinterpretation of versions like "8.18.0-beta1".

@rxerium
Copy link
Author

rxerium commented Oct 11, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 11, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
core/dsl_matcher.py (4)

336-341: Escape regex safely when translating wildcards

Only '.' is escaped; other regex metacharacters can misbehave. Prefer re.escape, then restore wildcard tokens.

Apply this diff:

-            # Convert wildcard to regex
-            pattern = dsl_expression.replace('.', r'\.')
-            pattern = pattern.replace('*', '.*')
-            pattern = pattern.replace('?', '.')
-            pattern = f'^{pattern}$'
+            # Convert wildcard to regex: escape everything then expand wildcards
+            escaped = re.escape(dsl_expression)
+            pattern = '^' + escaped.replace(r'\*', '.*').replace(r'\?', '.') + '$'

433-447: Swallowed regex errors in extraction; log for diagnostics

Exceptions are silently ignored. Log at debug to aid troubleshooting and satisfy static analysis (S112).

Apply this diff:

-            except Exception:
-                continue
+            except Exception as e:
+                self.logger.debug(f"extract_version_from_response: pattern {pattern!r} failed: {e}")
+                continue

34-36: Broad Exception catches reduce debuggability

Multiple blind excepts hide real errors and complicate testing. Narrow to specific exceptions (e.g., InvalidVersion, ValueError, RecursionError) and log expression/context. At minimum, convert to “except Exception as e: logger.debug(..., exc_info=True)”.

Example pattern (apply similarly across methods):

-        except Exception as e:
-            self.logger.debug(f"DSL evaluation error: {e}")
+        except RecursionError as e:
+            self.logger.debug(f"DSL evaluation recursion error for {dsl_expression!r}: {e}", exc_info=True)
+        except Exception as e:
+            self.logger.debug(f"DSL evaluation error for {dsl_expression!r}: {e}", exc_info=True)

Ruff BLE001 flagged these spots. As per coding guidelines

Also applies to: 126-128, 183-187, 222-223, 258-259, 291-293, 327-329, 342-343, 381-383, 444-445


1-3: Shebang without executable bit

Either remove the shebang or make the file executable to satisfy EXE001.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6b5cd6 and 95acde5.

📒 Files selected for processing (1)
  • core/dsl_matcher.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.13.3)
core/dsl_matcher.py

1-1: Shebang is present but file is not executable

(EXE001)


34-34: Do not catch blind exception: Exception

(BLE001)


126-126: Do not catch blind exception: Exception

(BLE001)


183-183: Do not catch blind exception: Exception

(BLE001)


220-220: Consider moving this statement to an else block

(TRY300)


222-222: Do not catch blind exception: Exception

(BLE001)


256-256: Consider moving this statement to an else block

(TRY300)


258-258: Do not catch blind exception: Exception

(BLE001)


291-291: Do not catch blind exception: Exception

(BLE001)


327-327: Do not catch blind exception: Exception

(BLE001)


342-342: Do not catch blind exception: Exception

(BLE001)


381-381: Do not catch blind exception: Exception

(BLE001)


444-445: try-except-continue detected, consider logging the exception

(S112)


444-444: Do not catch blind exception: Exception

(BLE001)


478-478: Do not catch blind exception: Exception

(BLE001)

Comment on lines +85 to +97
# Handle logical connectors (OR and AND) before operator dispatch
# Check for OR operators first (|| or 'or')
for splitter, combiner in [('||', any), (' or ', any), ('&&', all), (' and ', all)]:
# Use case-insensitive search for word-based operators
if splitter in ['||', '&&']:
check = splitter in normalized
else:
check = re.search(rf'\s+{re.escape(splitter.strip())}\s+', normalized, re.IGNORECASE)

if check:
parts = [part.strip() for part in re.split(rf'\s*(?:{re.escape(splitter)})\s*', normalized, flags=re.IGNORECASE) if part.strip()]
return combiner(self._evaluate_dsl(part, target_version) for part in parts)

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 | 🔴 Critical

Connector split bug can cause deep recursion; unify detection and splitting

Detection allows multi-space around textual operators, but split pattern requires single spaces, so expressions like "a or b" won’t split and recurse until RecursionError. Split using compiled regexes and check len(parts) > 1.

Apply this diff:

-            # Handle logical connectors (OR and AND) before operator dispatch
-            # Check for OR operators first (|| or 'or')
-            for splitter, combiner in [('||', any), (' or ', any), ('&&', all), (' and ', all)]:
-                # Use case-insensitive search for word-based operators
-                if splitter in ['||', '&&']:
-                    check = splitter in normalized
-                else:
-                    check = re.search(rf'\s+{re.escape(splitter.strip())}\s+', normalized, re.IGNORECASE)
-                
-                if check:
-                    parts = [part.strip() for part in re.split(rf'\s*(?:{re.escape(splitter)})\s*', normalized, flags=re.IGNORECASE) if part.strip()]
-                    return combiner(self._evaluate_dsl(part, target_version) for part in parts)
+            # Handle logical connectors with robust splitting (AND has higher precedence)
+            connectors = [
+                (re.compile(r'\s*\|\|\s*'), any),
+                (re.compile(r'\s*\bOR\b\s*', re.IGNORECASE), any),
+            ]
+            for regex, combiner in connectors:
+                parts = [p.strip() for p in regex.split(normalized) if p.strip()]
+                if len(parts) > 1:
+                    return combiner(self._evaluate_dsl(part, target_version) for part in parts)
+            # AND after OR to preserve precedence within each OR branch
+            connectors_and = [
+                (re.compile(r'\s*&&\s*'), all),
+                (re.compile(r'\s*\bAND\b\s*', re.IGNORECASE), all),
+            ]
+            for regex, combiner in connectors_and:
+                parts = [p.strip() for p in regex.split(normalized) if p.strip()]
+                if len(parts) > 1:
+                    return combiner(self._evaluate_dsl(part, target_version) for part in parts)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Handle logical connectors (OR and AND) before operator dispatch
# Check for OR operators first (|| or 'or')
for splitter, combiner in [('||', any), (' or ', any), ('&&', all), (' and ', all)]:
# Use case-insensitive search for word-based operators
if splitter in ['||', '&&']:
check = splitter in normalized
else:
check = re.search(rf'\s+{re.escape(splitter.strip())}\s+', normalized, re.IGNORECASE)
if check:
parts = [part.strip() for part in re.split(rf'\s*(?:{re.escape(splitter)})\s*', normalized, flags=re.IGNORECASE) if part.strip()]
return combiner(self._evaluate_dsl(part, target_version) for part in parts)
# Handle logical connectors with robust splitting (AND has higher precedence)
connectors = [
(re.compile(r'\s*\|\|\s*'), any),
(re.compile(r'\s*\bOR\b\s*', re.IGNORECASE), any),
]
for regex, combiner in connectors:
parts = [p.strip() for p in regex.split(normalized) if p.strip()]
if len(parts) > 1:
return combiner(self._evaluate_dsl(part, target_version) for part in parts)
# AND after OR to preserve precedence within each OR branch
connectors_and = [
(re.compile(r'\s*&&\s*'), all),
(re.compile(r'\s*\bAND\b\s*', re.IGNORECASE), all),
]
for regex, combiner in connectors_and:
parts = [p.strip() for p in regex.split(normalized) if p.strip()]
if len(parts) > 1:
return combiner(self._evaluate_dsl(part, target_version) for part in parts)
🤖 Prompt for AI Agents
In core/dsl_matcher.py around lines 85 to 97, the connector detection and
splitting are inconsistent: detection allows variable whitespace for textual
operators like " or " but the split pattern requires single spaces which can
leave strings unsplit and trigger deep recursion; replace ad-hoc checks with
precompiled regex patterns for each connector (OR: r'\s*\|\|\s*' and r'\s+or\s+'
and AND: r'\s*&&\s*' and r'\s+and\s+', all with re.IGNORECASE) use the same
regex both to test for a match and to split, and after splitting validate with
if len(parts) > 1 before recursing/combining to avoid accidental recursion.
Ensure parts are stripped and pass them to combiner(self._evaluate_dsl(part,
target_version) for part in parts).

Comment on lines +211 to +221
# Check if v1 < next minor version
if len(v2_parts) >= 2:
next_minor = v2_parts.copy()
next_minor[1] += 1
for i in range(2, len(next_minor)):
next_minor[i] = 0

return self._compare_version_parts(v1_parts, next_minor, '<')

return True

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

Tilde (~) semantics incorrect when only major is provided

When version2 has only a major segment (e.g., "~1"), the function returns True unconditionally. Use a sane bound (>=1.0.0 and <2.0.0) or return False.

Apply this diff to cap at next major:

-            # Check if v1 < next minor version
-            if len(v2_parts) >= 2:
+            # Check upper bound: next minor if minor present, else next major
+            if len(v2_parts) >= 2:
                 next_minor = v2_parts.copy()
                 next_minor[1] += 1
                 for i in range(2, len(next_minor)):
                     next_minor[i] = 0
                     
                 return self._compare_version_parts(v1_parts, next_minor, '<')
-                        
-            return True
+            else:
+                next_major = v2_parts.copy()
+                next_major[0] += 1
+                for i in range(1, len(next_major)):
+                    next_major[i] = 0
+                return self._compare_version_parts(v1_parts, next_major, '<')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Check if v1 < next minor version
if len(v2_parts) >= 2:
next_minor = v2_parts.copy()
next_minor[1] += 1
for i in range(2, len(next_minor)):
next_minor[i] = 0
return self._compare_version_parts(v1_parts, next_minor, '<')
return True
# Check upper bound: next minor if minor present, else next major
if len(v2_parts) >= 2:
next_minor = v2_parts.copy()
next_minor[1] += 1
for i in range(2, len(next_minor)):
next_minor[i] = 0
return self._compare_version_parts(v1_parts, next_minor, '<')
else:
next_major = v2_parts.copy()
next_major[0] += 1
for i in range(1, len(next_major)):
next_major[i] = 0
return self._compare_version_parts(v1_parts, next_major, '<')
🧰 Tools
🪛 Ruff (0.13.3)

220-220: Consider moving this statement to an else block

(TRY300)

🤖 Prompt for AI Agents
In core/dsl_matcher.py around lines 211 to 221, the tilde (~) handling currently
returns True when version2 has only a major (e.g., "~1"); change it to cap at
the next major (treat "~1" as >=1.0.0 and <2.0.0). Specifically, when
len(v2_parts) == 1, build next_major by copying v2_parts, incrementing the major
(next_major[0] += 1) and zeroing any remaining segments, then use
self._compare_version_parts(v1_parts, next_major, '<') instead of returning
True; keep existing behavior for the case with at least two segments.

@rxerium
Copy link
Author

rxerium commented Oct 19, 2025

Sorry for going AWOL, this is still on my to-do list.

I will create a new PR with the base branch being master instead of dev.

@rxerium rxerium closed this Oct 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants