Skip to content

bughunt(yarn-classic): probe offline mirror + git-sourced deps on mac… #1

bughunt(yarn-classic): probe offline mirror + git-sourced deps on mac…

bughunt(yarn-classic): probe offline mirror + git-sourced deps on mac… #1

name: bughunt yarn-classic probe
on:
push:
branches: ['bughunt/yarn-classic/**']
permissions:
contents: read
jobs:
probe:
name: probe ${{ matrix.os }} yarn ${{ matrix.yarn }}
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
yarn: ['1.10.1', '1.22.22']
defaults:
run:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
run: rustup show
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '24.x'
- name: Build socket-patch
run: cargo build -p socket-patch-cli
- name: Probe (offline mirror + git-sourced dep)
env:
COREPACK_ENABLE_DOWNLOAD_PROMPT: '0'
YARN_V: ${{ matrix.yarn }}
run: |
cat > "$RUNNER_TEMP/probe.sh" <<'PROBE_EOF'
#!/usr/bin/env bash
# probe.sh SP_BIN YARN_VERSION — prints RESULT lines
set -u
SP=$1; V=$2; W=$(pwd)/probe-work; rm -rf "$W"; mkdir -p "$W"; cd "$W"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
PY=python3; command -v python3 >/dev/null && python3 -c 1 2>/dev/null || PY=python
PORT=18555; API=http://127.0.0.1:$PORT
winpath() { if command -v cygpath >/dev/null; then cygpath -m "$1"; else echo "$1"; fi; }
Y() { corepack yarn@$V "$@"; }
sp() { "$SP" "$@" --api-url $API --org test-org --api-token fake; }
marker() { if [ -f "$1" ] && head -c 22 "$1" | grep -q SOCKET-PATCHED; then echo PATCHED; elif [ -f "$1" ]; then echo UNPATCHED; else echo MISSING; fi; }
fresh() { rm -rf "$2"; mkdir -p "$2"; (cd "$1" && tar cf - --exclude=node_modules --exclude=.c . ) | (cd "$2" && tar xf -); }
# --- patched left-pad tarball + mock API ---
mkdir pk && (cd pk && npm pack left-pad@1.3.0 --silent >/dev/null)
cat > mk.py <<'PY'
import tarfile,io,hashlib,base64,json,sys
src=tarfile.open("pk/left-pad-1.3.0.tgz"); buf=io.BytesIO(); dst=tarfile.open(fileobj=buf,mode="w:gz")
for m in src.getmembers():
if not m.isfile(): continue
d=src.extractfile(m).read(); rel=m.name.split("/",1)[1]
if rel=="index.js": b=d; d=b"/* SOCKET-PATCHED */\n"+d; a=d
t=tarfile.TarInfo("package/"+rel); t.mode=0o644; t.mtime=0; t.size=len(d); dst.addfile(t,io.BytesIO(d))
dst.close(); tgz=buf.getvalue(); open("pk/patched.tgz","wb").write(tgz)
g=lambda d: hashlib.sha256(b"blob %d\0"%len(d)+d).hexdigest()
json.dump({"org":"test-org","pkgs":[{"name":"left-pad","version":"1.3.0","uuid":"7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f","tgz":"pk/patched.tgz","sha1":hashlib.sha1(tgz).hexdigest(),"sri":"sha512-"+base64.b64encode(hashlib.sha512(tgz).digest()).decode(),"files":{"package/index.js":{"before":g(b),"after":g(a),"blob":base64.b64encode(a).decode()}}}]},open("cfg.json","w"))
PY
$PY mk.py
cat > mock.py <<'PY'
#!/usr/bin/env python3
"""Hold-open mock Socket patch API. Usage: mock.py PORT CONFIG.json
CONFIG: {"org":"test-org","token":"...","pkgs":[{"name","version","uuid","tgz","files":{"package/index.js":{"before":hex,"after":hex}}, "sha1","sri"}]}"""
import json, sys, os, re, urllib.parse
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
PORT=int(sys.argv[1]); CFG=json.load(open(sys.argv[2]))
ORG=CFG["org"]; TOKEN=CFG.get("token","33333333-3333-4333-8333-333333333333")
LOG=open(sys.argv[2]+".log","a",buffering=1)
def purl(p):
n=p["name"]
pass
return f"pkg:npm/{n}@{p['version']}"
def hurl(p):
return f"http://127.0.0.1:{PORT}/patch/npm/{p['name']}/{p['version']}/{TOKEN}/{p['uuid']}/{p['name'].split('/')[-1]}-{p['version']}.tgz"
class H(BaseHTTPRequestHandler):
def log_message(self,*a): pass
def send(self,obj,code=200,raw=None):
b=raw if raw is not None else json.dumps(obj).encode()
self.send_response(code); self.send_header("Content-Length",str(len(b)))
self.send_header("Content-Type","application/octet-stream" if raw is not None else "application/json"); self.end_headers(); self.wfile.write(b)
def body(self):
n=int(self.headers.get("Content-Length") or 0); return self.rfile.read(n).decode() if n else ""
def summary(self,p):
return {"uuid":p["uuid"],"purl":purl(p),"tier":"free","cveIds":[],"ghsaIds":["GHSA-x-"+p["uuid"][:4]],"severity":"high","title":"fixture "+p["name"],
"publishedAt":"2026-01-01T00:00:00Z","description":"x","license":"MIT","vulnerabilities":{}}
def do_POST(self):
b=self.body(); LOG.write(f"POST {self.path} {b[:300]}\n")
if self.path.endswith("/patches/batch"):
pk=[{"purl":purl(p),"patches":[self.summary(p)]} for p in CFG["pkgs"] if purl(p) in urllib.parse.unquote(b) or purl(p) in b]
return self.send({"packages":pk,"canAccessPaidPatches":False})
if self.path.endswith("/patches/package"):
res={}
for p in CFG["pkgs"]:
if p["uuid"] in b:
u=hurl(p); res[p["uuid"]]={"status":"granted","url":u,"purl":purl(p),"artifacts":[{"kind":"tarball","url":u,"integrity":{"sha512":p["sri"],"sha1":p["sha1"]}}],"registryOverride":None}
return self.send({"results":res})
self.send({"error":"nf"},404)
def do_GET(self):
LOG.write(f"GET {self.path}\n")
m=re.search(r"/patches/by-package/(.+)$",self.path)
if m:
q=urllib.parse.unquote(m.group(1))
return self.send({"patches":[self.summary(p) for p in CFG["pkgs"] if purl(p)==q or urllib.parse.unquote(purl(p))==q],"canAccessPaidPatches":False})
m=re.search(r"/patches/view/([0-9a-f-]+)$",self.path)
if m:
for p in CFG["pkgs"]:
if p["uuid"]==m.group(1):
files={k:{"beforeHash":v["before"],"afterHash":v["after"],**({"blobContent":v["blob"]} if "blob" in v else {})} for k,v in p["files"].items()}
return self.send({"uuid":p["uuid"],"purl":purl(p),"publishedAt":"2026-01-01T00:00:00Z","files":files,
"vulnerabilities":{"GHSA-x-"+p["uuid"][:4]:{"cves":["CVE-2026-1"],"summary":"s","severity":"high","description":"d"}},"description":"x","license":"MIT","tier":"free"})
if self.path.startswith("/patch/"):
for p in CFG["pkgs"]:
if p["uuid"] in self.path:
return self.send(None,raw=open(p["tgz"],"rb").read())
# blob fetch
self.send({"error":"nf"},404)
ThreadingHTTPServer(("127.0.0.1",PORT),H).serve_forever()
PY
$PY mock.py $PORT cfg.json > mock.out 2>&1 &
MOCKPID=$!; sleep 2
result() { echo "RESULT os=${RUNNER_OS:-local} yarn=$V $*"; }
# --- case 1: hosted + yarn-offline-mirror ---
P=$W/mir; mkdir -p $P; echo '{"name":"b","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > $P/package.json
printf 'yarn-offline-mirror "./mirror"\n' > $P/.yarnrc
(cd $P && YARN_CACHE_FOLDER=$P/.c Y install --no-progress >/dev/null 2>&1)
fresh $P $W/mirctl; (cd $W/mirctl && YARN_CACHE_FOLDER=$W/cc0 Y install --frozen-lockfile --no-progress >/dev/null 2>&1); result "case=mirror-control(no-socket-patch) rc=$? $(marker $W/mirctl/node_modules/left-pad/index.js)"
sp scan --mode hosted --json --yes --cwd "$(winpath $P)" > $W/mirscan.json 2>/dev/null; result "case=mirror-hosted-scan rc=$? $(tr -d '\n' < $W/mirscan.json | grep -o '"redirected": *[0-9]*' | head -1) warnings=$(grep -o '"code": *"[a-z_]*"' $W/mirscan.json | tr -d ' \n')"
for mode in "" "--offline"; do fresh $P $W/mirf; (cd $W/mirf && YARN_CACHE_FOLDER=$W/cc$RANDOM Y install --frozen-lockfile $mode --no-progress > $W/mirf.log 2>&1); rc=$?; result "case=mirror-hosted-fresh-install${mode} rc=$rc $(marker $W/mirf/node_modules/left-pad/index.js) err=$(grep -o 'Integrity check failed\|offline mode' $W/mirf.log | head -1 | tr ' ' _)"; done
# --- case 2: git-sourced dep, hosted & vendored ---
G=$W/lpgit; mkdir -p $G; tar xzf pk/left-pad-1.3.0.tgz -C $G --strip-components=1; (cd $G && git init -q && git add -A && git -c user.email=a@b -c user.name=a commit -qm v && git tag v1.3.0)
for m in hosted vendored; do
P=$W/git-$m; mkdir -p $P; echo "{\"name\":\"g\",\"version\":\"1.0.0\",\"private\":true,\"dependencies\":{\"left-pad\":\"git+file:///$(winpath $G | sed 's#^/##')#v1.3.0\"}}" > $P/package.json
(cd $P && YARN_CACHE_FOLDER=$P/.c Y install --no-progress > $P/../i-$m.log 2>&1) || result "case=git-$m fixture-install-failed $(tail -1 $P/../i-$m.log)"
extra=""; [ $m = vendored ] && extra="--detached --vendor-source build"
sp scan --mode $m $extra --json --yes --cwd "$(winpath $P)" --vex "$(winpath $W/v-$m.json)" > $W/g-$m.json 2>/dev/null; result "case=git-$m-scan rc=$? status=$(grep -o '"status": *"[a-z_]*"' $W/g-$m.json | head -1 | tr -d ' ') inrun-vex=$(grep -o '"not_affected"' $W/v-$m.json | head -1)"
fresh $P $W/gf-$m; (cd $W/gf-$m && YARN_CACHE_FOLDER=$W/gc$RANDOM Y install --frozen-lockfile --no-progress > $W/gf-$m.log 2>&1); rc=$?; result "case=git-$m-fresh-install rc=$rc $(marker $W/gf-$m/node_modules/left-pad/index.js) err=$(grep -m1 '^error' $W/gf-$m.log | cut -c1-80 | tr ' ' _)"
done
kill $MOCKPID 2>/dev/null
PROBE_EOF
BIN="$PWD/target/debug/socket-patch"; [ -f "$BIN.exe" ] && BIN="$BIN.exe"
cd "$RUNNER_TEMP" && bash probe.sh "$BIN" "$YARN_V" 2>&1 | tee probe.log | grep RESULT || true
echo '--- mock log ---'; cat probe-work/cfg.json.log 2>/dev/null | cut -c1-200 | tail -20