From 7c45f84e5e3d2e0a75b3946408fec1a4d5c72841 Mon Sep 17 00:00:00 2001 From: Andreas Maier Date: Fri, 2 Aug 2024 19:28:35 +0200 Subject: [PATCH] Reject invalid HTTP methods and resources (#1019) This change addresses the issue that currently, any HTTP method is handled by returning success and metrics data, which causes network scanners to report issues. Details: * This change rejects any HTTP methods and resources other than the following: OPTIONS (any) - returns 200 and an 'Allow' header indicating allowed methods GET (any) - returns 200 and metrics GET /favicon.ico - returns 200 and no body (this is no change) Other HTTP methods than these are rejected with 405 "Method Not Allowed" and an 'Allow' header indicating the allowed HTTP methods. Any returned HTTP errors are also displayed in the response body after a hash sign and with a brief hint, e.g. "# HTTP 405 Method Not Allowed: XXX; use OPTIONS or GET". Signed-off-by: Andreas Maier --- docs/content/exporting/http/_index.md | 21 ++++++++++++++++++++- prometheus_client/exposition.py | 14 +++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/content/exporting/http/_index.md b/docs/content/exporting/http/_index.md index 71edc7e3..dc1b8f2c 100644 --- a/docs/content/exporting/http/_index.md +++ b/docs/content/exporting/http/_index.md @@ -52,4 +52,23 @@ chain is used (see Python [ssl.SSLContext.load_default_certs()](https://docs.pyt from prometheus_client import start_http_server start_http_server(8000, certfile="server.crt", keyfile="server.key") -``` \ No newline at end of file +``` + +# Supported HTTP methods + +The prometheus client will handle the following HTTP methods and resources: + +* `OPTIONS (any)` - returns HTTP status 200 and an 'Allow' header indicating the + allowed methods (OPTIONS, GET) +* `GET (any)` - returns HTTP status 200 and the metrics data +* `GET /favicon.ico` - returns HTTP status 200 and an empty response body. Some + browsers support this to display the returned icon in the browser tab. + +Other HTTP methods than these are rejected with HTTP status 405 "Method Not Allowed" +and an 'Allow' header indicating the allowed methods (OPTIONS, GET). + +Any returned HTTP errors are also displayed in the response body after a hash +sign and with a brief hint. Example: +``` +# HTTP 405 Method Not Allowed: XXX; use OPTIONS or GET +``` diff --git a/prometheus_client/exposition.py b/prometheus_client/exposition.py index 3a47917c..fab139df 100644 --- a/prometheus_client/exposition.py +++ b/prometheus_client/exposition.py @@ -118,12 +118,24 @@ def prometheus_app(environ, start_response): accept_header = environ.get('HTTP_ACCEPT') accept_encoding_header = environ.get('HTTP_ACCEPT_ENCODING') params = parse_qs(environ.get('QUERY_STRING', '')) - if environ['PATH_INFO'] == '/favicon.ico': + method = environ['REQUEST_METHOD'] + + if method == 'OPTIONS': + status = '200 OK' + headers = [('Allow', 'OPTIONS,GET')] + output = b'' + elif method != 'GET': + status = '405 Method Not Allowed' + headers = [('Allow', 'OPTIONS,GET')] + output = '# HTTP {}: {}; use OPTIONS or GET\n'.format(status, method).encode() + elif environ['PATH_INFO'] == '/favicon.ico': # Serve empty response for browsers status = '200 OK' headers = [('', '')] output = b'' else: + # Note: For backwards compatibility, the URI path for GET is not + # constrained to the documented /metrics, but any path is allowed. # Bake output status, headers, output = _bake_output(registry, accept_header, accept_encoding_header, params, disable_compression) # Return output