Skip to content

Commit

Permalink
fix #89
Browse files Browse the repository at this point in the history
  • Loading branch information
lovasoa committed Sep 18, 2023
1 parent cc61e23 commit 3f73964
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 24 deletions.
2 changes: 1 addition & 1 deletion sqlpage/sqlpage.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"database_url": "sqlite://./sqlpage.db?mode=rwc",
"listen_on": "localhost:8081"
"listen_on": "localhost:8080"
}
7 changes: 5 additions & 2 deletions src/file_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl<T: AsyncFromStrWithState> FileCache<T> {
}
match app_state
.file_system
.modified_since(app_state, path, cached.last_check_time())
.modified_since(app_state, path, cached.last_check_time(), true)
.await
{
Ok(false) => {
Expand All @@ -115,7 +115,10 @@ impl<T: AsyncFromStrWithState> FileCache<T> {
}
// Read lock is released
log::trace!("Loading and parsing {:?}", path);
let file_contents = app_state.file_system.read_to_string(app_state, path).await;
let file_contents = app_state
.file_system
.read_to_string(app_state, path, true)
.await;

let parsed = match file_contents {
Ok(contents) => {
Expand Down
44 changes: 34 additions & 10 deletions src/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use chrono::{DateTime, Utc};
use sqlx::any::{AnyKind, AnyStatement, AnyTypeInfo};
use sqlx::postgres::types::PgTimeTz;
use sqlx::{Postgres, Statement, Type};
use std::borrow::Cow;
use std::io::ErrorKind;
use std::path::{Component, Path, PathBuf};

Expand Down Expand Up @@ -37,8 +38,13 @@ impl FileSystem {
app_state: &AppState,
path: &Path,
since: DateTime<Utc>,
priviledged: bool,
) -> anyhow::Result<bool> {
let local_path = self.safe_local_path(path)?;
let local_path = if priviledged {
Cow::Borrowed(path)
} else {
Cow::Owned(self.safe_local_path(path)?)
};
let local_result = file_modified_since_local(&local_path, since).await;
match (local_result, &self.db_fs_queries) {
(Ok(modified), _) => Ok(modified),
Expand All @@ -58,14 +64,27 @@ impl FileSystem {
&self,
app_state: &AppState,
path: &Path,
priviledged: bool,
) -> anyhow::Result<String> {
let bytes = self.read_file(app_state, path).await?;
let bytes = self.read_file(app_state, path, priviledged).await?;
String::from_utf8(bytes)
.with_context(|| format!("The file at {path:?} contains invalid UTF8 characters"))
}

pub async fn read_file(&self, app_state: &AppState, path: &Path) -> anyhow::Result<Vec<u8>> {
let local_path = self.safe_local_path(path)?;
/**
* Priviledged files are the ones that are in sqlpage's config directory.
*/
pub async fn read_file(
&self,
app_state: &AppState,
path: &Path,
priviledged: bool,
) -> anyhow::Result<Vec<u8>> {
let local_path = if priviledged {
Cow::Borrowed(path)
} else {
Cow::Owned(self.safe_local_path(path)?)
};
let local_result = tokio::fs::read(&local_path).await;
match (local_result, &self.db_fs_queries) {
(Ok(f), _) => Ok(f),
Expand All @@ -82,11 +101,16 @@ impl FileSystem {
}

fn safe_local_path(&self, path: &Path) -> anyhow::Result<PathBuf> {
for component in path.components() {
anyhow::ensure!(
matches!(component, Component::Normal(_)),
"Unsupported path: {path:?}. Path component {component:?} is not allowed."
);
for (i, component) in path.components().enumerate() {
if let Component::Normal(c) = component {
if c.eq_ignore_ascii_case("sqlpage") && i == 0 {
anyhow::bail!("Access to the sqlpage config directory is not allowed.");
}
} else {
anyhow::bail!(
"Unsupported path: {path:?}. Path component '{component:?}' is not allowed."
);
}
}
Ok(self.local_root.join(path))
}
Expand Down Expand Up @@ -202,7 +226,7 @@ async fn test_sql_file_read_utf8() -> anyhow::Result<()> {
.await?;
let fs = FileSystem::init("/", &state.db).await;
let actual = fs
.read_to_string(&state, "unit test file.txt".as_ref())
.read_to_string(&state, "unit test file.txt".as_ref(), false)
.await?;
assert_eq!(actual, "Héllö world! 😀");
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/webserver/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ async fn serve_file(
let since = DateTime::<Utc>::from(SystemTime::from(date));
let modified = state
.file_system
.modified_since(state, path.as_ref(), since)
.modified_since(state, path.as_ref(), since, false)
.await
.map_err(actix_web::error::ErrorBadRequest)?;
if !modified {
Expand All @@ -448,7 +448,7 @@ async fn serve_file(
}
state
.file_system
.read_file(state, path.as_ref())
.read_file(state, path.as_ref(), false)
.await
.map_err(actix_web::error::ErrorBadRequest)
.map(|b| {
Expand Down
22 changes: 13 additions & 9 deletions tests/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@ use sqlpage::{app_config::AppConfig, webserver::http::main_handler, AppState};

#[actix_web::test]
async fn test_index_ok() {
let resp = req_path("/").await;
assert_eq!(resp.status(), http::StatusCode::OK);
let body = test::read_body(resp).await;
assert!(body.starts_with(b"<!DOCTYPE html>"));
// the body should contain the strint "It works!" and should not contain the string "error"
let body = String::from_utf8(body.to_vec()).unwrap();
assert!(body.contains("It works !"));
assert!(!body.contains("error"));
}

async fn req_path(path: &str) -> actix_web::dev::ServiceResponse {
init_log();
let config = test_config();
let state = AppState::init(&config).await.unwrap();
let data = actix_web::web::Data::new(state);
let req = test::TestRequest::get()
.uri("/")
.uri(path)
.app_data(data)
.insert_header(ContentType::plaintext())
.to_srv_request();
let resp = main_handler(req).await.unwrap();
assert_eq!(resp.status(), http::StatusCode::OK);
let body = test::read_body(resp).await;
assert!(body.starts_with(b"<!DOCTYPE html>"));
// the body should contain the strint "It works!" and should not contain the string "error"
let body = String::from_utf8(body.to_vec()).unwrap();
assert!(body.contains("It works !"));
assert!(!body.contains("error"));
main_handler(req).await.unwrap()
}

pub fn test_config() -> AppConfig {
Expand Down

0 comments on commit 3f73964

Please sign in to comment.