Skip to content

Conversation

@vermakhushboo
Copy link
Member

@vermakhushboo vermakhushboo commented Nov 4, 2025

Summary by CodeRabbit

  • New Features

    • Added DNS record validation for A, AAAA, CNAME and enhanced CAA handling (apex/parent-domain checks) to improve domain verification.
  • Bug Fixes / Reliability

    • DNS client now validates DNS server addresses and surfaces errors for invalid servers.
  • Tests

    • Added unit and end-to-end tests for A/AAAA/CNAME/CAA and server validation; updated loopback usage to 127.0.0.1.
  • Chores

    • Added validator and domains packages to dependencies.

@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds three composer dependencies (utopia-php/validators ^0.0.2, utopia-php/domains 0.9., keeps utopia-php/telemetry 0.1.). Introduces a new DNS validator class Utopia\DNS\Validator\DNS with constructor (target, type, dnsServer), public properties (records, value, count, reason), and methods getDescription, isValid, isArray, getType; includes CAA-specific parent-domain/apex logic and failure reason constants. Updates DNS client to validate that the provided server is an IP address and throw if not. Adds unit tests for CNAME/A/AAAA/CAA and an e2e DNS test update that replaces localhost with 127.0.0.1 and adds testInvalidServer.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review DNS validator logic: record filtering by type, population of records/count/reason, and CAA parent-domain recursion/apex handling.
  • Verify FAILURE_REASON_* assignments and error-handling paths in isValid.
  • Inspect DNS Client constructor change: IP validation implementation and thrown exception text.
  • Review new tests for reliability (live DNS lookups, potential flakiness) and correctness of assertions.
  • Confirm composer dependency additions do not conflict with autoloading or type imports.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main changes: adding a DNS validator class and comprehensive tests for it.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-add-dns-validator

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bb1aa8 and 33ed350.

📒 Files selected for processing (2)
  • src/DNS/Validator/DNS.php (1 hunks)
  • tests/unit/DNS/Validator/DNSTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/unit/DNS/Validator/DNSTest.php (3)
src/DNS/Validator/DNS.php (2)
  • DNS (12-200)
  • isValid (95-175)
src/DNS/Message.php (1)
  • Message (11-208)
src/DNS/Message/Record.php (1)
  • Record (7-527)
src/DNS/Validator/DNS.php (4)
src/DNS/Client.php (3)
  • Client (8-74)
  • __construct (16-38)
  • query (44-73)
src/DNS/Message.php (2)
  • Message (11-208)
  • response (111-139)
src/DNS/Message/Question.php (1)
  • Question (7-51)
src/DNS/Message/Record.php (2)
  • Record (7-527)
  • typeCodeToName (316-319)

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.

@vermakhushboo vermakhushboo changed the title Add DNS validator and test SER-518 Add DNS validator and test Nov 4, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a DNS validator class that verifies DNS records (A, AAAA, CNAME, and CAA) for domains, along with comprehensive test coverage. The validator supports custom DNS resolvers and implements special CAA record inheritance logic per RFC 8659.

  • Adds DNS validator class with support for A, AAAA, CNAME, and CAA record validation
  • Implements CAA record parent domain inheritance behavior
  • Adds comprehensive unit tests for all supported record types

Reviewed Changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 8 comments.

File Description
src/DNS/Validator/DNS.php New DNS validator class with record validation logic and error reporting
tests/unit/DNS/Validator/DNSTest.php Comprehensive unit tests for DNS validator covering all record types
composer.json Adds dependencies for utopia-php/validators and utopia-php/domains
composer.lock Updates lock file with new dependencies and PHPUnit version

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

public string $reason = '';

