Skip to content

Forbid access to hidden dotfiles over http #866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
7 changes: 6 additions & 1 deletion examples/official-site/sqlpage/migrations/38_run_sql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions src/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
1 change: 1 addition & 0 deletions tests/.hidden.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
select 'text' as component, 'This is a hidden file that should not be accessible' as contents;
16 changes: 16 additions & 0 deletions tests/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AppState>,
Expand Down