@@ -79,19 +79,17 @@ import { isSeaBinary } from '../sea/detect.mts'
7979const logger = getDefaultLogger ( )
8080
8181// External tool names bundled in VFS.
82- // Includes both npm packages and standalone binaries.
82+ // Currently only includes standalone binaries that are packaged in the VFS tarball.
83+ // npm packages (cdxgen, coana, socket-patch, synp) will be added in future updates.
8384export const EXTERNAL_TOOLS = [
8485 'sfw' ,
85- 'cdxgen' ,
86- 'coana' ,
87- 'socket-patch' ,
88- 'synp' ,
8986] as const
9087
9188export type ExternalTool = ( typeof EXTERNAL_TOOLS ) [ number ]
9289
9390// Map of npm package tools to their node_modules/ paths.
94- // Standalone binaries (like sfw) are NOT in this map and extract to direct paths.
91+ // These are full npm packages with dependencies and node_modules/ subdirectories.
92+ // Standalone binaries (like sfw) are NOT in this map - they use direct file paths.
9593const TOOL_NPM_PATHS : Partial < Record < ExternalTool , { packageName : string ; binPath : string } > > = {
9694 cdxgen : {
9795 packageName : '@cyclonedx/cdxgen' ,
@@ -111,6 +109,33 @@ const TOOL_NPM_PATHS: Partial<Record<ExternalTool, { packageName: string; binPat
111109 } ,
112110}
113111
112+ // Map of standalone binary tools to their VFS paths.
113+ // These tools are stored under node_modules/ for VFS security but are single binaries without dependencies.
114+ const TOOL_STANDALONE_PATHS : Partial < Record < ExternalTool , string > > = {
115+ sfw : 'node_modules/@socketsecurity/sfw-bin/sfw' ,
116+ }
117+
118+ /**
119+ * Get the file system path for a tool based on its type (npm package or standalone binary).
120+ *
121+ * @param tool - Tool name.
122+ * @param nodeSmolBase - Base dlx directory path.
123+ * @returns Path to the tool binary (without .exe extension).
124+ */
125+ function getToolFilePath ( tool : ExternalTool , nodeSmolBase : string ) : string {
126+ const npmPath = TOOL_NPM_PATHS [ tool ]
127+ const standalonePath = TOOL_STANDALONE_PATHS [ tool ]
128+
129+ // For npm packages, use node_modules/ path with binPath.
130+ // For standalone binaries under node_modules/, use standalonePath.
131+ // For other standalone binaries, use direct tool name.
132+ return npmPath
133+ ? normalizePath ( path . join ( nodeSmolBase , npmPath . binPath ) )
134+ : standalonePath
135+ ? normalizePath ( path . join ( nodeSmolBase , standalonePath ) )
136+ : normalizePath ( path . join ( nodeSmolBase , tool ) )
137+ }
138+
114139/**
115140 * Get the base dlx directory path for node-smol.
116141 * This is where both VFS-extracted tools and npm-installed packages live.
@@ -270,13 +295,23 @@ async function extractTool(tool: ExternalTool): Promise<string> {
270295 const toolPath = normalizePath ( path . join ( nodeSmolBase , npmPath . binPath ) )
271296 extractedPath = isPlatWin ? `${ toolPath } .exe` : toolPath
272297 } else {
273- // Extract standalone binary from VFS root.
274- const vfsBinaryPath = `/snapshot/${ tool } `
298+ // Extract standalone binary - check if it's under node_modules/ or VFS root.
299+ const standalonePath = TOOL_STANDALONE_PATHS [ tool ]
300+ const vfsBinaryPath = standalonePath ? `/snapshot/${ standalonePath } ` : `/snapshot/${ tool } `
275301 const binaryPath = processWithSmol . smol . mount ( vfsBinaryPath )
276302
277303 logger . info ( ` ✓ Extracted ${ tool } binary to ${ binaryPath } ` )
278304
279305 extractedPath = isPlatWin ? `${ binaryPath } .exe` : binaryPath
306+
307+ // Make executable on Unix.
308+ if ( ! isPlatWin && existsSync ( extractedPath ) ) {
309+ try {
310+ await fs . chmod ( extractedPath , 0o755 )
311+ } catch {
312+ // Ignore chmod errors - file might already be executable.
313+ }
314+ }
280315 }
281316
282317 if ( ! existsSync ( extractedPath ) ) {
@@ -397,10 +432,7 @@ export async function extractExternalTools(
397432 const toolPaths : Partial < Record < ExternalTool , string > > = { }
398433 let allValid = true
399434 for ( const tool of EXTERNAL_TOOLS ) {
400- const npmPath = TOOL_NPM_PATHS [ tool ]
401- const toolPath = npmPath
402- ? normalizePath ( path . join ( nodeSmolBase , npmPath . binPath ) )
403- : normalizePath ( path . join ( nodeSmolBase , tool ) )
435+ const toolPath = getToolFilePath ( tool , nodeSmolBase )
404436 const toolPathWithExt = isPlatWin ? `${ toolPath } .exe` : toolPath
405437 // Validate tool exists and is executable.
406438 if ( ! existsSync ( toolPathWithExt ) ) {
@@ -470,10 +502,7 @@ export async function extractExternalTools(
470502 const toolPaths : Partial < Record < ExternalTool , string > > = { }
471503 let allValid = true
472504 for ( const tool of EXTERNAL_TOOLS ) {
473- const npmPath = TOOL_NPM_PATHS [ tool ]
474- const toolPath = npmPath
475- ? normalizePath ( path . join ( nodeSmolBase , npmPath . binPath ) )
476- : normalizePath ( path . join ( nodeSmolBase , tool ) )
505+ const toolPath = getToolFilePath ( tool , nodeSmolBase )
477506 const toolPathWithExt = isPlatWin ? `${ toolPath } .exe` : toolPath
478507 if ( ! existsSync ( toolPathWithExt ) ) {
479508 allValid = false
@@ -504,10 +533,7 @@ export async function extractExternalTools(
504533 const toolPaths : Partial < Record < ExternalTool , string > > = { }
505534 let allValid = true
506535 for ( const tool of EXTERNAL_TOOLS ) {
507- const npmPath = TOOL_NPM_PATHS [ tool ]
508- const toolPath = npmPath
509- ? normalizePath ( path . join ( nodeSmolBase , npmPath . binPath ) )
510- : normalizePath ( path . join ( nodeSmolBase , tool ) )
536+ const toolPath = getToolFilePath ( tool , nodeSmolBase )
511537 const toolPathWithExt = isPlatWin ? `${ toolPath } .exe` : toolPath
512538 // Validate tool exists before adding to paths.
513539 if ( ! existsSync ( toolPathWithExt ) ) {
@@ -548,10 +574,7 @@ export async function extractExternalTools(
548574 const toolPaths : Partial < Record < ExternalTool , string > > = { }
549575
550576 for ( const tool of EXTERNAL_TOOLS ) {
551- const npmPath = TOOL_NPM_PATHS [ tool ]
552- const toolPath = npmPath
553- ? normalizePath ( path . join ( nodeSmolBase , npmPath . binPath ) )
554- : normalizePath ( path . join ( nodeSmolBase , tool ) )
577+ const toolPath = getToolFilePath ( tool , nodeSmolBase )
555578 const toolPathWithExt = isPlatWin ? `${ toolPath } .exe` : toolPath
556579
557580 // Check if tool already exists and is executable.
@@ -624,14 +647,7 @@ export function getToolPaths(): Record<ExternalTool, string> {
624647 const paths : Partial < Record < ExternalTool , string > > = { }
625648
626649 for ( const tool of EXTERNAL_TOOLS ) {
627- const npmPath = TOOL_NPM_PATHS [ tool ]
628-
629- // For npm packages, use node_modules/ path.
630- // For standalone binaries, use direct path.
631- const toolPath = npmPath
632- ? normalizePath ( path . join ( nodeSmolBase , npmPath . binPath ) )
633- : normalizePath ( path . join ( nodeSmolBase , tool ) )
634-
650+ const toolPath = getToolFilePath ( tool , nodeSmolBase )
635651 paths [ tool ] = isPlatWin ? `${ toolPath } .exe` : toolPath
636652 }
637653
0 commit comments