Skip to content

Commit ce3a87e

Browse files
committed
fix(sfw): use separate versions for SEA and npm CLI distributions
- SEA builds use GitHub binary from SocketDev/sfw-free (v1.6.1) - npm CLI uses npm package sfw (2.0.4) which downloads binary on demand - Rename 'version' to 'githubRelease' for github-release type tools - Add 'npmVersion' field for sfw npm package version - Update environment-variables, vfs-extract, and resolve-binary accordingly
1 parent 4a2bc48 commit ce3a87e

File tree

8 files changed

+80
-38
lines changed

8 files changed

+80
-38
lines changed

packages/cli/external-tools.json

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "External tools configuration for Socket CLI VFS bundling",
3-
"$comment": "Build process uses @npmcli/arborist (scripts/sea-build-utils/npm-packages.mjs) to download npm packages with full dependency trees. npm packages are bundled with node_modules/ into VFS alongside security tool binaries.",
3+
"$comment": "Build process uses @npmcli/arborist (scripts/sea-build-utils/npm-packages.mjs) to download npm packages with full dependency trees. npm packages are bundled with node_modules/ into VFS alongside security tool binaries. For github-release types, 'githubRelease' is the release tag (any format: v1.6.1, 3.11.14, etc.).",
44
"@coana-tech/cli": {
55
"description": "Coana CLI for static analysis and reachability detection",
66
"type": "npm",
@@ -17,13 +17,13 @@
1717
"description": "OpenGrep SAST/code analysis engine (fork of Semgrep)",
1818
"type": "github-release",
1919
"repository": "opengrep/opengrep",
20-
"version": "v1.16.0"
20+
"githubRelease": "v1.16.0"
2121
},
2222
"python": {
2323
"description": "Python runtime from python-build-standalone",
2424
"type": "github-release",
2525
"repository": "astral-sh/python-build-standalone",
26-
"version": "3.11.14",
26+
"githubRelease": "3.11.14",
2727
"buildTag": "20260203"
2828
},
2929
"socket-basics": {
@@ -42,13 +42,15 @@
4242
"description": "Socket Patch CLI for applying security patches (Rust binary)",
4343
"type": "github-release",
4444
"repository": "SocketDev/socket-patch",
45-
"version": "v2.0.0"
45+
"githubRelease": "v2.0.0"
4646
},
4747
"sfw": {
48-
"description": "Socket Firewall (sfw)",
48+
"description": "Socket Firewall (sfw) - GitHub binary for SEA, npm package for CLI",
4949
"type": "github-release",
5050
"repository": "SocketDev/sfw-free",
51-
"version": "v1.6.0"
51+
"githubRelease": "v1.6.1",
52+
"npmPackage": "sfw",
53+
"npmVersion": "2.0.4"
5254
},
5355
"synp": {
5456
"description": "Tool for converting between yarn.lock and package-lock.json",
@@ -60,12 +62,12 @@
6062
"description": "Trivy container and filesystem vulnerability scanner",
6163
"type": "github-release",
6264
"repository": "aquasecurity/trivy",
63-
"version": "v0.69.2"
65+
"githubRelease": "v0.69.2"
6466
},
6567
"trufflehog": {
6668
"description": "TruffleHog secret and credential detection",
6769
"type": "github-release",
6870
"repository": "trufflesecurity/trufflehog",
69-
"version": "v3.93.1"
71+
"githubRelease": "v3.93.1"
7072
}
7173
}

packages/cli/scripts/environment-variables.mjs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,22 @@ export class EnvironmentVariables {
8080
return value
8181
}
8282