/**
* @param string $target
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

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

The @param documentation for $target doesn't explain what it represents. For different record types, $target has different meanings (IP address for A/AAAA, domain for CNAME, issuer domain for CAA). Add a description explaining that $target is the expected value to validate against, with its format depending on the record type.

Suggested change
* @param string $target
* @param string $target The expected value to validate against. For A/AAAA records, this should be an IP address. For CNAME records, a domain name. For CAA records, an issuer domain.
* @param string $type The DNS record type to validate (A, AAAA, CNAME, CAA).
* @param string $dnsServer Optional DNS server to use for validation.

Copilot uses AI. Check for mistakes.
Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6eca18 and 78ec2af.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • composer.json (1 hunks)
  • src/DNS/Validator/DNS.php (1 hunks)
  • tests/unit/DNS/Validator/DNSTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/unit/DNS/Validator/DNSTest.php (1)
src/DNS/Validator/DNS.php (2)
  • DNS (12-225)
  • isValid (109-112)
src/DNS/Validator/DNS.php (5)
src/DNS/Client.php (1)
  • Client (7-68)
src/DNS/Message.php (2)
  • Message (11-208)
  • response (111-139)
src/DNS/Message/Question.php (1)
  • Question (7-51)
src/DNS/Message/Record.php (3)
  • Record (7-527)
  • typeNameToCode (311-314)
  • typeCodeToName (316-319)
src/DNS/Message/Domain.php (1)
  • Domain (7-149)
🪛 GitHub Actions: CodeQL
src/DNS/Validator/DNS.php

[error] 31-31: PHPStan: Property Utopia\DNS\Validator\DNS::$recordValues type has no value specified in iterable type array. missingType.iterableValue


[error] 109-109: PHPStan: PHPDoc tag @param references unknown parameter: $domain


[error] 121-121: PHPStan: PHPDoc tag @param references unknown parameter: $domain


[error] 137-137: PHPStan: Parameter #2 $type of class Utopia\DNS\Message\Question constructor expects int, int|null given. argument.type


[error] 184-184: PHPStan: Offset 0 on non-empty-list on left side of ?? always exists and is not nullable. nullCoalesce.offset

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78ec2af and 95bc503.

📒 Files selected for processing (1)
  • src/DNS/Validator/DNS.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/DNS/Validator/DNS.php (5)
src/DNS/Client.php (1)
  • Client (7-68)
src/DNS/Message.php (2)
  • Message (11-208)
  • response (111-139)
src/DNS/Message/Question.php (1)
  • Question (7-51)
src/DNS/Message/Record.php (3)
  • Record (7-527)
  • typeNameToCode (311-314)
  • typeCodeToName (316-319)
src/DNS/Message/Domain.php (1)
  • Domain (7-149)

Copy link

@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

♻️ Duplicate comments (4)
src/DNS/Validator/DNS.php (4)

192-193: Validate CAA record format explicitly instead of masking errors.

Using ?? '' after explode(' ', $rdata, 3)[2] silently falls back to an empty string if the CAA record is malformed. This masks data errors. Explicitly validate the CAA record format and handle malformed records as failures.

Apply this diff:

                 // Extract domain; comments showcase extraction steps in most complex scenario
                 $rdata = $record->rdata; // 255 issuewild "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"
-                $rdata = \explode(' ', $rdata, 3)[2] ?? ''; // "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"
+                $parts = \explode(' ', $rdata, 3);
+                if (\count($parts) < 3) {
+                    $this->reason = self::FAILURE_REASON_INTERNAL;
+                    $this->logs = ['error' => 'Malformed CAA record'];
+                    return false;
+                }
+                $rdata = $parts[2]; // "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"

Based on past review comments.


176-182: Propagate parent CAA state to maintain diagnostic coherence.

When delegating to a parent domain validator, only the boolean result is returned. This leaves the current instance with count = 0, empty recordValues, and default reason. Callers will see "missing records" even when the parent provided conflicting CAA data. Copy the child validator's diagnostic properties before returning.

Apply this diff:

                 $parts = \explode('.', $value);
                 \array_shift($parts);
                 $parentDomain = \implode('.', $parts);
                 $validator = new self($this->target, DNS::RECORD_CAA, $dnsServer);
-                return $validator->isValidWithDNSServer($parentDomain, $dnsServer);
+                $result = $validator->isValidWithDNSServer($parentDomain, $dnsServer);
+
+                $this->recordValues = $validator->recordValues;
+                $this->count = $validator->count;
+                $this->logs = $validator->getLogs();
+                $this->reason = $validator->reason;
+
+                return $result;

Based on past review comments.


197-206: Use case-insensitive comparison for domain values.

DNS labels are case-insensitive (RFC 4343), but the code uses strict === comparison. A CAA record like 0 issue "LetsEncrypt.org" or CNAME Foo.EXAMPLE.COM. will fail to match even when semantically correct. Switch to strcasecmp() for domain comparisons.

Apply this diff:

                 $this->recordValues[] = $rdata;
-                if ($rdata === $this->target) {
+                if (\strcasecmp($rdata, $this->target) === 0) {
                     return true;
                 }
             } else {
                 $this->recordValues[] = $record->rdata;
             }

-            if ($record->rdata === $this->target) {
+            if (
+                ($this->type === self::RECORD_CNAME && \strcasecmp($record->rdata, $this->target) === 0)
+                || (($this->type === self::RECORD_A || $this->type === self::RECORD_AAAA) && $record->rdata === $this->target)
+            ) {
                 return true;
             }

Note: For A/AAAA records (IP addresses), keep strict comparison as IPs are case-sensitive in their string representation.

Based on past review comments.


204-206: Remove unreachable code for CAA records.

For CAA records, after the extracted domain is checked at line 197, the code continues to line 204 where it compares $record->rdata (the full CAA format like "255 issuewild \"certainly.com\"") against $this->target (just the domain). This comparison will never match for CAA records. Add a continue statement after line 199 or restructure the logic to skip this check for CAA records.

Apply this diff:

                 $this->recordValues[] = $rdata;
                 if (\strcasecmp($rdata, $this->target) === 0) {
                     return true;
                 }
+                continue; // Skip the unreachable comparison below for CAA records
             } else {
                 $this->recordValues[] = $record->rdata;
+                if (
+                    ($this->type === self::RECORD_CNAME && \strcasecmp($record->rdata, $this->target) === 0)
+                    || (($this->type === self::RECORD_A || $this->type === self::RECORD_AAAA) && $record->rdata === $this->target)
+                ) {
+                    return true;
+                }
             }
-
-            if ($record->rdata === $this->target) {
-                return true;
-            }
         }

Based on past review comments.

🧹 Nitpick comments (2)
src/DNS/Validator/DNS.php (2)

17-17: Clarify the inline comment for CAA validation.

The comment "You can provide domain only (as $target) for CAA validation" is somewhat unclear. Consider rephrasing to: "For CAA validation, $target should be the expected issuer domain (e.g., 'letsencrypt.org')."


92-92: Consider clarifying the CAA error message.

The message "Add new CAA record, or remove all other CAA records" may be confusing because it presents two contradictory actions without context. Consider making it more specific, such as: "Add a CAA record for '{$this->target}' or remove conflicting CAA records."

Based on past review comments.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95bc503 and c336f4e.

📒 Files selected for processing (1)
  • src/DNS/Validator/DNS.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/DNS/Validator/DNS.php (5)
src/DNS/Client.php (1)
  • Client (7-68)
src/DNS/Message.php (2)
  • Message (11-208)
  • response (111-139)
src/DNS/Message/Question.php (1)
  • Question (7-51)
src/DNS/Message/Record.php (3)
  • Record (7-527)
  • typeNameToCode (311-314)
  • typeCodeToName (316-319)
src/DNS/Message/Domain.php (1)
  • Domain (7-149)
🔇 Additional comments (3)
src/DNS/Validator/DNS.php (3)

98-115: LGTM!

These delegation and accessor methods follow a clean pattern.


126-148: LGTM!

The initial validation setup correctly checks input types, initializes state, validates the type code (addressing previous PHPStan errors), and constructs the DNS query.


212-234: LGTM!

The isArray() and getType() methods correctly implement the validator interface.

Copy link

@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 (3)
src/DNS/Validator/DNS.php (3)

178-183: Propagate parent CAA validator state.

When recursively validating the parent domain for CAA records, the child validator's diagnostic state (count, recordValues, reason, logs) is not copied back to the current instance. Callers will see incorrect diagnostics (e.g., "missing records" when the parent actually had conflicting CAA data).

Apply this diff to synchronize the state:

                 $validator = new self($this->target, DNS::RECORD_CAA, $dnsServer);
-                return $validator->isValidWithDNSServer($parentDomain, $dnsServer);
+                $result = $validator->isValidWithDNSServer($parentDomain, $dnsServer);
+
+                $this->recordValues = $validator->recordValues;
+                $this->count = $validator->count;
+                $this->logs = $validator->getLogs();
+                $this->reason = $validator->reason;
+
+                return $result;

Based on learnings


141-166: Remove debug statements before merging.

Two more var_dump statements will output to the response and must be removed.

Apply this diff:

             $response = $dns->query($queryMessage);
             $rawQuery = $response->answers;
-            var_dump($rawQuery);
 
             // Some DNS servers return all records, not only type that's asked for
             // Likely occurs when no records of specific type are found
             $query = array_filter($rawQuery, function ($record) {
                 $typeName = Record::typeCodeToName($record->type);
                 return $typeName === $this->type;
             });
 
             $this->logs = $query;
         } catch (\Exception $e) {
-            var_dump("error in isValidWithDNSServer");
             $this->reason = self::FAILURE_REASON_QUERY;
             $this->logs = ['error' => $e->getMessage()];
             return false;

Based on learnings


189-211: Fix CAA validation logic and add case-insensitive domain comparison.

This code segment has two major issues:

  1. CAA records fall through to incorrect comparison: After extracting and comparing the CAA domain at lines 191-201, execution continues to line 206 where $record->rdata (the full CAA format like "255 issuewild "certainly.com..."") is compared against $this->target. This comparison will never match and is unreachable for CAA records.

  2. DNS domain comparisons are case-sensitive: DNS labels are case-insensitive, but the code uses strict === equality. Records like 0 issue "LetsEncrypt.org" or CNAME Foo.EXAMPLE.COM. will fail to match even when semantically correct.

Apply this diff to fix both issues:

         foreach ($query as $record) {
             // CAA validation only needs to ensure domain
             if ($this->type === self::RECORD_CAA) {
                 // Extract domain; comments showcase extraction steps in most complex scenario
                 $rdata = $record->rdata; // 255 issuewild "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"
                 $rdata = \explode(' ', $rdata, 3)[2] ?? ''; // "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"
                 $rdata = \trim($rdata, '"'); // certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600
                 $rdata = \explode(';', $rdata, 2)[0]; // certainly.com
 
                 $this->recordValues[] = $rdata;
-                if ($rdata === $this->target) {
+                if (\strcasecmp($rdata, $this->target) === 0) {
                     return true;
                 }
+                continue; // Skip the comparison below for CAA records
             } else {
                 $this->recordValues[] = $record->rdata;
             }
 
-            if ($record->rdata === $this->target) {
+            // Use case-insensitive comparison for CNAME, case-sensitive for A/AAAA (IP addresses)
+            if ($this->type === self::RECORD_CNAME) {
+                if (\strcasecmp($record->rdata, $this->target) === 0) {
+                    return true;
+                }
+            } elseif ($record->rdata === $this->target) {
                 return true;
             }
         }

Based on learnings

🧹 Nitpick comments (2)
src/DNS/Validator/DNS.php (2)

38-41: Clarify $target parameter documentation.

The $target parameter has different meanings depending on the record type (IP address for A/AAAA, domain for CNAME, issuer domain for CAA). The current docblock doesn't explain this, which may confuse users.

Apply this diff to improve the documentation:

     /**
-     * @param string $target
+     * @param string $target The expected value to validate against. For A/AAAA records, this should be an IP address. For CNAME records, a domain name. For CAA records, an issuer domain.
+     * @param string $type The DNS record type to validate (A, AAAA, CNAME, CAA).
+     * @param string $dnsServer Optional DNS server to use for validation.
      */
     public function __construct(protected string $target, protected string $type = self::RECORD_CNAME, string $dnsServer = '')

