Skip to content

Commit e13927a

Browse files
authored
Merge pull request #2939 from fengzeroz/main
http check body length
2 parents 5727089 + 3167ff7 commit e13927a

8 files changed

Lines changed: 77 additions & 18 deletions

File tree

‎include/neuron/define.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
#define NEU_MSG_MAX_SIZE 2048
5858
#define NEU_PATH_LEN 128
5959

60+
#define NEU_HTTP_BODY_MAX_SIZE (1 * 1024 * 1024)
61+
#define NEU_HTTP_BODY_MAX_SIZE_LARGE (4 * 1024 * 1024)
62+
#define NEU_HTTP_BODY_TOO_BIG (-2)
63+
6064
#define NEU_TAG_META_LENGTH 20
6165
#define NEU_TAG_META_SIZE 32
6266
#define NEU_TAG_FORMAT_LENGTH 16

‎include/neuron/utils/http.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ extern "C" {
3535
// buffer overflow.
3636
ssize_t neu_url_decode(const char *s, size_t len, char *buf, size_t size);
3737

38+
// Get a NUL terminated copy of the request body, which the caller must free.
39+
// Returns 0 on success, NEU_HTTP_BODY_TOO_BIG if the body exceeds
40+
// NEU_HTTP_BODY_MAX_SIZE_LARGE, and -1 if the body is empty or on allocation
41+
// failure. On failure `*data` is NULL and `*data_size` is zero.
3842
int neu_http_get_body(nng_aio *aio, void **data, size_t *data_size);
3943

4044
// Find query parameter value of the given name.

‎include/neuron/utils/http_handler.h‎

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,27 @@
2525
#include <nng/nng.h>
2626
#include <nng/supplemental/http/http.h>
2727

28+
#include "define.h"
2829
#include "utils/neu_jwt.h"
2930
#include "json/neu_json_error.h"
3031

31-
#define NEU_PROCESS_HTTP_REQUEST(aio, req_type, decode_fun, func) \
32-
{ \
33-
char * req_data = NULL; \
34-
size_t req_data_size = 0; \
35-
req_type *req = NULL; \
36-
\
37-
if (neu_http_get_body((aio), (void **) &req_data, &req_data_size) == \
38-
0 && \
39-
decode_fun(req_data, &req) == 0) { \
40-
{ func }; \
41-
decode_fun##_free(req); \
42-
} else { \
43-
neu_http_bad_request(aio, "{\"error\": 1002}"); \
44-
} \
45-
free(req_data); \
32+
#define NEU_PROCESS_HTTP_REQUEST(aio, req_type, decode_fun, func) \
33+
{ \
34+
char * req_data = NULL; \
35+
size_t req_data_size = 0; \
36+
req_type *req = NULL; \
37+
int body_rv = \
38+
neu_http_get_body((aio), (void **) &req_data, &req_data_size); \
39+
\
40+
if (0 == body_rv && decode_fun(req_data, &req) == 0) { \
41+
{ func }; \
42+
decode_fun##_free(req); \
43+
} else if (NEU_HTTP_BODY_TOO_BIG == body_rv) { \
44+
neu_http_bad_request(aio, "{\"error\": 1017}"); \
45+
} else { \
46+
neu_http_bad_request(aio, "{\"error\": 1002}"); \
47+
} \
48+
free(req_data); \
4649
}
4750

4851
#define NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT(aio, req_type, decode_fun, func) \
@@ -119,6 +122,8 @@ struct neu_http_handler {
119122
char *path;
120123
char *dst_url;
121124
} value;
125+
126+
size_t max_body;
122127
};
123128

124129
int neu_http_add_handler(nng_http_server * server,

‎plugins/restful/handle.c‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,25 +269,28 @@ static struct neu_http_handler rest_handlers[] = {
269269
.type = NEU_HTTP_HANDLER_FUNCTION,
270270
.url = "/api/v2/tags",
271271
.value.handler = handle_add_tags,
272-
272+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
273273
},
274274
{
275275
.method = NEU_HTTP_METHOD_POST,
276276
.type = NEU_HTTP_HANDLER_FUNCTION,
277277
.url = "/api/v2/gtags",
278278
.value.handler = handle_add_gtags,
279+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
279280
},
280281
{
281282
.method = NEU_HTTP_METHOD_POST,
282283
.type = NEU_HTTP_HANDLER_FUNCTION,
283284
.url = "/api/v2/tags/import",
284285
.value.handler = handle_import_tags,
286+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
285287
},
286288
{
287289
.method = NEU_HTTP_METHOD_PUT,
288290
.type = NEU_HTTP_HANDLER_FUNCTION,
289291
.url = "/api/v2/tags",
290292
.value.handler = handle_update_tags,
293+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
291294
},
292295
{
293296
.method = NEU_HTTP_METHOD_GET,
@@ -408,12 +411,14 @@ static struct neu_http_handler rest_handlers[] = {
408411
.type = NEU_HTTP_HANDLER_FUNCTION,
409412
.url = "/api/v2/write/tags",
410413
.value.handler = handle_write_tags,
414+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
411415
},
412416
{
413417
.method = NEU_HTTP_METHOD_POST,
414418
.type = NEU_HTTP_HANDLER_FUNCTION,
415419
.url = "/api/v2/write/gtags",
416420
.value.handler = handle_write_gtags,
421+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
417422
},
418423
{
419424
.method = NEU_HTTP_METHOD_POST,
@@ -450,6 +455,7 @@ static struct neu_http_handler rest_handlers[] = {
450455
.type = NEU_HTTP_HANDLER_FUNCTION,
451456
.url = "/api/v2/subscribes",
452457
.value.handler = handle_grp_subscribes,
458+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
453459
},
454460
{
455461
.method = NEU_HTTP_METHOD_GET,
@@ -516,12 +522,14 @@ static struct neu_http_handler rest_handlers[] = {
516522
.type = NEU_HTTP_HANDLER_FUNCTION,
517523
.url = "/api/v2/global/config",
518524
.value.handler = handle_put_global_config,
525+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
519526
},
520527
{
521528
.method = NEU_HTTP_METHOD_PUT,
522529
.type = NEU_HTTP_HANDLER_FUNCTION,
523530
.url = "/api/v2/global/drivers",
524531
.value.handler = handle_put_drivers,
532+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
525533
},
526534
{
527535
.method = NEU_HTTP_METHOD_GET,
@@ -540,6 +548,7 @@ static struct neu_http_handler rest_handlers[] = {
540548
.type = NEU_HTTP_HANDLER_FUNCTION,
541549
.url = "/api/v2/global/apps",
542550
.value.handler = handle_put_apps,
551+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
543552
},
544553
{
545554
.method = NEU_HTTP_METHOD_GET,
@@ -746,6 +755,7 @@ static struct neu_http_handler rest_handlers[] = {
746755
.type = NEU_HTTP_HANDLER_FUNCTION,
747756
.url = "/api/v2/simulator/config",
748757
.value.handler = handle_simulator_set_config,
758+
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
749759
},
750760
{
751761
.method = NEU_HTTP_METHOD_GET,

‎src/utils/http.c‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,18 @@ int neu_http_get_body(nng_aio *aio, void **data, size_t *data_size)
100100
nng_http_req_get_data(req, data, data_size);
101101
if (*data_size == 0) {
102102
return -1;
103+
} else if (*data_size > NEU_HTTP_BODY_MAX_SIZE_LARGE) {
104+
nlog_error("<%p> request body too big: %zu", aio, *data_size);
105+
*data = NULL;
106+
*data_size = 0;
107+
return NEU_HTTP_BODY_TOO_BIG;
103108
} else {
104109
char *buf = calloc(*data_size + 1, sizeof(char));
110+
if (NULL == buf) {
111+
*data = NULL;
112+
*data_size = 0;
113+
return -1;
114+
}
105115
memcpy(buf, *data, *data_size);
106116
*data = buf;
107117
return 0;

‎src/utils/http_handler.c‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <nng/nng.h>
2525
#include <nng/supplemental/http/http.h>
2626

27+
#include "define.h"
2728
#include "utils/http_handler.h"
2829
#include "utils/log.h"
2930

@@ -53,6 +54,12 @@ int neu_http_add_handler(nng_http_server * server,
5354
return -1;
5455
}
5556

57+
if (NEU_HTTP_HANDLER_FUNCTION == http_handler->type) {
58+
size_t max_body = http_handler->max_body > 0 ? http_handler->max_body
59+
: NEU_HTTP_BODY_MAX_SIZE;
60+
nng_http_handler_collect_body(handler, true, max_body);
61+
}
62+
5663
switch (http_handler->method) {
5764
case NEU_HTTP_METHOD_GET:
5865
ret = nng_http_handler_set_method(handler, "GET");

‎tests/ft/http_api/test_http_api.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import copy
22

3+
import requests
4+
35
import neuron.api as api
46
import neuron.error as error
57
import neuron.config as config
@@ -649,6 +651,23 @@ def test_get_node_or_group_by_filter(self):
649651
assert 1 == len(response.json()["groups"])
650652

651653

654+
@description(given="running neuron", when="post an over sized body", then="should be rejected before parsing")
655+
def test_body_too_big(self):
656+
# far above the 1MB cap of an ordinary endpoint, but below the 4MB
657+
# cap of the bulk endpoints
658+
payload = '{"name": "' + 'a' * (2 * 1024 * 1024) + '"}'
659+
headers = {"Authorization": config.default_jwt,
660+
"Content-Type": "application/json"}
661+
662+
response = requests.post(
663+
url=config.BASE_URL + '/api/v2/node', headers=headers, data=payload)
664+
assert 400 == response.status_code
665+
assert error.NEU_ERR_BODY_TOO_BIG == response.json()['error']
666+
667+
# neuron is still alive and serving after the rejection
668+
response = api.get_version()
669+
assert 200 == response.status_code
670+
652671
@description(given="running neuron", when="test jwt error", then="failed")
653672
def test_jwt_err(self):
654673
response = api.change_password(new_password='123456', jwt='invalid')

‎tests/ft/neuron/error.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
NEU_ERR_COMMAND_EXECUTION_FAILED = 1014
1717
NEU_ERR_IP_ADDRESS_INVALID = 1015
1818
NEU_ERR_IP_ADDRESS_IN_USE = 1016
19-
NEU_ERR_INVALID_USER = 1017
20-
NEU_ERR_INVALID_PASSWORD = 1018
19+
NEU_ERR_BODY_TOO_BIG = 1017
20+
NEU_ERR_INVALID_CID = 1018
2121

2222
NEU_ERR_NODE_EXIST = 2002
2323
NEU_ERR_NODE_NOT_EXIST = 2003

0 commit comments

Comments
 (0)