Skip to content

Conversation

@Aarush289
Copy link

Proposed change

Introduce a fast, parallel pre-check stage that validates targets before scanning and drops invalid/bogus entries early.

Current behavior attempts socket connects on every port; for hostnames this triggers DNS lookups that can block for a long time on nonsense names. With multiple ports, bogus inputs can consume minutes and significant resources.

This PR:

  • Runs a lightweight, parallel target validation pass via resolve_quick(...):

    • Rejects obviously invalid IPv4/IPv6 literals (bad octets, syntax).
    • For hostnames, verifies label syntax and attempts a short-timeout A/AAAA resolve.
  • Skips the scan stage for rejected targets, preventing expensive connect/DNS fallbacks.

  • Canonicalizes valid targets (e.g., lowercases hostnames, strips trailing dot) and preserves input order.

  • Adds only a small up-front delay (seconds for large batches) while saving minutes on bad inputs.

Privacy safeguard (proxy mode):
If the user requests a proxy (e.g., -R or --socks-proxy), the pre-check is disabled to avoid any local DNS lookups that could expose the user’s IP to their ISP/resolver. In proxy mode, targets proceed directly to the scanning path (letting the proxy handle DNS), preserving user anonymity.

Examples dropped early now: 290.52.245.54 (invalid IPv4), google (no resolvable FQDN), random strings like dsjfiejb.

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
  • Other improvement (best practice, cleanup, optimization, etc)

Checklist

Additional context

  • Adds and uses resolve_quick(...) from app.py prior to scheduling scans.
  • Bounded concurrency (e.g., min(len(targets), 64)) to avoid spikes.
  • Logs dropped targets with reasons (invalid IP, DNS NXDOMAIN/timeout, bad label).
  • Automatically bypasses pre-check when -R/--socks-proxy is set to prevent local DNS leaks.

… not and changes are done accordingly in app.py to drop buggy target names . e.g. 465.54.543.35 , google , ajkbfdsv etc.
Signed-off-by: Aarush289 <[email protected]>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 24, 2025

Summary by CodeRabbit

  • New Features
    • Scan workflows now support parallel target validation with optional deduplication, improving preparation speed and data consistency.
    • Enhanced hostname and IP address resolution with timeout-safe DNS lookups, ensuring more reliable target processing and error handling.

Walkthrough

This pull request introduces target validation and hostname resolution utilities to the nettacker application. It adds a new module for DNS resolution with timeout handling, integrates parallel target validation into the scan workflow, and updates build configuration files.

Changes

Cohort / File(s) Summary
Build Configuration
.gitignore
Added three new entries (Public_sign, Public_sign.pub, cks_proxy) under the venv section.
Host Validation Utilities
nettacker/core/hostcheck.py
New module providing hostname and IP resolution utilities: IPv4/IPv6 validation, RFC 1123 hostname validation, DNS resolution with configurable timeout using ThreadPoolExecutor, host string normalization, and socket.getaddrinfo wrapper with AI_ADDRCONFIG flag support.
Scan Integration
nettacker/core/app.py
Added filter_valid_targets method to perform parallel target validation and deduplication using ThreadPoolExecutor. Modified scan_target_group to conditionally invoke target validation before scanning. Introduced imports for ThreadPoolExecutor, as_completed, and validation utilities from hostcheck module.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • DNS resolution timeout implementation: Verify correct ThreadPoolExecutor usage and timeout enforcement in resolve_quick
  • Hostname validation logic: Confirm RFC 1123 compliance and edge case handling (trailing dots, single-label domains, length constraints)
  • Parallel validation integration: Review conditional logic in scan_target_group and thread pool resource management
  • Error handling: Ensure socket and resolver errors are properly caught and handled across all new functions

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.75% 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
Title Check ✅ Passed The title "Improve robustness: canonicalize & validate targets before scan" directly and clearly summarizes the main change in the pull request. It captures the two primary objectives (canonicalization and validation of targets) and specifies when this occurs (before scan). The title is concise, specific, and provides a teammate scanning the history with a clear understanding of the primary change without being overly verbose or vague. The title aligns well with the substantial code additions across three files: the new hostcheck.py module with validation utilities, the filter_valid_targets method in app.py, and the .gitignore updates.
Description Check ✅ Passed The pull request description is directly related to the changeset and provides a detailed explanation of the problem being addressed (DNS lookups blocking on invalid targets, consuming excessive resources), the solution being implemented (parallel target validation with short timeouts), and key implementation details (canonicalization, privacy safeguards in proxy mode, bounded concurrency). The description accurately reflects the code changes visible in the raw_summary, particularly the new hostcheck.py module with hostname and IP resolution utilities and the filter_valid_targets method added to nettacker/core/app.py. The description includes substantive context about motivation, scope, and trade-offs.
✨ 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

@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: 5

🧹 Nitpick comments (6)
nettacker/core/hostcheck.py (4)

35-51: Tighten exception handling and clean up one-liners.

Catching bare Exception and using semicolons harms readability and linting. Catch OS errors only and expand statements.

 def _system_search_suffixes() -> list[str]:
@@
-    try:
-        with open("/etc/resolv.conf") as f:
-            for line in f:
-                line = line.strip()
-                if not line or line.startswith("#"): continue
-                if line.startswith("search") or line.startswith("domain"):
-                    sufs += [x for x in line.split()[1:] if x]
-    except Exception:
-        pass
-    seen = set(); out: list[str] = []
+    try:
+        with open("/etc/resolv.conf") as f:
+            for line in f:
+                line = line.strip()
+                if not line or line.startswith("#"):
+                    continue
+                if line.startswith("search") or line.startswith("domain"):
+                    sufs += [x for x in line.split()[1:] if x]
+    except OSError:
+        pass
+    seen: set[str] = set()
+    out: list[str] = []
     for s in sufs:
-        if s not in seen:
-            seen.add(s); out.append(s)
+        if s not in seen:
+            seen.add(s)
+            out.append(s)
     return out

13-21: Style and hints: remove semicolons; add basic docstrings.

  • Avoid multiple statements per line.
  • Add succinct docstrings for public APIs.
 def is_ip_literal(name: str) -> bool:
-    try:
-        socket.inet_pton(socket.AF_INET, name); return True
+    """Return True if name is a valid IPv4 or IPv6 address literal."""
+    try:
+        socket.inet_pton(socket.AF_INET, name)
+        return True
     except OSError:
         pass
     try:
-        socket.inet_pton(socket.AF_INET6, name); return True
+        socket.inet_pton(socket.AF_INET6, name)
+        return True
     except OSError:
         return False
@@
-def _gai_once(name: str, use_ai_addrconfig: bool, port):
+def _gai_once(name: str, use_ai_addrconfig: bool, port):
+    """Single getaddrinfo attempt with optional AI_ADDRCONFIG."""
     flags = getattr(socket, "AI_ADDRCONFIG", 0) if use_ai_addrconfig else 0
     return socket.getaddrinfo(
         name, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, flags
     )

Also applies to: 55-60


61-65: Add a docstring describing timeout/pass behavior.

Clarify that there are two passes (AI_ADDRCONFIG on/off) and total runtime can be up to ~2× timeout.

 def resolve_quick(host: str,
                   timeout_sec: float = 2.0,
                   try_search_suffixes: bool = True,
                   allow_single_label: bool = True) -> tuple[bool, str | None]:
+    """
+    Attempt a fast DNS-style resolution and return (ok, canonical_host).
+    Two passes are tried (AI_ADDRCONFIG on, then off). Each pass has its own
+    deadline of `timeout_sec`, so worst-case runtime ≈ 2 × timeout.
+    """

82-89: Minor cleanups: dedupe and one-liners.

Use dict.fromkeys for stable dedupe and avoid one-liners.

-    seen, uniq = set(), []
-    for c in candidates:
-        if c not in seen:
-            seen.add(c); uniq.append(c)
-    candidates = uniq
+    candidates = list(dict.fromkeys(candidates))
@@
-                        # cancel stragglers
-                        for p in pending: p.cancel()
+                        for p in pending:
+                            p.cancel()
@@
-            for f in fut2cand: f.cancel()
+            for f in fut2cand:
+                f.cancel()

Also applies to: 112-113, 119-119

nettacker/core/app.py (2)

346-352: PEP8: simplify condition; align call.

Use if not ...: and fix indentation.

-        if(not self.arguments.socks_proxy):
-            targets = self.filter_valid_targets(
-            targets,
-            timeout_per_target=2.0,
-            max_workers=self.arguments.parallel_module_scan or None,
-            dedupe=True,
-        )
+        if not self.arguments.socks_proxy:
+            targets = self.filter_valid_targets(
+                targets,
+                timeout_per_target=2.0,
+                max_workers=self.arguments.parallel_module_scan or None,
+                dedupe=True,
+            )

26-26: Import only what you use.

If is_ip_literal/valid_hostname are unused here, drop them to reduce surface and lint noise.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c538fa and aa994c5.

📒 Files selected for processing (2)
  • nettacker/core/app.py (4 hunks)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/app.py
  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/app.py
  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/app.py
  • nettacker/core/hostcheck.py
🧬 Code graph analysis (1)
nettacker/core/app.py (1)
nettacker/core/hostcheck.py (3)
  • resolve_quick (61-120)
  • is_ip_literal (13-21)
  • valid_hostname (23-33)
🪛 Ruff (0.14.1)
nettacker/core/app.py

302-302: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


302-302: Avoid specifying long messages outside the exception class

(TRY003)


322-322: Do not catch blind exception: Exception

(BLE001)

nettacker/core/hostcheck.py

15-15: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


19-19: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


42-42: Multiple statements on one line (colon)

(E701)


45-46: try-except-pass detected, consider logging the exception

(S110)


45-45: Do not catch blind exception: Exception

(BLE001)


47-47: Multiple statements on one line (semicolon)

(E702)


50-50: Multiple statements on one line (semicolon)

(E702)


64-64: Unused function argument: allow_single_label

(ARG001)


85-85: Multiple statements on one line (semicolon)

(E702)


90-90: Loop control variable pass_ix not used within loop body

Rename unused pass_ix to _pass_ix

(B007)


109-109: Abstract raise to an inner function

(TRY301)


109-109: Avoid specifying long messages outside the exception class

(TRY003)


112-112: Multiple statements on one line (colon)

(E701)


114-117: try-except-continue detected, consider logging the exception

(S112)


114-114: Do not catch blind exception: Exception

(BLE001)


114-114: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


119-119: Multiple statements on one line (colon)

(E701)

🔇 Additional comments (1)
nettacker/core/app.py (1)

