Skip to content

Commit 83c4ce3

Browse files
committed
Fix issues with <a rel> values.
Thanks to kyle-simmons for the testcase which showed an out of bounds failure. Also made sure that explicit skip lists are respected on values from the existing rel attribute.
1 parent b8fdc05 commit 83c4ce3

File tree

2 files changed

+76
-8
lines changed

2 files changed

+76
-8
lines changed

src/main/java/org/owasp/html/HtmlPolicyBuilder.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ public HtmlPolicyBuilder requireRelsOnLinks(String... linkValues) {
438438

439439
/**
440440
* Opts out of some of the {@link #DEFAULT_RELS_ON_TARGETTED_LINKS} from being added
441-
* to links, and reverses pre
441+
* to links, and reverses previous calls to requireRelsOnLinks with the given link values.
442442
*
443443
* @see #requireRelsOnLinks
444444
*/
@@ -730,7 +730,9 @@ private CompiledState compilePolicies() {
730730
stylingPolicySchema,
731731
new Function<String, String>() {
732732
public String apply(String url) {
733-
return styleUrlPolicyFinal.apply("img", "src", url);
733+
return styleUrlPolicyFinal.apply(
734+
"img", "src",
735+
url != null ? url : "about:invalid");
734736
}
735737
});
736738
}
@@ -990,12 +992,27 @@ public String apply(String elementName, List<String> attrs) {
990992
if (hasTarget || !extra.isEmpty()) {
991993
int relIndex = indexOfAttributeValue("rel", attrs);
992994
String relValue;
995+
993996
if (relIndex < 0 && hasTarget && extra.isEmpty() && skip.isEmpty()) {
994997
relValue = DEFAULT_RELS_ON_TARGETTED_LINKS_STR;
995998
} else {
996999
StringBuilder sb = new StringBuilder();
9971000
if (relIndex >= 0) {
998-
sb.append(attrs.get(relIndex)).append(' ');
1001+
// Preserve values that are not explicitly skipped.
1002+
String rels = attrs.get(relIndex);
1003+
int left = 0, n = rels.length();
1004+
for (int i = 0; i <= n; ++i) {
1005+
if (i == n || Strings.isHtmlSpace(rels.charAt(i))) {
1006+
if (left < i) {
1007+
if (skip.isEmpty()
1008+
|| !skip.contains(
1009+
Strings.toLowerCase(rels.substring(left, i)))) {
1010+
sb.append(rels, left, i).append(' ');
1011+
}
1012+
}
1013+
left = i + 1;
1014+
}
1015+
}
9991016
}
10001017
for (String s : extra) {
10011018
sb.append(s).append(' ');
@@ -1005,13 +1022,24 @@ public String apply(String elementName, List<String> attrs) {
10051022
sb.append(s).append(' ');
10061023
}
10071024
}
1008-
relValue = sb.substring(0, sb.length() - 1);
1025+
int sblen = sb.length();
1026+
if (sblen == 0) {
1027+
relValue = "";
1028+
} else {
1029+
relValue = sb.substring(0, sb.length() - 1);
1030+
}
10091031
}
1010-
if (relIndex < 0) {
1011-
attrs.add("rel");
1012-
attrs.add(relValue);
1032+
if (relValue.isEmpty()) {
1033+
if (relIndex >= 0) {
1034+
attrs.subList(relIndex - 1, relIndex + 1).clear();
1035+
}
10131036
} else {
1014-
attrs.set(relIndex, relValue);
1037+
if (relIndex < 0) {
1038+
attrs.add("rel");
1039+
attrs.add(relValue);
1040+
} else {
1041+
attrs.set(relIndex, relValue);
1042+
}
10151043
}
10161044
}
10171045
}

src/test/java/org/owasp/html/HtmlPolicyBuilderTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,46 @@ public static final void testFailFastOnSpaceSeparatedStrings() {
736736
assertTrue(failed);
737737
}
738738

739+
@Test
740+
public static final void testEmptyDefaultLinkRelsSet() {
741+
PolicyFactory pf = new HtmlPolicyBuilder()
742+
.allowElements("a")
743+
.allowAttributes("href", "target").onElements("a")
744+
.allowStandardUrlProtocols()
745+
.skipRelsOnLinks("noopener", "noreferrer")
746+
.toFactory();
747+
748+
assertEquals(
749+
"<a href=\"http://example.com\" target=\"_blank\">eg</a>",
750+
pf.sanitize("<a href=\"http://example.com\" target=\"_blank\">eg</a>"));
751+
}
752+
753+
@Test
754+
public static final void testExplicitRelsSkip() {
755+
PolicyFactory pf = new HtmlPolicyBuilder()
756+
.allowElements("a")
757+
.allowAttributes("href", "target", "rel").onElements("a")
758+
.allowStandardUrlProtocols()
759+
.skipRelsOnLinks("noopener", "noreferrer")
760+
.toFactory();
761+
762+
assertEquals(
763+
"<a href=\"http://example.com\" target=\"_blank\">text</a>",
764+
pf.sanitize(
765+
"<a href=\"http://example.com\" target=\"_blank\""
766+
+ " rel=\"noopener\">text</a>"));
767+
assertEquals(
768+
"<a href=\"http://example.com\" target=\"_blank\">text</a>",
769+
pf.sanitize(
770+
"<a href=\"http://example.com\" target=\"_blank\""
771+
+ " rel=\"noreferrer noopener\">text</a>"));
772+
assertEquals(
773+
"<a href=\"http://example.com\" target=\"_blank\" rel=\"nofoo nobar nobaz\">text</a>",
774+
pf.sanitize(
775+
"<a href=\"http://example.com\" target=\"_blank\""
776+
+ " rel=\"nofoo noopener nobar NOREFERRER nobaz \">text</a>"));
777+
}
778+
739779
@Test
740780
public static final void testScopingExitInNoContent() {
741781
PolicyFactory pf = new HtmlPolicyBuilder()

0 commit comments

Comments
 (0)