Redact WHOIS output instead of removing the fields altogether

See https://www.icann.org/resources/pages/gtld-registration-data-specs-en/#appendixA for details on how certain fields are redacted.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=199295355
This commit is contained in:
jianglai 2018-06-05 07:45:00 -07:00 committed by Ben McIlwain
parent e1bcc2e64d
commit 643b30d31f
6 changed files with 236 additions and 70 deletions

View file

@ -81,7 +81,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
.stream() .stream()
.filter(RegistrarContact::getVisibleInDomainWhoisAsAbuse) .filter(RegistrarContact::getVisibleInDomainWhoisAsAbuse)
.findFirst(); .findFirst();
DomainEmitter domainEmitter = return WhoisResponseResults.create(
new DomainEmitter() new DomainEmitter()
.emitField( .emitField(
"Domain Name", "Domain Name",
@ -104,25 +104,22 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
"Registrar Abuse Contact Phone", "Registrar Abuse Contact Phone",
abuseContact.map(RegistrarContact::getPhoneNumber).orElse(null)) abuseContact.map(RegistrarContact::getPhoneNumber).orElse(null))
.emitStatusValues(domain.getStatusValues(), domain.getGracePeriods()) .emitStatusValues(domain.getStatusValues(), domain.getGracePeriods())
.emitContact( .emitContact("Registrant", Optional.of(domain.getRegistrant()), preferUnicode)
"Registrant", Optional.of(domain.getRegistrant()), preferUnicode, fullOutput); .emitContact("Admin", getContactReference(Type.ADMIN), preferUnicode)
if (fullOutput) { .emitContact("Tech", getContactReference(Type.TECH), preferUnicode)
domainEmitter .emitContact("Billing", getContactReference(Type.BILLING), preferUnicode)
.emitContact("Admin", getContactReference(Type.ADMIN), preferUnicode, fullOutput) .emitSet(
.emitContact("Tech", getContactReference(Type.TECH), preferUnicode, fullOutput) "Name Server",
.emitContact("Billing", getContactReference(Type.BILLING), preferUnicode, fullOutput); domain.loadNameserverFullyQualifiedHostNames(),
} hostName -> maybeFormatHostname(hostName, preferUnicode))
domainEmitter .emitField(
.emitSet( "DNSSEC", isNullOrEmpty(domain.getDsData()) ? "unsigned" : "signedDelegation")
"Name Server", .emitWicfLink()
domain.loadNameserverFullyQualifiedHostNames(), .emitLastUpdated(getTimestamp())
hostName -> maybeFormatHostname(hostName, preferUnicode)) .emitAwipMessage()
.emitField("DNSSEC", isNullOrEmpty(domain.getDsData()) ? "unsigned" : "signedDelegation") .emitFooter(disclaimer)
.emitWicfLink() .toString(),
.emitLastUpdated(getTimestamp()) 1);
.emitAwipMessage()
.emitFooter(disclaimer);
return WhoisResponseResults.create(domainEmitter.toString(), 1);
} }
/** Returns the contact of the given type. */ /** Returns the contact of the given type. */
@ -139,17 +136,15 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
if (phoneNumber == null) { if (phoneNumber == null) {
return this; return this;
} }
return emitFieldIfDefined(ImmutableList.of(contactType, title), phoneNumber.getPhoneNumber()) return emitFieldIfDefined(
ImmutableList.of(contactType, title), phoneNumber.getPhoneNumber(), fullOutput)
.emitFieldIfDefined( .emitFieldIfDefined(
ImmutableList.of(contactType, title, "Ext"), phoneNumber.getExtension()); ImmutableList.of(contactType, title, "Ext"), phoneNumber.getExtension(), fullOutput);
} }
/** Emit the contact entry of the given type. */ /** Emit the contact entry of the given type. */
DomainEmitter emitContact( DomainEmitter emitContact(
String contactType, String contactType, Optional<Key<ContactResource>> contact, boolean preferUnicode) {
Optional<Key<ContactResource>> contact,
boolean preferUnicode,
boolean fullOutput) {
if (!contact.isPresent()) { if (!contact.isPresent()) {
return this; return this;
} }
@ -168,24 +163,21 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
preferUnicode, preferUnicode,
contactResource.getLocalizedPostalInfo(), contactResource.getLocalizedPostalInfo(),
contactResource.getInternationalizedPostalInfo()); contactResource.getInternationalizedPostalInfo());
if (fullOutput) { // ICANN Consistent Labeling & Display policy requires that this be the ROID.
// If the full output is to be displayed, show all fields for all contact types. emitField(
// ICANN Consistent Labeling & Display policy requires that this be the ROID. ImmutableList.of("Registry", contactType, "ID"), contactResource.getRepoId(), fullOutput);
emitField(ImmutableList.of("Registry", contactType, "ID"), contactResource.getRepoId()); if (postalInfo != null) {
if (postalInfo != null) { emitFieldIfDefined(ImmutableList.of(contactType, "Name"), postalInfo.getName(), fullOutput);
emitFieldIfDefined(ImmutableList.of(contactType, "Name"), postalInfo.getName()); emitFieldIfDefined(
emitFieldIfDefined(ImmutableList.of(contactType, "Organization"), postalInfo.getOrg()); ImmutableList.of(contactType, "Organization"),
emitAddress(contactType, postalInfo.getAddress(), fullOutput); postalInfo.getOrg(),
} fullOutput || contactType.equals("Registrant"));
emitPhone(contactType, "Phone", contactResource.getVoiceNumber()); emitAddress(contactType, postalInfo.getAddress(), fullOutput);
emitPhone(contactType, "Fax", contactResource.getFaxNumber());
emitField(ImmutableList.of(contactType, "Email"), contactResource.getEmailAddress());
} else {
if (postalInfo != null) {
emitFieldIfDefined(ImmutableList.of(contactType, "Organization"), postalInfo.getOrg());
emitAddress(contactType, postalInfo.getAddress(), fullOutput);
}
} }
emitPhone(contactType, "Phone", contactResource.getVoiceNumber());
emitPhone(contactType, "Fax", contactResource.getFaxNumber());
emitField(
ImmutableList.of(contactType, "Email"), contactResource.getEmailAddress(), fullOutput);
return this; return this;
} }

