diff --git a/CHANGELOG.md b/CHANGELOG.md index d974f357..e0a75bb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - New `unsafe_contents_md` property in the text component to allow rendering markdown with embedded HTML tags. - New `_sqlpage_footer` property for table rows. When applied, that row will be rendered as the table footer. It is recommended to use this on the last data row. - New `freeze_footers` property in table component. If the footer is enabled, this will make it always visible. Works similarly to `freeze_headers`. +- Hidden files and folders (those with a name starting with a `.`) are now inaccessible. This allows you to easily create internal files to use with `sqlpage.run_sql(...)` that will not be directly accessible. ## 0.33.1 (2025-02-25) diff --git a/examples/official-site/sqlpage/migrations/38_run_sql.sql b/examples/official-site/sqlpage/migrations/38_run_sql.sql index be123213..4db6a7e8 100644 --- a/examples/official-site/sqlpage/migrations/38_run_sql.sql +++ b/examples/official-site/sqlpage/migrations/38_run_sql.sql @@ -27,7 +27,12 @@ select ''dynamic'' as component, sqlpage.run_sql(''common_header.sql'') as prope #### Notes - **recursion**: you can use `run_sql` to include a file that itself includes another file, and so on. However, be careful to avoid infinite loops. SQLPage will throw an error if the inclusion depth is superior to `max_recursion_depth` (10 by default). - - **security**: be careful when using `run_sql` to include files. Never use `run_sql` with a user-provided parameter. Never run a file uploaded by a user, or a file that is not under your control. + - **security**: be careful when using `run_sql` to include files. + - Never use `run_sql` with a user-provided parameter. + - Never run a file uploaded by a user, or a file that is not under your control. + - Remember that users can also run the files you include with `sqlpage.run_sql(...)` directly just by loading the file in the browser. + - Make sure this does not allow users to bypass security measures you put in place such as [access control](/component.sql?component=authentication). + - If you need to include a file, but make it inaccessible to users, you can use hidden files and folders (starting with a `.`), or put files in the special `sqlpage/` folder that is not accessible to users. - **variables**: the included file will have access to the same variables (URL parameters, POST variables, etc.) as the calling file. If the included file changes the value of a variable or creates a new variable, the change will not be visible in the calling file. diff --git a/src/filesystem.rs b/src/filesystem.rs index 22fcd61e..f9d51422 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -122,6 +122,11 @@ impl FileSystem { status: actix_web::http::StatusCode::FORBIDDEN, }); } + if c.as_encoded_bytes().starts_with(b".") { + anyhow::bail!(ErrorWithStatus { + status: actix_web::http::StatusCode::FORBIDDEN, + }); + } } else { anyhow::bail!( "Unsupported path: {path:?}. Path component '{component:?}' is not allowed." diff --git a/tests/.hidden.sql b/tests/.hidden.sql new file mode 100644 index 00000000..a7631239 --- /dev/null +++ b/tests/.hidden.sql @@ -0,0 +1 @@ +select 'text' as component, 'This is a hidden file that should not be accessible' as contents; \ No newline at end of file diff --git a/tests/index.rs b/tests/index.rs index 1f1dee8b..7e5dfcaa 100644 --- a/tests/index.rs +++ b/tests/index.rs @@ -880,6 +880,22 @@ async fn test_request_body_base64() -> actix_web::Result<()> { Ok(()) } +#[actix_web::test] +async fn test_hidden_files() { + let resp_result = req_path("/tests/.hidden.sql").await; + assert!( + resp_result.is_err(), + "Accessing a hidden file should be forbidden, but received success: {resp_result:?}" + ); + let resp = resp_result.unwrap_err().error_response(); + assert_eq!(resp.status(), http::StatusCode::FORBIDDEN); + assert!( + String::from_utf8_lossy(&resp.into_body().try_into_bytes().unwrap()) + .to_lowercase() + .contains("forbidden"), + ); +} + async fn get_request_to_with_data( path: &str, data: actix_web::web::Data,