Based on learnings


91-93: Consider clarifying the CAA error message.

The message "Add new CAA record, or remove all other CAA records" could be more specific about the resolution path.

Consider this alternative:

         if ($this->type === self::RECORD_CAA) {
-            $messages[] = 'Add new CAA record, or remove all other CAA records';
+            $messages[] = "Add a CAA record for {$this->target} or remove conflicting CAA records";
         }

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c336f4e and d0cc670.

📒 Files selected for processing (1)
  • src/DNS/Validator/DNS.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/DNS/Validator/DNS.php (3)
src/DNS/Client.php (1)
  • Client (7-68)
src/DNS/Message.php (2)
  • Message (11-208)
  • response (111-139)
src/DNS/Message/Record.php (3)
  • Record (7-527)
  • typeNameToCode (311-314)
  • typeCodeToName (316-319)
🔇 Additional comments (5)
src/DNS/Validator/DNS.php (5)

12-23: LGTM!

The class constants are well-defined with clear naming for record types, failure reasons, and a reasonable default DNS server.


24-36: LGTM!

The properties are properly typed, and the recordValues array now has the correct PHPDoc annotation addressing the previous PHPStan concern.


98-104: LGTM!

The getLogs() method is straightforward and correctly returns the logs.


106-115: LGTM!