View file

@ -39,6 +39,9 @@ abstract class WhoisResponseImpl implements WhoisResponse {
/** ICANN problem reporting URL appended to all WHOIS responses. */ /** ICANN problem reporting URL appended to all WHOIS responses. */
private static final String ICANN_REPORTING_URL = "https://www.icann.org/wicf/"; private static final String ICANN_REPORTING_URL = "https://www.icann.org/wicf/";
/** Text to display when the field is redacted for privacy. */
static final String REDACT_TEXT = "REDACTED FOR PRIVACY";
/** The time at which this response was created. */ /** The time at which this response was created. */
private final DateTime timestamp; private final DateTime timestamp;
@ -94,59 +97,105 @@ abstract class WhoisResponseImpl implements WhoisResponse {
return emitList(title, values.stream().map(transform).sorted().collect(toImmutableList())); return emitList(title, values.stream().map(transform).sorted().collect(toImmutableList()));
} }
/** Helper method that loops over a list of values and calls {@link #emitField}. */ /**
E emitList(String title, Iterable<String> values) { * Helper method that loops over a list of values and calls {@link #emitField}.
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitList(String title, Iterable<String> values, boolean fullOutput) {
for (String value : values) { for (String value : values) {
emitField(title, value); emitField(title, value, fullOutput);
} }
return thisCastToDerived(); return thisCastToDerived();
} }
/** Emit the field name and value followed by a newline, but only if a value exists. */ /** Helper method that loops over a list of values and calls {@link #emitField}. */
E emitFieldIfDefined(String name, @Nullable String value) { E emitList(String title, Iterable<String> values) {
return emitList(title, values, true);
}
/**
* Emit the field name and value followed by a newline, but only if a value exists.
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitFieldIfDefined(String name, @Nullable String value, boolean fullOutput) {
if (isNullOrEmpty(value)) { if (isNullOrEmpty(value)) {
return thisCastToDerived(); return thisCastToDerived();
} }
stringBuilder.append(cleanse(name)).append(':'); stringBuilder.append(cleanse(name)).append(':');
stringBuilder.append(' ').append(cleanse(value)); stringBuilder.append(' ').append(fullOutput ? cleanse(value) : REDACT_TEXT);
return emitNewline();
}
/** Emit the field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(String name, @Nullable String value) {
return emitFieldIfDefined(name, value, true);
}
/**
* Emit a multi-part field name and value followed by a newline, but only if a value exists.
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitFieldIfDefined(List<String> nameParts, String value, boolean fullOutput) {
if (isNullOrEmpty(value)) {
return thisCastToDerived();
}
return emitField(nameParts, value, fullOutput);
}
/** Emit a multi-part field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(List<String> nameParts, String value) {
return emitFieldIfDefined(nameParts, value, true);
}
/**
* Emit the field name and value followed by a newline. /*
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitField(String name, @Nullable String value, boolean fullOutput) {
stringBuilder.append(cleanse(name)).append(':');
if (!isNullOrEmpty(value)) {
stringBuilder.append(' ').append(fullOutput ? cleanse(value) : REDACT_TEXT);
}
return emitNewline(); return emitNewline();
} }
/** Emit the field name and value followed by a newline. */ /** Emit the field name and value followed by a newline. */
E emitField(String name, @Nullable String value) { E emitField(String name, @Nullable String value) {
stringBuilder.append(cleanse(name)).append(':'); return emitField(name, value, true);
if (!isNullOrEmpty(value)) {
stringBuilder.append(' ').append(cleanse(value));
}
return emitNewline();
} }
/** Emit a multi-part field name and value followed by a newline, but only if a value exists. */ /**
E emitFieldIfDefined(List<String> nameParts, String value) { * Emit a multi-part field name and value followed by a newline.
if (isNullOrEmpty(value)) { *
return thisCastToDerived(); * <p>This method redacts the output unless {@code fullOutput} is {@code true}.
} */
return emitField(nameParts, value); E emitField(List<String> nameParts, String value, boolean fullOutput) {
return emitField(Joiner.on(' ').join(nameParts), value, fullOutput);
} }
/** Emit a multi-part field name and value followed by a newline. */ /** Emit a multi-part field name and value followed by a newline. */
E emitField(List<String> nameParts, String value) { E emitField(List<String> nameParts, String value) {
return emitField(Joiner.on(' ').join(nameParts), value); return emitField(nameParts, value, true);
} }
/** Emit a contact address. */ /** Emit a contact address. */
E emitAddress(@Nullable String prefix, @Nullable Address address, boolean fullOutput) { E emitAddress(@Nullable String prefix, @Nullable Address address, boolean fullOutput) {
prefix = isNullOrEmpty(prefix) ? "" : prefix + " "; prefix = isNullOrEmpty(prefix) ? "" : prefix + " ";
if (address != null) { if (address != null) {
if (fullOutput) { emitList(prefix + "Street", address.getStreet(), fullOutput);
emitList(prefix + "Street", address.getStreet()); emitField(prefix + "City", address.getCity(), fullOutput);
emitField(prefix + "City", address.getCity()); emitField(
} prefix + "State/Province",
emitField(prefix + "State/Province", address.getState()); address.getState(),
if (fullOutput) { (fullOutput || prefix.equals("Registrant ")));
emitField(prefix + "Postal Code", address.getZip()); emitField(prefix + "Postal Code", address.getZip(), fullOutput);
} emitField(
emitField(prefix + "Country", address.getCountryCode()); prefix + "Country",
address.getCountryCode(),
fullOutput || prefix.equals("Registrant "));
} }
return thisCastToDerived(); return thisCastToDerived();
} }

