Skip to content

Commit

Permalink
MeshMS RESTful API stricter on incoming form data
Browse files Browse the repository at this point in the history
/restful/meshms/SID/SID/sendmessage now enforces Content-Disposition:
form-data (was ignoring), and enforces Content-Type: text/plain;
charset=utf-8 (was ignoring)

New negative test cases to ensure that these are treated strictly
  • Loading branch information
quixotique committed Jun 23, 2014
1 parent 533c0be commit 819b8dc
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 7 deletions.
33 changes: 33 additions & 0 deletions httpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,39 @@ void form_buf_malloc_release(struct form_buf_malloc *f)
f->size_limit = 0;
}

int http_response_content_type(httpd_request *r, const char *what, const struct mime_content_type *ct)
{
if (config.debug.httpd)
DEBUGF("%s Content-Type: %s/%s%s%s%s%s", what, ct->type, ct->subtype,
ct->charset[0] ? "; charset=" : "",
ct->charset,
ct->multipart_boundary[0] ? "; boundary=" : "",
ct->multipart_boundary
);
strbuf msg = strbuf_alloca(200);
strbuf_sprintf(msg, "%s Content-Type:", what);
if (ct->type[0])
strbuf_sprintf(msg, " %s", ct->type);
if (ct->subtype[0])
strbuf_sprintf(msg, "/%s", ct->subtype);
if (ct->charset[0])
strbuf_sprintf(msg, "; charset=%s", ct->charset);
if (ct->multipart_boundary[0])
strbuf_sprintf(msg, "; boundary=%s", ct->multipart_boundary);
http_request_simple_response(&r->http, 403, strbuf_str(msg));
return 403;
}

int http_response_content_disposition(httpd_request *r, const char *what, const char *type)
{
if (config.debug.httpd)
DEBUGF("%s Content-Disposition: %s %s", what, type);
strbuf msg = strbuf_alloca(100);
strbuf_sprintf(msg, "%s Content-Disposition: %s", what, type);
http_request_simple_response(&r->http, 403, strbuf_str(msg));
return 403;
}

int http_response_form_part(httpd_request *r, const char *what, const char *partname, const char *text, size_t textlen)
{
if (config.debug.httpd)
Expand Down
2 changes: 2 additions & 0 deletions httpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ typedef int HTTP_HANDLER(httpd_request *r, const char *remainder);

int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call);
int authorize(struct http_request *r);
int http_response_content_type(httpd_request *r, const char *what, const struct mime_content_type *ct);
int http_response_content_disposition(httpd_request *r, const char *what, const char *type);
int http_response_form_part(httpd_request *r, const char *what, const char *partname, const char *text, size_t textlen);
int accumulate_text(httpd_request *r, const char *partname, char *textbuf, size_t textsiz, size_t *textlenp, const char *buf, size_t len);