310-343: Order-preserving dedupe is good; consider lowercase canonicalization at source.

filter_valid_targets keeps order and dedupes. Ensure resolve_quick lowercases hostnames so dedupe is case-insensitive (see hostcheck comment). No code change needed here if upstream is fixed.

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: 3

♻️ Duplicate comments (3)
nettacker/core/hostcheck.py (3)

62-62: Honor the allow_single_label parameter.

The allow_single_label parameter is declared but never checked (flagged by ruff ARG001). If allow_single_label=False, the function should reject single-label hostnames early.

Apply this diff to honor the parameter:

     else:
         # single label (e.g., "intranet")
+        if not allow_single_label:
+            return False, None
         if try_search_suffixes:

Also applies to: 72-78


111-111: Canonicalize hostname before returning.

Hostnames should be normalized to lowercase for consistent matching and deduplication downstream. The current return statement omits this canonicalization step.

Apply this diff:

                         chosen = fut2cand[fut]
                         # cancel stragglers
                         for p in pending: p.cancel()
-                        return True,  (chosen[:-1] if chosen.endswith(".") else chosen)
+                        canon = chosen[:-1] if chosen.endswith(".") else chosen
+                        return True, canon.lower()

88-118: Prevent ThreadPoolExecutor from blocking on early return.

Returning inside the with ThreadPoolExecutor(...) block triggers shutdown(wait=True), which blocks until all in-flight getaddrinfo calls finish. This negates the "first success wins" optimization and can add full timeout delays per candidate. Additionally, max_workers=len(candidates) can spawn excessive threads when resolve_quick is called concurrently from filter_valid_targets.

Refactor to manage the executor explicitly and call shutdown(wait=False, cancel_futures=True) before returning:

-    for pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
+    for _pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
         deadline = time.monotonic() + timeout_sec
-        with concurrent.futures.ThreadPoolExecutor(max_workers=len(candidates)) as ex:
-            fut2cand = {ex.submit(_gai_once, c, use_ai_addrconfig, port): c for c in candidates}
+        max_workers = min(len(candidates), 8)
+        ex = concurrent.futures.ThreadPoolExecutor(max_workers=max_workers)
+        try:
+            fut2cand = {ex.submit(_gai_once, c, use_ai_addrconfig, port): c for c in candidates}
             pending = set(fut2cand)
             while pending:
                 remaining = deadline - time.monotonic()
                 if remaining <= 0:
                     break
                 done, pending = concurrent.futures.wait(
                     pending, timeout=remaining,
                     return_when=concurrent.futures.FIRST_COMPLETED
                 )
                 for fut in done:
                     try:
                         res = fut.result()  # success if no exception
-                        # Optional: assert non-empty result
                         if not res:
-                            # treat as failure
-                            raise RuntimeError("empty getaddrinfo result")
+                            continue
                         chosen = fut2cand[fut]
-                        # cancel stragglers
-                        for p in pending: p.cancel()
-                        return True,  (chosen[:-1] if chosen.endswith(".") else chosen)
-                    except Exception as e:
-                        # TEMPORARY DEBUG LOGS — remove later
-                        # print(f"[pass {pass_ix}] {fut2cand[fut]} -> {type(e).__name__}: {e!s}")
+                        for p in pending:
+                            p.cancel()
+                        # non-blocking shutdown before returning
+                        try:
+                            ex.shutdown(wait=False, cancel_futures=True)
+                        except TypeError:  # Python <3.9 lacks cancel_futures
+                            ex.shutdown(wait=False)
+                        canon = chosen[:-1] if chosen.endswith(".") else chosen
+                        return True, canon.lower()
+                    except Exception:
                         continue
-            # cancel any survivors in this pass
-            for f in fut2cand: f.cancel()
+            for f in fut2cand:
+                f.cancel()
+        finally:
+            try:
+                ex.shutdown(wait=False, cancel_futures=True)
+            except TypeError:
+                ex.shutdown(wait=False)

This also renames pass_ix_pass_ix (unused) and removes the unused exception variable e.

🧹 Nitpick comments (2)
nettacker/core/hostcheck.py (2)

33-49: Consider adding a docstring and splitting multi-statement lines.

While private, a brief docstring would clarify the function's purpose (parsing /etc/resolv.conf for DNS search suffixes). Additionally, splitting the semicolon-joined statements on lines 40, 45, and 48 would align with the project's style guidelines.

Example diff for docstring:

 def _system_search_suffixes() -> list[str]:
+    """Parse /etc/resolv.conf and return DNS search suffixes."""
     # Only used when host has no dots; mirrors OS resolver search behavior (UNIX).

53-57: Consider adding a docstring.

A brief docstring would clarify the wrapper's purpose and the role of the use_ai_addrconfig parameter.

Example:

 def _gai_once(name: str, use_ai_addrconfig: bool, port):
+    """Wrap socket.getaddrinfo with optional AI_ADDRCONFIG flag."""
     flags = getattr(socket, "AI_ADDRCONFIG", 0) if use_ai_addrconfig else 0
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa994c5 and ed7c81e.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

15-15: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


19-19: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


40-40: Multiple statements on one line (colon)

(E701)


43-44: try-except-pass detected, consider logging the exception

(S110)


43-43: Do not catch blind exception: Exception

(BLE001)


45-45: Multiple statements on one line (semicolon)

(E702)


48-48: Multiple statements on one line (semicolon)

(E702)


62-62: Unused function argument: allow_single_label

(ARG001)


83-83: Multiple statements on one line (semicolon)

(E702)


88-88: Loop control variable pass_ix not used within loop body

Rename unused pass_ix to _pass_ix

(B007)


107-107: Abstract raise to an inner function

(TRY301)


107-107: Avoid specifying long messages outside the exception class

(TRY003)


110-110: Multiple statements on one line (colon)

(E701)


112-115: try-except-continue detected, consider logging the exception

(S112)


112-112: Do not catch blind exception: Exception

(BLE001)


112-112: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


117-117: Multiple statements on one line (colon)

(E701)

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

♻️ Duplicate comments (4)
nettacker/core/hostcheck.py (4)

13-21: Add docstring and refactor semicolon statements.

The missing docstring and semicolon-joined statements flagged in the previous review remain unaddressed.


23-33: Add docstring for public API.

The missing docstring flagged in the previous review remains unaddressed.


61-66: Add docstring for public API.

The missing docstring flagged in the previous review remains unaddressed.


94-122: Early-return still blocks on executor threads; restructure required.

The ThreadPoolExecutor context manager issue flagged in the previous review remains unaddressed. Returning inside with ThreadPoolExecutor(...) triggers shutdown(wait=True), negating the "first-success wins" optimization and causing the function to wait for all in-flight getaddrinfo calls. Additionally, using max_workers=len(candidates) can spawn excessive threads.

🧹 Nitpick comments (1)
nettacker/core/hostcheck.py (1)

35-51: Consider refactoring semicolon statements for readability.

Lines 42, 47, and 50 use semicolons to join statements. While functional, splitting them improves readability.

-                if not line or line.startswith("#"): continue
+                if not line or line.startswith("#"):
+                    continue
                 if line.startswith("search") or line.startswith("domain"):
                     sufs += [x for x in line.split()[1:] if x]
     except Exception:
         pass
-    seen = set(); out: list[str] = []
+    seen = set()
+    out: list[str] = []
     for s in sufs:
         if s not in seen:
-            seen.add(s); out.append(s)
+            seen.add(s)
+            out.append(s)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed7c81e and ccd870e.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

15-15: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


19-19: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


42-42: Multiple statements on one line (colon)

(E701)


45-46: try-except-pass detected, consider logging the exception

(S110)


45-45: Do not catch blind exception: Exception

(BLE001)


47-47: Multiple statements on one line (semicolon)

(E702)


50-50: Multiple statements on one line (semicolon)

(E702)


89-89: Multiple statements on one line (semicolon)

(E702)


94-94: Loop control variable pass_ix not used within loop body

Rename unused pass_ix to _pass_ix

(B007)


112-112: Abstract raise to an inner function

(TRY301)


112-112: Avoid specifying long messages outside the exception class

(TRY003)


115-115: Multiple statements on one line (colon)

(E701)


118-119: try-except-continue detected, consider logging the exception

(S112)


118-118: Do not catch blind exception: Exception

(BLE001)


118-118: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


121-121: Multiple statements on one line (colon)

(E701)

🔇 Additional comments (1)
nettacker/core/hostcheck.py (1)

55-59: LGTM!

Clean wrapper for getaddrinfo with configurable AI_ADDRCONFIG flag.

Signed-off-by: Aarush289 <[email protected]>
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: 0

♻️ Duplicate comments (4)
nettacker/core/hostcheck.py (4)

13-21: Add docstring for public API.

Per coding guidelines, public APIs in the nettacker package require docstrings. This was flagged in a previous review but remains unaddressed.

Apply this diff to add a docstring and improve readability:

 def is_ip_literal(name: str) -> bool:
+    """Return True if name is a valid IPv4 or IPv6 address literal."""
     try:
-        socket.inet_pton(socket.AF_INET, name); return True
+        socket.inet_pton(socket.AF_INET, name)
+        return True
     except OSError:
         pass
     try:
-        socket.inet_pton(socket.AF_INET6, name); return True
+        socket.inet_pton(socket.AF_INET6, name)
+        return True
     except OSError:
         return False

As per coding guidelines


30-32: Remove debug print statement.

Line 31 contains print("itegb") which pollutes production output. This critical issue was flagged in a previous review but remains unaddressed.

Remove the debug code:

     if len(parts) < 2 and not allow_single_label:
-        # log.warn("Its a name like google")
-        print("itegb")
         return False

23-33: Add docstring for public API.

Per coding guidelines, public APIs in the nettacker package require docstrings. This was flagged in a previous review but remains unaddressed.

Apply this diff:

 def valid_hostname(host: str, allow_single_label: bool = False) -> bool:
+    """
+    Validate hostname syntax per RFC 1123.
+    
+    Args:
+        host: Hostname to validate.
+        allow_single_label: If True, accept single-label names (e.g., "localhost").
+    
+    Returns:
+        True if the hostname is syntactically valid.
+    """
     if len(host) > 253:

As per coding guidelines


105-133: Early-return still blocks on executor threads; restructure and cap inner concurrency.

Returning inside a with ThreadPoolExecutor(...) triggers shutdown(wait=True) and waits for all in-flight getaddrinfo calls. This negates "first-success wins" and can add full timeouts per candidate. Also, spinning len(candidates) threads per resolve risks thread explosion when called in parallel from filter_valid_targets. This critical architectural issue was flagged in a previous review but remains unaddressed.

