@@ -48,7 +48,7 @@ def arrows(text):
48
48
# path with `..`, symbolic links or similar.
49
49
# We always return the same error message including the path and file parameter, never `filename` as
50
50
# otherwise we might disclose if certain files exist or not.
51
- def join_paths (path , path2 , mode = None ):
51
+ def join_paths (path , path2 , mode = 'file' ):
52
52
filename = os .path .realpath (os .path .join (path , path2 ))
53
53
54
54
# If the original path is a symlink we need to resolve it.
@@ -59,23 +59,23 @@ def join_paths(path, path2, mode=None):
59
59
return filename
60
60
61
61
if not filename .startswith (path ):
62
- raise ValueError (f"{ path2 } not in { path } " )
62
+ raise ValueError (f"{ path2 } must not be in folder above { path } " )
63
63
64
64
# To double check we also check if it is in the files allow list
65
65
66
66
if mode == 'file' :
67
67
folder_content = [str (item ) for item in Path (path ).rglob ("*" ) if item .is_file ()]
68
- elif mode == 'dir ' :
68
+ elif mode == 'directory ' :
69
69
folder_content = [str (item ) for item in Path (path ).rglob ("*" ) if item .is_dir ()]
70
70
else :
71
- folder_content = [ str ( item ) for item in Path ( path ). rglob ( "*" )]
71
+ raise RuntimeError ( f"Unknown mode supplied for join_paths: { mode } " )
72
72
73
73
if filename not in folder_content :
74
- raise ValueError (f"{ path2 } not in { path } " )
74
+ raise ValueError (f"{ mode . capitalize () } ' { path2 } ' not in ' { path } ' " )
75
75
76
76
# Another way to implement this. This is checking the third time but we want to be extra secure 👾
77
77
if Path (path ).resolve (strict = True ) not in Path (path , path2 ).resolve (strict = True ).parents :
78
- raise ValueError (f"{ path2 } not in { path } " )
78
+ raise ValueError (f"{ mode . capitalize () } ' { path2 } ' not in folder ' { path } ' " )
79
79
80
80
if os .path .exists (filename ):
81
81
return filename
@@ -574,7 +574,7 @@ def build_docker_images(self):
574
574
self .__notes_helper .add_note ({'note' : f"Building { service ['image' ]} " , 'detail_name' : '[NOTES]' , 'timestamp' : int (time .time_ns () / 1_000 )})
575
575
576
576
# Make sure the context docker file exists and is not trying to escape some root. We don't need the returns
577
- context_path = join_paths (self ._folder , context , 'dir ' )
577
+ context_path = join_paths (self ._folder , context , 'directory ' )
578
578
join_paths (context_path , dockerfile , 'file' )
579
579
580
580
docker_build_command = ['docker' , 'run' , '--rm' ,
@@ -742,16 +742,19 @@ def setup_services(self):
742
742
# We always assume the format to be ./dir:dir:[flag] as if we allow none bind mounts people
743
743
# could create volumes that would linger on our system.
744
744
path = os .path .realpath (os .path .join (self ._folder , vol [0 ]))
745
- if not os .path .exists (path ):
746
- raise RuntimeError (f"Service '{ service_name } ' volume path does not exist: { path } " )
747
745
748
746
# Check that the path starts with self._folder
747
+ # We need to do this first, as telling first if path exists or not will tell about our filesystem structure
749
748
if not path .startswith (self ._folder ):
750
- raise RuntimeError (f"Service '{ service_name } ' trying to escape folder: { path } " )
749
+ raise RuntimeError (f"Service '{ service_name } ' volume path ({ vol [0 ]} ) is outside allowed folder: { path } " )
750
+
751
+ if not os .path .exists (path ):
752
+ raise RuntimeError (f"Service '{ service_name } ' volume path does not exist: { path } " )
751
753
752
754
# To double check we also check if it is in the files allow list
753
- if path not in [str (item ) for item in Path (self ._folder ).rglob ("*" )]:
754
- raise RuntimeError (f"Service '{ service_name } ' volume '{ path } ' not in allowed file list" )
755
+ allowed_files_list = [str (item ) for item in Path (self ._folder ).rglob ("*" )]
756
+ if path not in allowed_files_list :
757
+ raise RuntimeError (f"Service '{ service_name } ' volume '{ path } ' not in allowed file list:\n { chr (10 ).join (allowed_files_list )} \n \n Only files from the supplied repository are allowed." )
755
758
756
759
if len (vol ) == 3 :
757
760
if vol [2 ] != 'ro' :
@@ -1505,13 +1508,13 @@ def run(self):
1505
1508
print ('####################################################################################\n \n ' , TerminalColors .ENDC )
1506
1509
1507
1510
except FileNotFoundError as e :
1508
- error_helpers .log_error ('File or executable not found' , e , runner ._run_id )
1511
+ error_helpers .log_error ('File or executable not found' , e , " \n " , f"Run-ID: { runner ._run_id } " )
1509
1512
except subprocess .CalledProcessError as e :
1510
- error_helpers .log_error ('Command failed' , 'Stdout:' , e .stdout , 'Stderr:' , e .stderr , runner ._run_id )
1513
+ error_helpers .log_error ('Command failed' , 'Stdout:' , e .stdout , 'Stderr:' , e .stderr , " \n " , f"Run-ID: { runner ._run_id } " )
1511
1514
except RuntimeError as e :
1512
- error_helpers .log_error ('RuntimeError occured in runner.py: ' , e , runner ._run_id )
1515
+ error_helpers .log_error ('RuntimeError occured in runner.py: ' , e , " \n " , f"Run-ID: { runner ._run_id } " )
1513
1516
except BaseException as e :
1514
- error_helpers .log_error ('Base exception occured in runner.py: ' , e , runner ._run_id )
1517
+ error_helpers .log_error ('Base exception occured in runner.py: ' , e , " \n " , f"Run-ID: { runner ._run_id } " )
1515
1518
finally :
1516
1519
if args .print_logs :
1517
1520
for container_id_outer , std_out in runner .get_logs ().items ():
0 commit comments