Skip to content

#4856 #4825: cname record validations#4955

Open
DaisyGuti wants to merge 5 commits intomainfrom
dg/4856_cname_record_validations
Open

#4856 #4825: cname record validations#4955
DaisyGuti wants to merge 5 commits intomainfrom
dg/4856_cname_record_validations

Conversation

@DaisyGuti
Copy link
Copy Markdown
Contributor

@DaisyGuti DaisyGuti commented Apr 27, 2026

Ticket

Resolves #4856 #4825

Changes

Context for reviewers

This branch builds from work already done in dg/4779-no-dup-name-dns-records, which added form-level CNAME name != hostname validation. That PR left a gap: DnsRecord.clean() never enforced the same constraint, so it could be bypassed at the model level. This PR does that. No migrations needed.

Setup

Ensure the DNS hosting waffle flag is ON and the test domain is enrolled. On sandbox (e.g. DG) or local with DNS_MOCK_EXTERNAL_APIS=False:

  1. Go to /domain/<id>/dns/records on a DNS-enrolled domain (e.g. daisyepp.gov if on DG)
  2. Create a CNAME record where the name resolves to the same hostname as the content (e.g. name www, content www.daisyepp.gov)
  3. Confirm Add multi-field validations for CNAME record form #4825:
    • A single banner appears at the top: Name and target can't be the same.
    • The name field shows: Name can't be the same as the target.
    • The target field shows: Target can't be the same as the record name.
  4. Repeat using shorthand forms — @ with content daisyepp.gov, and bare label sub with content sub.daisyepp.gov — and confirm each is rejected

Code Review Verification Steps

Screenshot 2026-05-08 at 9 38 13 AM Screenshot 2026-04-27 at 10 14 00 AM Screenshot 2026-04-27 at 10 06 14 AM Screenshot 2026-04-27 at 10 00 13 AM

As the original developer, I have

Satisfied acceptance criteria and met development standards

  • Met the acceptance criteria, or will meet them in a subsequent PR
  • Created/modified automated tests
  • Update documentation in READMEs and/or onboarding guide

Ensured code standards are met (Original Developer)

  • If any updated dependencies on Pipfile, also update dependencies in requirements.txt.
  • Interactions with external systems are wrapped in try/except
  • Error handling exists for unusual or missing values

Validated user-facing changes (if applicable)

  • Follow the process for requesting a design review. If code is not user-facing, delete design reviewer checklist
  • Verify new pages have been added to .pa11yci file so that they will be tested with our automated accessibility testing
  • Checked keyboard navigability
  • Reviewed accessibility checklist using screen reader (such as NVDA with Chrome or Voiceover with Safari), ANDI, or WAVE:
    • Tested general usability
    • Page header structure
    • Landmarks
    • Links and buttons
  • Checked for errors or warnings with an a11y browser tool (such as ANDI or WAVE)

As a code reviewer, I have

Reviewed, tested, and left feedback about the changes

  • Pulled this branch locally and tested it
  • Verified code meets all checks above. Address any checks that are not satisfied
  • Reviewed this code and left comments. Indicate if comments must be addressed before code is merged
  • Checked that all code is adequately covered by tests
  • Verify migrations are valid and do not conflict with existing migrations

Validated user-facing changes as a developer

Note: Multiple code reviewers can share the checklists above, a second reviewer should not make a duplicate checklist. All checks should be checked before approving, even those labeled N/A.

  • New pages have been added to .pa11yci file so that they will be tested with our automated accessibility testing
  • Meets all designs and user flows provided by design/product
  • Checked keyboard navigability
  • Reviewed accessibility checklist using screen reader (such as NVDA with Chrome or Voiceover with Safari), ANDI, or WAVE:
    • Tested general usability
    • Page header structure
    • Landmarks
    • Links and buttons
  • Checked for errors or warnings with an a11y browser tool (such as ANDI or WAVE)
  • (Rarely needed) Tested as both an analyst and applicant user

As a designer reviewer, I have

Verified that the changes match the design intention

  • Checked that the design translated visually
  • Checked behavior. Comment any found issues or broken flows.
  • Checked different states (empty, one, some, error)
  • Checked for landmarks, page heading structure, and links

Validated user-facing changes as a designer

  • Checked keyboard navigability
  • Reviewed accessibility checklist using screen reader (such as NVDA with Chrome or Voiceover with Safari), ANDI, or WAVE:
    • Tested general usability
    • Page header structure
    • Landmarks
    • Links and buttons
  • Checked for errors or warnings with an a11y browser tool (such as ANDI or WAVE)
  • Tested with multiple browsers (check off which ones were used)
    • Chrome
    • Microsoft Edge
    • FireFox
    • Safari
  • (Rarely needed) Tested as both an analyst and applicant user

References

Screenshots

@DaisyGuti DaisyGuti marked this pull request as draft April 27, 2026 14:09
@DaisyGuti DaisyGuti marked this pull request as ready for review April 27, 2026 14:26
@DaisyGuti DaisyGuti moved this to 🎯 Ready in .gov Product Board Apr 27, 2026
@erinysong erinysong self-assigned this Apr 27, 2026
@katypies katypies removed the status in .gov Product Board Apr 28, 2026
@katypies katypies moved this to 👀 In review in .gov Product Board Apr 28, 2026
@erinysong
Copy link
Copy Markdown
Contributor

Tried making a CNAME record with the same name and hostname and was blocked by the new clean() updates 🎉 . Interestingly the error appears at the name field whereas when the error is caught at the form field, the error appears for the hostname field.
image
image
image

Comment thread src/registrar/forms/domain.py Outdated
@@ -1,6 +1,5 @@
"""Forms for domain management."""

import logging
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

didn't realize we weren't logging anything in forms - thank you for removing this!

… for cname on the form. Cleaned up the file a bit. Tiny refactor to minimize db calls.
@DaisyGuti DaisyGuti force-pushed the dg/4856_cname_record_validations branch from ba337aa to 3d2162e Compare May 6, 2026 12:38
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wires the existing CNAME "name must not equal hostname" check into the model's clean() so it runs on every save. Also handles the shorthand (@ expands to the domain, www expands to www.example.gov) so users can't bypass the check by typing the name in a different format. Pulls the zone's domain name lookup into a single helper (_resolve_domain_name) so it isn't queried twice per clean().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Adds a test class covering the form-level CNAME self-loop check across the three name shapes users can submit: FQDN, bare label, and @, plus the happy path. Also enables the previously-disabled CNAME entries in the shared test fixtures so the generic form-validation tests now cover CNAME alongside A/AAAA/MX.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Mirrors the form tests at the model layer by exercising DnsRecord.clean() directly. Confirms CNAME self-loops are rejected for every name shape and that the check is skipped when it shouldn't fire — empty content, or non-CNAME types where name == content is ok. Guards against regressions if a future code path saves a record while bypassing the form.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Love the tests!

vendor_dns_record=vendor_dns_record,
)

except Exception as e:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

logger.exception automatically attaches the traceback of the currently-handled exception. The old logger.error(f"...{str(e)}") only logged the exception's string form (e.g. "connection refused") — you got the what but not the where. With .exception you can see the exact line that blew up, which is what you actually need to debug a vendor-API failure.

@chaswick
Copy link
Copy Markdown
Contributor

chaswick commented May 7, 2026

When testing I noticed that if i didn't use the same case between the two fields, I would get no message (I do see a response error 400 in the javascript console). This was on -dg with all-trade-president.gov

image

Copy link
Copy Markdown
Contributor

@chaswick chaswick left a comment

Choose a reason for hiding this comment

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

Per comments below and the behavior i observed in sb, I believe those items should be addressed before approval.

"""CNAME record whose FQDN name equals its content should fail clean()."""
from django.core.exceptions import ValidationError

record = DnsRecord(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice. Would it make sense to also or in addition test case sensitivity, e.g. 'name=' and 'content=' are the same but different cases? name="sub.dns-test.gov" content="Sub.dns-test.gov"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Love the tests!


HOSTNAME_ERROR = "CNAME record hostname must not match record name."

def test_cname_fqdn_name_matches_content_raises_content_error(self):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice. Would it make sense to also or in addition test case sensitivity, e.g. data["name"] and "CNAME", <> are the same but different cases? Like "sub.dns-test.gov" and "Sub.dns-test.gov" for example.

errors["priority"] = [DNS_RECORD_PRIORITY_REQUIRED_ERROR_MESSAGE]

def _validate_exclusive_names(self, record_type, errors):
def _resolve_domain_name(self) -> str | None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nice!

self.VALID_CONTENT_BY_TYPE = {
"A": "192.0.2.10",
"AAAA": "2001:db8::1234:5678",
"CNAME": "www.example.com",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

glad we can use our unit tests for these now! thank you for getting them to work!

Copy link
Copy Markdown
Contributor

@erinysong erinysong left a comment

Choose a reason for hiding this comment

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

Tested and LGTM on my end! Small observation that when name == hostname, the validation error happens on the name field on admin but on the content field on the registrar. I don't think it's significant enough to block though but happy to consider otherwise

@DaisyGuti
Copy link
Copy Markdown
Contributor Author

Nice catch, thanks! @chaswick Fixed. Ready for rev.

Screenshot 2026-05-08 at 8 12 08 AM

@DaisyGuti
Copy link
Copy Markdown
Contributor Author

When testing I noticed that if i didn't use the same case between the two fields, I would get no message (I do see a response error 400 in the javascript console). This was on -dg with all-trade-president.gov

image

Just a heads up this work is not on dg. Notice no [dg] in the PR title. You can test locally or on your env. dg is currently being used by design to verify a different pr.

@DaisyGuti DaisyGuti changed the title #4856: cname record validations #4856 #4825: cname record validations May 8, 2026
@DaisyGuti DaisyGuti requested a review from erinysong May 8, 2026 13:44
@DaisyGuti
Copy link
Copy Markdown
Contributor Author

DaisyGuti commented May 8, 2026

It was a simple add that built on this work this PR Resolves #4856 & #4825 @erinysong @chaswick

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

Evaluate and implement validating CNAME record name!=hostname in DnsRecord model clean when record name uses shorthand

4 participants