Refactor to:

  • Instantiate the executor explicitly and call shutdown(wait=False, cancel_futures=True) before returning.
  • Cap inner max_workers to a small value (e.g., 4–8) to bound threads.
  • Rename unused pass_ix to _pass_ix.
  • Remove unused exception variable e.

Apply:

-    for pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
+    for _pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
         deadline = time.monotonic() + timeout_sec
-        with concurrent.futures.ThreadPoolExecutor(max_workers=len(candidates)) as ex:
-            fut2cand = {ex.submit(_gai_once, c, use_ai_addrconfig, port): c for c in candidates}
+        maxw = min(len(candidates), 8)
+        ex = concurrent.futures.ThreadPoolExecutor(max_workers=maxw)
+        try:
+            fut2cand = {ex.submit(_gai_once, c, use_ai_addrconfig, port): c for c in candidates}
             pending = set(fut2cand)
             while pending:
                 remaining = deadline - time.monotonic()
                 if remaining <= 0:
                     break
                 done, pending = concurrent.futures.wait(
                     pending, timeout=remaining,
                     return_when=concurrent.futures.FIRST_COMPLETED
                 )
                 for fut in done:
                     try:
-                        res = fut.result()  # success if no exception
-                        # Optional: assert non-empty result
+                        res = fut.result()
                         if not res:
-                            # treat as failure
-                            raise RuntimeError("empty getaddrinfo result")
+                            continue
                         chosen = fut2cand[fut]
-                        # cancel stragglers
-                        for p in pending: p.cancel()
+                        for p in pending:
+                            p.cancel()
+                        try:
+                            ex.shutdown(wait=False, cancel_futures=True)
+                        except TypeError:  # Python <3.9
+                            ex.shutdown(wait=False)
                         canon = chosen[:-1] if chosen.endswith(".") else chosen
                         return True, canon.lower()
-                    except Exception as e:
+                    except Exception:
                         continue
-            # cancel any survivors in this pass
-            for f in fut2cand: f.cancel()
+            for f in fut2cand:
+                f.cancel()
+        finally:
+            try:
+                ex.shutdown(wait=False, cancel_futures=True)
+            except TypeError:  # Python <3.9
+                ex.shutdown(wait=False)
     return False, None
🧹 Nitpick comments (1)
nettacker/core/hostcheck.py (1)

35-51: Consider splitting semicolon-joined statements for consistency.

The logic correctly extracts and deduplicates search suffixes from /etc/resolv.conf. The blind exception handling on line 45 is appropriate since the file may not exist on all platforms.

Optionally, split semicolon-joined statements for better consistency with project style:

                 if not line or line.startswith("#"): continue
+                if not line or line.startswith("#"):
+                    continue
     seen = set(); out: list[str] = []
+    seen = set()
+    out: list[str] = []
     for s in sufs:
         if s not in seen:
-            seen.add(s); out.append(s)
+            seen.add(s)
+            out.append(s)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccd870e and b5da801.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

15-15: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


19-19: Multiple statements on one line (semicolon)

(E702)


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

(TRY300)


42-42: Multiple statements on one line (colon)

(E701)


45-46: try-except-pass detected, consider logging the exception

(S110)


45-45: Do not catch blind exception: Exception

(BLE001)


47-47: Multiple statements on one line (semicolon)

(E702)


50-50: Multiple statements on one line (semicolon)

(E702)


100-100: Multiple statements on one line (semicolon)

(E702)


105-105: Loop control variable pass_ix not used within loop body

Rename unused pass_ix to _pass_ix

(B007)


123-123: Abstract raise to an inner function

(TRY301)


123-123: Avoid specifying long messages outside the exception class

(TRY003)


126-126: Multiple statements on one line (colon)

(E701)


129-130: try-except-continue detected, consider logging the exception

(S112)


129-129: Do not catch blind exception: Exception

(BLE001)


129-129: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


132-132: Multiple statements on one line (colon)

(E701)

🔇 Additional comments (3)
nettacker/core/hostcheck.py (3)

1-12: LGTM!

Imports and module constants are appropriate. The _LABEL regex correctly validates DNS labels per RFC 1123.


55-59: LGTM!

The wrapper correctly applies AI_ADDRCONFIG conditionally, with appropriate fallback handling via getattr.


79-103: Candidate-building logic is sound.

The hostname candidate generation correctly handles both dotted and single-label names, with appropriate suffix expansion and deduplication.

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

♻️ Duplicate comments (2)
nettacker/core/app.py (1)

296-309: Wrap long signature and fix exception raising style.

This is a duplicate of a previous review comment. The signature exceeds the 99-char limit and the TypeError should be raised with from None to satisfy B904.

Apply this diff:

-    def filter_valid_targets(self, targets, timeout_per_target=2.0, max_workers=None, dedupe=True):
+    def filter_valid_targets(
+        self,
+        targets,
+        timeout_per_target: float = 2.0,
+        max_workers: int | None = None,
+        dedupe: bool = True,
+    ) -> list[str]:
         """
         Parallel validation of targets via resolve_quick(target, timeout_sec).
         Returns a list of canonical targets (order preserved, invalids removed).
         """
         # Ensure it's a concrete list (len, indexing OK)
         try:
             targets = list(targets)
         except TypeError:
-            raise TypeError(f"`targets` must be iterable, got {type(targets).__name__}")
+            raise TypeError(
+                f"`targets` must be iterable, got {type(targets).__name__}"
+            ) from None
nettacker/core/hostcheck.py (1)

45-46: Remove debug print statement.

This is a duplicate of a previous critical issue. The print("itegb") statement on line 46 is leftover debug code that pollutes production output and must be removed.

Apply this diff:

     if len(parts) < 2 and not allow_single_label:
-        # log.warn("Its a name like google")
-        print("itegb")
         return False
🧹 Nitpick comments (8)
nettacker/core/app.py (3)

26-26: Remove unused imports.

is_ip_literal and valid_hostname are imported but never used in this file. Only resolve_quick is actually called.

Apply this diff:

-from nettacker.core.hostcheck import resolve_quick, is_ip_literal, valid_hostname
+from nettacker.core.hostcheck import resolve_quick

210-210: Use professional, i18n-compatible log messages.

The log message "Process started!!" is informal. Consider using the existing internationalization pattern with a message key (e.g., _("process_started")), consistent with other logs in this file like _("scan_started") on line 47.

Apply this diff:

-        log.info("Process started!!")
+        log.info(_("process_started"))

(Similarly for "Process done!!" on lines 219 and 226.)


332-332: Consider using debug level for invalid target logs.

Logging each invalid target at info level could be noisy when processing many invalid targets. Consider using log.debug() instead, or add a summary count at info level after processing.

-                    log.info(f"Invalid target -> dropping: {orig_target}")
+                    log.debug(f"Invalid target -> dropping: {orig_target}")
nettacker/core/hostcheck.py (5)

57-57: Split multiple statements on one line.

Line 57 combines conditional and continue with a colon separator. Per Ruff E701, place continue on a separate line for readability.

-                if not line or line.startswith("#"): continue
+                if not line or line.startswith("#"):
+                    continue

62-65: Split semicolon-joined statements.

Lines 62 and 65 use semicolons to join statements on one line. Per coding guidelines (E702), split these onto separate lines for better readability.

-    seen = set(); out: list[str] = []
+    seen = set()
+    out: list[str] = []
     for s in sufs:
         if s not in seen:
-            seen.add(s); out.append(s)
+            seen.add(s)
+            out.append(s)

115-115: Split semicolon-joined statements.

Line 115 uses a semicolon to join two statements. Per coding guidelines (E702), split onto separate lines.

-            seen.add(c); uniq.append(c)
+            seen.add(c)
+            uniq.append(c)

120-120: Rename unused loop variable.

The loop variable pass_ix is not used within the loop body. Per Ruff B007, prefix with underscore to indicate it's intentionally unused.

-    for pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
+    for _pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):

150-151: Consider logging failed resolution attempts.

The bare Exception catch on line 150 silently continues, which might hide unexpected errors during development. Consider using log.debug() to capture failure details while maintaining resilience.

                     except Exception:
+                        log.debug(f"Resolution failed for candidate {fut2cand.get(fut)}: {exc!s}")
                         continue

Note: This requires importing the logger and binding the exception (e.g., except Exception as exc:).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5da801 and b66a598.

📒 Files selected for processing (2)
  • nettacker/core/app.py (6 hunks)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/app.py
  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/app.py
  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/app.py
  • nettacker/core/hostcheck.py
🧬 Code graph analysis (1)
nettacker/core/app.py (1)
nettacker/core/hostcheck.py (3)
  • resolve_quick (76-161)
  • is_ip_literal (13-24)
  • valid_hostname (26-48)
🪛 Ruff (0.14.1)
nettacker/core/app.py

305-305: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


305-305: Avoid specifying long messages outside the exception class

(TRY003)

nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)


57-57: Multiple statements on one line (colon)

(E701)


60-61: try-except-pass detected, consider logging the exception

(S110)


60-60: Do not catch blind exception: Exception

(BLE001)


62-62: Multiple statements on one line (semicolon)

(E702)


65-65: Multiple statements on one line (semicolon)

(E702)


115-115: Multiple statements on one line (semicolon)

(E702)


120-120: Loop control variable pass_ix not used within loop body

Rename unused pass_ix to _pass_ix

(B007)


150-151: try-except-continue detected, consider logging the exception

(S112)


150-150: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (2)
nettacker/core/hostcheck.py (2)

70-74: LGTM: Portable DNS lookup helper.

The getattr fallback for AI_ADDRCONFIG ensures compatibility with platforms that don't support this flag. Clean implementation.


144-160: Well done: Non-blocking executor shutdown.

The explicit non-blocking shutdown with wait=False, cancel_futures=True (with Python 3.8 fallback) correctly addresses the previous review concern about blocking on in-flight DNS queries. This ensures the "first success wins" behavior works as intended.

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: 0

♻️ Duplicate comments (1)
nettacker/core/hostcheck.py (1)

140-140: Fix indentation to 4 spaces.

Line 140 appears to have inconsistent indentation (11 spaces instead of 12). Ensure consistent 4-space indentation.

Apply this diff:

                         chosen = fut2cand[fut]
                         for p in pending:
-                           p.cancel()
+                            p.cancel()

Based on coding guidelines (use 4-space indents).

🧹 Nitpick comments (3)
nettacker/core/hostcheck.py (3)

48-64: Refactor semicolons for readability and document platform limitation.

