Skip to content
This repository has been archived by the owner on Sep 12, 2018. It is now read-only.

Commit

Permalink
mail: Add 'rel=noreferrer' to external links
Browse files Browse the repository at this point in the history
Add 'rel=noreferrer' to every external link only if
application.noreferrer is true. It requests clients not to add
Referer header in the request to the link.

We assume a link is a external if the hostname is not empty and does not
equal to the system's hostname from Config.getHostname().

Note: It works only for clients which support HTML5.
  • Loading branch information
Yi EungJun committed Aug 8, 2014
1 parent 53f9101 commit 4d4f0a9
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 7 deletions.
40 changes: 33 additions & 7 deletions app/models/NotificationMail.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import models.enumeration.UserState;
import models.resource.Resource;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.mail.HtmlEmail;
import org.joda.time.DateTime;
import org.jsoup.Jsoup;
Expand Down Expand Up @@ -188,12 +189,12 @@ private static void sendNotification(NotificationEvent event) {
Lang lang = Lang.apply(langCode);

String message = event.getMessage(lang);
String urlToView = Url.create(event.getUrlToView());
String urlToView = event.getUrlToView();
String reference = Url.removeFragment(event.getUrlToView());

email.setSubject(event.title);
email.setHtmlMsg(getHtmlMessage(lang, message, urlToView, event.getResource()));
email.setTextMsg(getPlainMessage(lang, message, urlToView));
email.setTextMsg(getPlainMessage(lang, message, Url.create(urlToView)));
email.setCharset("utf-8");
email.addHeader("References", "<" + reference + "@" + Config.getHostname() + ">");
email.setSentDate(event.created);
Expand Down Expand Up @@ -222,18 +223,43 @@ private static String getRenderedHTMLWithTemplate(Lang lang, String message, Str
return views.html.common.notificationMail.render(lang, message, urlToView, resource).toString();
}

private static void handleLinks(Document doc){
/**
* Make every link to be absolute and to have 'rel=noreferrer' if
* necessary.
*/
public static void handleLinks(Document doc){
String hostname = Config.getHostname();
String[] attrNames = {"src", "href"};
Boolean noreferrer =
play.Configuration.root().getBoolean("application.noreferrer");

for (String attrName : attrNames) {
Elements tags = doc.select("*[" + attrName + "]");
for (Element tag : tags) {
String uri = tag.attr(attrName);
boolean isNoreferrerRequired = false;
String uriString = tag.attr(attrName);

if (noreferrer && attrName.equals("href")) {
isNoreferrerRequired = true;
}

try {
if (!new URI(uri).isAbsolute()) {
tag.attr(attrName, Url.create(uri));
URI uri = new URI(uriString);

if (!uri.isAbsolute()) {
tag.attr(attrName, Url.create(uriString));
}

if (uri.getHost() == null || uri.getHost().equals(hostname)) {
isNoreferrerRequired = false;
}
} catch (URISyntaxException e) {
play.Logger.info("A malformed URI is ignored", e);
play.Logger.info("A malformed URI is detected while" +
" checking an email to send", e);
}

if (isNoreferrerRequired) {
tag.attr("rel", tag.attr("rel") + " noreferrer");
}
}
}
Expand Down
93 changes: 93 additions & 0 deletions test/models/NotificationMailTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Yobi, Project Hosting SW
*
* Copyright 2014 NAVER Corp.
* http://yobi.io
*
* @Author Yi EungJun
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package models;

import java.util.Map;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import play.test.FakeApplication;
import play.test.Helpers;

import static org.fest.assertions.Assertions.assertThat;

public class NotificationMailTest {
protected static FakeApplication app;
private static Map<String, String> additionalConfiguration;

@BeforeClass
public static void before() {
additionalConfiguration = support.Helpers.makeTestConfig();
additionalConfiguration.put("application.noreferrer", "true");
additionalConfiguration.put("application.hostname", "yobi.io");
app = support.Helpers.makeTestApplication(additionalConfiguration);
Helpers.start(app);
}

@AfterClass
public static void after() {
Helpers.stop(app);
}

@Test
public void handleLinks() {
// Given
Document doc = Jsoup.parse(
"<a href=\"http://y/foo/bar\">external link</a>" +
"<a href=\"http://y/foo/bar\" rel=\"nofollow\">external link</a>" +
"<a href=\"http://yobi.io/foo/bar\">internal link</a>" +
"<a href=\"/foo/bar\">relative link</a>" +
"<a href=\"http://yobi.io/%ag\">malformed link</a>");

// When
NotificationMail.handleLinks(doc);

// Then
Elements links = doc.select("a");

assertThat(links.get(0).attr("rel"))
.describedAs("rel attribute of external link")
.isEqualTo(" noreferrer");

assertThat(links.get(1).attr("rel"))
.describedAs("rel attribute of external link whose rel attribute was 'nofollow'")
.isEqualTo("nofollow noreferrer");

assertThat(links.get(2).hasAttr("rel"))
.describedAs("rel attribute of internal link contains 'noreferrer'.")
.isFalse();

assertThat(links.get(3).hasAttr("rel"))
.describedAs("rel attribute of relative link contains 'noreferrer'.")
.isFalse();

assertThat(links.get(4).attr("rel"))
.describedAs("rel attribute of malformed link")
.isEqualTo(" noreferrer");

}
}

0 comments on commit 4d4f0a9

Please sign in to comment.