@@ -6,6 +6,7 @@ use chrono::{DateTime, Utc};
66use sqlx:: any:: { AnyKind , AnyStatement , AnyTypeInfo } ;
77use sqlx:: postgres:: types:: PgTimeTz ;
88use sqlx:: { Postgres , Statement , Type } ;
9+ use std:: borrow:: Cow ;
910use std:: io:: ErrorKind ;
1011use std:: path:: { Component , Path , PathBuf } ;
1112
@@ -37,8 +38,13 @@ impl FileSystem {
3738 app_state : & AppState ,
3839 path : & Path ,
3940 since : DateTime < Utc > ,
41+ priviledged : bool ,
4042 ) -> anyhow:: Result < bool > {
41- let local_path = self . safe_local_path ( path) ?;
43+ let local_path = if priviledged {
44+ Cow :: Borrowed ( path)
45+ } else {
46+ Cow :: Owned ( self . safe_local_path ( path) ?)
47+ } ;
4248 let local_result = file_modified_since_local ( & local_path, since) . await ;
4349 match ( local_result, & self . db_fs_queries ) {
4450 ( Ok ( modified) , _) => Ok ( modified) ,
@@ -58,14 +64,27 @@ impl FileSystem {
5864 & self ,
5965 app_state : & AppState ,
6066 path : & Path ,
67+ priviledged : bool ,
6168 ) -> anyhow:: Result < String > {
62- let bytes = self . read_file ( app_state, path) . await ?;
69+ let bytes = self . read_file ( app_state, path, priviledged ) . await ?;
6370 String :: from_utf8 ( bytes)
6471 . with_context ( || format ! ( "The file at {path:?} contains invalid UTF8 characters" ) )
6572 }
6673
67- pub async fn read_file ( & self , app_state : & AppState , path : & Path ) -> anyhow:: Result < Vec < u8 > > {
68- let local_path = self . safe_local_path ( path) ?;
74+ /**
75+ * Priviledged files are the ones that are in sqlpage's config directory.
76+ */
77+ pub async fn read_file (
78+ & self ,
79+ app_state : & AppState ,
80+ path : & Path ,
81+ priviledged : bool ,
82+ ) -> anyhow:: Result < Vec < u8 > > {
83+ let local_path = if priviledged {
84+ Cow :: Borrowed ( path)
85+ } else {
86+ Cow :: Owned ( self . safe_local_path ( path) ?)
87+ } ;
6988 let local_result = tokio:: fs:: read ( & local_path) . await ;
7089 match ( local_result, & self . db_fs_queries ) {
7190 ( Ok ( f) , _) => Ok ( f) ,
@@ -82,11 +101,16 @@ impl FileSystem {
82101 }
83102
84103 fn safe_local_path ( & self , path : & Path ) -> anyhow:: Result < PathBuf > {
85- for component in path. components ( ) {
86- anyhow:: ensure!(
87- matches!( component, Component :: Normal ( _) ) ,
88- "Unsupported path: {path:?}. Path component {component:?} is not allowed."
89- ) ;
104+ for ( i, component) in path. components ( ) . enumerate ( ) {
105+ if let Component :: Normal ( c) = component {
106+ if c. eq_ignore_ascii_case ( "sqlpage" ) && i == 0 {
107+ anyhow:: bail!( "Access to the sqlpage config directory is not allowed." ) ;
108+ }
109+ } else {
110+ anyhow:: bail!(
111+ "Unsupported path: {path:?}. Path component '{component:?}' is not allowed."
112+ ) ;
113+ }
90114 }
91115 Ok ( self . local_root . join ( path) )
92116 }
@@ -202,7 +226,7 @@ async fn test_sql_file_read_utf8() -> anyhow::Result<()> {
202226 . await ?;
203227 let fs = FileSystem :: init ( "/" , & state. db ) . await ;
204228 let actual = fs
205- . read_to_string ( & state, "unit test file.txt" . as_ref ( ) )
229+ . read_to_string ( & state, "unit test file.txt" . as_ref ( ) , false )
206230 . await ?;
207231 assert_eq ! ( actual, "Héllö world! 😀" ) ;
208232 Ok ( ( ) )
0 commit comments