The function logic is correct, but several style issues reduce readability:

  1. Lines 55, 60, 63: Multiple statements on one line with semicolons make the code harder to read.
  2. Unix-only implementation: The function only works on Unix-like systems with /etc/resolv.conf. On Windows, it silently returns an empty list, which may cause single-label hostnames to fail resolution unexpectedly.

Apply this diff to improve readability:

             for line in f:
                 line = line.strip()
-                if not line or line.startswith("#"): continue
+                if not line or line.startswith("#"):
+                    continue
                 if line.startswith("search") or line.startswith("domain"):
                     sufs += [x for x in line.split()[1:] if x]
     except Exception:
         pass
-    seen = set(); out: list[str] = []
+    seen = set()
+    out: list[str] = []
     for s in sufs:
         if s not in seen:
-            seen.add(s); out.append(s)
+            seen.add(s)
+            out.append(s)
     return out

Consider documenting the Unix-only behavior in a docstring or comment, or implement Windows support using platform-specific DNS configuration sources.


118-118: Rename unused loop variable.

The loop control variable pass_ix is not used within the loop body. Rename it to _pass_ix to indicate it's intentionally unused.

Apply this diff:

-    for pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
+    for _pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):

Based on coding guidelines (follow Python conventions and static analysis hints).


148-149: Narrow exception handling to avoid masking errors.

The bare except Exception: on lines 148-149 catches all exceptions from fut.result(), which could inadvertently suppress unexpected errors. Consider catching specific exceptions like socket.gaierror or OSError that getaddrinfo raises, or at minimum add a comment explaining why broad catching is necessary.

Apply this diff to narrow the exception handling:

                     try:
                         res = fut.result() 
                         if not res:
                             continue
                         chosen = fut2cand[fut]
                         for p in pending:
                             p.cancel()
                         # ensure we don't wait on the executor shutdown
                         try:
                             ex.shutdown(wait=False, cancel_futures=True)
                         except TypeError:  # Py<3.9
                             ex.shutdown(wait=False)
                         canon = chosen[:-1] if chosen.endswith(".") else chosen
                         return True, canon.lower()
-                    except Exception:
+                    except (OSError, socket.gaierror):
+                        # DNS resolution failed for this candidate, try next
                         continue
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b66a598 and a83c17f.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)


55-55: Multiple statements on one line (colon)

(E701)


58-59: try-except-pass detected, consider logging the exception

(S110)


58-58: Do not catch blind exception: Exception

(BLE001)


60-60: Multiple statements on one line (semicolon)

(E702)


63-63: Multiple statements on one line (semicolon)

(E702)


113-113: Multiple statements on one line (semicolon)

(E702)


118-118: Loop control variable pass_ix not used within loop body

Rename unused pass_ix to _pass_ix

(B007)


148-149: try-except-continue detected, consider logging the exception

(S112)


148-148: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (5)
nettacker/core/hostcheck.py (5)

1-12: LGTM: Imports and regex pattern are appropriate.

The imports and hostname label regex pattern are well-structured. The _LABEL pattern correctly implements RFC 1123 label validation.


13-24: LGTM: IP literal validation is correct.

The function correctly validates both IPv4 and IPv6 address literals using socket.inet_pton. Docstring is clear and complete.


26-46: LGTM: Hostname validation correctly implements RFC 1123.

The validation logic is sound with proper length checks, trailing dot handling, and label validation. Docstring is complete and clear.


68-72: LGTM: Helper function correctly wraps getaddrinfo.

The function safely handles the optional AI_ADDRCONFIG flag and correctly passes parameters to socket.getaddrinfo.


74-159: Well-implemented concurrent DNS resolution with proper cleanup.

The function correctly implements timeout-aware concurrent DNS resolution with several good practices:

  • Bounded concurrency (max 4 workers) prevents thread explosion.
  • Deadline-based timeout enforcement across all candidates.
  • Non-blocking executor shutdown on success and in cleanup.
  • Proper future cancellation to avoid waiting for stragglers.
  • Canonical hostname normalization (lowercase, no trailing dot).
  • Python 3.7-3.8 compatibility with TypeError handling.

The two-pass approach (with/without AI_ADDRCONFIG) provides good fallback behavior.

Signed-off-by: Aarush289 <[email protected]>
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 (6)
nettacker/core/hostcheck.py (6)

113-117: Remove semicolon; follow ruff E702 and project style.

-    for c in candidates:
-        if c not in seen:
-            seen.add(c); uniq.append(c)
+    for c in candidates:
+        if c not in seen:
+            seen.add(c)
+            uniq.append(c)

77-95: Clarify/enforce timeout semantics (per-pass vs total).

Current code applies timeout per pass, but the docstring reads like a total budget. Choose one:

  • Option A (update docstring to “per pass”).
  • Option B (enforce a single total deadline across both passes — recommended).

I’m including a minimal diff for Option B.

 def resolve_quick(
@@
-    Args:
+    Args:
         host: Hostname or IP literal to resolve.
-        timeout_sec: Maximum time to wait for resolution.
+        timeout_sec: Maximum total time budget across both passes.
@@
-    for _pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
-        deadline = time.monotonic() + timeout_sec
+    deadline_total = time.monotonic() + timeout_sec
+    for _pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
@@
-            while pending:
-                remaining = deadline - time.monotonic()
+            while pending:
+                remaining = deadline_total - time.monotonic()
                 if remaining <= 0:
                     break

95-112: Fast-path IP literals to avoid unnecessary DNS queries.

     candidates: list[str] = []
+    # Fast-path: numeric IP literals don't need DNS
+    if is_ip_literal(host):
+        return True, host

48-60: Narrow exception handling for resolv.conf parsing; optional debug.

Catching bare Exception and silently passing hides real errors. Limit to expected I/O/decoding issues; optionally add a debug log.

-    try:
-        with open("/etc/resolv.conf") as f:
+    try:
+        with open("/etc/resolv.conf") as f:
@@
-    except Exception:
-        pass
+    except (OSError, UnicodeDecodeError):
+        # Optional: add debug log if module logger is available
+        # logger.debug("Could not read /etc/resolv.conf for search suffixes", exc_info=True)
+        pass

69-69: Remove leftover meta comment.

Looks like an internal note from a refactor. Safe to delete.

-# --- safer, more robust pieces to replace in hostcheck.py ---

121-121: Drop enumerate since index is unused.

Slight cleanup; avoids unnecessary tuple unpacking.

-    for _pass_ix, (use_ai_addrconfig, port) in enumerate(((True, None), (False, None))):
+    for (use_ai_addrconfig, port) in ((True, None), (False, None)):
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a83c17f and d852609.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)


59-60: try-except-pass detected, consider logging the exception

(S110)


59-59: Do not catch blind exception: Exception

(BLE001)


116-116: Multiple statements on one line (semicolon)

(E702)

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 (3)
nettacker/core/hostcheck.py (3)

13-24: Optional: Flatten exception handling with else blocks.

The current pattern is clear and correct. However, ruff suggests using else blocks after except to make the control flow more explicit (lines 17, 22). This is purely stylistic.

Apply this diff if you prefer the explicit else pattern:

 def is_ip_literal(name: str) -> bool:
     """Return True if name is a valid IPv4 or IPv6 address literal."""
     try:
         socket.inet_pton(socket.AF_INET, name)
-        return True
     except OSError:
-        pass
-    try:
-        socket.inet_pton(socket.AF_INET6, name)
-        return True
-    except OSError:
-        return False
+        try:
+            socket.inet_pton(socket.AF_INET6, name)
+        except OSError:
+            return False
+        else:
+            return True
+    else:
+        return True

48-67: Note Unix-specific behavior; consider Windows compatibility.

This function only works on Unix-like systems that have /etc/resolv.conf. On Windows, it will silently return an empty list (caught by the exception handler). If Nettacker needs to support suffix search on Windows, you would need to query the registry or use socket.getfqdn() behavior.

If Windows support is needed, consider using platform-specific logic:

def _system_search_suffixes() -> list[str]:
    sufs: list[str] = []
    if sys.platform == "win32":
        # Windows: could query registry or use alternative approach
        pass
    else:
        # Unix: read /etc/resolv.conf
        try:
            with open("/etc/resolv.conf") as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith("#"): 
                        continue
                    if line.startswith("search") or line.startswith("domain"):
                        sufs += [x for x in line.split()[1:] if x]
        except Exception:
            pass
    # deduplicate...

77-163: Consider short-circuiting for IP literals.

If host is already an IP address, calling getaddrinfo is unnecessary overhead. Since the module exports is_ip_literal, consider checking it first and returning immediately. This avoids spawning threads and DNS timeouts for IP literals.

Apply this diff:

 def resolve_quick(
         host: str,
         timeout_sec: float = 2.0,
         try_search_suffixes: bool = True,
         allow_single_label: bool = True
 ) -> tuple[bool, str | None]:
     """
     Perform fast DNS resolution with timeout and suffix search.
     
     Args:
         host: Hostname or IP literal to resolve.
         timeout_sec: Maximum time to wait for resolution.
         try_search_suffixes: If True, append /etc/resolv.conf search suffixes for single-label hosts.
         allow_single_label: If True, allow single-label hostnames (e.g., "intranet").
     
     Returns:
         (True, canonical_hostname) on success, (False, None) on failure/timeout.
     """
+    # Short-circuit if already an IP literal
+    if is_ip_literal(host):
+        return True, host.lower()
+    
     candidates: list[str] = []
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d852609 and 4de4cca.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)


59-60: try-except-pass detected, consider logging the exception

(S110)


59-59: Do not catch blind exception: Exception

(BLE001)


116-116: Multiple statements on one line (semicolon)

(E702)

🔇 Additional comments (2)
nettacker/core/hostcheck.py (2)

26-46: LGTM!

The hostname validation logic is correct, follows RFC 1123, and properly handles trailing dots before length validation. The docstring clearly documents the parameters and behavior.


71-75: LGTM!

Clean wrapper around getaddrinfo with proper handling of the optional AI_ADDRCONFIG flag.

Signed-off-by: Aarush289 <[email protected]>
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: 0

♻️ Duplicate comments (1)
nettacker/core/hostcheck.py (1)

39-42: Length check before trimming breaks RFC compliance (edge case).

The length validation (line 39) occurs before trimming the trailing dot (lines 41-42), incorrectly rejecting valid FQDNs. For example, a hostname of exactly 253 characters with a trailing dot (254 total) is valid but will be rejected before the dot is trimmed.

Apply this diff to check length after trimming:

-    if len(host) > 253:
-        return False
     if host.endswith("."):
         host = host[:-1]