83+
// npm packages use 'version' field.
8384
const cdxgenVersion = getExternalToolVersion('@cyclonedx/cdxgen')
8485
const coanaVersion = getExternalToolVersion('@coana-tech/cli')
85-
const opengrepVersion = getExternalToolVersion('opengrep')
86+
const synpVersion = getExternalToolVersion('synp')
87+
// pypi packages use 'version' field.
8688
const pyCliVersion = getExternalToolVersion('socketsecurity')
89+
// github-release tools use 'githubRelease' field (release tag, any format).
90+
const opengrepVersion = getExternalToolVersion('opengrep', 'githubRelease')
8791
const pythonBuildTag = getExternalToolVersion('python', 'buildTag')
88-
const pythonVersion = getExternalToolVersion('python')
89-
const sfwVersion = getExternalToolVersion('sfw')
90-
const socketPatchVersion = getExternalToolVersion('socket-patch')
91-
const synpVersion = getExternalToolVersion('synp')
92-
const trivyVersion = getExternalToolVersion('trivy')
93-
const trufflehogVersion = getExternalToolVersion('trufflehog')
92+
const pythonVersion = getExternalToolVersion('python', 'githubRelease')
93+
const socketPatchVersion = getExternalToolVersion('socket-patch', 'githubRelease')
94+
const trivyVersion = getExternalToolVersion('trivy', 'githubRelease')
95+
const trufflehogVersion = getExternalToolVersion('trufflehog', 'githubRelease')
96+
// sfw uses both: GitHub binary for SEA, npm package for CLI.
97+
const sfwVersion = getExternalToolVersion('sfw', 'githubRelease')
98+
const sfwNpmVersion = getExternalToolVersion('sfw', 'npmVersion')
9499

