From 5d806b423d1570de855bd19ece1f8b1753a699f8 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Wed, 10 Jun 2026 15:55:45 +0200 Subject: [PATCH] docs: clarify persist_uploaded_file folder is trusted app input Document that the folder/destination_folder argument of sqlpage.persist_uploaded_file must be chosen by the app author and never derived from untrusted request data. It is joined directly to the web root, so a value containing '..' or an absolute path would write the uploaded file outside the web root. Docs-only clarification of existing intended behavior; no logic change. --- .../sqlpage/migrations/39_persist_uploaded_file.sql | 4 +++- src/webserver/database/sqlpage_functions/functions.rs | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/official-site/sqlpage/migrations/39_persist_uploaded_file.sql b/examples/official-site/sqlpage/migrations/39_persist_uploaded_file.sql index b45943aa..76619a2d 100644 --- a/examples/official-site/sqlpage/migrations/39_persist_uploaded_file.sql +++ b/examples/official-site/sqlpage/migrations/39_persist_uploaded_file.sql @@ -52,7 +52,9 @@ VALUES ( 'persist_uploaded_file', 2, 'destination_folder', - 'Optional. Path to the folder where the file will be saved, relative to the web root (the root folder of your website files). By default, the file will be saved in the `uploads` folder.', + 'Optional. Path to the folder where the file will be saved, relative to the web root (the root folder of your website files). By default, the file will be saved in the `uploads` folder. + +**Security note**: this value must be a folder name you choose yourself in your SQL code (a trusted constant). Never build it from untrusted input such as a form field, a query parameter, a request header, or anything else the visitor controls. The folder is joined directly to the web root, so a value containing `..` or an absolute path would cause the uploaded file to be written *outside* the web root, anywhere the SQLPage process can write. Keeping `destination_folder` a fixed value chosen by the application author avoids this.', 'TEXT' ), ( diff --git a/src/webserver/database/sqlpage_functions/functions.rs b/src/webserver/database/sqlpage_functions/functions.rs index bf669470..7710ef27 100644 --- a/src/webserver/database/sqlpage_functions/functions.rs +++ b/src/webserver/database/sqlpage_functions/functions.rs @@ -512,7 +512,12 @@ async fn persist_uploaded_file<'a>( let exts = allowed_extensions.collect::>().join(", "); anyhow::bail!("file extension {extension} is not allowed. Allowed extensions: {exts}"); } - // resolve the folder path relative to the web root + // Resolve the folder path relative to the web root. + // `folder` is trusted application input: it is expected to be a constant chosen by the + // app author in their SQL code, never attacker-controlled request data. It is joined + // directly to the web root, so a `folder` containing `..` or an absolute path would let + // the caller write the uploaded file outside the web root. Callers must not pass + // untrusted input (form fields, query parameters, headers, ...) as the folder. let web_root = &request.app_state.config.web_root; let target_folder = web_root.join(&*folder); // create the folder if it doesn't exist