+    if len(host) > 253:
+        return False
🧹 Nitpick comments (1)
nettacker/core/hostcheck.py (1)

48-67: Consider logging exceptions for troubleshooting.

The broad exception handler (lines 59-60) silently ignores errors when reading /etc/resolv.conf. While this is acceptable for an optional configuration file, logging exceptions would help diagnose issues like permission problems or file corruption during troubleshooting.

Optionally, add logging:

+    import logging
+    logger = logging.getLogger(__name__)
+
     try:
         with open("/etc/resolv.conf") as f:
             ...
-    except Exception:
+    except Exception as e:
+        logger.debug(f"Could not read /etc/resolv.conf: {e}")
         pass
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4de4cca and c472e71.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🧠 Learnings (1)
📚 Learning: 2025-10-25T11:31:26.476Z
Learnt from: Aarush289
PR: OWASP/Nettacker#1155
File: nettacker/core/hostcheck.py:121-122
Timestamp: 2025-10-25T11:31:26.476Z
Learning: In nettacker/core/hostcheck.py, the resolve_quick() function intentionally applies timeout_sec per resolution pass (not cumulatively), allowing up to 2× timeout_sec total execution time across both AI_ADDRCONFIG passes by design.

Applied to files:

  • nettacker/core/hostcheck.py
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)


59-60: try-except-pass detected, consider logging the exception

(S110)


59-59: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (3)
nettacker/core/hostcheck.py (3)

1-12: LGTM!

The imports and regex pattern for hostname label validation are appropriate and correctly implement RFC 1123 syntax requirements.


13-24: LGTM!

The function correctly validates IPv4/IPv6 address literals using socket.inet_pton. Docstring is clear, and previous style issues have been resolved.


71-164: LGTM! Significant improvements from previous review.

The resolution logic is well-structured:

  • Candidate building correctly handles dotted/single-label hostnames and search suffixes
  • Explicit executor management with non-blocking shutdown prevents blocking on in-flight DNS calls
  • Bounded concurrency (maxw = min(len(candidates), 4)) prevents thread explosion
  • Per-pass timeout is intentional and allows fallback without AI_ADDRCONFIG
  • Specific exception handling for DNS errors is appropriate

All major issues flagged in previous reviews have been addressed.

# Only used when host has no dots; mirrors OS resolver search behavior (UNIX).
sufs: list[str] = []
try:
with open("/etc/resolv.conf") as f:
Copy link
Collaborator

Choose a reason for hiding this comment

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

@Aarush289 using /etc/resolv.conf here is not acceptable. This will not work on Windows and other non-Linux platforms and we have just spent lots of effort making Nettacker platform-independent

@securestep9
Copy link
Collaborator

Can you also provide justification/reasoning behind this proposed PR. Nettacker mainly operates on Networks (as the name suggests) using IP ranges and subdomain discovery. While theoretically it can be fed whole bunch of unresolvable hostnames as targets our standard recommended way of validating them is to use ping_before_scan option which will filter out non-pingable hosts. I suggest that you re-work your proposal as an option e.g. validate_before_scan rather than modification of the core functionality. This way the users will have a choice to turn the validation on or off

@Aarush289
Copy link
Author

Hi @securestep9 ,

Thank you for the thoughtful review and for pointing out the OS-specific concerns and the risk of changing default behavior. You’re absolutely right—this should be an opt-in feature rather than a core change.

A few notes on my original motivation:

  • While ping_before_scan is useful, ICMP reachability can be an unreliable filter in practice: many networks drop ICMP for policy/privacy, and transient host issues can cause false negatives.
  • A DNS reachability check (e.g., resolving A/AAAA) can often be a more robust pre-filter for typical Nettacker workflows that start from domain names. (Of course, it’s not infallible—split-horizon DNS, stale cache, or transient resolver issues can still occur—but it generally avoids the ICMP-blocked case and is a more reliable option .)

As Per your guidance, I’ll rework the PR as an optional feature rather than modifying core flow. Concretely, I plan to:

  1. Add a new flag (name as you suggested): --validate_before_scan
  2. Make the implementation cross-platform (no OS-specific calls), using stdlib wherever possible and clean fallbacks.

Thank you again for the clear direction and for flagging the Windows issue—totally agree it shouldn’t be OS-specific. I’ll incorporate your feedback and resubmit as an opt-in feature with proper documentation.

Best,
Aarush

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: 3

♻️ Duplicate comments (2)
nettacker/core/app.py (2)

293-306: Fix type error raising style and wrap the long signature.

Two issues remain from previous review:

  1. Raise from None: Line 302 should raise with from None to satisfy B904 and keep tracebacks clean.
  2. Line length: The signature at line 293 exceeds the 99-character limit and should be wrapped.

Apply this diff:

-def filter_valid_targets(self, targets, timeout_per_target=2.0, max_workers=None, dedupe=True):
+def filter_valid_targets(
+    self,
+    targets,
+    timeout_per_target: float = 2.0,
+    max_workers: int | None = None,
+    dedupe: bool = True,
+) -> list[str]:
     """
     Parallel validation of targets via resolve_quick(target, timeout_sec).
     Returns a list of canonical targets (order preserved, invalids removed).
     """
     # Ensure it's a concrete list (len, indexing OK)
     try:
         targets = list(targets)
     except TypeError:
-        raise TypeError(f"`targets` must be iterable, got {type(targets).__name__}")
+        raise TypeError(
+            f"`targets` must be iterable, got {type(targets).__name__}"
+        ) from None

345-351: Fix formatting and indentation.

The formatting issues flagged in the previous review remain unaddressed:

  1. Line 345: Missing space after if keyword — should be if not instead of if(not.
  2. Lines 346-351: Inconsistent indentation in the function call.

Apply this diff:

-    if(not self.arguments.socks_proxy and self.arguments.validate_before_scan):
+    if not self.arguments.socks_proxy and self.arguments.validate_before_scan:
         targets = self.filter_valid_targets(
-        targets,
-        timeout_per_target=2.0,
-        max_workers=self.arguments.parallel_module_scan or None,
-        dedupe=True,
-    )
+            targets,
+            timeout_per_target=2.0,
+            max_workers=self.arguments.parallel_module_scan or None,
+            dedupe=True,
+        )
🧹 Nitpick comments (1)
nettacker/locale/ru.yaml (1)

35-35: Consider moving the entry next to related *_before_scan options for consistency.

The locale entry is functionally correct and the Russian translation is appropriate. However, it's inserted at line 35 (between error-related entries) rather than grouped with the similar ping_before_scan entry at line 134. Relocating it to maintain logical grouping of related feature flags would improve file maintainability and readability.

Consider moving this line to be adjacent to ping_before_scan at line 134 to keep related validation options grouped together.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb43add and c92c7f3.

📒 Files selected for processing (28)
  • .gitignore (1 hunks)
  • nettacker/config.py (1 hunks)
  • nettacker/core/app.py (4 hunks)
  • nettacker/core/arg_parser.py (1 hunks)
  • nettacker/core/hostcheck.py (1 hunks)
  • nettacker/locale/ar.yaml (1 hunks)
  • nettacker/locale/bn.yaml (1 hunks)
  • nettacker/locale/de.yaml (1 hunks)
  • nettacker/locale/el.yaml (1 hunks)
  • nettacker/locale/en.yaml (1 hunks)
  • nettacker/locale/es.yaml (1 hunks)
  • nettacker/locale/fa.yaml (1 hunks)
  • nettacker/locale/fr.yaml (1 hunks)
  • nettacker/locale/hi.yaml (1 hunks)
  • nettacker/locale/hy.yaml (1 hunks)
  • nettacker/locale/id.yaml (1 hunks)
  • nettacker/locale/it.yaml (1 hunks)
  • nettacker/locale/iw.yaml (1 hunks)
  • nettacker/locale/ja.yaml (1 hunks)
  • nettacker/locale/ko.yaml (1 hunks)
  • nettacker/locale/nl.yaml (1 hunks)
  • nettacker/locale/ps.yaml (1 hunks)
  • nettacker/locale/pt-br.yaml (1 hunks)
  • nettacker/locale/ru.yaml (1 hunks)
  • nettacker/locale/tr.yaml (1 hunks)
  • nettacker/locale/ur.yaml (1 hunks)
  • nettacker/locale/vi.yaml (1 hunks)
  • nettacker/locale/zh-cn.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • nettacker/locale/nl.yaml
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/app.py
  • nettacker/config.py
  • nettacker/core/hostcheck.py
  • nettacker/core/arg_parser.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/app.py
  • nettacker/config.py
  • nettacker/core/hostcheck.py
  • nettacker/core/arg_parser.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/app.py
  • nettacker/core/hostcheck.py
  • nettacker/core/arg_parser.py
nettacker/config.py

📄 CodeRabbit inference engine (AGENTS.md)

Manage defaults (API key, DB path, paths) in nettacker/config.py and review sensitive headers list before logging

Files:

  • nettacker/config.py
🧠 Learnings (1)
📚 Learning: 2025-10-25T11:31:26.498Z
Learnt from: Aarush289
PR: OWASP/Nettacker#1155
File: nettacker/core/hostcheck.py:121-122
Timestamp: 2025-10-25T11:31:26.498Z
Learning: In nettacker/core/hostcheck.py, the resolve_quick() function intentionally applies timeout_sec per resolution pass (not cumulatively), allowing up to 2× timeout_sec total execution time across both AI_ADDRCONFIG passes by design.

Applied to files:

  • nettacker/core/hostcheck.py
🧬 Code graph analysis (3)
nettacker/core/app.py (1)
nettacker/core/hostcheck.py (3)
  • resolve_quick (76-128)
  • is_ip_literal (24-35)
  • valid_hostname (37-57)
nettacker/core/hostcheck.py (1)
nettacker/core/ip.py (8)
  • get_ip_range (29-48)
  • generate_ip_range (7-26)
  • is_single_ipv4 (51-61)
  • is_ipv4_range (64-73)
  • is_ipv4_cidr (76-85)
  • is_single_ipv6 (88-98)
  • is_ipv6_range (101-110)
  • is_ipv6_cidr (113-122)
nettacker/core/arg_parser.py (1)
nettacker/config.py (1)
  • Config (184-188)
🪛 Ruff (0.14.1)
nettacker/core/app.py

302-302: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


302-302: Avoid specifying long messages outside the exception class

(TRY003)

nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)


79-79: Unused function argument: try_search_suffixes

(ARG001)

🪛 YAMLlint (1.37.1)
nettacker/locale/el.yaml