95100
// Build-time constants that can be overridden by environment variables.
96101
const publishedBuild =
@@ -116,6 +121,7 @@ export class EnvironmentVariables {
116121
INLINED_SOCKET_CLI_PYTHON_BUILD_TAG: pythonBuildTag,
117122
INLINED_SOCKET_CLI_PYTHON_VERSION: pythonVersion,
118123
INLINED_SOCKET_CLI_SENTRY_BUILD: sentryBuild ? '1' : '',
124+
INLINED_SOCKET_CLI_SFW_NPM_VERSION: sfwNpmVersion,
119125
INLINED_SOCKET_CLI_SFW_VERSION: sfwVersion,
120126
INLINED_SOCKET_CLI_SOCKET_PATCH_VERSION: socketPatchVersion,
121127
INLINED_SOCKET_CLI_SYNP_VERSION: synpVersion,
@@ -142,9 +148,10 @@ export class EnvironmentVariables {
142148
externalTools['@coana-tech/cli']?.version || '',
143149
INLINED_SOCKET_CLI_PYCLI_VERSION:
144150
externalTools.socketsecurity?.version || '',
145-
INLINED_SOCKET_CLI_SFW_VERSION: externalTools.sfw?.version || '',
151+
INLINED_SOCKET_CLI_SFW_NPM_VERSION: externalTools.sfw?.npmVersion || '',
152+
INLINED_SOCKET_CLI_SFW_VERSION: externalTools.sfw?.githubRelease || '',
146153
INLINED_SOCKET_CLI_SOCKET_PATCH_VERSION:
147-
externalTools['socket-patch']?.version || '',
154+
externalTools['socket-patch']?.githubRelease || '',
148155
}
149156
} catch {
150157
return {}

packages/cli/scripts/sea-build-utils/downloads.mjs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,11 @@ export async function downloadExternalTools(platform, arch, isMusl = false) {
271271
TOOL_REPOS[toolName] = {
272272
owner,
273273
repo,
274-
// Python uses buildTag for version, others use version field.
274+
// Python uses buildTag for release tag, others use githubRelease field.
275275
version:
276-
toolName === 'python' ? toolConfig.buildTag : toolConfig.version,
276+
toolName === 'python'
277+
? toolConfig.buildTag
278+
: toolConfig.githubRelease,
277279
}
278280
}
279281
}
@@ -321,8 +323,8 @@ export async function downloadExternalTools(platform, arch, isMusl = false) {
321323
const archivePath = normalizePath(path.join(toolsDir, assetName))
322324

323325
// Download archive directly from GitHub releases.
324-
// Python uses date-based tags without 'v' prefix.
325-
const tag = toolName === 'python' ? config.version : config.version
326+
// Release tags can be any format (v1.6.1, 3.11.14, 20260203, etc.).
327+
const tag = config.version
326328
const url = `https://github.com/${config.owner}/${config.repo}/releases/download/${tag}/${assetName}`
327329
await httpDownload(url, archivePath, {
328330
logger,
@@ -337,11 +339,11 @@ export async function downloadExternalTools(platform, arch, isMusl = false) {
337339
const isStandalone = !isZip && !isTarGz
338340

339341
if (isStandalone) {
340-
// Standalone binary (e.g., sfw) - create node_modules structure for VFS compatibility.
342+
// Standalone binary - create node_modules structure for VFS compatibility.
341343
// node-smol VFS requires all files to be under node_modules/ for security.
342344
logger.log(` Preparing ${toolName}...`)
343345

344-
// Create node_modules/@socketsecurity/sfw-bin/ structure.
346+
// Create node_modules/@socketsecurity/{toolName}-bin/ structure.
345347
const packageDir = normalizePath(
346348
path.join(
347349
toolsDir,

packages/cli/src/env/sfw-version.mts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
/**
2-
* Socket Firewall (sfw) version getter function.
2+
* Socket Firewall (sfw) version getter functions.
33
* Uses direct process.env access so esbuild define can inline values.
44
* IMPORTANT: esbuild's define plugin can only replace direct process.env['KEY'] references.
55
* If we imported from env modules, esbuild couldn't inline the values at build time.
66
* This is critical for embedding version info into the binary.
7+
*
8+
* sfw uses two different distributions:
9+
* - GitHub binary (SocketDev/sfw-free): Used for SEA builds, version like "v1.6.1"
10+
* - npm package (sfw): Used for CLI dlx, version like "2.0.4"
711
*/
812

913
import process from 'node:process'
1014

15+
/**
16+
* Get the GitHub release version for sfw (used in SEA builds).
17+
*/
1118
export function getSwfVersion(): string {
1219
const version = process.env['INLINED_SOCKET_CLI_SFW_VERSION']
1320
if (!version) {
@@ -17,3 +24,16 @@ export function getSwfVersion(): string {
1724
}
1825
return version
1926
}
27+
28+
/**
29+
* Get the npm package version for sfw (used in CLI dlx).
30+
*/
31+
export function getSfwNpmVersion(): string {
32+
const version = process.env['INLINED_SOCKET_CLI_SFW_NPM_VERSION']
33+
if (!version) {
34+
throw new Error(
35+
'INLINED_SOCKET_CLI_SFW_NPM_VERSION not found. Please ensure sfw npmVersion is configured in external-tools.json.',
36+
)
37+
}
38+
return version
39+
}

packages/cli/src/utils/dlx/resolve-binary.mts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { SOCKET_CLI_COANA_LOCAL_PATH } from '../../env/socket-cli-coana-local-pa
1212
import { SOCKET_CLI_PYCLI_LOCAL_PATH } from '../../env/socket-cli-pycli-local-path.mts'
1313
import { SOCKET_CLI_SFW_LOCAL_PATH } from '../../env/socket-cli-sfw-local-path.mts'
1414
import { SOCKET_CLI_SOCKET_PATCH_LOCAL_PATH } from '../../env/socket-cli-socket-patch-local-path.mts'
15-
import { getSwfVersion } from '../../env/sfw-version.mts'
15+
import { getSfwNpmVersion } from '../../env/sfw-version.mts'
1616
import { getSocketPatchVersion } from '../../env/socket-patch-version.mts'
1717
import { getSynpVersion } from '../../env/synp-version.mts'
1818

@@ -94,6 +94,9 @@ export function resolvePyCli(): BinaryResolution | { type: 'python' } {
9494
/**
9595
* Resolve path for Socket Firewall (sfw) binary.
9696
* Checks SOCKET_CLI_SFW_LOCAL_PATH environment variable first.
97+
*
98+
* Note: This returns the npm package version for dlx usage.
99+
* SEA builds use the GitHub binary directly via VFS extraction.
97100
*/
98101
export function resolveSfw(): BinaryResolution {
99102
if (SOCKET_CLI_SFW_LOCAL_PATH) {
@@ -104,7 +107,7 @@ export function resolveSfw(): BinaryResolution {
104107
type: 'dlx',
105108
details: {
106109
name: 'sfw',
107-
version: getSwfVersion(),
110+
version: getSfwNpmVersion(),
108111
binaryName: 'sfw',
109112
},
110113
}

packages/cli/src/utils/dlx/vfs-extract.mts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
* ```
3030
*
3131
* VFS structure in SEA binaries:
32-
* sfw # Standalone binary from GitHub release
3332
* socket-patch # Standalone Rust binary from GitHub release
3433
* node_modules/
3534
* ├── @cyclonedx/cdxgen/ # Full package with dependencies
@@ -40,6 +39,8 @@
4039
* │ ├── bin/coana
4140
* │ ├── package.json
4241
* │ └── node_modules/
42+
* ├── @socketsecurity/sfw-bin/ # Standalone binary from GitHub release
43+
* │ └── sfw
4344
* └── synp/
4445
* ├── bin/synp
4546
* ├── package.json
@@ -89,7 +90,7 @@ export type ExternalTool = (typeof EXTERNAL_TOOLS)[number]
8990

9091
// Map of npm package tools to their node_modules/ paths.
9192
// 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.
93+
// Note: sfw uses GitHub binary for SEA (standalone), npm package for CLI (dlx).
9394
const TOOL_NPM_PATHS: Partial<
9495
Record<ExternalTool, { packageName: string; binPath: string }>
9596
> = {
@@ -109,8 +110,10 @@ const TOOL_NPM_PATHS: Partial<
109110

110111
// Map of standalone binary tools to their VFS paths.
111112
// These tools are single binaries from GitHub releases without npm dependencies.
112-
// sfw is stored under node_modules/ for VFS structure, socket-patch is at root level.
113+
// sfw is stored under node_modules/@socketsecurity/sfw-bin/ for VFS structure.
113114
const TOOL_STANDALONE_PATHS: Partial<Record<ExternalTool, string>> = {
115+
// sfw is a standalone binary from GitHub releases (SocketDev/sfw-free).
116+
// Note: npm CLI uses the sfw npm package via dlx instead.
114117
sfw: 'node_modules/@socketsecurity/sfw-bin/sfw',
115118
// socket-patch is a Rust binary downloaded from GitHub releases.
116119
// As of v2.0.0, it's bundled directly (not as an npm package).
@@ -145,7 +148,6 @@ function getToolFilePath(tool: ExternalTool, nodeSmolBase: string): string {
145148
* Structure:
146149
* ~/.socket/_dlx/<node-smol-hash>/
147150
* ├── node/node # Node binary
148-
* ├── sfw # Standalone binary (GitHub release)
149151
* ├── socket-patch # Standalone Rust binary (GitHub release)
150152
* └── node_modules/ # npm packages with dependencies
151153
* ├── @cyclonedx/cdxgen/
@@ -154,6 +156,8 @@ function getToolFilePath(tool: ExternalTool, nodeSmolBase: string): string {
154156
* ├── @coana-tech/cli/
155157
* │ ├── bin/coana
156158
* │ └── node_modules/
159+
* ├── @socketsecurity/sfw-bin/ # Standalone sfw binary (GitHub release)
160+
* │ └── sfw
157161
* └── synp/
158162
* ├── bin/synp
159163
* └── node_modules/
@@ -659,7 +663,7 @@ export async function extractExternalTools(
659663
*
660664
* @example
661665
* const paths = getToolPaths()
662-
* logger.log('sfw:', paths.sfw) // ~/.socket/_dlx/<hash>/sfw
666+
* logger.log('sfw:', paths.sfw) // ~/.socket/_dlx/<hash>/node_modules/@socketsecurity/sfw-bin/sfw
663667
* logger.log('cdxgen:', paths.cdxgen) // ~/.socket/_dlx/<hash>/node_modules/@cyclonedx/cdxgen/bin/cdxgen
664668
*/
665669
export function getToolPaths(): Record<ExternalTool, string> {

packages/cli/test/setup.mts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ if (existsSync(externalToolsPath)) {
1818
const externalTools = JSON.parse(readFileSync(externalToolsPath, 'utf8'))
1919

2020
// Set inlined environment variables if not already set.
21+
// npm packages use 'version', github-release uses 'githubRelease', pypi uses 'version'.
2122
const toolVersions: Record<string, string | undefined> = {
2223
INLINED_SOCKET_CLI_CDXGEN_VERSION:
2324
externalTools['@cyclonedx/cdxgen']?.version,
@@ -27,20 +28,23 @@ if (existsSync(externalToolsPath)) {
2728
externalTools['@cyclonedx/cdxgen']?.version,
2829
INLINED_SOCKET_CLI_HOMEPAGE: 'https://github.com/SocketDev/socket-cli',
2930
INLINED_SOCKET_CLI_NAME: '@socketsecurity/cli',
30-
INLINED_SOCKET_CLI_OPENGREP_VERSION: externalTools['opengrep']?.version,
31+
INLINED_SOCKET_CLI_OPENGREP_VERSION:
32+
externalTools['opengrep']?.githubRelease,
3133
INLINED_SOCKET_CLI_PUBLISHED_BUILD: '',
3234
INLINED_SOCKET_CLI_PYCLI_VERSION:
3335
externalTools['socketsecurity']?.version,
3436
INLINED_SOCKET_CLI_PYTHON_BUILD_TAG: externalTools['python']?.buildTag,
35-
INLINED_SOCKET_CLI_PYTHON_VERSION: externalTools['python']?.version,
37+
INLINED_SOCKET_CLI_PYTHON_VERSION:
38+
externalTools['python']?.githubRelease,
3639
INLINED_SOCKET_CLI_SENTRY_BUILD: '',
37-
INLINED_SOCKET_CLI_SFW_VERSION: externalTools['sfw']?.version,
40+
INLINED_SOCKET_CLI_SFW_NPM_VERSION: externalTools['sfw']?.npmVersion,
41+
INLINED_SOCKET_CLI_SFW_VERSION: externalTools['sfw']?.githubRelease,
3842
INLINED_SOCKET_CLI_SOCKET_PATCH_VERSION:
39-
externalTools['socket-patch']?.version,
43+
externalTools['socket-patch']?.githubRelease,
4044
INLINED_SOCKET_CLI_SYNP_VERSION: externalTools['synp']?.version,
41-
INLINED_SOCKET_CLI_TRIVY_VERSION: externalTools['trivy']?.version,
45+
INLINED_SOCKET_CLI_TRIVY_VERSION: externalTools['trivy']?.githubRelease,
4246
INLINED_SOCKET_CLI_TRUFFLEHOG_VERSION:
43-
externalTools['trufflehog']?.version,
47+
externalTools['trufflehog']?.githubRelease,
4448
INLINED_SOCKET_CLI_VERSION: '0.0.0-test',
4549
INLINED_SOCKET_CLI_VERSION_HASH: '0.0.0-test:abc1234:test',
4650
}

packages/cli/test/unit/utils/dlx/resolve-binary.test.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ describe('resolve-binary', () => {
174174
SOCKET_CLI_SFW_LOCAL_PATH: undefined,
175175
}))
176176
vi.doMock('../../../../src/env/sfw-version.mts', () => ({
177-
getSwfVersion: () => '2.0.0',
177+
getSfwNpmVersion: () => '2.0.0',
178178
}))
179179

180180
const { resolveSfw } =

0 commit comments

Comments
 (0)