diff options
| author | eunhhu <[email protected]> | 2026-09-30 13:06:41 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-30 13:06:41 +0100 |
| commit | c58b554592b984f3c60b95b85d982f5662aa6e34 (patch) | |
| tree | e9d790f199f8bfaba1e56bc93aceba2f1d8fb802 /serve | |
| parent | d9b02855dad6369c63479b24238f05acfd2312fb (diff) | |
| download | pure-c58b554592b984f3c60b95b85d982f5662aa6e34.tar.gz pure-c58b554592b984f3c60b95b85d982f5662aa6e34.zip | |
Decode a query's %XX for an input's fields, as for a view's arguments
A view's arguments were decoded and an input's fixed fields were not,
so a client that sends an exponent's + as %2B, as Python's urlencode and
browsers do, had ?n=1e%2B21 refused. show_given's comment is back above
show_given.
Diffstat (limited to 'serve')
| -rw-r--r-- | serve/serve.c | 49 |
1 files changed, 26 insertions, 23 deletions
diff --git a/serve/serve.c b/serve/serve.c index 775c440..f2d455f 100644 --- a/serve/serve.c +++ b/serve/serve.c @@ -709,9 +709,27 @@ static void put_u64(unsigned char *p, uint64_t v) { for (unsigned i = 0; i < 8; static uint32_t get_u32(const unsigned char *p) { return p[0] | p[1] << 8 | (uint32_t)p[2] << 16 | (uint32_t)p[3] << 24; } static uint64_t get_u64(const unsigned char *p) { return get_u32(p) | (uint64_t)get_u32(p + 4) << 32; } +static int hex_value(char c) { + return c >= '0' && c <= '9' ? c - '0' : c >= 'a' && c <= 'f' ? c - 'a' + 10 : c >= 'A' && c <= 'F' ? c - 'A' + 10 : -1; +} + +/* %XX decoded in place (never longer): the length after, or -1 for a bad escape. */ +static long percent_decoded(char *text, size_t length) { + size_t o = 0; + for (size_t i = 0; i < length; ++i) { + if (text[i] != '%') { text[o++] = text[i]; continue; } + int high = i + 2 < length ? hex_value(text[i + 1]) : -1, low = high >= 0 ? hex_value(text[i + 2]) : -1; + if (low < 0) return -1; + text[o++] = (char)(high << 4 | low); + i += 2; + } + return (long)o; +} + /* What an app route names in the module, with a message's fixed fields - * taken from the query into c->fields. NULL, or the error to answer (404 - * for a name the module lacks, 400 otherwise). */ + * taken from the query (%XX decoded, as a view's arguments are) into + * c->fields. NULL, or the error to answer (404 for a name the module lacks, + * 400 otherwise). */ static const char *resolve(const Host *host, Connection *c, char *query, Route *route, int *status) { const PureModule *m = host->module; *status = 404; @@ -786,8 +804,10 @@ static const char *resolve(const Host *host, Connection *c, char *query, Route * uint32_t f = 0; while (f < (message ? shape->field_count : 0) && strcmp(m->field[shape->field_start + f].name, pair)) ++f; const PureField *field = message && f < shape->field_count ? &m->field[shape->field_start + f] : NULL; + long length = percent_decoded(value, strlen(value)); /* as a view's arguments are */ if (!field || c->given[f] || (field->type != PURE_FIELD_U32 && field->type != PURE_FIELD_NUMBER) || - !pure_json_fixed(field, (const unsigned char *)value, strlen(value), c->fields)) return "query"; + length < 0 || !pure_json_fixed(field, (const unsigned char *)value, (size_t)length, c->fields)) + return "query"; c->given[f] = true; } pair = end ? end + 1 : NULL; @@ -1205,26 +1225,6 @@ static void put_number(Output *o, double value) { put_bytes(o, digits, pure_format_number(value, digits)); } -/* A view's value: a number in Pure's shortest form or a text, as text; a - * table as {"rows":N,"from":F,"items":[{column: value}...]} for the rows - * asked, each cell computed only for them (a cell without a value is null). */ -static int hex_value(char c) { - return c >= '0' && c <= '9' ? c - '0' : c >= 'a' && c <= 'f' ? c - 'a' + 10 : c >= 'A' && c <= 'F' ? c - 'A' + 10 : -1; -} - -/* %XX decoded in place (never longer): the length after, or -1 for a bad escape. */ -static long percent_decoded(char *text, size_t length) { - size_t o = 0; - for (size_t i = 0; i < length; ++i) { - if (text[i] != '%') { text[o++] = text[i]; continue; } - int high = i + 2 < length ? hex_value(text[i + 1]) : -1, low = high >= 0 ? hex_value(text[i + 2]) : -1; - if (low < 0) return -1; - text[o++] = (char)(high << 4 | low); - i += 2; - } - return (long)o; -} - /* A view's arguments from a query's name=value pairs (values %XX decoded; * from and count page a table): a payload of their input's shape, the * fixed fields in its prefix, the texts and bytes after it in order, each's @@ -1290,6 +1290,9 @@ static PureResult show_view(PureApp *app, const Route *route, size_t available, return status; } +/* A view's value: a number in Pure's shortest form or a text, as text; a + * table as {"rows":N,"from":F,"items":[{column: value}...]} for the rows + * asked, each cell computed only for them (a cell without a value is null). */ static PureResult show_given(PureApp *app, const Route *route, size_t available, Applied *answer) { const PureModule *m = app->module; uint32_t view = route->target; |