[error] 27-27: duplication of key "validate_before_scan" in mapping

(key-duplicates)

🔇 Additional comments (28)
nettacker/locale/es.yaml (1)

22-22: Locale key is appropriately translated and correctly placed.

The Spanish translation clearly describes the pre-scan validation behavior. No issues identified.

.gitignore (1)

34-36: Clarify the purpose of the newly added .gitignore entries.

The three entries (Public_sign, Public_sign.pub, cks_proxy) appear to be cryptographic keys and proxy-related artifacts. While they follow existing patterns, their purpose is not immediately clear from context. Confirm these are intentional and not temporary/debug artifacts.

nettacker/locale/pt-br.yaml (1)

31-31: Portuguese translation is clear and consistent.

The translation appropriately describes the pre-scan validation feature. No issues identified.

nettacker/locale/ur.yaml (1)

31-31: Urdu translation is properly formatted.

The translation correctly describes the feature in Urdu. No issues identified.

nettacker/locale/bn.yaml (1)

24-24: Bengali translation is properly formatted.

The translation appropriately describes the pre-scan validation feature. No issues identified.

nettacker/locale/hi.yaml (1)

25-25: Hindi translation is clear and properly formatted.

The translation appropriately describes the feature. No issues identified.

nettacker/locale/iw.yaml (1)

30-30: Hebrew translation is properly formatted.

The translation accurately conveys the feature intent. No issues identified.

nettacker/locale/fa.yaml (1)

23-23: Persian translation is properly formatted.

The translation clearly describes the feature in Persian. No issues identified.

nettacker/locale/zh-cn.yaml (1)

26-26: Locale entry for new feature is good.

The Chinese translation for validate_before_scan is grammatically correct and clearly describes the pre-scan validation feature.

nettacker/locale/ps.yaml (1)

25-25: Locale entry for new feature is good.

The Pashto translation for validate_before_scan appropriately describes the pre-scan validation behavior.

nettacker/locale/hy.yaml (1)

26-26: Locale entry for new feature is good.

The Armenian translation for validate_before_scan clearly describes the pre-scan validation feature and its behavior.

nettacker/locale/vi.yaml (1)

25-25: Locale entry for new feature is good.

The Vietnamese translation for validate_before_scan appropriately describes the pre-scan validation behavior and target skipping.

nettacker/locale/ko.yaml (1)

29-29: Locale entry for new feature is good.

The Korean translation for validate_before_scan clearly describes the pre-scan validation feature and target skipping behavior.

nettacker/locale/fr.yaml (1)

25-25: Locale entry for new feature is good.

The French translation for validate_before_scan is grammatically correct and accurately conveys the pre-scan validation behavior.

nettacker/locale/id.yaml (1)

27-27: Locale entry for new feature is good.

The Indonesian translation for validate_before_scan accurately describes the pre-scan validation feature and target skipping behavior.

nettacker/locale/ar.yaml (1)

25-25: Locale entry for new feature is good.

The Arabic translation for validate_before_scan accurately describes the pre-scan validation feature and unreachable target skipping.

nettacker/locale/tr.yaml (1)

27-27: LGTM! Turkish locale entry added correctly.

The Turkish translation for validate_before_scan is properly formatted and aligns with the new pre-scan validation feature being introduced across all locales.

nettacker/locale/de.yaml (1)

18-18: LGTM! German locale entry added correctly.

The German translation for validate_before_scan is well-formatted and consistent with the feature's intent to pre-validate targets before scanning.

nettacker/locale/en.yaml (1)

14-14: LGTM! English locale entry is clear and well-positioned.

The description clearly communicates the feature's purpose: pre-validating targets before scanning and skipping unreachable ones.

nettacker/config.py (1)

165-165: LGTM! Configuration attribute added with correct opt-in default.

The validate_before_scan attribute is properly added with a False default, making the pre-scan validation feature opt-in as agreed upon in the PR discussion. This preserves existing behavior by default while allowing users to enable validation via the CLI flag.

nettacker/locale/it.yaml (1)

26-26: LGTM! Italian locale entry added correctly.

The Italian translation for validate_before_scan is well-formatted and accurately conveys the feature's behavior.

nettacker/locale/ja.yaml (1)

28-28: LGTM! Japanese locale entry added correctly.

The Japanese translation for validate_before_scan is properly formatted and clearly describes the pre-scan validation behavior.

nettacker/core/app.py (2)

10-10: LGTM!

The concurrent.futures imports are correctly added for the parallel target validation feature.


26-26: LGTM!

The hostcheck module imports are correctly added for target validation.

nettacker/core/hostcheck.py (4)

1-22: LGTM!

Imports and setup are appropriate. The _LABEL regex correctly validates hostname labels per RFC 1123.


24-35: LGTM!

The function correctly validates IPv4/IPv6 literals using socket.inet_pton. Docstring is present and clear.


37-57: LGTM!

The hostname validation logic correctly implements RFC 1123 requirements. Trailing dot handling and label validation are properly ordered.


60-74: LGTM!

The helper functions are well-designed. _gai_once correctly wraps getaddrinfo with optional AI_ADDRCONFIG, and _clean_host robustly normalizes input.

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

♻️ Duplicate comments (1)
nettacker/core/hostcheck.py (1)

94-94: Fix formatting: missing space after if keyword.

Line 94 uses if(...) with parentheses directly after the keyword; Python style convention requires a space and prefers implicit condition boolean evaluation without parens.

Apply this diff:

-        if(is_ip_literal(host)):
+        if is_ip_literal(host):
🧹 Nitpick comments (1)
nettacker/core/hostcheck.py (1)

81-83: Update docstring: remove stale mention of "suffix search".

The docstring (line 82) states "Perform fast DNS resolution with timeout and suffix search," but the try_search_suffixes parameter was removed in prior commits. Update the docstring to reflect the current API.

