commit 2ed590a4100e6c6ecb4ae72dbed322774f85baf0
parent c28430201687c4c4e4dc0ddb049ae72d47acb741
Author: Chris Bracken <chris@bracken.jp>
Date: Sat, 6 Jun 2026 14:42:13 +0900
writer/html: fix XSS/HTML injection in me_url
Escapes the me_url using print_xml_encoded before printing to link
rel=me tag to prevent arbitrary HTML/JavaScript injection.
Diffstat:
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/writer/html/repo_index.c b/src/writer/html/repo_index.c
@@ -47,7 +47,9 @@ void html_repoindex_begin(HtmlRepoIndex* index) {
"<link rel=\"icon\" type=\"image/png\" href=\"favicon.png\" />\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n");
if (index->me_url) {
- fprintf(out, "<link rel=\"me\" href=\"%s\" />\n", index->me_url);
+ fprintf(out, "<link rel=\"me\" href=\"");
+ print_xml_encoded(out, index->me_url);
+ fprintf(out, "\" />\n");
}
fprintf(
out,
diff --git a/src/writer/html/repo_index_tests.c b/src/writer/html/repo_index_tests.c
@@ -65,3 +65,28 @@ UTEST(html_repo_index, multiple) {
free(buf);
}
+
+UTEST(html_repo_index, escaping) {
+ char* buf = NULL;
+ size_t size = 0;
+ FILE* out = open_memstream(&buf, &size);
+
+ HtmlRepoIndex* index = html_repoindex_create(out);
+ ASSERT_NE(NULL, index);
+ html_repoindex_set_me_url(
+ index, "https://me.example.com/?a=1&b=2\" onclick=\"alert(1)");
+
+ html_repoindex_begin(index);
+ html_repoindex_end(index);
+ html_repoindex_free(index);
+ fclose(out);
+
+ ASSERT_NE(NULL, buf);
+
+ // Verify that characters like & and " are escaped, and not written literally.
+ EXPECT_TRUE(strstr(buf,
+ "href=\"https://me.example.com/?a=1&b=2" "
+ "onclick="alert(1)\"") != NULL);
+
+ free(buf);
+}