Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ else
CARGO_FLAGS = --profile $(PROFILE)
endif

.PHONY: build addon addon-fast install-dev uninstall-dev qos-global test verify test-node-matrix bench clean npm-build npm-publish npm-publish-dry
.PHONY: build addon addon-fast install-dev uninstall-dev qos-global test verify fix test-node-matrix bench clean npm-build npm-publish npm-publish-dry

build: addon
$(CARGO) build $(CARGO_FLAGS)
Expand Down Expand Up @@ -123,6 +123,23 @@ verify:
tests/brand-lint/check-path-literals.sh
NUB_SHARED_TARGET="$(CURDIR)/target" "$(RUST_BUILD)" test

# Apply clippy's machine-applicable autofixes across the root workspace AND the
# nub-native workspace (the separate one `verify` lints from inside the crate).
# Run on a dirty tree is intended (--allow-dirty --allow-staged); review the
# diff before committing.
fix:
@# `--all-features` activates `embed-runtime`, whose build.rs reads
@# runtime/addons/nub-native.node for integrity hashing and panics when
@# it is absent (it is gitignored, built by `make addon` / staged in CI).
@# Stage a placeholder — the same trick CI's lint job uses — so `make fix`
@# works on a fresh tree without a full addon build. clippy --fix never
@# runs the binary, so the placeholder is never loaded.
@mkdir -p runtime/addons
@test -s runtime/addons/nub-native.node || printf 'placeholder-addon' > runtime/addons/nub-native.node
@set -e; \
NUB_SHARED_TARGET="$(CURDIR)/target" "$(RUST_BUILD)" clippy --fix --workspace --all-targets --all-features --profile fast --allow-dirty --allow-staged -- -D warnings; \
Comment thread
jdalton marked this conversation as resolved.
(cd crates/nub-native && NUB_SHARED_TARGET="$(CURDIR)/target" "$(RUST_BUILD)" clippy --fix --all-features --profile fast --allow-dirty --allow-staged -- -D warnings)

