From: Maximilian Mader Date: Tue, 20 Apr 2021 22:22:50 +0000 (+0200) Subject: MINOR: uri_normalizer: Add a `strip-dot` normalizer X-Git-Tag: v2.4-dev17~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ff3bb8b609006f40229fdecbf3c4af07bc4ddbaf;p=thirdparty%2Fhaproxy.git MINOR: uri_normalizer: Add a `strip-dot` normalizer This normalizer removes "/./" segments from the path component. Usually the dot refers to the current directory which renders those segments redundant. See GitHub Issue #714. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index ce96c44e74..de91004398 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -6013,6 +6013,7 @@ http-request early-hint [ { if | unless } ] http-request normalize-uri [ { if | unless } ] http-request normalize-uri path-merge-slashes [ { if | unless } ] +http-request normalize-uri path-strip-dot [ { if | unless } ] http-request normalize-uri path-strip-dotdot [ full ] [ { if | unless } ] http-request normalize-uri percent-to-uppercase [ strict ] [ { if | unless } ] http-request normalize-uri query-sort-by-name [ { if | unless } ] @@ -6035,6 +6036,14 @@ http-request normalize-uri query-sort-by-name [ { if | unless } ] The following normalizers are available: + - path-strip-dot: Removes "/./" segments within the "path" component. + + Example: + - /. -> / + - /./bar/ -> /bar/ + - /a/./a -> /a/a + - /.well-known/ -> /.well-known/ (no change) + - path-strip-dotdot: Normalizes "/../" segments within the "path" component. This merges segments that attempt to access the parent directory with their preceding segment. Empty segments do not receive special treatment. diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index 43e6b1addc..2e3edeaf3c 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -103,6 +103,7 @@ enum act_timeout_name { enum act_normalize_uri { ACT_NORMALIZE_URI_PATH_MERGE_SLASHES, + ACT_NORMALIZE_URI_PATH_STRIP_DOT, ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT, ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL, ACT_NORMALIZE_URI_QUERY_SORT_BY_NAME, diff --git a/include/haproxy/uri_normalizer.h b/include/haproxy/uri_normalizer.h index 180936eae4..81c7e00ce3 100644 --- a/include/haproxy/uri_normalizer.h +++ b/include/haproxy/uri_normalizer.h @@ -19,6 +19,7 @@ #include enum uri_normalizer_err uri_normalizer_percent_upper(const struct ist input, int strict, struct ist *dst); +enum uri_normalizer_err uri_normalizer_path_dot(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); enum uri_normalizer_err uri_normalizer_query_sort(const struct ist query, const char delim, struct ist *dst); diff --git a/reg-tests/http-rules/normalize_uri.vtc b/reg-tests/http-rules/normalize_uri.vtc index b997b31b8b..9884b6c438 100644 --- a/reg-tests/http-rules/normalize_uri.vtc +++ b/reg-tests/http-rules/normalize_uri.vtc @@ -8,7 +8,7 @@ feature ignore_unknown_macro server s1 { rxreq txresp -} -repeat 43 -start +} -repeat 54 -start haproxy h1 -conf { defaults @@ -82,6 +82,18 @@ haproxy h1 -conf { default_backend be + frontend fe_dot + bind "fd@${fe_dot}" + + http-request set-var(txn.before) url + http-request normalize-uri path-strip-dot + http-request set-var(txn.after) url + + http-response add-header before %[var(txn.before)] + http-response add-header after %[var(txn.after)] + + default_backend be + backend be server s1 ${s1_addr}:${s1_port} @@ -312,3 +324,70 @@ client c5 -connect ${h1_fe_percent_to_uppercase_strict_sock} { rxresp expect resp.status == 400 } -run + +client c6 -connect ${h1_fe_dot_sock} { + txreq -url "/" + rxresp + expect resp.http.before == "/" + expect resp.http.after == "/" + + txreq -url "/a/b" + rxresp + expect resp.http.before == "/a/b" + expect resp.http.after == "/a/b" + + txreq -url "/." + rxresp + expect resp.http.before == "/." + expect resp.http.after == "/" + + txreq -url "/./" + rxresp + expect resp.http.before == "/./" + expect resp.http.after == "/" + + txreq -url "/a/." + rxresp + expect resp.http.before == "/a/." + expect resp.http.after == "/a/" + + txreq -url "/a." + rxresp + expect resp.http.before == "/a." + expect resp.http.after == "/a." + + txreq -url "/.a" + rxresp + expect resp.http.before == "/.a" + expect resp.http.after == "/.a" + + txreq -url "/a/." + rxresp + expect resp.http.before == "/a/." + expect resp.http.after == "/a/" + + txreq -url "/a/./" + rxresp + expect resp.http.before == "/a/./" + expect resp.http.after == "/a/" + + txreq -url "/a/./a" + rxresp + expect resp.http.before == "/a/./a" + expect resp.http.after == "/a/a" + + txreq -url "/a/../" + rxresp + expect resp.http.before == "/a/../" + expect resp.http.after == "/a/../" + + txreq -url "/a/../a" + rxresp + expect resp.http.before == "/a/../a" + expect resp.http.after == "/a/../a" + + txreq -url "/?a=/./" + rxresp + expect resp.http.before == "/?a=/./" + expect resp.http.after == "/?a=/./" +} -run diff --git a/src/http_act.c b/src/http_act.c index 5da2d7d1ab..df2bbe41b9 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -232,6 +232,23 @@ static enum act_return http_action_normalize_uri(struct act_rule *rule, struct p break; } + case ACT_NORMALIZE_URI_PATH_STRIP_DOT: { + 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_dot(iststop(path, '?'), &newpath); + + if (err != URI_NORMALIZER_ERR_NONE) + break; + + if (!http_replace_req_path(htx, newpath, 0)) + goto fail_rewrite; + + break; + } case ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT: case ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL: { const struct ist path = http_get_path(uri); @@ -350,6 +367,11 @@ static enum act_parse_ret parse_http_normalize_uri(const char **args, int *orig_ rule->action = ACT_NORMALIZE_URI_PATH_MERGE_SLASHES; } + else if (strcmp(args[cur_arg], "path-strip-dot") == 0) { + cur_arg++; + + rule->action = ACT_NORMALIZE_URI_PATH_STRIP_DOT; + } else if (strcmp(args[cur_arg], "path-strip-dotdot") == 0) { cur_arg++; diff --git a/src/uri_normalizer.c b/src/uri_normalizer.c index ded9e1c01c..8d959365ec 100644 --- a/src/uri_normalizer.c +++ b/src/uri_normalizer.c @@ -75,6 +75,47 @@ enum uri_normalizer_err uri_normalizer_percent_upper(const struct ist input, int return err; } +/* Removes `/./` from the given path. */ +enum uri_normalizer_err uri_normalizer_path_dot(const struct ist path, struct ist *dst) +{ + enum uri_normalizer_err err; + + const size_t size = istclear(dst); + struct ist newpath = *dst; + + struct ist scanner = path; + + /* The path will either be shortened or have the same length. */ + if (size < istlen(path)) { + err = URI_NORMALIZER_ERR_ALLOC; + goto fail; + } + + while (istlen(scanner) > 0) { + const struct ist segment = istsplit(&scanner, '/'); + + if (!isteq(segment, ist("."))) { + if (istcat(&newpath, segment, size) < 0) { + /* This is impossible, because we checked the size of the destination buffer. */ + my_unreachable(); + err = URI_NORMALIZER_ERR_INTERNAL_ERROR; + goto fail; + } + + if (istend(segment) != istend(scanner)) + newpath = __istappend(newpath, '/'); + } + } + + *dst = newpath; + + return URI_NORMALIZER_ERR_NONE; + + fail: + + return err; +} + /* Merges `/../` with preceding path segments. * * If `full` is set to `0` then `/../` will be printed at the start of the resulting