From: Tim Duesterhus Date: Thu, 15 Apr 2021 19:46:00 +0000 (+0200) Subject: MINOR: uri_normalizer: Add support for supressing leading `../` for dotdot normalizer X-Git-Tag: v2.4-dev17~90 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=560e1a63524d40e9ffc552cf145f4cc3f918455d;p=thirdparty%2Fhaproxy.git MINOR: uri_normalizer: Add support for supressing leading `../` for dotdot normalizer This adds an option to supress `../` at the start of the resulting path. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 4b6e63d8a3..585c675239 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -6012,7 +6012,7 @@ http-request early-hint [ { if | unless } ] See RFC 8297 for more information. http-request normalize-uri [ { if | unless } ] -http-request normalize-uri dotdot [ { if | unless } ] +http-request normalize-uri dotdot [ full ] [ { if | unless } ] http-request normalize-uri merge-slashes [ { if | unless } ] Performs normalization of the request's URI. The following normalizers are @@ -6028,8 +6028,16 @@ http-request normalize-uri merge-slashes [ { if | unless } ] - /foo/../bar/ -> /bar/ - /foo/bar/../ -> /foo/ - /../bar/ -> /../bar/ + - /bar/../../ -> /../ - /foo//../ -> /foo/ + If the "full" option is specified then "../" at the beginning will be + removed as well: + + Example: + - /../bar/ -> /bar/ + - /bar/../../ -> / + - merge-slashes: Merges adjacent slashes within the "path" component into a single slash. diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index ac9399a6bc..5a8155929f 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -104,6 +104,7 @@ enum act_timeout_name { enum act_normalize_uri { ACT_NORMALIZE_URI_MERGE_SLASHES, ACT_NORMALIZE_URI_DOTDOT, + ACT_NORMALIZE_URI_DOTDOT_FULL, }; /* NOTE: if <.action_ptr> is defined, the referenced function will always be diff --git a/include/haproxy/uri_normalizer.h b/include/haproxy/uri_normalizer.h index 9dbbe58264..811a7ebb6d 100644 --- a/include/haproxy/uri_normalizer.h +++ b/include/haproxy/uri_normalizer.h @@ -18,7 +18,7 @@ #include -enum uri_normalizer_err uri_normalizer_path_dotdot(const struct ist path, struct ist *dst); +enum uri_normalizer_err uri_normalizer_path_dotdot(const struct ist path, int full, struct ist *dst); enum uri_normalizer_err uri_normalizer_path_merge_slashes(const struct ist path, struct ist *dst); #endif /* _HAPROXY_URI_NORMALIZER_H */ diff --git a/reg-tests/http-rules/normalize_uri.vtc b/reg-tests/http-rules/normalize_uri.vtc index e66bdc47b3..5ee73a308d 100644 --- a/reg-tests/http-rules/normalize_uri.vtc +++ b/reg-tests/http-rules/normalize_uri.vtc @@ -36,8 +36,13 @@ haproxy h1 -conf { http-request normalize-uri dotdot http-request set-var(txn.after) url + http-request set-uri %[var(txn.before)] + http-request normalize-uri dotdot full + http-request set-var(txn.after_full) url + http-response add-header before %[var(txn.before)] http-response add-header after %[var(txn.after)] + http-response add-header after-full %[var(txn.after_full)] default_backend be @@ -103,54 +108,65 @@ client c2 -connect ${h1_fe_dotdot_sock} { rxresp expect resp.http.before == "/foo/bar" expect resp.http.after == "/foo/bar" + expect resp.http.after-full == "/foo/bar" txreq -url "/foo/.." rxresp expect resp.http.before == "/foo/.." expect resp.http.after == "/" + expect resp.http.after-full == "/" txreq -url "/foo/../" rxresp expect resp.http.before == "/foo/../" expect resp.http.after == "/" + expect resp.http.after-full == "/" txreq -url "/foo/bar/../" rxresp expect resp.http.before == "/foo/bar/../" expect resp.http.after == "/foo/" + expect resp.http.after-full == "/foo/" txreq -url "/foo/../bar" rxresp expect resp.http.before == "/foo/../bar" expect resp.http.after == "/bar" + expect resp.http.after-full == "/bar" txreq -url "/foo/../bar/" rxresp expect resp.http.before == "/foo/../bar/" expect resp.http.after == "/bar/" + expect resp.http.after-full == "/bar/" txreq -url "/foo/../../bar/" rxresp expect resp.http.before == "/foo/../../bar/" expect resp.http.after == "/../bar/" + expect resp.http.after-full == "/bar/" txreq -url "/foo//../../bar/" rxresp expect resp.http.before == "/foo//../../bar/" expect resp.http.after == "/bar/" + expect resp.http.after-full == "/bar/" txreq -url "/foo/?bar=/foo/../" rxresp expect resp.http.before == "/foo/?bar=/foo/../" expect resp.http.after == "/foo/?bar=/foo/../" + expect resp.http.after-full == "/foo/?bar=/foo/../" txreq -url "/foo/../?bar=/foo/../" rxresp expect resp.http.before == "/foo/../?bar=/foo/../" expect resp.http.after == "/?bar=/foo/../" + expect resp.http.after-full == "/?bar=/foo/../" txreq -req OPTIONS -url "*" rxresp expect resp.http.before == "*" expect resp.http.after == "*" + expect resp.http.after-full == "*" } -run diff --git a/src/http_act.c b/src/http_act.c index 7e1829e9db..3751b002f8 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -232,14 +232,15 @@ static enum act_return http_action_normalize_uri(struct act_rule *rule, struct p break; } - case ACT_NORMALIZE_URI_DOTDOT: { + case ACT_NORMALIZE_URI_DOTDOT: + case ACT_NORMALIZE_URI_DOTDOT_FULL: { const struct ist path = http_get_path(uri); struct ist newpath = ist2(replace->area, replace->size); if (!isttest(path)) goto leave; - err = uri_normalizer_path_dotdot(iststop(path, '?'), &newpath); + err = uri_normalizer_path_dotdot(iststop(path, '?'), rule->action == ACT_NORMALIZE_URI_DOTDOT_FULL, &newpath); if (err != URI_NORMALIZER_ERR_NONE) break; @@ -317,7 +318,17 @@ static enum act_parse_ret parse_http_normalize_uri(const char **args, int *orig_ else if (strcmp(args[cur_arg], "dotdot") == 0) { cur_arg++; - rule->action = ACT_NORMALIZE_URI_DOTDOT; + if (strcmp(args[cur_arg], "full") == 0) { + cur_arg++; + rule->action = ACT_NORMALIZE_URI_DOTDOT_FULL; + } + else if (!*args[cur_arg]) { + rule->action = ACT_NORMALIZE_URI_DOTDOT; + } + else if (strcmp(args[cur_arg], "if") != 0 && strcmp(args[cur_arg], "unless") != 0) { + memprintf(err, "unknown argument '%s' for 'dotdot' normalizer", args[cur_arg]); + return ACT_RET_PRS_ERR; + } } else { memprintf(err, "unknown normalizer '%s'", args[cur_arg]); diff --git a/src/uri_normalizer.c b/src/uri_normalizer.c index 05e8cd5786..53a3321d4e 100644 --- a/src/uri_normalizer.c +++ b/src/uri_normalizer.c @@ -15,8 +15,13 @@ #include #include -/* Merges `/../` with preceding path segments. */ -enum uri_normalizer_err uri_normalizer_path_dotdot(const struct ist path, struct ist *dst) +/* Merges `/../` with preceding path segments. + * + * If `full` is set to `0` then `/../` will be printed at the start of the resulting + * path if the number of `/../` exceeds the number of other segments. If `full` is + * set to `1` these will not be printed. + */ +enum uri_normalizer_err uri_normalizer_path_dotdot(const struct ist path, int full, struct ist *dst) { enum uri_normalizer_err err; @@ -79,13 +84,15 @@ enum uri_normalizer_err uri_normalizer_path_dotdot(const struct ist path, struct /* Prepend a trailing slash. */ *(--head) = '/'; - /* Prepend unconsumed `/..`. */ - do { - *(--head) = '.'; - *(--head) = '.'; - *(--head) = '/'; - up--; - } while (up > 0); + if (!full) { + /* Prepend unconsumed `/..`. */ + do { + *(--head) = '.'; + *(--head) = '.'; + *(--head) = '/'; + up--; + } while (up > 0); + } } *dst = ist2(head, tail - head);