The delegation to isValidWithDNSServer is appropriate.


214-236: LGTM!

The isArray() and getType() methods correctly indicate this validator expects a single string value.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2259799 and 6ae024f.

📒 Files selected for processing (1)
  • src/DNS/Client.php (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Tests
src/DNS/Client.php

[error] 28-28: Exception: Failed to resolve DNS server hostname: ns1.digitalocean.com

Copy link

@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

♻️ Duplicate comments (1)
src/DNS/Validator/DNS.php (1)

171-176: Propagate parent validator state back to current instance.

When delegating to a parent domain for CAA validation, the current validator's diagnostic properties (count, recordValues, reason, logs) are not updated. This means callers will see count = 0 and empty recordValues even when the parent domain has CAA records, leading to incorrect error messages from getDescription().

Apply this diff to propagate the parent validator's state:

                 // Recursive validation by parent domain
                 $parts = \explode('.', $value);
                 \array_shift($parts);
                 $parentDomain = \implode('.', $parts);
                 $validator = new self($this->target, $this->type, $this->dnsServer);
-                return $validator->isValid($parentDomain);
+                $result = $validator->isValid($parentDomain);
+                
+                // Propagate diagnostic state from parent validator
+                $this->recordValues = $validator->recordValues;
+                $this->count = $validator->count;
+                $this->logs = $validator->getLogs();
+                $this->reason = $validator->reason;
+                
+                return $result;
             }