Apply this diff:

     """
-    Perform fast DNS resolution with timeout and suffix search.
+    Perform fast DNS resolution with timeout.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c92c7f3 and 7de608e.

📒 Files selected for processing (3)
  • nettacker/core/arg_parser.py (1 hunks)
  • nettacker/core/hostcheck.py (1 hunks)
  • nettacker/locale/el.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • nettacker/locale/el.yaml
  • nettacker/core/arg_parser.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🧠 Learnings (1)
📚 Learning: 2025-10-25T11:31:26.498Z
Learnt from: Aarush289
PR: OWASP/Nettacker#1155
File: nettacker/core/hostcheck.py:121-122
Timestamp: 2025-10-25T11:31:26.498Z
Learning: In nettacker/core/hostcheck.py, the resolve_quick() function intentionally applies timeout_sec per resolution pass (not cumulatively), allowing up to 2× timeout_sec total execution time across both AI_ADDRCONFIG passes by design.

Applied to files:

  • nettacker/core/hostcheck.py
🧬 Code graph analysis (1)
nettacker/core/hostcheck.py (1)
nettacker/core/ip.py (2)
  • is_single_ipv4 (51-61)
  • is_single_ipv6 (88-98)
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)

Signed-off-by: Aarush289 <[email protected]>
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 (5)
nettacker/core/hostcheck.py (5)

81-90: Clarify docstring: timeout is per pass and result is canonicalized.

-    Perform fast DNS resolution with timeout.
+    Perform fast DNS resolution with timeout.
+    The timeout applies per resolution pass (AI_ADDRCONFIG on, then off).
@@
-        timeout_sec: Maximum time to wait for resolution.
+        timeout_sec: Maximum time to wait per resolution pass.
@@
-        (True, host_name) on success, (False, None) on failure/timeout.
+        (True, canonical_host) on success (lowercased, trailing dot stripped),
+        (False, None) on failure/timeout across both passes.

Based on learnings


100-105: Use consistent single‑label validation; remove redundant check.

-    if not valid_hostname(host):
+    if not valid_hostname(host, allow_single_label=allow_single_label):
         return False, None
-
-    if "." not in host and not allow_single_label:
-        return False, None

92-96: Return canonicalized IP literal (lowercase) for consistency.

-    if is_single_ipv4(host) or is_single_ipv6(host):
-        if is_ip_literal(host):
-            return True, host
-        return False, None
+    if is_single_ipv4(host) or is_single_ipv6(host):
+        if is_ip_literal(host):
+            return True, host.lower()
+        return False, None

66-74: Align comments with behavior in _clean_host (no internal collapsing).

-def _clean_host(s: str) -> str:
-    # remove surrounding quotes and whitespace, lone commas, repeated dots
+def _clean_host(s: str) -> str:
+    # Remove surrounding quotes and whitespace
     s = s.strip().strip('"').strip("'")
     s = s.strip()  # again, after quote strip
-    # drop trailing commas that often sneak in from CSV-like inputs
+    # Drop a trailing comma (CSV leftovers)
     if s.endswith(","):
         s = s[:-1].rstrip()
-    # collapse accidental spaces inside
+    # Note: does not collapse internal spaces or repeated dots
     return s

24-35: Minor style: prefer try/except/else for early returns.

 def is_ip_literal(name: str) -> bool:
     """Return True if name is a valid IPv4 or IPv6 address literal."""
-    try:
-        socket.inet_pton(socket.AF_INET, name)
-        return True
-    except OSError:
-        pass
-    try:
-        socket.inet_pton(socket.AF_INET6, name)
-        return True
-    except OSError:
-        return False
+    try:
+        socket.inet_pton(socket.AF_INET, name)
+    except OSError:
+        pass
+    else:
+        return True
+    try:
+        socket.inet_pton(socket.AF_INET6, name)
+    except OSError:
+        return False
+    else:
+        return True
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7de608e and 87b773c.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🧠 Learnings (1)
📚 Learning: 2025-10-25T11:31:26.498Z
Learnt from: Aarush289
PR: OWASP/Nettacker#1155
File: nettacker/core/hostcheck.py:121-122
Timestamp: 2025-10-25T11:31:26.498Z
Learning: In nettacker/core/hostcheck.py, the resolve_quick() function intentionally applies timeout_sec per resolution pass (not cumulatively), allowing up to 2× timeout_sec total execution time across both AI_ADDRCONFIG passes by design.

Applied to files:

  • nettacker/core/hostcheck.py
🧬 Code graph analysis (1)
nettacker/core/hostcheck.py (1)
nettacker/core/ip.py (8)
  • get_ip_range (29-48)
  • generate_ip_range (7-26)
  • is_single_ipv4 (51-61)
  • is_ipv4_range (64-73)
  • is_ipv4_cidr (76-85)
  • is_single_ipv6 (88-98)
  • is_ipv6_range (101-110)
  • is_ipv6_cidr (113-122)
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)


110-110: Local variable deadline is assigned to but never used

Remove assignment to unused variable deadline

(F841)

🔇 Additional comments (1)
nettacker/core/hostcheck.py (1)

50-57: Hostname validation looks good.

Trailing-dot trim before length check and RFC 1123 label checks are correct.

Signed-off-by: Aarush289 <[email protected]>
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 (1)
nettacker/core/hostcheck.py (1)

24-35: LGTM! Optional: consider else block for style.

The validation logic is correct and the docstring is clear. The static analysis hint (TRY300) suggests using else blocks instead of a second try, but the current pattern is explicit and readable.

If you prefer a slightly more idiomatic style:

 def is_ip_literal(name: str) -> bool:
     """Return True if name is a valid IPv4 or IPv6 address literal."""
     try:
         socket.inet_pton(socket.AF_INET, name)
         return True
     except OSError:
-        pass
-    try:
+        try:
-        socket.inet_pton(socket.AF_INET6, name)
-        return True
+            socket.inet_pton(socket.AF_INET6, name)
+            return True
-    except OSError:
-        return False
+        except OSError:
+            return False
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87b773c and 0ac3a96.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🧠 Learnings (1)
📚 Learning: 2025-10-25T11:31:26.498Z
Learnt from: Aarush289
PR: OWASP/Nettacker#1155
File: nettacker/core/hostcheck.py:121-122
Timestamp: 2025-10-25T11:31:26.498Z
Learning: In nettacker/core/hostcheck.py, the resolve_quick() function intentionally applies timeout_sec per resolution pass (not cumulatively), allowing up to 2× timeout_sec total execution time across both AI_ADDRCONFIG passes by design.

Applied to files:

  • nettacker/core/hostcheck.py
🧬 Code graph analysis (1)
nettacker/core/hostcheck.py (1)
nettacker/core/ip.py (8)
  • get_ip_range (29-48)
  • generate_ip_range (7-26)
  • is_single_ipv4 (51-61)
  • is_ipv4_range (64-73)
  • is_ipv4_cidr (76-85)
  • is_single_ipv6 (88-98)
  • is_ipv6_range (101-110)
  • is_ipv6_cidr (113-122)
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)

🔇 Additional comments (3)
nettacker/core/hostcheck.py (3)

37-57: LGTM!

The hostname validation logic correctly implements RFC 1123 requirements, handles trailing dots before length validation (addressing previous feedback), and provides flexible single-label support.


60-74: LGTM!

The helper functions are well-structured: _gai_once provides a clean wrapper for getaddrinfo with AI_ADDRCONFIG support, and _clean_host handles common input normalization cases.


91-121: LGTM! Per-pass timeout correctly implemented.

The DNS resolution logic is sound:

  • Input normalization and validation prevent common issues
  • Defensive double-check (netaddr validation + inet_pton) for IP literals is reasonable
  • Per-pass timeout design (allowing up to 2× timeout_sec total) is intentional per learnings
  • continue on TimeoutError ensures both passes are attempted
  • Returns canonical lowercase hostname on success

Signed-off-by: Aarush289 <[email protected]>
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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ac3a96 and 8565db6.

📒 Files selected for processing (1)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
🧠 Learnings (1)
📚 Learning: 2025-10-25T11:31:26.498Z
Learnt from: Aarush289
PR: OWASP/Nettacker#1155
File: nettacker/core/hostcheck.py:121-122
Timestamp: 2025-10-25T11:31:26.498Z
Learning: In nettacker/core/hostcheck.py, the resolve_quick() function intentionally applies timeout_sec per resolution pass (not cumulatively), allowing up to 2× timeout_sec total execution time across both AI_ADDRCONFIG passes by design.

Applied to files:

  • nettacker/core/hostcheck.py
🧬 Code graph analysis (1)
nettacker/core/hostcheck.py (1)
nettacker/core/ip.py (8)
  • get_ip_range (29-48)
  • generate_ip_range (7-26)
  • is_single_ipv4 (51-61)
  • is_ipv4_range (64-73)
  • is_ipv4_cidr (76-85)
  • is_single_ipv6 (88-98)
  • is_ipv6_range (101-110)
  • is_ipv6_cidr (113-122)
🪛 Ruff (0.14.1)
nettacker/core/hostcheck.py

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

(TRY300)


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

(TRY300)

🔇 Additional comments (3)
nettacker/core/hostcheck.py (3)

22-33: LGTM! Past issues addressed.

The function now includes a proper docstring and uses separate statements instead of semicolons. The logic correctly validates IPv4/IPv6 literals using socket.inet_pton.


35-55: LGTM! Edge-case fix applied correctly.

The function properly strips the trailing dot before checking length (line 48-49 before 50), which addresses the past edge-case issue. The docstring is clear and the RFC 1123 validation logic is correct.


107-119: LGTM! Timeout logic correctly implements per-pass behavior.

The resolution logic correctly implements the intended per-pass timeout design from the retrieved learning:

  • Each pass (lines 107) gets a fresh timeout_sec seconds (line 112)
  • TimeoutError now triggers continue (line 115) instead of early return, allowing the second pass to execute
  • Clean implementation without manual deadline management

This addresses all past timeout-related concerns.

@Aarush289 Aarush289 requested a review from securestep9 October 28, 2025 08:44

if dedupe:
seen, unique = set(), []
for c in filtered:
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider better names, if iterator is not being used elsewhere then just use an "_"

max_workers = min(len(targets), 10) # cap threads

# Preserve order
canon_by_index = [None] * len(targets)
Copy link
Contributor

Choose a reason for hiding this comment

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

The names can be clearer

try:
idx, orig_target, canon = fut.result()
except (OSError, socket.gaierror) as exc:
log.debug(f"Invalid target (resolver error): {exc!s}")
Copy link
Contributor

Choose a reason for hiding this comment

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

The logger doesn't have a .debug() method. Change this to .error().

try:
socket.inet_pton(socket.AF_INET, name)
return True
except OSError:
Copy link
Contributor

Choose a reason for hiding this comment

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

exceptions are slower than if-else statements. You're better off with precompiling an IPv4 regex (IPv6 would be too complex) at least and not relying on a try-except block as part of the main execution flow.

"""
if host.endswith("."):
host = host[:-1]
if len(host) > 253:
Copy link
Contributor

Choose a reason for hiding this comment

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

Do add a comment here to specify this comes from RFC1123 which specifies the number of characters to be 250 at max (without dots) and 253 with dots.

Copy link
Contributor

Choose a reason for hiding this comment

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

You don't have to translate for all. Just en.yaml is enough, we'll leave the translations to the native speakers.

)

def _clean_host(s: str) -> str:
# remove surrounding quotes and whitespace, lone commas, repeated dots
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you handling repeated dots?

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: 80

♻️ Duplicate comments (14)
.venv/bin/py.test (1)

1-8: Critical: Virtual environment file should not be committed.

Same issue as other .venv/ files - this auto-generated wrapper contains hardcoded local paths and should not be in version control.

.venv/bin/pip3.10 (1)

1-8: Critical: Virtual environment file should not be committed.

Same issue - this .venv/ file contains hardcoded local paths and should not be in version control.

.venv/bin/pytest (1)

1-8: Critical: Virtual environment file should not be committed.

Same issue - this .venv/ file should not be in version control.

.venv/bin/futurize (1)

1-8: Critical: Virtual environment file should not be committed.

Same issue - this .venv/ file should not be in version control.

.venv/bin/coverage-3.10 (1)

1-8: Critical: Virtual environment file should not be committed.

Same issue - this .venv/ file should not be in version control.

.venv/bin/coverage (1)

1-8: Critical: Virtual environment file should not be committed.

Same issue - this .venv/ file should not be in version control.

.venv/bin/ipython3 (1)

1-8: Critical: Virtual environment file should not be committed.

Same issue - this .venv/ file should not be in version control.

.venv/bin/pip (1)

1-8: Critical: Virtual environment files must not be committed.

Same critical issue as .venv/bin/ipython: this is an auto-generated virtual environment wrapper with a machine-specific absolute path (Line 1) that will not work on other systems. All .venv/ files must be removed from this PR.

See the review comment on .venv/bin/ipython for detailed remediation steps.

.venv/bin/coverage3 (1)

1-8: Critical: Virtual environment files must not be committed.

Same critical issue as the other .venv/ files: this is an auto-generated wrapper with a machine-specific absolute path (Line 1). All .venv/ files must be removed from this PR.

See the review comment on .venv/bin/ipython for detailed remediation steps.

.venv/bin/ping6.py (5)

1-86: CRITICAL: Virtual environment files should not be committed to version control.

This file has the same critical issue as ping.py - it's located in .venv/bin/, which is a virtual environment directory that should never be committed to version control. Please refer to the detailed explanation in the review comment for ping.py.

Remove all .venv/ files from this PR:

git rm -r .venv/

1-1: CRITICAL: Hardcoded absolute path in shebang won't work on other systems.

Same issue as in ping.py - the shebang contains a hardcoded absolute path: /home/aarush_/Pictures/updated_owasp_by_me/Nettacker/.venv/bin/python

Use a portable shebang:

-#!/home/aarush_/Pictures/updated_owasp_by_me/Nettacker/.venv/bin/python
+#!/usr/bin/env python3

61-86: No graceful exit mechanism for infinite loop.

The script uses while 1: with no break condition or signal handler, identical to the issue in ping.py.

Add signal handling for graceful shutdown:

+import signal
+
+def signal_handler(sig, frame):
+    print('\nPing interrupted.')
+    s.close()
+    sys.exit(0)
+
+signal.signal(signal.SIGINT, signal_handler)
+
 while 1:

73-80: Missing error handling for socket operations.

The sendto and recvfrom operations lack error handling, which can cause the script to crash on network errors.

Add try-except blocks:

-    s.sendto(icmp.get_packet(), (dst, 0))
+    try:
+        s.sendto(icmp.get_packet(), (dst, 0))
+    except socket.error as e:
+        print(f"Send failed: {e}")
+        continue
 
     if s in select.select([s], [], [], 1)[0]:
-        reply = s.recvfrom(2000)[0]
+        try:
+            reply = s.recvfrom(2000)[0]
+        except socket.error as e:
+            print(f"Receive failed: {e}")
+            continue

83-86: Sleep only occurs after receiving a reply, not between all ping attempts.

The time.sleep(1) is inside the if block (line 76), causing the same timing issue as in ping.py.

