@@ -29,30 +29,63 @@ def setup_node_environment():
2929 # Look for zip in absolute path relative to current execution or fixed 'extras'
3030 # Assuming 'extras' is in the current working dir of the analyzer
3131 node_zip_path = os .path .abspath (os .path .join ("extras" , NODE_ZIP_NAME ))
32-
3332 node_bin_path = os .path .join (install_path , NODE_DIR_NAME )
34- node_exe = os .path .join (node_bin_path , "node.exe" )
3533
36- # 1. Check if we need to install
37- if not os .path .exists (node_exe ):
38- if not os .path .exists (node_zip_path ):
39- return None , f"Node zip not found at: { node_zip_path } "
34+ if not os .path .exists (node_zip_path ):
35+ return None , f"Zip not found at { node_zip_path } "
36+
37+ if not os .path .exists (node_bin_path ):
38+ os .makedirs (node_bin_path )
39+
40+ node_exe_path = None
41+
42+ # 1. Open Zip and Find node.exe BEFORE extracting
43+ with zipfile .ZipFile (node_zip_path , 'r' ) as z :
44+ # list of all files in zip
45+ file_list = z .namelist ()
46+
47+ # Find the internal path to node.exe
48+ # This works for both "node.exe" (root) and "node-v25.../node.exe" (subfolder)
49+ node_internal_path = next ((f for f in file_list if f .lower ().endswith ("node.exe" )), None )
50+
51+ if not node_internal_path :
52+ return None , "Archive does not contain node.exe"
53+
54+ # 2. Extract
55+ # We extract to a specific folder to avoid cluttering if it's a "root-files" zip
56+ # We use the zip name (minus extension) as a container folder
57+ extract_path = node_bin_path
58+
59+ if not os .path .exists (extract_path ):
60+ # Security: Check for path traversal before extraction.
61+ for member in z .infolist ():
62+ if member .filename .startswith ("/" ) or ".." in member .filename :
63+ return None , f"Aborting extraction. Zip contains potentially malicious path: { member .filename } "
64+
65+ os .makedirs (extract_path )
66+ log .info ("Extracting to %s..." , extract_path )
67+ z .extractall (extract_path )
4068
41- if not os .path .exists (install_path ):
42- os .makedirs (install_path )
69+ # 3. Construct the full path
70+ # extract_path + internal_path_inside_zip
71+ # e.g. C:\Apps\node-v25\ + node-v25-win-x64/node.exe
72+ node_exe_path = os .path .join (extract_path , node_internal_path )
4373
44- log .info ("Extracting Node.js to %s..." , install_path )
45- with zipfile .ZipFile (node_zip_path , 'r' ) as zip_ref :
46- zip_ref .extractall (install_path )
74+ # Normalizing path separators (fixes mix of / and \)
75+ node_exe_path = os .path .normpath (node_exe_path )
4776
48- # 2. Update Environment Variable
49- current_path = os .environ .get ("PATH" , "" )
50- # Prepend to ensure our node takes precedence
51- os .environ ["PATH" ] = f"{ node_bin_path } ;{ current_path } "
77+ # 4. Final Verification and Env Setup
78+ if node_exe_path and os .path .exists (node_exe_path ):
79+ # Add the folder containing node.exe to PATH
80+ node_dir = os .path .dirname (node_exe_path )
81+ current_path = os .environ .get ("PATH" , "" )
82+ os .environ ["PATH" ] = f"{ node_dir } ;{ current_path } "
5283
53- return node_exe , None
84+ return node_exe_path , None
85+ else :
86+ return None , "Extraction finished but node.exe not found on disk."
5487
55- except Exception as e :
88+ except ( zipfile . BadZipFile , OSError ) as e :
5689 return None , f"Exception during Node setup: { str (e )} "
5790
5891
0 commit comments