Based on learnings

🧹 Nitpick comments (1)
src/DNS/Validator/DNS.php (1)

101-103: Consider clarifying the CAA error message.

The message "Add new CAA record, or remove all other CAA records" is a bit ambiguous. Consider making it more specific to guide users on the resolution path.

Apply this diff for clearer guidance:

         if ($this->type === self::RECORD_CAA) {
-            $messages[] = 'Add new CAA record, or remove all other CAA records';
+            $messages[] = "Add a CAA record for {$this->target}, or remove all conflicting CAA records";
         }

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ae024f and 21a94fc.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • src/DNS/Validator/DNS.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/DNS/Validator/DNS.php (3)
src/DNS/Client.php (3)
  • Client (7-68)
  • __construct (15-32)
  • query (38-67)
src/DNS/Message.php (2)
  • Message (11-208)
  • response (111-139)
src/DNS/Message/Record.php (3)
  • Record (7-527)
  • typeNameToCode (311-314)
  • typeCodeToName (316-319)
🪛 GitHub Actions: CodeQL
src/DNS/Validator/DNS.php

[error] 46-46: PHPStan error: Found 1 error. Parameter #2 $type of class Utopia\DNS\Message\Question constructor expects int, int|null given. (argument.type). Command run: './vendor/bin/phpstan analyse --level 8 -c phpstan.neon src tests'

🪛 GitHub Actions: Linter
src/DNS/Validator/DNS.php

[error] 1-1: Pint lint failed. 1 style issue detected (PSR-12). Issue example: single_space_around_construct, no_whitespace_in...

Copy link

@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 (2)
src/DNS/Validator/DNS.php (2)

175-176: Propagate parent validator state to maintain diagnostic coherence.