# Run the integration suite across a Node version matrix (18.19 floor → 22.15
# fast-path floor) — the local mirror of ci.yml's `test` job. Locates or
# downloads each Node under ~/.cache/nub-test-node. See the script header.
Expand Down
38 changes: 20 additions & 18 deletions external-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
"pnpm": {
"description": "pnpm — the fleet's package manager.",
"version": "11.8.0",
"packageManager": "pnpm",
"repository": "github:pnpm/pnpm",
"release": "asset",
"notes": [
"Latest soaked pnpm — CI downloads + SRI-verifies (sha512) the pinned asset",
"darwin-x64 has no SEA asset upstream; its pin is the npm registry tarball"
Expand Down Expand Up @@ -35,15 +33,16 @@
"asset": "pnpm-linux-x64-musl.tar.gz",
"integrity": "sha512-5WXlo2yCDmoBIue5iHRK3zNSPVAmzci+5RhuxwUA4hnH9W3TFjDnQHyAzDVnkY878mfTXE35THZeEc1OKYPmag=="
},
"win-arm64": {
"win32-arm64": {
Comment thread
jdalton marked this conversation as resolved.
"asset": "pnpm-win32-arm64.zip",
"integrity": "sha512-iv1hJEj9FUiVFae2cmCYjbRK0Xn27Uh+kpglT2LWvQyl4WeIOgs1ouKrptVxk3X3t7DB5vuYKbsmE+9BnoQ4FA=="
},
"win-x64": {
"win32-x64": {
"asset": "pnpm-win32-x64.zip",
"integrity": "sha512-jyqMgedndbck/xJjXPem5Lw7V0YtDfiUJIB81e/hmnrg3yLKPoyCKO5ILegYkLPIr945IJRcVoJZwjtog8FCqg=="
}
}
},
"origin": "gh-asset"
Comment thread
jdalton marked this conversation as resolved.
},
"npm": {
"notes": [
Expand All @@ -53,13 +52,13 @@
"description": "npm — pinned, SRI-verified registry tarball; installed without self-update",
"repository": "npm:npm",
"version": "12.0.0",
"integrity": "sha512-qzvPQfNSY7louiM6rv7dL0hi5esBGLn1lLwxbdyL5XOIssWzoYMwn8xqvWhYcZL6onTkenYSxrtKxsFrFbUFyw=="
"integrity": "sha512-qzvPQfNSY7louiM6rv7dL0hi5esBGLn1lLwxbdyL5XOIssWzoYMwn8xqvWhYcZL6onTkenYSxrtKxsFrFbUFyw==",
"origin": "npm"
Comment thread
jdalton marked this conversation as resolved.
},
"sfw-free": {
"description": "Socket Firewall (free tier) — malware gate on dep installs.",
"version": "1.13.1",
"repository": "github:SocketDev/sfw-free",
"release": "asset",
"binaryName": "sfw",
"notes": [
"Used when SOCKET_SECURITY_KEY is not set",
Expand All @@ -82,17 +81,17 @@
"asset": "sfw-free-linux-x86_64",
"integrity": "sha512-waLrsPG2a7EOv0XuvXDQZGgCZ4MTtOfZh8TmGbM6gn2B6Nh6HI+15jaoKdAS9wgdTyIqTuqU+O+NtVYd+kuFaA=="
},
"win-x64": {
"win32-x64": {
"asset": "sfw-free-windows-x86_64.exe",
"integrity": "sha512-YYnfwR6M/PHo72LSyKtpY3bAUG4F4ckToJqGx5Fkz4rwg1+48hkxuBaF3hdxHUdHPkfO5grDyoNgXGe7FojGcg=="
}
}
},
"origin": "gh-asset"
},
"sfw-enterprise": {
"description": "Socket Firewall (enterprise tier) — selected when SOCKET_SECURITY_KEY is set.",
"version": "1.13.1",
"repository": "github:SocketDev/firewall-release",
"release": "asset",
"binaryName": "sfw",
"notes": [
"Used when SOCKET_SECURITY_KEY is set (the one env var every Socket product reads)",
Expand All @@ -115,17 +114,17 @@
"asset": "sfw-linux-x86_64",
"integrity": "sha512-lu9h8UzDZt34gdCEVHBGW6goE1Ayykq413EovV5B4nG7jBK27mI0GQstzVbWXA3wWaweT39PehXGtVpdqIDGSA=="
},
"win-x64": {
"win32-x64": {
"asset": "sfw-windows-x86_64.exe",
"integrity": "sha512-URZXauIsdUT12E2KTc4sfsxRmJm7nRJzAgM+IYGX4Xq+X0cl/eAbH5SpYIJKpsnW9csSztW9ceyPhlM+f3neIQ=="
}
}
},
"origin": "gh-asset"
},
"zizmor": {
"description": "GitHub Actions security linter — audits .github/ for workflow-injection / credential-leak patterns.",
"version": "1.26.1",
"repository": "github:zizmorcore/zizmor",
"release": "asset",
"notes": [
"Required: CI (blocks merges on medium+ findings)",
"Installed by the setup-and-install composite; SRI-verified (sha512) per platform"
Expand All @@ -147,23 +146,26 @@
"asset": "zizmor-x86_64-unknown-linux-gnu.tar.gz",
"integrity": "sha512-zTMERMDd3JfaRX12klj2fhZGDyrXeLkVUY1QJkCv8RRmAa9uVuH88gHOnv4CQtC5hXS7FPRJzvCVOnw38gV83g=="
},
"win-x64": {
"win32-x64": {
"asset": "zizmor-x86_64-pc-windows-msvc.zip",
"integrity": "sha512-Pijh/CrrOAkZzLiTr2LTHdI8d6+5Ql6B+suY6fXVmL8UVa+4Q36hHL5K67iRFz03v/V/UcrY6+dfhnmot/xfww=="
}
}
},
"origin": "gh-asset"
},
"agentshield": {
"description": "Claude AI config security scanner (prompt injection, secrets)",
"purl": "pkg:npm/ecc-agentshield@1.4.0",
"integrity": "sha512-R98OO1Ujyk2lezDLb+iQmMhF6FwTJCHajy3G4FCB6x7wkSTqR9f8+eAelC5KDzYDsGSbc0sOZvjXOOPRBtMpDg=="
"integrity": "sha512-R98OO1Ujyk2lezDLb+iQmMhF6FwTJCHajy3G4FCB6x7wkSTqR9f8+eAelC5KDzYDsGSbc0sOZvjXOOPRBtMpDg==",
"origin": "npm"
},
"skillspector": {
"description": "NVIDIA's third-party-skill security scanner (LangGraph-based; YARA + AST + OSV.dev CVE lookups + optional LLM analysis). No PyPI release / no GH tags upstream — pinned to a git SHA on main + installed via a locked uv project (pyproject.toml + uv.lock, `uv sync --locked`; the fleet uv pin + exclude-newer make it reproducible). Sibling to AgentShield: AgentShield audits the operator's .claude/ config; SkillSpector audits untrusted upstream skills before install.",
"release": "uv-project",
"repository": "github:NVIDIA/skillspector",
"version": "2eb84478",
"versionDate": "2026-05-18"
"versionDate": "2026-05-18",
"origin": "manager",
"manager": "uv"
}
}
}
30 changes: 22 additions & 8 deletions scripts/soak/external-tools.mts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ interface ToolPin {
description?: string
version?: string
repository?: string
release?: string
origin?: string
manager?: string
binaryName?: string
purl?: string
integrity?: string
Expand All @@ -72,7 +73,7 @@ function loadTools(): Record<string, ToolPin> {
}

function platformKey(): string {
const osKey = { darwin: 'darwin', linux: 'linux', win32: 'win' }[process.platform]
const osKey = { darwin: 'darwin', linux: 'linux', win32: 'win32' }[process.platform]
const archKey = { arm64: 'arm64', x64: 'x64' }[process.arch]
if (!osKey || !archKey) {
throw new Error(`unsupported platform ${process.platform}-${process.arch}`)
Expand All @@ -94,7 +95,7 @@ export function checkPins(tools: Record<string, ToolPin>): string[] {
...(pin.integrity ? [pin.integrity] : []),
...Object.values(pin.platforms ?? {}).map(p => p.integrity),
]
if (pin.release === 'asset' && integrities.length === 0) {
if (pin.origin === 'gh-asset' && integrities.length === 0) {
out.push(`${name}: release asset without any integrity pin`)
}
for (const sri of integrities) {
Expand Down Expand Up @@ -403,7 +404,7 @@ export async function installTool(name: string, tools: Record<string, ToolPin>):
if (!pin) {
throw new Error(`unknown tool ${name} (see external-tools.json)`)
}
if (pin.release === 'asset') {
if (pin.origin === 'gh-asset') {
await installAssetTool(name, pin)
return
}
Expand All @@ -424,15 +425,28 @@ export async function installTool(name: string, tools: Record<string, ToolPin>):
await installNpmTarball(name, pin.repository.slice('npm:'.length), pin.version!, pin.integrity!)
return
}
if (pin.release === 'uv-project') {
// Git-SHA-pinned python project; not auto-installed (needs uv).
if (pin.origin === 'npm') {
// npm registry tarball named only by `origin`. Pins that carry a `purl`
// or an `npm:` repository are already claimed by the branches above, so
// the package name here is always the tool name.
const pkg = name
if (!pin.version || !pin.integrity) {
throw new Error(`${name}: npm pin missing version or integrity`)
}
await installNpmTarball(name, pkg, pin.version, pin.integrity)
return
}
if (pin.origin === 'manager') {
// Git-SHA-pinned python project installed via an external package
// manager (uv); not auto-installed here.
const repo = pin.repository!.replace(/^github:/, '')
const mgr = pin.manager ?? 'uv'
console.log(
`[external-tools] ${name} is a uv project — run: uvx --from git+https://github.com/${repo}@${pin.version} ${name}`,
`[external-tools] ${name} is a ${mgr} project — run: uvx --from git+https://github.com/${repo}@${pin.version} ${name}`,
)
return
}
throw new Error(`${name}: no installable shape (release=${pin.release ?? 'none'})`)
throw new Error(`${name}: no installable shape (origin=${pin.origin ?? 'none'})`)
}

/**
Expand Down
10 changes: 5 additions & 5 deletions scripts/soak/external-tools.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test('the repo external-tools.json passes checkPins', () => {
test('checkPins flags missing pins, bad SRIs, and asset entries with no integrity', () => {
assert.equal(checkPins({ a: {} }).length, 1)
assert.equal(checkPins({ a: { version: '1.0.0', integrity: 'sha256-abc' } }).length, 1)
assert.equal(checkPins({ a: { version: '1.0.0', release: 'asset' } }).length, 1)
assert.equal(checkPins({ a: { version: '1.0.0', origin: 'gh-asset' } }).length, 1)
})

test('checkPins validates soakBypass dates, arithmetic, and expiry', () => {
Expand Down Expand Up @@ -174,7 +174,7 @@ test('installTool rejects unknown tools, foreign purls, and shapeless pins', asy
)
await assert.rejects(installTool('y', { y: { version: '1.0.0' } }), /no installable shape/)
await assert.rejects(
installTool('z', { z: { release: 'asset', version: '1.0.0', platforms: {} } }),
installTool('z', { z: { origin: 'gh-asset', version: '1.0.0', platforms: {} } }),
/no pinned asset for/,
)
})
Expand All @@ -194,10 +194,10 @@ test('installTool resolves the sfw flavor from SOCKET_SECURITY_KEY', async t =>
)
})

test('installTool prints the uvx line for uv-project pins without installing', async () => {
test('installTool prints the uvx line for manager pins without installing', async () => {
const tools = JSON.parse(readFileSync(EXTERNAL_TOOLS_JSON, 'utf8')).tools
const uv = Object.keys(tools).find(name => tools[name].release === 'uv-project')
assert.ok(uv, 'the manifest is expected to pin at least one uv project')
const uv = Object.keys(tools).find(name => tools[name].origin === 'manager')
assert.ok(uv, 'the manifest is expected to pin at least one manager project')
await installTool(uv!, tools)
})

Expand Down