diff --git a/java/google/registry/config/RegistryConfig.java b/java/google/registry/config/RegistryConfig.java index 0fa174519..4b5f3e544 100644 --- a/java/google/registry/config/RegistryConfig.java +++ b/java/google/registry/config/RegistryConfig.java @@ -815,6 +815,7 @@ public final class RegistryConfig { * * @see google.registry.reporting.icann.ReportingEmailUtils * @see google.registry.reporting.billing.BillingEmailUtils + * @see google.registry.reporting.spec11.Spec11EmailUtils */ @Provides @Config("alertRecipientEmailAddress") @@ -827,13 +828,35 @@ public final class RegistryConfig { * * @see google.registry.reporting.icann.ReportingEmailUtils * @see google.registry.reporting.billing.BillingEmailUtils + * @see google.registry.reporting.spec11.Spec11EmailUtils */ - @Provides @Config("alertSenderEmailAddress") public static String provideAlertSenderEmailAddress( @Config("projectId") String projectId, RegistryConfigSettings config) { - return String.format("%s@%s", projectId, config.misc.alertEmailSenderDomain); + return String.format("%s-no-reply@%s", projectId, config.misc.alertEmailSenderDomain); + } + + /** + * Returns the email address to which spec 11 email should be replied. + * + * @see google.registry.reporting.spec11.Spec11EmailUtils + */ + @Provides + @Config("spec11ReplyToEmailAddress") + public static String provideSpec11ReplyToEmailAddress(RegistryConfigSettings config) { + return config.misc.spec11ReplyToEmailAddress; + } + + /** + * Returns the template for the body of the spec 11 email to the registrars. + * + * @see google.registry.reporting.spec11.Spec11EmailUtils + */ + @Provides + @Config("spec11EmailBodyTemplate") + public static String provideSpec11EmailBodyTemplate(RegistryConfigSettings config) { + return config.registryPolicy.spec11EmailBodyTemplate; } /** diff --git a/java/google/registry/config/RegistryConfigSettings.java b/java/google/registry/config/RegistryConfigSettings.java index 15118679a..f5e23c2b4 100644 --- a/java/google/registry/config/RegistryConfigSettings.java +++ b/java/google/registry/config/RegistryConfigSettings.java @@ -90,6 +90,7 @@ public class RegistryConfigSettings { public String reservedTermsExportDisclaimer; public String whoisDisclaimer; public String rdapTos; + public String spec11EmailBodyTemplate; } /** Configuration for Cloud Datastore. */ @@ -160,6 +161,7 @@ public class RegistryConfigSettings { public static class Misc { public String sheetExportId; public String alertRecipientEmailAddress; + public String spec11ReplyToEmailAddress; public String alertEmailSenderDomain; public int asyncDeleteDelaySeconds; } diff --git a/java/google/registry/config/files/default-config.yaml b/java/google/registry/config/files/default-config.yaml index 912353b48..d4dc13f43 100644 --- a/java/google/registry/config/files/default-config.yaml +++ b/java/google/registry/config/files/default-config.yaml @@ -142,6 +142,33 @@ registryPolicy: We reserve the right to modify this agreement at any time. + # Body of the spec 11 email sent to registrars. + # Items in braces are to be replaced. + spec11EmailBodyTemplate: | + Dear registrar partner, + + The registry conducts periodic technical analyses of all domains registered + in its TLDs. As part of this analysis, the following domains that you + manage were flagged for potential security concerns: + + {LIST_OF_THREATS} + + Please communicate these findings to the registrant and work with the + registrant to mitigate any security issues and have the domains delisted. + + Some helpful sites for getting off a blocked list include: + + - Google Search Console (https://search.google.com/search-console/about) + -- includes information and tools for webmasters to learn about and + mitigate security threats and have their websites delisted + - first.org -- a registry of Computer Emergency Response Teams (CERTs) + that may be able to assist in mitigation + - stopbadware.org -- a non-profit anti-malware organization that provides + support and information for webmasters dealing with security threats + + If you have any questions regarding this notice, please contact + {REPLY_TO_EMAIL}. + datastore: # Number of commit log buckets in Datastore. Lowering this after initial # install risks losing up to a days' worth of differential backups. @@ -305,6 +332,10 @@ misc: # Address we send alert summary emails to. alertRecipientEmailAddress: email@example.com + # Address to which the Spec 11 emails to registrars should be replied. This needs + # to be a deliverable email address in case the registrars want to contact us. + spec11ReplyToEmailAddress: reply-to@example.com + # Domain for the email address we send alert summary emails from. alertEmailSenderDomain: appspotmail.com diff --git a/java/google/registry/reporting/spec11/Spec11EmailUtils.java b/java/google/registry/reporting/spec11/Spec11EmailUtils.java index 310afbd46..76b716446 100644 --- a/java/google/registry/reporting/spec11/Spec11EmailUtils.java +++ b/java/google/registry/reporting/spec11/Spec11EmailUtils.java @@ -47,8 +47,10 @@ public class Spec11EmailUtils { private final YearMonth yearMonth; private final String alertSenderAddress; private final String alertRecipientAddress; + private final String spec11ReplyToAddress; private final String reportingBucket; private final String spec11ReportDirectory; + private final String spec11EmailBodyTemplate; private final GcsUtils gcsUtils; private final Retrier retrier; @@ -58,6 +60,8 @@ public class Spec11EmailUtils { YearMonth yearMonth, @Config("alertSenderEmailAddress") String alertSenderAddress, @Config("alertRecipientEmailAddress") String alertRecipientAddress, + @Config("spec11ReplyToEmailAddress") String spec11ReplyToAddress, + @Config("spec11EmailBodyTemplate") String spec11EmailBodyTemplate, @Config("reportingBucket") String reportingBucket, @Spec11ReportDirectory String spec11ReportDirectory, GcsUtils gcsUtils, @@ -66,8 +70,10 @@ public class Spec11EmailUtils { this.yearMonth = yearMonth; this.alertSenderAddress = alertSenderAddress; this.alertRecipientAddress = alertRecipientAddress; + this.spec11ReplyToAddress = spec11ReplyToAddress; this.reportingBucket = reportingBucket; this.spec11ReportDirectory = spec11ReportDirectory; + this.spec11EmailBodyTemplate = spec11EmailBodyTemplate; this.gcsUtils = gcsUtils; this.retrier = retrier; } @@ -114,23 +120,24 @@ public class Spec11EmailUtils { JSONObject reportJSON = new JSONObject(line); String registrarEmail = reportJSON.getString(Spec11Pipeline.REGISTRAR_EMAIL_FIELD); JSONArray threatMatches = reportJSON.getJSONArray(Spec11Pipeline.THREAT_MATCHES_FIELD); - StringBuilder body = - new StringBuilder("Hello registrar partner,\n") - .append("We have detected problems with the following domains:\n"); + StringBuilder threatList = new StringBuilder(); for (int i = 0; i < threatMatches.length(); i++) { ThreatMatch threatMatch = ThreatMatch.fromJSON(threatMatches.getJSONObject(i)); - body.append( + threatList.append( String.format( "%s - %s\n", threatMatch.fullyQualifiedDomainName(), threatMatch.threatType())); } - body.append("At the moment, no action is required. This is purely informatory.") - .append("Regards,\nGoogle Registry\n"); + String body = + spec11EmailBodyTemplate + .replace("{REPLY_TO_EMAIL}", spec11ReplyToAddress) + .replace("{LIST_OF_THREATS}", threatList.toString()); Message msg = emailService.createMessage(); msg.setSubject( String.format("Google Registry Monthly Threat Detector [%s]", yearMonth.toString())); msg.setText(body.toString()); msg.setFrom(new InternetAddress(alertSenderAddress)); - msg.setRecipient(RecipientType.TO, new InternetAddress(registrarEmail)); + msg.addRecipient(RecipientType.TO, new InternetAddress(registrarEmail)); + msg.addRecipient(RecipientType.BCC, new InternetAddress(spec11ReplyToAddress)); emailService.sendMessage(msg); } diff --git a/javatests/google/registry/reporting/spec11/BUILD b/javatests/google/registry/reporting/spec11/BUILD index e7c9b00ba..fb60fa9eb 100644 --- a/javatests/google/registry/reporting/spec11/BUILD +++ b/javatests/google/registry/reporting/spec11/BUILD @@ -19,6 +19,7 @@ java_library( "@com_google_apis_google_api_services_dataflow", "@com_google_appengine_api_1_0_sdk", "@com_google_appengine_tools_appengine_gcs_client", + "@com_google_code_findbugs_jsr305", "@com_google_dagger", "@com_google_guava", "@com_google_truth", diff --git a/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java b/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java index ce43ad2ae..8bf3c1386 100644 --- a/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java +++ b/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java @@ -36,7 +36,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Properties; +import javax.annotation.Nullable; import javax.mail.Message; +import javax.mail.Message.RecipientType; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.InternetAddress; @@ -83,6 +85,8 @@ public class Spec11EmailUtilsTest { new YearMonth(2018, 6), "my-sender@test.com", "my-receiver@test.com", + "my-reply-to@test.com", + "{LIST_OF_THREATS}\n{REPLY_TO_EMAIL}", "test-bucket", "icann/spec11/2018-06/SPEC11_MONTHLY_REPORT", gcsUtils, @@ -99,27 +103,21 @@ public class Spec11EmailUtilsTest { capturedMessages.get(0), "my-sender@test.com", "a@fake.com", + "my-reply-to@test.com", "Google Registry Monthly Threat Detector [2018-06]", - "Hello registrar partner,\n" - + "We have detected problems with the following domains:\n" - + "a.com - MALWARE\n" - + "At the moment, no action is required. This is purely informatory." - + "Regards,\nGoogle Registry\n"); + "a.com - MALWARE\n\nmy-reply-to@test.com"); validateMessage( capturedMessages.get(1), "my-sender@test.com", "b@fake.com", + "my-reply-to@test.com", "Google Registry Monthly Threat Detector [2018-06]", - "Hello registrar partner,\n" - + "We have detected problems with the following domains:\n" - + "b.com - MALWARE\n" - + "c.com - MALWARE\n" - + "At the moment, no action is required. This is purely informatory." - + "Regards,\nGoogle Registry\n"); + "b.com - MALWARE\nc.com - MALWARE\n\nmy-reply-to@test.com"); validateMessage( capturedMessages.get(2), "my-sender@test.com", "my-receiver@test.com", + null, "Spec11 Pipeline Success 2018-06", "Spec11 reporting completed successfully."); } @@ -163,6 +161,7 @@ public class Spec11EmailUtilsTest { gotMessage.getValue(), "my-sender@test.com", "my-receiver@test.com", + null, "Spec11 Emailing Failure 2018-06", "Emailing spec11 reports failed due to expected"); } @@ -175,17 +174,31 @@ public class Spec11EmailUtilsTest { gotMessage.getValue(), "my-sender@test.com", "my-receiver@test.com", + null, "Spec11 Pipeline Alert: 2018-06", "Alert!"); } private void validateMessage( - Message message, String from, String recipient, String subject, String body) + Message message, + String from, + String recipient, + @Nullable String replyTo, + String subject, + String body) throws MessagingException, IOException { assertThat(message.getFrom()).asList().containsExactly(new InternetAddress(from)); - assertThat(message.getAllRecipients()) + assertThat(message.getRecipients(RecipientType.TO)) .asList() .containsExactly(new InternetAddress(recipient)); + if (replyTo == null) { + assertThat(message.getRecipients(RecipientType.BCC)).isNull(); + } else { + assertThat(message.getRecipients(RecipientType.BCC)) + .asList() + .containsExactly(new InternetAddress(replyTo)); + } + assertThat(message.getRecipients(RecipientType.CC)).isNull(); assertThat(message.getSubject()).isEqualTo(subject); assertThat(message.getContentType()).isEqualTo("text/plain"); assertThat(message.getContent().toString()).isEqualTo(body);