From a176f683b92d1b63570b5a69ab48cd9e4a686cbd Mon Sep 17 00:00:00 2001 From: cln-io <7887972+cln-io@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:39:04 +0100 Subject: [PATCH] Validate JSON request body on all POST API endpoints All POST endpoints called request.get_json() without checking the return type. Malformed but valid JSON (null, [], "string") would cause AttributeError on .get() calls, returning 500 instead of 400. Adds a @require_json_body decorator that returns a clear 400 error when the body is not a JSON object. Applied to all 6 POST endpoints. --- var/www/blueprints/api_rest.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/var/www/blueprints/api_rest.py b/var/www/blueprints/api_rest.py index edaa6657..5b360108 100644 --- a/var/www/blueprints/api_rest.py +++ b/var/www/blueprints/api_rest.py @@ -86,6 +86,15 @@ def api_token(*args, **kwargs): def create_json_response(data, status_code): return Response(json.dumps(data) + "\n", mimetype='application/json'), status_code +def require_json_body(funct): + """Decorator that rejects requests whose JSON body is not a dict.""" + @wraps(funct) + def wrapper(*args, **kwargs): + if not isinstance(request.get_json(silent=True), dict): + return create_json_response({'status': 'error', 'reason': 'Invalid JSON body'}, 400) + return funct(*args, **kwargs) + return wrapper + # ============= ROUTES ============== # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @@ -122,6 +131,7 @@ def v1_pyail_version(): # # TODO: ADD RESULT JSON Response @api_rest.route("api/v1/add/crawler/task", methods=['POST']) # TODO V2 Migration @token_required('user') +@require_json_body def add_crawler_task(): data = request.get_json() user_token = get_auth_from_header() @@ -150,6 +160,7 @@ def get_crawler_captures(): @api_rest.route("api/v1/add/crawler/capture", methods=['POST']) # TODO V2 Migration @token_required('user') +@require_json_body def add_crawler_capture(): data = request.get_json() user_token = get_auth_from_header() @@ -170,6 +181,7 @@ def get_onions_up_month(date_year_month): @api_rest.route("api/v1/lacus/cookiejar/import", methods=['POST']) @token_required('user') +@require_json_body def lacus_cookiejar_import(): data = request.get_json() user_token = get_auth_from_header() @@ -205,6 +217,7 @@ def get_crawler_blacklist(): @api_rest.route("api/v1/crawler/blacklist/add", methods=['POST']) @token_required('admin') +@require_json_body def add_crawler_blacklist(): data = request.get_json() res = crawlers.api_blacklist_domain(data) @@ -231,6 +244,7 @@ def import_json_item(): @api_rest.route("api/v1/import/crawler/capture", methods=['POST']) @token_required('user') +@require_json_body def import_crawler_capture(): data_json = request.get_json() res = crawlers.api_add_lacus_capture_to_import(data_json) @@ -354,6 +368,7 @@ def v1_investigation(investigation_uuid): @api_rest.route("api/v1/user/create", methods=['POST']) @token_required('admin') +@require_json_body def v1_user_create(): data_json = request.get_json() user_token = get_auth_from_header()