|
| 1 | +name: bughunt vlt probe |
| 2 | +on: |
| 3 | + push: |
| 4 | + branches: ['bughunt/vlt/**'] |
| 5 | +permissions: |
| 6 | + contents: read |
| 7 | +jobs: |
| 8 | + probe: |
| 9 | + strategy: |
| 10 | + fail-fast: false |
| 11 | + matrix: |
| 12 | + os: [ubuntu-latest, macos-latest] |
| 13 | + runs-on: ${{ matrix.os }} |
| 14 | + timeout-minutes: 45 |
| 15 | + defaults: |
| 16 | + run: |
| 17 | + shell: bash |
| 18 | + steps: |
| 19 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 20 | + with: |
| 21 | + persist-credentials: false |
| 22 | + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 |
| 23 | + with: |
| 24 | + node-version: 24.21.0 |
| 25 | + - name: Build CLI |
| 26 | + run: cargo build --release -p socket-patch-cli |
| 27 | + - name: Write probe |
| 28 | + run: | |
| 29 | + mkdir -p probe |
| 30 | + cat > probe/mock.mjs <<'MOCKEOF' |
| 31 | + import http from 'node:http'; |
| 32 | + import zlib from 'node:zlib'; |
| 33 | + import crypto from 'node:crypto'; |
| 34 | + import fs from 'node:fs'; |
| 35 | + const PORT = +process.env.PORT || 18555; |
| 36 | + const R = `http://127.0.0.1:${PORT}`; |
| 37 | + const NAME='left-pad', VER='1.3.0', UUID='aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa', TOKEN='bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb', ORG='test-org'; |
| 38 | + const PURL=`pkg:npm/${NAME}@${VER}`; |
| 39 | + const ALT = process.env.NO_ALT ? false : true; |
| 40 | + 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;} |
| 41 | + 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);} |
| 42 | + function tarOf(content){return tarFiles([['package/package.json',JSON.stringify({name:NAME,version:VER,main:'index.js'})+'\n'],['package/index.js',content]]);} |
| 43 | + const sri = b => 'sha512-'+crypto.createHash('sha512').update(b).digest('base64'); |
| 44 | + const git = b => crypto.createHash('sha256').update(Buffer.concat([Buffer.from(`blob ${b.length}\0`), b])).digest('hex'); |
| 45 | + const PRIST = Buffer.from("module.exports = 'pristine'\n"), PATCH = Buffer.from("module.exports = 'patched'\n"); |
| 46 | + const tgz = zlib.gzipSync(tarOf(PRIST)), br = zlib.brotliCompressSync(tarOf(PRIST)), ptgz = zlib.gzipSync(tarOf(PATCH)); |
| 47 | + 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"]]));} |
| 48 | + const wtgz = wrapTar(); |
| 49 | + const artPath = `/patch/npm/${NAME}/${VER}/${TOKEN}/${UUID}/${NAME}-${VER}.tgz`; |
| 50 | + const log = []; |
| 51 | + http.createServer((req,res)=>{ |
| 52 | + let body=''; req.on('data',c=>body+=c); req.on('end',()=>{ |
| 53 | + const u = req.url.split('?')[0]; log.push(req.method+' '+u); |
| 54 | + const j = o => { res.writeHead(200,{'content-type':'application/json'}); res.end(JSON.stringify(o)); }; |
| 55 | + const b = x => { res.writeHead(200,{'content-type':'application/octet-stream'}); res.end(x); }; |
| 56 | + if (u===`/${NAME}`) { |
| 57 | + const dist = {tarball:`${R}/${NAME}/-/${NAME}-${VER}.tgz`, integrity: sri(tgz)}; |
| 58 | + if (ALT) dist.alternates=[{kind:'tar.br', tarball:`${R}/${NAME}/-/${NAME}-${VER}.tar.br`, integrity: sri(br)}]; |
| 59 | + return j({name:NAME, 'dist-tags':{latest:VER}, versions:{[VER]:{name:NAME,version:VER,main:'index.js',dist}}}); |
| 60 | + } |
| 61 | + 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}); } |
| 62 | + 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)}}); |
| 63 | + 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)}}}}); |
| 64 | + if (u==='/wrapper/-/wrapper-1.0.0.tgz') return b(wtgz); |
| 65 | + if (u===`/${NAME}/-/${NAME}-${VER}.tgz`) return b(tgz); |
| 66 | + if (u===`/${NAME}/-/${NAME}-${VER}.tar.br`) return b(br); |
| 67 | + if (u===artPath) return b(ptgz); |
| 68 | + 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); } |
| 69 | + 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}); } |
| 70 | + 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}); |
| 71 | + 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}}}); |
| 72 | + 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'}); |
| 73 | + res.writeHead(404); res.end('nf'); |
| 74 | + }); |
| 75 | + }).listen(PORT, '127.0.0.1', ()=>console.log('listening', PORT)); |
| 76 | +
|
| 77 | + MOCKEOF |
| 78 | + cat > probe/ro.sh <<'PROBEEOF' |
| 79 | + #!/usr/bin/env bash |
| 80 | + set -u |
| 81 | + SP=$1; MOCK=$2 |
| 82 | + 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 |
| 83 | + NO_ALT=1 PORT=18555 node "$MOCK" & M1=$!; NO_ALT=1 PORT=18556 node "$MOCK" & M2=$!; sleep 2 |
| 84 | + W="$RUNNER_TEMP/ro$RANDOM"; mkdir -p "$W"; cd "$W" |
| 85 | + export HOME="$W/h" NPM_CONFIG_PREFIX="$W/gprefix" NPM_CONFIG_REGISTRY=http://127.0.0.1:18555/ NPM_CONFIG_CACHE="$W/.c"; mkdir -p "$HOME" |
| 86 | + npm install -g left-pad@1.3.0 >/dev/null 2>&1; GROOT=$(npm root -g); G="$GROOT/left-pad"; cp "$G/index.js" g.orig |
| 87 | + sudo chown -R root "$W/gprefix"; sudo chmod -R go-w "$W/gprefix"; ls -la "$G" |
| 88 | + req() { node -e "console.log(require(process.argv[1]))" "$1"; } |
| 89 | + for i in 1 2; do |
| 90 | + for form in "-g" "--global-prefix $GROOT" "env"; do |
| 91 | + if [ "$form" = env ]; then SOCKET_GLOBAL=1 "$SP" scan --mode agent --yes --json > out.json 2>err.txt; rc=$?; else "$SP" scan $form --mode agent --yes --json > out.json 2>err.txt; rc=$?; fi |
| 92 | + echo "RO[$i] form=$form rc=$rc status=$(grep -o '"status": "[a-z_]*"' out.json | head -1) global=$(req "$G") bytes=$(cmp -s "$G/index.js" g.orig && echo same || echo differ) errcode=$(grep -o '"errorCode": "[a-zA-Z_]*"\|"code": "[a-z_]*"' out.json | sort -u | tr '\n' ' ')" |
| 93 | + head -c 600 err.txt | tr '\n' ' '; echo |
| 94 | + "$SP" get pkg:npm/left-pad@1.3.0 -g --yes --json > g.json 2>/dev/null; echo "RO[$i] get -g rc=$? status=$(grep -o '"status": "[a-zA-Z_]*"' g.json | head -1) global=$(req "$G")" |
| 95 | + "$SP" rollback -g --yes --json > r.json 2>/dev/null; echo "RO[$i] rollback -g rc=$? global=$(req "$G")" |
| 96 | + done; done |
| 97 | + kill $M1 $M2 |
| 98 | + PROBEEOF |
| 99 | + - name: Probe |
| 100 | + run: bash probe/ro.sh "$PWD/target/release/socket-patch" "$PWD/probe/mock.mjs" |
0 commit comments