diff --git a/CHANGELOG.md b/CHANGELOG.md index b367e83c8..cd3709f9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ SPDX-License-Identifier: GPL-3.0-or-later - core: write configuration files on disk only if there are changes ([#2250](https://github.com/weechat/weechat/issues/2250)) - core: always enable partial completion for templates in option weechat.completion.partial_completion_templates, add option weechat.completion.partial_completion_auto_expand to expand word on new completion ([#2253](https://github.com/weechat/weechat/issues/2253)) - core: add script name in output of `/debug hooks ` +- relay/api: return HTTP error 405 (Method Not Allowed) when the method received is not allowed ### Added diff --git a/src/plugins/relay/api/relay-api-protocol.c b/src/plugins/relay/api/relay-api-protocol.c index e2e8d56c1..167cb5520 100644 --- a/src/plugins/relay/api/relay-api-protocol.c +++ b/src/plugins/relay/api/relay-api-protocol.c @@ -374,7 +374,7 @@ RELAY_API_PROTOCOL_CALLBACK(options) relay_api_msg_send_json ( client, RELAY_HTTP_204_NO_CONTENT, - "Access-Control-Allow-Methods: GET, POST, PUT, DELETE\r\n" + "Access-Control-Allow-Methods: " RELAY_API_ALLOWED_METHODS "\r\n" "Access-Control-Allow-Headers: origin, content-type, accept, authorization", NULL, /* body_type */ NULL); /* json_body */ @@ -1197,7 +1197,7 @@ relay_api_protocol_recv_json (struct t_relay_client *client, const char *json) void relay_api_protocol_recv_http (struct t_relay_client *client) { - int i, num_args; + int i, num_args, match_method, match_resource; char str_error[1024]; enum t_relay_api_protocol_rc return_code; struct t_relay_api_protocol_cb protocol_cb[] = { @@ -1238,18 +1238,22 @@ relay_api_protocol_recv_http (struct t_relay_client *client) || !client->http_req->path_items[1] || (strcmp (client->http_req->path_items[0], "api") != 0)) { - goto error_not_found; + goto resource_not_found; } num_args = client->http_req->num_path_items - 2; for (i = 0; protocol_cb[i].resource; i++) { - if (strcmp (protocol_cb[i].method, client->http_req->method) != 0) - continue; + match_method = (strcmp (protocol_cb[i].method, client->http_req->method) == 0); - if ((strcmp (protocol_cb[i].resource, "*") != 0) - && (strcmp (protocol_cb[i].resource, client->http_req->path_items[1]) != 0)) + match_resource = ((strcmp (protocol_cb[i].resource, "*") == 0) + || (strcmp (protocol_cb[i].resource, client->http_req->path_items[1]) == 0)); + + if (!match_method && (strcmp (protocol_cb[i].resource, "*") != 0) && match_resource) + goto error_method_not_allowed; + + if (!match_method || !match_resource) continue; if (protocol_cb[i].auth_required @@ -1331,7 +1335,18 @@ relay_api_protocol_recv_http (struct t_relay_client *client) } } - goto error_not_found; +resource_not_found: + if ((strcmp (client->http_req->method, "GET") != 0) + && (strcmp (client->http_req->method, "POST") != 0) + && (strcmp (client->http_req->method, "PUT") != 0) + && (strcmp (client->http_req->method, "DELETE") != 0)) + { + goto error_method_not_allowed; + } + else + { + goto error_not_found; + } error_bad_request: relay_api_msg_send_error_json (client, RELAY_HTTP_400_BAD_REQUEST, @@ -1343,6 +1358,12 @@ error_not_found: NULL, RELAY_HTTP_ERROR_NOT_FOUND); goto error; +error_method_not_allowed: + relay_api_msg_send_error_json (client, RELAY_HTTP_405_METHOD_NOT_ALLOWED, + "Allow: " RELAY_API_ALLOWED_METHODS, + RELAY_HTTP_ERROR_METHOD_NOT_ALLOWED); + goto error; + error_memory: relay_api_msg_send_error_json (client, RELAY_HTTP_503_SERVICE_UNAVAILABLE, NULL, RELAY_HTTP_ERROR_OUT_OF_MEMORY); diff --git a/src/plugins/relay/api/relay-api-protocol.h b/src/plugins/relay/api/relay-api-protocol.h index cb09bb548..c1bc1216f 100644 --- a/src/plugins/relay/api/relay-api-protocol.h +++ b/src/plugins/relay/api/relay-api-protocol.h @@ -22,6 +22,8 @@ #ifndef WEECHAT_PLUGIN_RELAY_API_PROTOCOL_H #define WEECHAT_PLUGIN_RELAY_API_PROTOCOL_H +#define RELAY_API_ALLOWED_METHODS "GET, POST, PUT, DELETE" + #define RELAY_API_CB(__command) &relay_api_protocol_cb_##__command #define RELAY_API_PROTOCOL_CALLBACK(__command) \ enum t_relay_api_protocol_rc \ diff --git a/src/plugins/relay/relay-http.h b/src/plugins/relay/relay-http.h index 4c28d48b9..14d9e943f 100644 --- a/src/plugins/relay/relay-http.h +++ b/src/plugins/relay/relay-http.h @@ -40,6 +40,7 @@ enum t_relay_client_http_status #define RELAY_HTTP_401_UNAUTHORIZED 401, "Unauthorized" #define RELAY_HTTP_403_FORBIDDEN 403, "Forbidden" #define RELAY_HTTP_404_NOT_FOUND 404, "Not Found" +#define RELAY_HTTP_405_METHOD_NOT_ALLOWED 405, "Method Not Allowed" #define RELAY_HTTP_500_INTERNAL_SERVER_ERROR 500, "Internal Server Error" #define RELAY_HTTP_503_SERVICE_UNAVAILABLE 503, "Service Unavailable" @@ -53,6 +54,7 @@ enum t_relay_client_http_status #define RELAY_HTTP_ERROR_INVALID_ITERATIONS "Invalid number of iterations" #define RELAY_HTTP_ERROR_BAD_REQUEST "Bad request" #define RELAY_HTTP_ERROR_NOT_FOUND "Resource not found" +#define RELAY_HTTP_ERROR_METHOD_NOT_ALLOWED "Method Not Allowed" #define RELAY_HTTP_ERROR_OUT_OF_MEMORY "Out of memory" struct t_relay_http_request diff --git a/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp b/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp index afa77de01..65891b693 100644 --- a/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp +++ b/tests/unit/plugins/relay/api/test-relay-api-protocol.cpp @@ -1052,3 +1052,44 @@ TEST(RelayApiProtocolWithClient, RecvHttpInvalidPassword) "{\"error\":\"Invalid password\"}", data_sent[0]); } + +/* + * Tests functions: + * relay_api_protocol_recv_http (method not allowed) + */ + +TEST(RelayApiProtocolWithClient, RecvHttpMethodNotAllowed) +{ + /* method not allowed (PATCH) with existing resource (/api/ping) */ + test_client_recv_http ("PATCH /api/ping", NULL, "{\"data\": \"abcdef\"}"); + STRCMP_EQUAL("HTTP/1.1 405 Method Not Allowed\r\n" + "Allow: GET, POST, PUT, DELETE\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: application/json; charset=utf-8\r\n" + "Content-Length: 30\r\n" + "\r\n" + "{\"error\":\"Method Not Allowed\"}", + data_sent[0]); + + /* method not allowed (PATCH) with unknown resource (/api/unknown) */ + test_client_recv_http ("PATCH /api/unknown", NULL, "{\"data\": \"abcdef\"}"); + STRCMP_EQUAL("HTTP/1.1 405 Method Not Allowed\r\n" + "Allow: GET, POST, PUT, DELETE\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: application/json; charset=utf-8\r\n" + "Content-Length: 30\r\n" + "\r\n" + "{\"error\":\"Method Not Allowed\"}", + data_sent[0]); + + /* method not allowed (PATCH) with unknown resource (/unknown) */ + test_client_recv_http ("PATCH /unknown", NULL, "{\"data\": \"abcdef\"}"); + STRCMP_EQUAL("HTTP/1.1 405 Method Not Allowed\r\n" + "Allow: GET, POST, PUT, DELETE\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: application/json; charset=utf-8\r\n" + "Content-Length: 30\r\n" + "\r\n" + "{\"error\":\"Method Not Allowed\"}", + data_sent[0]); +}