View file

@ -13,9 +13,39 @@ Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibit
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: GOOGLE INCORPORATED <script> Registrant Organization: GOOGLE INCORPORATED <script>
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: BM Registrant State/Province: BM
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: US Registrant Country: US
Registrant Phone: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
Registry Admin ID: REDACTED FOR PRIVACY
Admin Name: REDACTED FOR PRIVACY
Admin Organization: REDACTED FOR PRIVACY
Admin Street: REDACTED FOR PRIVACY
Admin City: REDACTED FOR PRIVACY
Admin State/Province: REDACTED FOR PRIVACY
Admin Postal Code: REDACTED FOR PRIVACY
Admin Country: REDACTED FOR PRIVACY
Admin Phone: REDACTED FOR PRIVACY
Admin Fax: REDACTED FOR PRIVACY
Admin Email: REDACTED FOR PRIVACY
Registry Tech ID: REDACTED FOR PRIVACY
Tech Name: REDACTED FOR PRIVACY
Tech Organization: REDACTED FOR PRIVACY
Tech Street: REDACTED FOR PRIVACY
Tech City: REDACTED FOR PRIVACY
Tech State/Province: REDACTED FOR PRIVACY
Tech Postal Code: REDACTED FOR PRIVACY
Tech Country: REDACTED FOR PRIVACY
Tech Phone: REDACTED FOR PRIVACY
Tech Fax: REDACTED FOR PRIVACY
Tech Email: REDACTED FOR PRIVACY
Name Server: ns1.cat.lol Name Server: ns1.cat.lol
Name Server: ns2.cat.lol Name Server: ns2.cat.lol
DNSSEC: signedDelegation DNSSEC: signedDelegation

View file

