@@ -92,6 +92,24 @@ def safe_name(value, context, allow_path=False):
9292 if re .search (pattern , value ):
9393 raise ValueError (f"Unsafe { context } : '{ value } ' contains illegal characters." )
9494 return value
95+
96+ def safe_relpath (value , context ):
97+ """
98+ Allow relative subpaths while blocking traversal and absolute/drive paths.
99+ """
100+ if not value :
101+ raise ValueError (f"{ context } cannot be empty" )
102+ normalized = value .replace ("\\ " , "/" )
103+ safe_name (normalized , context , allow_path = True )
104+ if normalized .startswith ("/" ) or normalized .startswith ("~" ):
105+ raise ValueError (f"Unsafe { context } : absolute paths are not allowed." )
106+ if re .match (r"^[A-Za-z]:" , normalized ):
107+ raise ValueError (f"Unsafe { context } : drive paths are not allowed." )
108+ if ":" in normalized :
109+ raise ValueError (f"Unsafe { context } : ':' is not allowed in relative paths." )
110+ if any (part in ("" , ".." ) for part in normalized .split ("/" )):
111+ raise ValueError (f"Unsafe { context } : invalid path segment." )
112+ return normalized
95113
96114MKCONCORE_VER = "22-09-18"
97115
@@ -273,14 +291,15 @@ def cleanup_script_files():
273291 node_label = re .sub (r'(\s+|\n)' , ' ' , node_label )
274292
275293 #Validate node labels
276- if ':' in node_label :
277- container_part , source_part = node_label .split (':' , 1 )
278- safe_name (container_part , f"Node container name '{ container_part } '" )
279- safe_name (source_part , f"Node source file '{ source_part } '" )
280- else :
281- safe_name (node_label , f"Node label '{ node_label } '" )
282- # Explicitly reject incorrect format to prevent later crashes and ambiguity
283- raise ValueError (f"Invalid node label '{ node_label } ': expected format 'container:source' with a ':' separator." )
294+ if ':' in node_label :
295+ container_part , source_part = node_label .split (':' , 1 )
296+ safe_name (container_part , f"Node container name '{ container_part } '" )
297+ source_part = safe_relpath (source_part , f"Node source file '{ source_part } '" )
298+ node_label = f"{ container_part } :{ source_part } "
299+ else :
300+ safe_name (node_label , f"Node label '{ node_label } '" )
301+ # Explicitly reject incorrect format to prevent later crashes and ambiguity
302+ raise ValueError (f"Invalid node label '{ node_label } ': expected format 'container:source' with a ':' separator." )
284303
285304 nodes_dict [node ['id' ]] = node_label
286305 node_id_to_label_map [node ['id' ]] = node_label .split (':' )[0 ]
@@ -466,12 +485,15 @@ def cleanup_script_files():
466485 if not sourcecode :
467486 continue
468487
469- if "." in sourcecode :
470- dockername , langext = os .path .splitext (sourcecode )
471- else :
472- dockername , langext = sourcecode , ""
473-
474- script_target_path = os .path .join (outdir , "src" , sourcecode )
488+ if "." in sourcecode :
489+ dockername , langext = os .path .splitext (sourcecode )
490+ else :
491+ dockername , langext = sourcecode , ""
492+
493+ script_target_path = os .path .join (outdir , "src" , sourcecode )
494+ script_target_parent = os .path .dirname (script_target_path )
495+ if script_target_parent :
496+ os .makedirs (script_target_parent , exist_ok = True )
475497
476498 # If the script was specialized, it's already in outdir/src. If not, copy from sourcedir.
477499 if node_id_key not in node_edge_params :
@@ -661,27 +683,33 @@ def cleanup_script_files():
661683
662684# 4. Write final iport/oport files
663685logging .info ("Writing .iport and .oport files..." )
664- for node_label , ports in node_port_mappings .items ():
686+ for node_label , ports in node_port_mappings .items ():
665687 try :
666688 containername , sourcecode = node_label .split (':' , 1 )
667- if not sourcecode or "." not in sourcecode : continue
668- dockername = os .path .splitext (sourcecode )[0 ]
669- with open (os .path .join (outdir , "src" , f"{ dockername } .iport" ), "w" ) as fport :
670- fport .write (str (ports ['iport' ]).replace ("'" + prefixedgenode , "'" ))
671- with open (os .path .join (outdir , "src" , f"{ dockername } .oport" ), "w" ) as fport :
672- fport .write (str (ports ['oport' ]).replace ("'" + prefixedgenode , "'" ))
689+ if not sourcecode or "." not in sourcecode : continue
690+ dockername = os .path .splitext (sourcecode )[0 ]
691+ iport_path = os .path .join (outdir , "src" , f"{ dockername } .iport" )
692+ oport_path = os .path .join (outdir , "src" , f"{ dockername } .oport" )
693+ iport_parent = os .path .dirname (iport_path )
694+ if iport_parent :
695+ os .makedirs (iport_parent , exist_ok = True )
696+ with open (iport_path , "w" ) as fport :
697+ fport .write (str (ports ['iport' ]).replace ("'" + prefixedgenode , "'" ))
698+ with open (oport_path , "w" ) as fport :
699+ fport .write (str (ports ['oport' ]).replace ("'" + prefixedgenode , "'" ))
673700 except ValueError :
674701 continue
675702
676703
677704#if docker, make docker-dirs, generate build, run, stop, clear scripts and quit
678- if (concoretype == "docker" ):
679- for node in nodes_dict :
680- containername ,sourcecode = nodes_dict [node ].split (':' )
681- if len (sourcecode )!= 0 and sourcecode .find ("." )!= - 1 : #3/28/21
682- dockername ,langext = sourcecode .split ("." )
683- if not os .path .exists (outdir + "/src/Dockerfile." + dockername ): # 3/30/21
684- try :
705+ if (concoretype == "docker" ):
706+ for node in nodes_dict :
707+ containername ,sourcecode = nodes_dict [node ].split (':' )
708+ if len (sourcecode )!= 0 and sourcecode .find ("." )!= - 1 : #3/28/21
709+ dockername ,langext = sourcecode .split ("." )
710+ dockerfile_path = os .path .join (outdir , "src" , f"Dockerfile.{ dockername } " )
711+ if not os .path .exists (dockerfile_path ): # 3/30/21
712+ try :
685713 if langext == "py" :
686714 src_path = CONCOREPATH + "/Dockerfile.py"
687715 logging .info ("assuming .py extension for Dockerfile" )
@@ -699,11 +727,14 @@ def cleanup_script_files():
699727 logging .info ("assuming .m extension for Dockerfile" )
700728 with open (src_path ) as fsource :
701729 source_content = fsource .read ()
702- except :
703- logging .error (f"{ CONCOREPATH } is not correct path to concore" )
704- quit ()
705- with open (outdir + "/src/Dockerfile." + dockername ,"w" ) as fcopy :
706- fcopy .write (source_content )
730+ except :
731+ logging .error (f"{ CONCOREPATH } is not correct path to concore" )
732+ quit ()
733+ dockerfile_parent = os .path .dirname (dockerfile_path )
734+ if dockerfile_parent :
735+ os .makedirs (dockerfile_parent , exist_ok = True )
736+ with open (dockerfile_path ,"w" ) as fcopy :
737+ fcopy .write (source_content )
707738 if langext == "py" :
708739 fcopy .write ('CMD ["python", "-i", "' + sourcecode + '"]\n ' )
709740 if langext == "m" :
@@ -947,16 +978,22 @@ def cleanup_script_files():
947978if concoretype == "posix" :
948979 fbuild .write ('#!/bin/bash' + "\n " )
949980
950- for node in nodes_dict :
951- containername ,sourcecode = nodes_dict [node ].split (':' )
952- if len (sourcecode )!= 0 :
953- if sourcecode .find ("." )== - 1 :
954- logging .error ("cannot pull container " + sourcecode + " with control core type " + concoretype ) #3/28/21
955- quit ()
956- dockername ,langext = sourcecode .split ("." )
957- fbuild .write ('mkdir ' + containername + "\n " )
958- if concoretype == "windows" :
959- fbuild .write ("copy .\\ src\\ " + sourcecode + " .\\ " + containername + "\\ " + sourcecode + "\n " )
981+ for node in nodes_dict :
982+ containername ,sourcecode = nodes_dict [node ].split (':' )
983+ if len (sourcecode )!= 0 :
984+ if sourcecode .find ("." )== - 1 :
985+ logging .error ("cannot pull container " + sourcecode + " with control core type " + concoretype ) #3/28/21
986+ quit ()
987+ dockername ,langext = sourcecode .split ("." )
988+ fbuild .write ('mkdir ' + containername + "\n " )
989+ source_subdir = os .path .dirname (sourcecode ).replace ("\\ " , "/" )
990+ if source_subdir :
991+ if concoretype == "windows" :
992+ fbuild .write ("mkdir .\\ " + containername + "\\ " + source_subdir .replace ("/" , "\\ " )+ "\n " )
993+ else :
994+ fbuild .write ("mkdir -p ./" + containername + "/" + source_subdir + "\n " )
995+ if concoretype == "windows" :
996+ fbuild .write ("copy .\\ src\\ " + sourcecode + " .\\ " + containername + "\\ " + sourcecode + "\n " )
960997 if langext == "py" :
961998 fbuild .write ("copy .\\ src\\ concore.py .\\ " + containername + "\\ concore.py\n " )
962999 elif langext == "cpp" :
0 commit comments