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.
This commit is contained in:
Maximilian Mader 2021-04-21 00:22:50 +02:00 committed by Christopher Faulet
parent c9c79570d4
commit ff3bb8b609
6 changed files with 154 additions and 1 deletions

View File

@ -6013,6 +6013,7 @@ http-request early-hint <name> <fmt> [ { if | unless } <condition> ]
http-request normalize-uri <normalizer> [ { if | unless } <condition> ] http-request normalize-uri <normalizer> [ { if | unless } <condition> ]
http-request normalize-uri path-merge-slashes [ { if | unless } <condition> ] http-request normalize-uri path-merge-slashes [ { if | unless } <condition> ]
http-request normalize-uri path-strip-dot [ { if | unless } <condition> ]
http-request normalize-uri path-strip-dotdot [ full ] [ { if | unless } <condition> ] http-request normalize-uri path-strip-dotdot [ full ] [ { if | unless } <condition> ]
http-request normalize-uri percent-to-uppercase [ strict ] [ { if | unless } <condition> ] http-request normalize-uri percent-to-uppercase [ strict ] [ { if | unless } <condition> ]
http-request normalize-uri query-sort-by-name [ { if | unless } <condition> ] http-request normalize-uri query-sort-by-name [ { if | unless } <condition> ]
@ -6035,6 +6036,14 @@ http-request normalize-uri query-sort-by-name [ { if | unless } <condition> ]
The following normalizers are available: 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. - path-strip-dotdot: Normalizes "/../" segments within the "path" component.
This merges segments that attempt to access the parent directory with This merges segments that attempt to access the parent directory with
their preceding segment. Empty segments do not receive special treatment. their preceding segment. Empty segments do not receive special treatment.

View File

@ -103,6 +103,7 @@ enum act_timeout_name {
enum act_normalize_uri { enum act_normalize_uri {
ACT_NORMALIZE_URI_PATH_MERGE_SLASHES, ACT_NORMALIZE_URI_PATH_MERGE_SLASHES,
ACT_NORMALIZE_URI_PATH_STRIP_DOT,
ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT, ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT,
ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL, ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL,
ACT_NORMALIZE_URI_QUERY_SORT_BY_NAME, ACT_NORMALIZE_URI_QUERY_SORT_BY_NAME,

View File

@ -19,6 +19,7 @@
#include <haproxy/uri_normalizer-t.h> #include <haproxy/uri_normalizer-t.h>
enum uri_normalizer_err uri_normalizer_percent_upper(const struct ist input, int strict, struct ist *dst); 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_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_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); enum uri_normalizer_err uri_normalizer_query_sort(const struct ist query, const char delim, struct ist *dst);

View File

@ -8,7 +8,7 @@ feature ignore_unknown_macro
server s1 { server s1 {
rxreq rxreq
txresp txresp
} -repeat 43 -start } -repeat 54 -start
haproxy h1 -conf { haproxy h1 -conf {
defaults defaults
@ -82,6 +82,18 @@ haproxy h1 -conf {
default_backend be 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 backend be
server s1 ${s1_addr}:${s1_port} server s1 ${s1_addr}:${s1_port}
@ -312,3 +324,70 @@ client c5 -connect ${h1_fe_percent_to_uppercase_strict_sock} {
rxresp rxresp
expect resp.status == 400 expect resp.status == 400
} -run } -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

View File

@ -232,6 +232,23 @@ static enum act_return http_action_normalize_uri(struct act_rule *rule, struct p
break; 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:
case ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL: { case ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL: {
const struct ist path = http_get_path(uri); 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; 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) { else if (strcmp(args[cur_arg], "path-strip-dotdot") == 0) {
cur_arg++; cur_arg++;

View File

@ -75,6 +75,47 @@ enum uri_normalizer_err uri_normalizer_percent_upper(const struct ist input, int
return err; 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. /* Merges `/../` with preceding path segments.
* *
* If `full` is set to `0` then `/../` will be printed at the start of the resulting * If `full` is set to `0` then `/../` will be printed at the start of the resulting