@ -13,9 +13,39 @@ Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibit
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: GOOGLE INCORPORATED <script> Registrant Organization: GOOGLE INCORPORATED <script>
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: BM Registrant State/Province: BM
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: US Registrant Country: US
Registrant Phone: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
Registry Admin ID: REDACTED FOR PRIVACY
Admin Name: REDACTED FOR PRIVACY
Admin Organization: REDACTED FOR PRIVACY
Admin Street: REDACTED FOR PRIVACY
Admin City: REDACTED FOR PRIVACY
Admin State/Province: REDACTED FOR PRIVACY
Admin Postal Code: REDACTED FOR PRIVACY
Admin Country: REDACTED FOR PRIVACY
Admin Phone: REDACTED FOR PRIVACY
Admin Fax: REDACTED FOR PRIVACY
Admin Email: REDACTED FOR PRIVACY
Registry Tech ID: REDACTED FOR PRIVACY
Tech Name: REDACTED FOR PRIVACY
Tech Organization: REDACTED FOR PRIVACY
Tech Street: REDACTED FOR PRIVACY
Tech City: REDACTED FOR PRIVACY
Tech State/Province: REDACTED FOR PRIVACY
Tech Postal Code: REDACTED FOR PRIVACY
Tech Country: REDACTED FOR PRIVACY
Tech Phone: REDACTED FOR PRIVACY
Tech Fax: REDACTED FOR PRIVACY
Tech Email: REDACTED FOR PRIVACY
Name Server: ns1.cat.xn--q9jyb4c Name Server: ns1.cat.xn--q9jyb4c
Name Server: ns2.cat.xn--q9jyb4c Name Server: ns2.cat.xn--q9jyb4c
DNSSEC: signedDelegation DNSSEC: signedDelegation

View file

@ -13,9 +13,39 @@ Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibit
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: GOOGLE INCORPORATED <script> Registrant Organization: GOOGLE INCORPORATED <script>
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: BM Registrant State/Province: BM
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: US Registrant Country: US
Registrant Phone: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
Registry Admin ID: REDACTED FOR PRIVACY
Admin Name: REDACTED FOR PRIVACY
Admin Organization: REDACTED FOR PRIVACY
Admin Street: REDACTED FOR PRIVACY
Admin City: REDACTED FOR PRIVACY
Admin State/Province: REDACTED FOR PRIVACY
Admin Postal Code: REDACTED FOR PRIVACY
Admin Country: REDACTED FOR PRIVACY
Admin Phone: REDACTED FOR PRIVACY
Admin Fax: REDACTED FOR PRIVACY
Admin Email: REDACTED FOR PRIVACY
Registry Tech ID: REDACTED FOR PRIVACY
Tech Name: REDACTED FOR PRIVACY
Tech Organization: REDACTED FOR PRIVACY
Tech Street: REDACTED FOR PRIVACY
Tech City: REDACTED FOR PRIVACY
Tech State/Province: REDACTED FOR PRIVACY
Tech Postal Code: REDACTED FOR PRIVACY
Tech Country: REDACTED FOR PRIVACY
Tech Phone: REDACTED FOR PRIVACY
Tech Fax: REDACTED FOR PRIVACY
Tech Email: REDACTED FOR PRIVACY
Name Server: ns1.cat.みんな Name Server: ns1.cat.みんな
Name Server: ns2.cat.みんな Name Server: ns2.cat.みんな
DNSSEC: signedDelegation DNSSEC: signedDelegation

View file

@ -15,9 +15,44 @@ Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Domain Status: transferPeriod https://icann.org/epp#transferPeriod Domain Status: transferPeriod https://icann.org/epp#transferPeriod
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: Tom & Jerry Corp. Registrant Organization: Tom & Jerry Corp.
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: AP Registrant State/Province: AP
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: EX Registrant Country: EX
Registrant Phone: REDACTED FOR PRIVACY
Registrant Phone Ext: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Fax Ext: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
Registry Admin ID: REDACTED FOR PRIVACY
Admin Name: REDACTED FOR PRIVACY
Admin Organization: REDACTED FOR PRIVACY
Admin Street: REDACTED FOR PRIVACY
Admin City: REDACTED FOR PRIVACY
Admin State/Province: REDACTED FOR PRIVACY
Admin Postal Code: REDACTED FOR PRIVACY
Admin Country: REDACTED FOR PRIVACY
Admin Phone: REDACTED FOR PRIVACY
Admin Phone Ext: REDACTED FOR PRIVACY
Admin Fax: REDACTED FOR PRIVACY
Admin Email: REDACTED FOR PRIVACY
Registry Tech ID: REDACTED FOR PRIVACY
Tech Name: REDACTED FOR PRIVACY
Tech Organization: REDACTED FOR PRIVACY
Tech Street: REDACTED FOR PRIVACY
Tech City: REDACTED FOR PRIVACY
Tech State/Province: REDACTED FOR PRIVACY
Tech Postal Code: REDACTED FOR PRIVACY
Tech Country: REDACTED FOR PRIVACY
Tech Phone: REDACTED FOR PRIVACY
Tech Phone Ext: REDACTED FOR PRIVACY
Tech Fax: REDACTED FOR PRIVACY
Tech Fax Ext: REDACTED FOR PRIVACY
Tech Email: REDACTED FOR PRIVACY
Name Server: ns01.exampleregistrar.tld Name Server: ns01.exampleregistrar.tld
Name Server: ns02.exampleregistrar.tld Name Server: ns02.exampleregistrar.tld
DNSSEC: signedDelegation DNSSEC: signedDelegation