Skip to content

Commit 7e8f091

Browse files
committed
Add more negative test cases
1 parent 8d7de40 commit 7e8f091

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

rehype-harden/src/tests/index.test.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,133 @@ describe("Wildcard prefix support", () => {
10151015
expect(link4!.properties.href).toBe("/parent");
10161016
});
10171017

1018+
it("wildcard with no defaultOrigin still blocks dangerous protocols", async () => {
1019+
// Test javascript: protocol is blocked
1020+
const tree1 = await processMarkdown("[XSS](javascript:alert('XSS'))", {
1021+
allowedLinkPrefixes: ["*"],
1022+
});
1023+
1024+
const blocked1 = findSpanWithText(tree1, "[blocked]");
1025+
expect(blocked1).not.toBeNull();
1026+
1027+
// Test data: protocol is blocked for links
1028+
const tree2 = await processMarkdown(
1029+
"[Data](data:text/html,<script>alert('XSS')</script>)",
1030+
{
1031+
allowedLinkPrefixes: ["*"],
1032+
},
1033+
);
1034+
1035+
const blocked2 = findSpanWithText(tree2, "[blocked]");
1036+
expect(blocked2).not.toBeNull();
1037+
1038+
// Test vbscript: protocol is blocked
1039+
const tree3 = await processMarkdown("[VBS](vbscript:msgbox('XSS'))", {
1040+
allowedLinkPrefixes: ["*"],
1041+
});
1042+
1043+
const blocked3 = findSpanWithText(tree3, "[blocked]");
1044+
expect(blocked3).not.toBeNull();
1045+
1046+
// Test file: protocol is blocked
1047+
const tree4 = await processMarkdown("[File](file:///etc/passwd)", {
1048+
allowedLinkPrefixes: ["*"],
1049+
});
1050+
1051+
const blocked4 = findSpanWithText(tree4, "[blocked]");
1052+
expect(blocked4).not.toBeNull();
1053+
1054+
// Test data:image is blocked for images without allowDataImages
1055+
const tree5 = await processMarkdown(
1056+
"![Image](data:image/png;base64,iVBORw0KGg)",
1057+
{
1058+
allowedImagePrefixes: ["*"],
1059+
allowDataImages: false,
1060+
},
1061+
);
1062+
1063+
const blockedImg = findSpanWithText(tree5, "[Image blocked");
1064+
expect(blockedImg).not.toBeNull();
1065+
});
1066+
1067+
it("wildcard with no defaultOrigin blocks invalid/unparseable URLs", async () => {
1068+
// Test empty URL - markdown parsers typically convert () to href=""
1069+
const tree1 = await processMarkdown("[Empty]()", {
1070+
allowedLinkPrefixes: ["*"],
1071+
});
1072+
1073+
const link1 = findElement(tree1, "a");
1074+
// Empty href should either be blocked or converted to #
1075+
if (link1) {
1076+
expect(link1.properties.href).toBe("#");
1077+
} else {
1078+
const blocked1 = findSpanWithText(tree1, "[blocked]");
1079+
expect(blocked1).not.toBeNull();
1080+
}
1081+
1082+
// Test malformed protocol-like string
1083+
const tree2 = await processMarkdown("[Malformed](ht!tp://invalid)", {
1084+
allowedLinkPrefixes: ["*"],
1085+
});
1086+
1087+
// This should either be blocked or the markdown parser might treat it as text
1088+
const link2 = findElement(tree2, "a");
1089+
if (link2) {
1090+
// If parsed as a link, it should be blocked or normalized
1091+
const blocked2 = findSpanWithText(tree2, "[blocked]");
1092+
expect(blocked2).not.toBeNull();
1093+
}
1094+
});
1095+
1096+
it("wildcard with no defaultOrigin blocks disallowed protocols even with allowedProtocols", async () => {
1097+
// javascript: should be blocked even with allowedProtocols: ["*"]
1098+
const tree1 = await processMarkdown("[JS](javascript:alert(1))", {
1099+
allowedLinkPrefixes: ["*"],
1100+
allowedProtocols: ["*"],
1101+
});
1102+
1103+
const blocked1 = findSpanWithText(tree1, "[blocked]");
1104+
expect(blocked1).not.toBeNull();
1105+
1106+
// data: should be blocked for links even with allowedProtocols: ["*"]
1107+
const tree2 = await processMarkdown("[Data](data:text/html,test)", {
1108+
allowedLinkPrefixes: ["*"],
1109+
allowedProtocols: ["*"],
1110+
});
1111+
1112+
const blocked2 = findSpanWithText(tree2, "[blocked]");
1113+
expect(blocked2).not.toBeNull();
1114+
1115+
// file: should be blocked even with allowedProtocols: ["*"]
1116+
const tree3 = await processMarkdown("[File](file:///etc/passwd)", {
1117+
allowedLinkPrefixes: ["*"],
1118+
allowedProtocols: ["*"],
1119+
});
1120+
1121+
const blocked3 = findSpanWithText(tree3, "[blocked]");
1122+
expect(blocked3).not.toBeNull();
1123+
});
1124+
1125+
it("relative URLs are blocked when wildcard is not used and defaultOrigin is different", async () => {
1126+
// With specific prefixes and a defaultOrigin that doesn't match, relative URLs should be blocked
1127+
const tree1 = await processMarkdown("[Relative](/path)", {
1128+
defaultOrigin: "https://mysite.com",
1129+
allowedLinkPrefixes: ["https://example.com/"],
1130+
});
1131+
1132+
const blocked1 = findSpanWithText(tree1, "[blocked]");
1133+
expect(blocked1).not.toBeNull();
1134+
1135+
// Images should also be blocked
1136+
const tree2 = await processMarkdown("![Image](/image.png)", {
1137+
defaultOrigin: "https://mysite.com",
1138+
allowedImagePrefixes: ["https://example.com/"],
1139+
});
1140+
1141+
const blockedImg = findSpanWithText(tree2, "[Image blocked");
1142+
expect(blockedImg).not.toBeNull();
1143+
});
1144+
10181145
it("wildcard works alongside other prefixes", async () => {
10191146
const tree = await processMarkdown(
10201147
"[Any Link](https://random-site.com/path)",

0 commit comments

Comments
 (0)