Move the sleep outside the if block:

        if ICMP6.ICMP6.ECHO_REPLY == rip.get_type():
            print("%d bytes from %s: icmp_seq=%d " % (rip.child().get_size()-4, dst, rip.get_echo_sequence_number()))
 
-        time.sleep(1)
+    time.sleep(1)
🧹 Nitpick comments (13)
.venv/bin/mqtt_check.py (1)

67-71: Address exception handling issues (only if file legitimately belongs in the codebase).

Given that this file should not be committed (see previous comments), these issues are secondary. However, if similar error handling patterns exist elsewhere in your codebase:

  1. Lines 69-71: Catching bare Exception is too broad. Consider catching specific exception types (e.g., argparse.ArgumentError).

  2. Lines 70, 89: Use logging.exception(str(e)) instead of logging.error(str(e)) to automatically include traceback information, eliminating the need for the manual traceback logic on lines 86-88.

Apply this pattern only to legitimate application code, not third-party tool scripts:

     try:
         options = parser.parse_args()
     except Exception as e:
-        logging.error(str(e))
+        logging.exception("Failed to parse arguments")
         sys.exit(1)
     try:
         check_mqtt.run()
     except Exception as e:
-        if logging.getLogger().level == logging.DEBUG:
-            import traceback
-            traceback.print_exc()
-        logging.error(e)
+        logging.exception("MQTT connection failed")

Note: This suggestion assumes the code would be part of the application, which it should not be based on the current file location.

Also applies to: 83-89

.venv/bin/getST.py (1)

91-91: Static analysis issues are present but secondary to the main problem.

The static analysis tool (Ruff) has flagged several legitimate code quality issues in this file:

  • Unused method parameters oldSessionKey (lines 91, 298)
  • Broad exception catching without specific handling (lines 656, 738)
  • Using logging.error instead of logging.exception in exception handlers (lines 658, 660, 662)

However, since this is third-party library code from Impacket that should not be committed to this repository, these issues should not be fixed here. When you properly add Impacket as a dependency via pip/requirements.txt, you'll get the maintained version from the upstream project.

The correct approach is to use Impacket as a dependency rather than vendoring or committing its installed files.

Also applies to: 298-298, 656-662, 738-738

.venv/bin/esentutl.py (4)

44-49: Simplify logging with logging.exception.

Line 48 passes exc_info=True manually; logging.exception does this automatically.

-        except Exception:
-            logging.debug('Exception:', exc_info=True)
-            logging.error('Error while calling getNextRow(), trying the next one')
+        except Exception:
+            logging.exception('Error while calling getNextRow(), trying the next one')

Note: The bare Exception catch is acceptable for this utility script context where continuing iteration after row errors is desired behavior.


38-38: Fix quote inconsistency.

Line 38 uses double quotes inconsistently with the rest of the file.

-        logging.error('Can"t get a cursor for table: %s' % tableName)
+        logging.error("Can't get a cursor for table: %s" % tableName)

55-56: Remove extra whitespace.

Line 55 has unnecessary leading whitespace before the if statement.

-           if record[j] is not None:
-               print("%-30s: %r" % (j, record[j]))
+            if record[j] is not None:
+                print("%-30s: %r" % (j, record[j]))

115-117: Fix exit code for success case.

sys.exit(1) indicates failure, but main() completed successfully. Either remove the exit call (implicit 0) or use sys.exit(0).

 if __name__ == '__main__':
     main()
-    sys.exit(1)
+    sys.exit(0)
.venv/bin/Get-GPPPassword.py (2)

91-91: Refactor lambda to def for better readability.

Per PEP8 E731, lambda expressions should not be assigned to named variables. Using def provides better tracebacks and clearer intent.

As per coding guidelines from static analysis.

-        read_or_empty = lambda element, attribute: (element.getAttribute(attribute) if element.getAttribute(attribute) is not None else "")
+        def read_or_empty(element, attribute):
+            value = element.getAttribute(attribute)
+            return value if value is not None else ""

318-321: Use logging.exception for better error reporting.

The main exception handler should use logging.exception() to automatically include the full traceback, making debugging easier.

Based on learnings from static analysis.

         except Exception as e:
             if logging.getLogger().level == logging.DEBUG:
                 traceback.print_exc()
-            logging.error(str(e))
+            logging.exception("Failed to execute GPP password extraction")
.venv/bin/get_gprof (3)

55-68: Use subprocess module instead of deprecated os.system().

Line 63 uses os.system() to execute external commands, which is deprecated and has several drawbacks:

  • More prone to shell injection vulnerabilities
  • Limited error handling and process control
  • Harder to capture output

Use subprocess.run() instead:

-    try:
-        res = os.system(msg)
-    except Exception:
-        print ("Please verify install of 'gprof2dot' to view profile graphs")
-    if res:
-        print ("Please verify install of 'gprof2dot' to view profile graphs")
+    import subprocess
+    try:
+        result = subprocess.run(msg, shell=True, check=True, capture_output=True, text=True)
+    except subprocess.CalledProcessError:
+        print ("Please verify install of 'gprof2dot' to view profile graphs")
+    except FileNotFoundError:
+        print ("Please verify install of 'gprof2dot' to view profile graphs")

45-49: Avoid bare exception handlers; catch specific exceptions or provide error details.

Lines 47 and 64 catch bare Exception and provide generic error messages without any details about what actually failed. This makes debugging difficult and hides potentially important error information from users.

Catch specific exceptions or at least log the actual error:

     try:
         obj = eval(obj[-1])
-    except Exception:
-        print ("Error processing object instance")
+    except (SyntaxError, NameError, TypeError, ValueError) as e:
+        print(f"Error processing object instance: {e}")
         sys.exit()
     try:
         res = os.system(msg)
-    except Exception:
-        print ("Please verify install of 'gprof2dot' to view profile graphs")
+    except OSError as e:
+        print(f"Error running gprof2dot: {e}")
+        print("Please verify install of 'gprof2dot' to view profile graphs")

Also applies to: 62-67


59-59: Remove commented-out code.

Line 59 contains commented-out code that should be removed. Version control preserves the history, so there's no need to keep dead code in comments.

-    #name = os.path.splitext(os.path.basename(__file__))[0]
     cProfile.run("dill.dumps(obj)", filename="%s.prof" % name)
.venv/bin/atexec.py (2)

115-116: Consider using secrets module for temp name generation.

While the collision risk is low with 8 random ASCII letters, using secrets.choice instead of random.choice provides cryptographically strong randomness and better aligns with security best practices for generating unpredictable identifiers.

Apply this diff:

+        import secrets
-        tmpName = ''.join([random.choice(string.ascii_letters) for _ in range(8)])
+        tmpName = ''.join([secrets.choice(string.ascii_letters) for _ in range(8)])
         tmpFileName = tmpName + '.tmp'

217-233: Consider adding a retry limit to prevent indefinite hangs.

The file retrieval loop (lines 217-233) retries indefinitely on 'SHARING' errors with 3-second delays. If the file never becomes available (e.g., task failed silently, permissions issue), the script will hang forever.

Consider adding a maximum retry count:

smbConnection = rpctransport.get_smb_connection()
waitOnce = True
max_retries = 20  # 60 seconds total for SHARING errors
retry_count = 0
while True:
    try:
        logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
        smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback)
        break
    except Exception as e:
        if str(e).find('SHARING') > 0:
            retry_count += 1
            if retry_count > max_retries:
                logging.error('Max retries exceeded waiting for output file')
                raise
            time.sleep(3)
        elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
            if waitOnce is True:
                time.sleep(3)
                waitOnce = False
            else:
                raise
        else:
            raise

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.

Review continued from previous batch...

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

♻️ Duplicate comments (1)
nettacker/core/app.py (1)

293-302: Raise TypeError with from None.

We still re-raise the TypeError without suppressing the original List conversion traceback, which violates B904 and makes the error noisy. Please add from None (as previously requested) when re-raising.

-        raise TypeError(f"`targets` must be iterable, got {type(targets).__name__}")
+        raise TypeError(
+            f"`targets` must be iterable, got {type(targets).__name__}"
+        ) from None
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4491c2d and ec97266.

📒 Files selected for processing (2)
  • nettacker/core/app.py (4 hunks)
  • nettacker/core/hostcheck.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/hostcheck.py
  • nettacker/core/app.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/hostcheck.py
  • nettacker/core/app.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/hostcheck.py
  • nettacker/core/app.py
🧠 Learnings (4)
📚 Learning: 2025-10-25T11:31:26.498Z
Learnt from: Aarush289
PR: OWASP/Nettacker#1155
File: nettacker/core/hostcheck.py:121-122
Timestamp: 2025-10-25T11:31:26.498Z
Learning: In nettacker/core/hostcheck.py, the resolve_quick() function intentionally applies timeout_sec per resolution pass (not cumulatively), allowing up to 2× timeout_sec total execution time across both AI_ADDRCONFIG passes by design.

Applied to files:

  • nettacker/core/hostcheck.py
📚 Learning: 2025-09-07T19:20:58.332Z
Learnt from: CR
PR: OWASP/Nettacker#0
File: AGENTS.md:0-0
Timestamp: 2025-09-07T19:20:58.332Z
Learning: Applies to nettacker/**/*.py : Add docstrings for public APIs in the nettacker package

Applied to files:

  • nettacker/core/hostcheck.py
📚 Learning: 2025-09-07T19:20:58.332Z
Learnt from: CR
PR: OWASP/Nettacker#0
File: AGENTS.md:0-0
Timestamp: 2025-09-07T19:20:58.332Z
Learning: Applies to nettacker/main.py : CLI entry point code resides in nettacker/main.py

Applied to files:

  • nettacker/core/app.py
📚 Learning: 2025-09-07T19:20:58.332Z
Learnt from: CR
PR: OWASP/Nettacker#0
File: AGENTS.md:0-0
Timestamp: 2025-09-07T19:20:58.332Z
Learning: Applies to nettacker.py : Repository provides nettacker.py as an entry script (use as Python entry point)

Applied to files:

  • nettacker/core/app.py
🧬 Code graph analysis (2)
nettacker/core/hostcheck.py (1)
nettacker/core/ip.py (2)
  • is_single_ipv4 (51-61)
  • is_single_ipv6 (88-98)
nettacker/core/app.py (1)
nettacker/core/hostcheck.py (3)
  • resolve_quick (85-127)
  • is_ip_literal (32-44)
  • valid_hostname (46-66)
🪛 Ruff (0.14.2)
nettacker/core/hostcheck.py

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

(TRY300)

nettacker/core/app.py

302-302: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


302-302: Avoid specifying long messages outside the exception class

(TRY003)


323-323: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

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

Labels

None yet