Repository navigation
bughunt(vlt): global-mode probe #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: bughunt vlt probe | |
| on: | |
| push: | |
| branches: ['bughunt/vlt/**'] | |
| permissions: | |
| contents: read | |
| jobs: | |
| probe: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| vlt: ['1.0.10', '1.2.0', '1.3.3'] | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| LANG: C | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 24.21.0 | |
| - name: Build CLI | |
| run: cargo build --release -p socket-patch-cli | |
| - name: Install vlt | |
| run: | | |
| js=$(bash scripts/install-vlt.sh "${{ matrix.vlt }}" "$RUNNER_TEMP/vlt" | tail -n 1) | |
| echo "VLT_JS=$js" >> "$GITHUB_ENV" | |
| - name: Write probe | |
| run: | | |
| mkdir -p probe | |
| cat > probe/mock.mjs <<'MOCKEOF' | |
| import http from 'node:http'; | |
| import zlib from 'node:zlib'; | |
| import crypto from 'node:crypto'; | |
| import fs from 'node:fs'; | |
| const PORT = +process.env.PORT || 18555; | |
| const R = `http://127.0.0.1:${PORT}`; | |
| const NAME='left-pad', VER='1.3.0', UUID='aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa', TOKEN='bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb', ORG='test-org'; | |
| const PURL=`pkg:npm/${NAME}@${VER}`; | |
| const ALT = process.env.NO_ALT ? false : true; | |
| function hdr(name,size){const b=Buffer.alloc(512);b.write(name,0);b.write('0000644\0',100);b.write('0000000\0',108);b.write('0000000\0',116);b.write(size.toString(8).padStart(11,'0')+'\0',124);b.write('00000000000\0',136);b.write(' ',148);b.write('0',156);b.write('ustar\0',257);b.write('00',263);let c=0;for(const x of b)c+=x;b.write(c.toString(8).padStart(6,'0')+'\0 ',148);return b;} | |
| function tarFiles(files){const parts=[];for(const [n,d] of files){const buf=Buffer.from(d);parts.push(hdr(n,buf.length),buf,Buffer.alloc((512-buf.length%512)%512));}parts.push(Buffer.alloc(1024));return Buffer.concat(parts);} | |
| function tarOf(content){return tarFiles([['package/package.json',JSON.stringify({name:NAME,version:VER,main:'index.js'})+'\n'],['package/index.js',content]]);} | |
| const sri = b => 'sha512-'+crypto.createHash('sha512').update(b).digest('base64'); | |
| const git = b => crypto.createHash('sha256').update(Buffer.concat([Buffer.from(`blob ${b.length}\0`), b])).digest('hex'); | |
| const PRIST = Buffer.from("module.exports = 'pristine'\n"), PATCH = Buffer.from("module.exports = 'patched'\n"); | |
| const tgz = zlib.gzipSync(tarOf(PRIST)), br = zlib.brotliCompressSync(tarOf(PRIST)), ptgz = zlib.gzipSync(tarOf(PATCH)); | |
| function wrapTar(){return zlib.gzipSync(tarFiles([['package/package.json',JSON.stringify({name:'wrapper',version:'1.0.0',main:'index.js',dependencies:{'left-pad':'1.3.0'}})+'\n'],['package/index.js',"module.exports = require('left-pad')\n"]]));} | |
| const wtgz = wrapTar(); | |
| const artPath = `/patch/npm/${NAME}/${VER}/${TOKEN}/${UUID}/${NAME}-${VER}.tgz`; | |
| const log = []; | |
| http.createServer((req,res)=>{ | |
| let body=''; req.on('data',c=>body+=c); req.on('end',()=>{ | |
| const u = req.url.split('?')[0]; log.push(req.method+' '+u); | |
| const j = o => { res.writeHead(200,{'content-type':'application/json'}); res.end(JSON.stringify(o)); }; | |
| const b = x => { res.writeHead(200,{'content-type':'application/octet-stream'}); res.end(x); }; | |
| if (u===`/${NAME}`) { | |
| const dist = {tarball:`${R}/${NAME}/-/${NAME}-${VER}.tgz`, integrity: sri(tgz)}; | |
| if (ALT) dist.alternates=[{kind:'tar.br', tarball:`${R}/${NAME}/-/${NAME}-${VER}.tar.br`, integrity: sri(br)}]; | |
| return j({name:NAME, 'dist-tags':{latest:VER}, versions:{[VER]:{name:NAME,version:VER,main:'index.js',dist}}}); | |
| } | |
| if (u===`/${NAME}/${VER}`) { const dist = {tarball:`${R}/${NAME}/-/${NAME}-${VER}.tgz`, integrity: sri(tgz)}; return j({name:NAME,version:VER,main:'index.js',dist}); } | |
| if (u==='/wrapper/1.0.0') return j({name:'wrapper',version:'1.0.0',dist:{tarball:`${R}/wrapper/-/wrapper-1.0.0.tgz`,integrity:sri(wtgz)}}); | |
| if (u==='/wrapper') return j({name:'wrapper','dist-tags':{latest:'1.0.0'},versions:{'1.0.0':{name:'wrapper',version:'1.0.0',main:'index.js',dependencies:{'left-pad':'1.3.0'},dist:{tarball:`${R}/wrapper/-/wrapper-1.0.0.tgz`,integrity:sri(wtgz)}}}}); | |
| if (u==='/wrapper/-/wrapper-1.0.0.tgz') return b(wtgz); | |
| if (u===`/${NAME}/-/${NAME}-${VER}.tgz`) return b(tgz); | |
| if (u===`/${NAME}/-/${NAME}-${VER}.tar.br`) return b(br); | |
| if (u===artPath) return b(ptgz); | |
| if (u.startsWith(`/v0/orgs/${ORG}/patches/blob/`)) { const h=u.split('/').pop(); for (const x of [PRIST,PATCH]) if (git(x)===h) return b(x); } | |
| if (u===`/v0/orgs/${ORG}/patches/batch`) { if (!body.includes(PURL)) return j({packages:[],canAccessPaidPatches:false}); return j({packages:[{purl:PURL,patches:[{uuid:UUID,purl:PURL,tier:'free',cveIds:[],ghsaIds:[],severity:'high',title:'fx'}]}],canAccessPaidPatches:false}); } | |
| if (u.startsWith(`/v0/orgs/${ORG}/patches/by-package/`)) return j({patches:[{uuid:UUID,purl:PURL,publishedAt:'2024-01-01T00:00:00Z',description:'x',license:'MIT',tier:'free',vulnerabilities:{}}],canAccessPaidPatches:false}); | |
| if (u===`/v0/orgs/${ORG}/patches/package`) return j({results:{[UUID]:{status:'granted',url:R+artPath,purl:PURL,artifacts:[{kind:'tarball',url:R+artPath,integrity:{sha512:sri(ptgz)}}],registryOverride:null}}}); | |
| if (u===`/v0/orgs/${ORG}/patches/view/${UUID}`) return j({uuid:UUID,purl:PURL,publishedAt:'2024-01-01T00:00:00Z',files:{'package/index.js':{beforeHash:git(PRIST),afterHash:git(PATCH),blobContent:PATCH.toString('base64'),beforeBlobContent:PRIST.toString('base64')}},vulnerabilities:{'GHSA-vlth-aaaa-bbbb':{cves:['CVE-2026-4242'],summary:'s',severity:'high',description:'d'}},description:'x',license:'MIT',tier:'free'}); | |
| res.writeHead(404); res.end('nf'); | |
| }); | |
| }).listen(PORT, '127.0.0.1', ()=>console.log('listening', PORT)); | |
| MOCKEOF | |
| cat > probe/gprobe.sh <<'PROBEEOF' | |
| #!/usr/bin/env bash | |
| # usage: gprobe.sh <socket-patch> <vlt.js> <mock.mjs> | |
| set -u | |
| SP=$1; VJS=$2; MOCK=$3 | |
| export LANG=C SOCKET_NPM_REGISTRY=http://127.0.0.1:18555 SOCKET_API_URL=http://127.0.0.1:18556 SOCKET_PATCH_SERVER_URL=http://127.0.0.1:18556 SOCKET_ORG_SLUG=test-org SOCKET_API_TOKEN=fake | |
| NO_ALT=1 PORT=18555 node "$MOCK" & M1=$!; NO_ALT=1 PORT=18556 node "$MOCK" & M2=$!; sleep 2 | |
| T=${RUNNER_TEMP:-$(mktemp -d)}; W="$T/gp$RANDOM"; mkdir -p "$W"; cd "$W"; FAIL=0 | |
| export XDG_CACHE_HOME="$W/.cache" XDG_DATA_HOME="$W/.data" XDG_CONFIG_HOME="$W/.config" LOCALAPPDATA="$W/.localappdata" HOME="$W/.home"; mkdir -p "$HOME" | |
| export NPM_CONFIG_PREFIX="$W/gprefix" NPM_CONFIG_REGISTRY=http://127.0.0.1:18555/ NPM_CONFIG_CACHE="$W/.npmcache" NPM_CONFIG_AUDIT=false NPM_CONFIG_FUND=false | |
| ok() { if [ "$2" = "$3" ]; then echo "PASS $1"; else echo "FAIL $1 (got '$2' want '$3')"; FAIL=1; fi; } | |
| obs() { echo "OBS $1: $2"; } | |
| vlt() { node "$VJS" "$@" >>"$W/vlt.log" 2>&1; } | |
| req() { (cd "$W" && node -e "try{console.log(require(require('path').resolve(process.argv[1])))}catch(e){console.log('MISSING')}" "$1"); } | |
| lp() { node -e "try{console.log(require('left-pad'))}catch(e){console.log('MISSING')}"; } | |
| same() { cmp -s "$1" "$2" && echo same || echo differ; } | |
| npm install -g left-pad@1.3.0 >"$W/npm.log" 2>&1; ok setup.npm.global $? 0 | |
| GROOT=$(npm root -g | tr -d '\r'); echo "GROOT=$GROOT" | |
| G="$GROOT/left-pad"; ok setup.global "$(req "$G")" pristine; cp "$G/index.js" "$W/g.orig" | |
| mkproj() { cd "$W"; rm -rf "$1"; mkdir -p "$1"; cd "$1"; echo '{"name":"app","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json; echo '{"config":{"registries":{"npm":"http://127.0.0.1:18555/"}}}' > vlt.json; vlt install; } | |
| mkproj proj; cp vlt-lock.json "$W/lock.orig" | |
| # report-only scan -g | |
| "$SP" scan -g --json > "$W/s1.json" 2>"$W/s1.err"; ok scan_g.rc $? 0 | |
| ok scan_g.found "$(node -e "const d=require(process.argv[1]);console.log(d.packagesWithPatches)" "$W/s1.json")" 1 | |
| ok scan_g.proj_lock "$(same "$W/lock.orig" vlt-lock.json)" same; ok scan_g.no_socket "$([ -e .socket ] && echo yes || echo no)" no | |
| ok scan_g.global_untouched "$(req "$G")" pristine | |
| # outside any project | |
| (cd "$W" && "$SP" scan -g --json > "$W/s1o.json" 2>/dev/null); ok scan_g.outside.found "$(node -e "const d=require(process.argv[1]);console.log(d.packagesWithPatches)" "$W/s1o.json")" 1 | |
| # hosted refusal | |
| "$SP" scan -g --mode hosted --json > /dev/null 2>&1; ok scan_g_hosted.rc $? 2 | |
| "$SP" scan --global-prefix "$GROOT" --mode hosted --json > /dev/null 2>&1; ok scan_prefix_hosted.rc $? 2 | |
| SOCKET_GLOBAL=1 "$SP" scan --mode hosted --json > /dev/null 2>&1; ok scan_envglobal_hosted.rc $? 2 | |
| ok hosted_refusal.proj_lock "$(same "$W/lock.orig" vlt-lock.json)" same | |
| # agent apply/vex/rollback | |
| "$SP" scan -g --mode agent --yes --json > "$W/a.json" 2>&1; ok agent_g.rc $? 0 | |
| ok agent_g.global "$(req "$G")" patched; ok agent_g.project "$(lp)" pristine | |
| "$SP" vex -g --output "$W/vex.json" > /dev/null 2>&1; ok vex_g.rc $? 0 | |
| "$SP" rollback -g --yes --json > /dev/null 2>&1; ok rollback_g.rc $? 0 | |
| ok rollback_g.bytes "$(same "$G/index.js" "$W/g.orig")" same; ok rollback_g.project "$(lp)" pristine | |
| "$SP" get pkg:npm/left-pad@1.3.0 -g --yes --json > /dev/null 2>&1; ok get_g.rc $? 0; ok get_g.global "$(req "$G")" patched | |
| "$SP" rollback -g --yes --json > /dev/null 2>&1; ok get_g.rollback "$(same "$G/index.js" "$W/g.orig")" same | |
| SOCKET_GLOBAL=1 "$SP" scan --mode agent --yes --json > /dev/null 2>&1; ok envglobal.rc $? 0; ok envglobal.global "$(req "$G")" patched; ok envglobal.project "$(lp)" pristine | |
| SOCKET_GLOBAL=1 "$SP" rollback --yes --json > /dev/null 2>&1; ok envglobal.rollback "$(same "$G/index.js" "$W/g.orig")" same | |
| # custom prefix with space + unicode | |
| CP="$W/my prefix café/node_modules"; mkdir -p "$CP"; cp -R "$G" "$CP/" | |
| "$SP" scan --global-prefix "$CP" --mode agent --yes --json > /dev/null 2>&1; ok prefix_unicode.rc $? 0 | |
| ok prefix_unicode.copy "$(req "$CP/left-pad")" patched; ok prefix_unicode.default_global "$(req "$G")" pristine | |
| "$SP" rollback --global-prefix "$CP" --yes --json > /dev/null 2>&1; ok prefix_unicode.rollback "$(same "$CP/left-pad/index.js" "$W/g.orig")" same | |
| # local scan never touches global | |
| "$SP" scan --mode agent --yes --json > /dev/null 2>&1; ok local.project "$(lp)" patched; ok local.global "$(req "$G")" pristine | |
| "$SP" rollback --yes --json > /dev/null 2>&1; ok local.rollback "$(lp)" pristine | |
| # read-only global (non-root unix only) | |
| if [ "$(uname -s | cut -c1-5)" != MINGW ] && [ "$(id -u)" != 0 ]; then | |
| chmod -R a-w "$G"; "$SP" scan -g --mode agent --yes --json > "$W/ro.json" 2>&1; rc=$? | |
| ok readonly.nonzero "$([ $rc != 0 ] && echo yes || echo no)" yes; ok readonly.global "$(req "$G")" pristine; chmod -R u+w "$G" | |
| fi | |
| # BUG candidates: rollback -g / remove -g inside hosted / vendored vlt projects | |
| for mode in hosted vendored; do for cmd in rollback remove; do | |
| mkproj "bug-$mode-$cmd" | |
| if [ $mode = hosted ]; then "$SP" scan --yes --json >/dev/null 2>&1; else "$SP" scan --mode vendored --yes --json >/dev/null 2>&1; fi | |
| rm -rf node_modules; vlt ci; cp vlt-lock.json wired.lock; before=$(lp) | |
| if [ $cmd = rollback ]; then "$SP" rollback -g --yes --json > out.json 2>/dev/null; rc=$?; else "$SP" remove pkg:npm/left-pad@1.3.0 -g --yes --json > out.json 2>/dev/null; rc=$?; fi | |
| obs "bug.$mode.${cmd}_g" "rc=$rc project_lock=$(same wired.lock vlt-lock.json) project_left_pad=$before->$(lp) .socket/vendor=$([ -d .socket/vendor ] && echo kept || echo gone)" | |
| done; done | |
| mkproj vend-agent; "$SP" scan --mode vendored --yes --json >/dev/null 2>&1 | |
| "$SP" scan -g --mode agent --yes --json > out.json 2>/dev/null; obs "bug.vendored_project.scan_g_agent" "rc=$? global=$(req "$G") warn=$(grep -o 'vendored_ownership_retained' out.json | head -1)" | |
| "$SP" rollback -g --yes --json >/dev/null 2>&1 | |
| kill $M1 $M2 2>/dev/null | |
| exit $FAIL | |
| PROBEEOF | |
| - name: Probe | |
| run: | | |
| cli="$PWD/target/release/socket-patch" | |
| if [ "$RUNNER_OS" = Windows ]; then cli="$cli.exe"; fi | |
| echo "npm root -g: $(npm root -g)" | |
| bash probe/gprobe.sh "$cli" "$VLT_JS" "$PWD/probe/mock.mjs" |