Expand Down
10 changes: 10 additions & 0 deletions meshms_restful.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ static int send_mime_part_end(struct http_request *hr)
static int send_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
{
httpd_request *r = (httpd_request *) hr;
if (strcmp(h->content_disposition.type, "form-data") != 0)
return http_response_content_disposition(r, "Unsupported", h->content_disposition.type);
if (strcmp(h->content_disposition.name, PART_MESSAGE) == 0) {
if (r->u.sendmsg.received_message)
return http_response_form_part(r, "Duplicate", PART_MESSAGE, NULL, 0);
Expand All @@ -531,6 +533,14 @@ static int send_mime_part_header(struct http_request *hr, const struct mime_part
}
else
return http_response_form_part(r, "Unsupported", h->content_disposition.name, NULL, 0);
if (!h->content_type.type[0] || !h->content_type.subtype[0])
return http_response_content_type(r, "Missing", &h->content_type);
if (strcmp(h->content_type.type, "text") != 0 || strcmp(h->content_type.subtype, "plain") != 0)
return http_response_content_type(r, "Unsupported", &h->content_type);
if (!h->content_type.charset[0])
return http_response_content_type(r, "Missing charset", &h->content_type);
if (strcmp(h->content_type.charset, "utf-8") != 0)
return http_response_content_type(r, "Unsupported charset", &h->content_type);
return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions rhizome_restful.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ static int insert_make_manifest(httpd_request *r)
static int insert_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
{
httpd_request *r = (httpd_request *) hr;
if (strcmp(h->content_disposition.type, "form-data") != 0)
return http_response_content_disposition(r, "Unsupported", h->content_disposition.type);
if (strcmp(h->content_disposition.name, PART_AUTHOR) == 0) {
if (r->u.insert.received_author)
return http_response_form_part(r, "Duplicate", PART_AUTHOR, NULL, 0);
Expand Down
102 changes: 95 additions & 7 deletions tests/rhizomehttp
Original file line number Diff line number Diff line change
Expand Up @@ -1190,22 +1190,22 @@ test_MeshmsSend() {
--silent --fail --show-error \
--output sendmessage.json \
--basic --user harry:potter \
--form "message=Hello World" \
--form "message=Hello World;type=text/plain;charset=utf-8" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA1/$SIDA2/sendmessage"
executeOk_servald meshms list messages $SIDA1 $SIDA2
assertStdoutGrep --matches=1 ':>:Hello World'
executeOk curl \
--silent --fail --show-error \
--output sendmessage.json \
--basic --user ron:weasley \
--form "message=Hello back!" \
--form "message=Hello back!;type=text/plain;charset=utf-8" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
executeOk_servald meshms list messages $SIDA1 $SIDA2
assertStdoutGrep --matches=1 ':>:Hello World$'
assertStdoutGrep --matches=1 ':<:Hello back!$'
}

doc_MeshmsSendMissingMessage="HTTP RESTful MeshMS send missing 'message' form part "
doc_MeshmsSendMissingMessage="HTTP RESTful MeshMS send missing 'message' form part"
setup_MeshmsSendMissingMessage() {
IDENTITY_COUNT=2
setup
Expand All @@ -1227,7 +1227,7 @@ test_MeshmsSendMissingMessage() {
assertStdoutLineCount '==' 2
}

doc_MeshmsSendDuplicateMessage="HTTP RESTful MeshMS send missing 'message' form part "
doc_MeshmsSendDuplicateMessage="HTTP RESTful MeshMS send duplicate 'message' form parts"
setup_MeshmsSendDuplicateMessage() {
IDENTITY_COUNT=2
setup
Expand All @@ -1238,8 +1238,8 @@ test_MeshmsSendDuplicateMessage() {
--output http.body \
--dump-header http.header \
--basic --user harry:potter \
--form "message=Hello one" \
--form "message=Hello two" \
--form "message=Hello one;type=text/plain;charset=utf-8" \
--form "message=Hello two;type=text/plain;charset=utf-8" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
tfw_cat http.header http.body
assertExitStatus == 0
Expand All @@ -1250,6 +1250,94 @@ test_MeshmsSendDuplicateMessage() {
assertStdoutLineCount '==' 2
}

doc_MeshmsSendMessageMissingContentType="HTTP RESTful MeshMS send 'message' form part missing Content-Type"
setup_MeshmsSendMessageMissingContentType() {
IDENTITY_COUNT=2
setup
}
test_MeshmsSendMessageMissingContentType() {
execute curl \
--silent --show-error --write-out '%{http_code}' \
--output http.body \
--dump-header http.header \
--basic --user harry:potter \
--form "message=Hello there" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
tfw_cat http.header http.body
assertExitStatus == 0
assertStdoutIs 403
assertJq http.body 'contains({"http_status_code": 403})'
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*content.*type'
executeOk_servald meshms list messages $SIDA1 $SIDA2
assertStdoutLineCount '==' 2
}

doc_MeshmsSendMessageUnsupportedContentType="HTTP RESTful MeshMS send 'message' form part unsupported Content-Type"
setup_MeshmsSendMessageUnsupportedContentType() {
IDENTITY_COUNT=2
setup
}
test_MeshmsSendMessageUnsupportedContentType() {
execute curl \
--silent --show-error --write-out '%{http_code}' \
--output http.body \
--dump-header http.header \
--basic --user harry:potter \
--form "message=Hello there;type=text/rich" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
tfw_cat http.header http.body
assertExitStatus == 0
assertStdoutIs 403
assertJq http.body 'contains({"http_status_code": 403})'
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*content.*type'
executeOk_servald meshms list messages $SIDA1 $SIDA2
assertStdoutLineCount '==' 2
}

doc_MeshmsSendMessageMissingCharset="HTTP RESTful MeshMS send 'message' form part missing charset"
setup_MeshmsSendMessageMissingCharset() {
IDENTITY_COUNT=2
setup
}
test_MeshmsSendMessageMissingCharset() {
execute curl \
--silent --show-error --write-out '%{http_code}' \
--output http.body \
--dump-header http.header \
--basic --user harry:potter \
--form "message=Hello there;type=text/plain" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
tfw_cat http.header http.body
assertExitStatus == 0
assertStdoutIs 403
assertJq http.body 'contains({"http_status_code": 403})'
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*charset'
executeOk_servald meshms list messages $SIDA1 $SIDA2
assertStdoutLineCount '==' 2
}

doc_MeshmsSendMessageUnsupportedCharset="HTTP RESTful MeshMS send 'message' form part unsupported charset"
setup_MeshmsSendMessageUnsupportedCharset() {
IDENTITY_COUNT=2
setup
}
test_MeshmsSendMessageUnsupportedCharset() {
execute curl \
--silent --show-error --write-out '%{http_code}' \
--output http.body \
--dump-header http.header \
--basic --user harry:potter \
--form "message=Hello there;type=text/plain;charset=latin-1" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
tfw_cat http.header http.body
assertExitStatus == 0
assertStdoutIs 403
assertJq http.body 'contains({"http_status_code": 403})'
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*charset'
executeOk_servald meshms list messages $SIDA1 $SIDA2
assertStdoutLineCount '==' 2
}

doc_MeshmsSendNoIdentity="HTTP RESTful MeshMS send from unknown identity"
setup_MeshmsSendNoIdentity() {
setup
Expand All @@ -1261,7 +1349,7 @@ test_MeshmsSendNoIdentity() {
--output http.body \
--dump-header http.header \
--basic --user harry:potter \
--form "message=Hello" \
--form "message=Hello;type=text/plain;charset=utf-8" \
"http://$addr_localhost:$PORTA/restful/meshms/$SIDX/$SIDA/sendmessage"
tfw_cat http.header http.body
assertExitStatus == 0
Expand Down

0 comments on commit 819b8dc

Please sign in to comment.