When recursively validating the parent domain for CAA records, only the boolean result is returned. This leaves the current instance with count = 0, empty recordValues, and default reason, so callers see "missing records" even when the parent domain provides conflicting CAA data (e.g., issue "digicert.com").

Apply this diff to propagate diagnostics:

         $validator = new self($this->target, $this->type, $this->dnsServer);
-        return $validator->isValid($parentDomain);
+        $result = $validator->isValid($parentDomain);
+
+        $this->recordValues = $validator->recordValues;
+        $this->count = $validator->count;
+        $this->logs = $validator->getLogs();
+        $this->reason = $validator->reason;
+
+        return $result;

182-202: Fix CAA record processing flow and normalize domain comparisons.

The record comparison logic has several issues:

  1. CAA fall-through bug (lines 184-201): After processing CAA records at lines 184-194, the code falls through to line 199, which compares raw $record->rdata (format "FLAGS TAG VALUE") against $this->target. For CAA records, this comparison will never match and should be skipped.

  2. Case sensitivity (lines 192-193, 199-200): DNS domain names are case-insensitive, but comparisons use strict ===. Records like CNAME Foo.EXAMPLE.COM. or CAA 0 issue "LetsEncrypt.org" will fail even when valid.

  3. Fragile CAA parsing (line 187): Using [2] ?? '' after explode with limit 3 masks malformed CAA records instead of handling them explicitly.

Apply this diff to fix all three issues:

     foreach ($query as $record) {
         // CAA validation only needs to ensure domain
         if ($this->type === self::RECORD_CAA) {
             // Extract domain; comments showcase extraction steps in most complex scenario
             $rdata = $record->rdata; // 255 issuewild "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"
-            $rdata = \explode(' ', $rdata, 3)[2] ?? ''; // "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"
+            $parts = \explode(' ', $rdata, 3);
+            if (\count($parts) < 3) {
+                // Malformed CAA record, skip it
+                continue;
+            }
+            $rdata = $parts[2]; // "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600"
             $rdata = \trim($rdata, '"'); // certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600
             $rdata = \explode(';', $rdata, 2)[0]; // certainly.com
 
             $this->recordValues[] = $rdata;
-            if ($rdata === $this->target) {
+            if (\strcasecmp($rdata, $this->target) === 0) {
                 return true;
             }
+            // Skip the generic check below for CAA records
+            continue;
         } else {
             $this->recordValues[] = $record->rdata;
-        }
-
-        if ($record->rdata === $this->target) {
-            return true;
+            
+            // Use case-insensitive comparison for domain-based records (CNAME)
+            if ($this->type === self::RECORD_CNAME) {
+                if (\strcasecmp($record->rdata, $this->target) === 0) {
+                    return true;
+                }
+            } else {
+                // Use strict comparison for IP addresses (A/AAAA records)
+                if ($record->rdata === $this->target) {
+                    return true;
+                }
+            }
         }
     }
🧹 Nitpick comments (1)
src/DNS/Validator/DNS.php (1)

172-174: Consider using Domain class methods for parent extraction.

The manual parent domain extraction using explode, array_shift, and implode is functional but could be simplified. Since you're already using the Utopia\Domains\Domain class (line 166), check if it provides a method to get the parent domain directly.

For example, if Domain has a getParent() method:

-        // Recursive validation by parent domain
-        $parts = \explode('.', $value);
-        \array_shift($parts);
-        $parentDomain = \implode('.', $parts);
+        $parentDomain = $domain->getParent();

Otherwise, the current implementation is acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21a94fc and 6ef67e1.

📒 Files selected for processing (1)
  • src/DNS/Validator/DNS.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/DNS/Validator/DNS.php (4)
src/DNS/Client.php (3)
  • Client (7-68)
  • __construct (15-32)
  • query (38-67)
src/DNS/Message.php (2)
  • Message (11-208)
  • response (111-139)
src/DNS/Message/Question.php (1)
  • Question (7-51)
src/DNS/Message/Record.php (3)
  • Record (7-527)
  • typeNameToCode (311-314)
  • typeCodeToName (316-319)

@Meldiron Meldiron merged commit b69202f into main Nov 6, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants