From 9c4a558d7b6dc15f7aa3778bc73fae6c5971c0da Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Fri, 24 Jul 2026 10:26:38 -0400 Subject: [PATCH] Censor domain authcode by default in tools (#3170) When inspecting domain entities with tools like GetDomainCommand, displaying plaintext authentication codes is unnecessary and presents a security risk for credential exposure in logs and bug ticket comments (b/537293980). This commit modifies GetDomainCommand to redact sensitive authcodes by default using an in-memory entity builder snapshot while preserving database records. An optional --show_authcode flag with arity 1 is introduced to explicitly display authcodes when required for transfer authorization. BUG= http://b/537293980 --- .../registry/tools/GetDomainCommand.java | 24 +++++++++++++++-- .../registry/tools/GetDomainCommandTest.java | 26 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/google/registry/tools/GetDomainCommand.java b/core/src/main/java/google/registry/tools/GetDomainCommand.java index b7b8ec340..ec13a99f6 100644 --- a/core/src/main/java/google/registry/tools/GetDomainCommand.java +++ b/core/src/main/java/google/registry/tools/GetDomainCommand.java @@ -20,6 +20,8 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import google.registry.model.ForeignKeyUtils; import google.registry.model.domain.Domain; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.persistence.transaction.QueryComposer.Comparator; import google.registry.util.DomainNameUtils; import java.util.List; @@ -32,6 +34,12 @@ final class GetDomainCommand extends GetEppResourceCommand { @Parameter(names = "--show_deleted", description = "Include deleted domains in the print out") private boolean showDeleted = false; + @Parameter( + names = "--show_authcode", + description = "Include domain authentication code in output", + arity = 1) + private boolean showAuthcode = false; + @Parameter( description = "Fully qualified domain name(s)", required = true) @@ -51,14 +59,26 @@ final class GetDomainCommand extends GetEppResourceCommand { .stream() .forEach( d -> { - printResource("Domain", canonicalDomain, Optional.of(d)); + printResource( + "Domain", canonicalDomain, Optional.of(censorAuthcode(d))); })); } else { printResource( "Domain", canonicalDomain, - ForeignKeyUtils.loadResource(Domain.class, canonicalDomain, readTimestamp)); + ForeignKeyUtils.loadResource(Domain.class, canonicalDomain, readTimestamp) + .map(this::censorAuthcode)); } } } + + private Domain censorAuthcode(Domain domain) { + if (showAuthcode || domain.getAuthInfo() == null) { + return domain; + } + return domain + .asBuilder() + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("[REDACTED]"))) + .build(); + } } diff --git a/core/src/test/java/google/registry/tools/GetDomainCommandTest.java b/core/src/test/java/google/registry/tools/GetDomainCommandTest.java index 8657cf536..f1c6a9e9b 100644 --- a/core/src/test/java/google/registry/tools/GetDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/GetDomainCommandTest.java @@ -24,6 +24,8 @@ import static google.registry.util.DateTimeUtils.plusMonths; import static org.junit.jupiter.api.Assertions.assertThrows; import com.beust.jcommander.ParameterException; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.testing.DatabaseHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -136,4 +138,28 @@ class GetDomainCommandTest extends CommandTestCase { // Deleted assertInStdout("Websafe key: kind:Domain@sql:rO0ABXQABTMtVExE"); } + + @Test + void testSuccess_censorsAuthcodeByDefault() throws Exception { + persistResource( + DatabaseHelper.newDomain("example.tld") + .asBuilder() + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("secret123"))) + .build()); + runCommand("example.tld"); + assertInStdout("value=[REDACTED]"); + assertNotInStdout("secret123"); + } + + @Test + void testSuccess_showAuthcode() throws Exception { + persistResource( + DatabaseHelper.newDomain("example.tld") + .asBuilder() + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("secret123"))) + .build()); + runCommand("example.tld", "--show_authcode=true"); + assertInStdout("value=secret123"); + assertNotInStdout("[REDACTED]"); + } }