Properly HTML-escape names in dirlistings - quark - quark web server | |
git clone git://git.suckless.org/quark | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit 48e74a598247f4b81e09a0f652faf15163f9f525 | |
parent 5ee8c07e7e3e601fce49fbc2b170227924be3804 | |
Author: Laslo Hunhold <[email protected]> | |
Date: Wed, 25 Mar 2020 14:07:17 +0100 | |
Properly HTML-escape names in dirlistings | |
Based on a patch by guysv. We now make sure that the valid | |
path-characters ", ', <, >, & can not be used for XSS on a target, for | |
example with a file called | |
"><img src="blabla" onerror="alert(1)" | |
by properly HTML-escaping these characters. | |
Signed-off-by: Laslo Hunhold <[email protected]> | |
Diffstat: | |
M resp.c | 54 +++++++++++++++++++++++++++++… | |
1 file changed, 51 insertions(+), 3 deletions(-) | |
--- | |
diff --git a/resp.c b/resp.c | |
@@ -38,6 +38,51 @@ suffix(int t) | |
return ""; | |
} | |
+static void | |
+html_escape(char *src, char *dst, size_t dst_siz) | |
+{ | |
+ const struct { | |
+ char c; | |
+ char *s; | |
+ } escape[] = { | |
+ { '&', "&" }, | |
+ { '<', "<" }, | |
+ { '>', ">" }, | |
+ { '"', """ }, | |
+ { '\'', "'" }, | |
+ }; | |
+ size_t i, j, k, esclen; | |
+ | |
+ for (i = 0, j = 0; src[i] != '\0'; i++) { | |
+ for (k = 0; k < LEN(escape); k++) { | |
+ if (src[i] == escape[k].c) { | |
+ break; | |
+ } | |
+ } | |
+ if (k == LEN(escape)) { | |
+ /* no escape char at src[i] */ | |
+ if (j == dst_siz - 1) { | |
+ /* silent truncation */ | |
+ break; | |
+ } else { | |
+ dst[j++] = src[i]; | |
+ } | |
+ } else { | |
+ /* escape char at src[i] */ | |
+ esclen = strlen(escape[k].s); | |
+ | |
+ if (j >= dst_siz - esclen) { | |
+ /* silent truncation */ | |
+ break; | |
+ } else { | |
+ memcpy(&dst[j], escape[k].s, esclen); | |
+ j += esclen; | |
+ } | |
+ } | |
+ } | |
+ dst[j] = '\0'; | |
+} | |
+ | |
enum status | |
resp_dir(int fd, char *name, struct request *r) | |
{ | |
@@ -45,6 +90,7 @@ resp_dir(int fd, char *name, struct request *r) | |
size_t i; | |
int dirlen, s; | |
static char t[TIMESTAMP_LEN]; | |
+ char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */ | |
/* read directory */ | |
if ((dirlen = scandir(name, &e, NULL, compareent)) < 0) { | |
@@ -65,11 +111,12 @@ resp_dir(int fd, char *name, struct request *r) | |
if (r->method == M_GET) { | |
/* listing header */ | |
+ html_escape(name, esc, sizeof(esc)); | |
if (dprintf(fd, | |
"<!DOCTYPE html>\n<html>\n\t<head>" | |
"<title>Index of %s</title></head>\n" | |
"\t<body>\n\t\t<a href=\"..\">..</a>", | |
- name) < 0) { | |
+ esc) < 0) { | |
s = S_REQUEST_TIMEOUT; | |
goto cleanup; | |
} | |
@@ -82,10 +129,11 @@ resp_dir(int fd, char *name, struct request *r) | |
} | |
/* entry line */ | |
+ html_escape(e[i]->d_name, esc, sizeof(esc)); | |
if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>… | |
- e[i]->d_name, | |
+ esc, | |
(e[i]->d_type == DT_DIR) ? "/" : "", | |
- e[i]->d_name, | |
+ esc, | |
suffix(e[i]->d_type)) < 0) { | |
s = S_REQUEST_TIMEOUT; | |
goto cleanup; |