bughunt uv probe: requirements lane on 3 OS #10
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 uv probe | |
| on: | |
| push: | |
| branches: ["bughunt/uv/**"] | |
| permissions: | |
| contents: read | |
| jobs: | |
| probe: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.11" | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| - name: Build CLI | |
| run: cargo build --release -p socket-patch-cli | |
| - name: Write probe files | |
| run: | | |
| mkdir -p "$RUNNER_TEMP/mock" "$RUNNER_TEMP/work" | |
| cat > "$RUNNER_TEMP/mock/mock.py" <<'PYEOF' | |
| #!/usr/bin/env python3 | |
| """Local mock of the Socket patch API serving one patched pypi package (six 1.16.0).""" | |
| import base64, hashlib, io, json, os, re, sys, zipfile | |
| from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer | |
| HERE = os.path.dirname(os.path.abspath(__file__)) | |
| PORT = int(os.environ.get("MOCK_PORT", "18080")) | |
| ORG = "test-org" | |
| UUID = "0f6e7c1a-1111-4222-8333-444455556666" | |
| TOKEN = "11111111-2222-4333-8444-555555555555" | |
| SUFFIX = b"\n# SOCKET-PATCHED\nSOCKET_PATCHED = 1\n" | |
| NAME, VER = "six", "1.16.0" | |
| PURL = f"pkg:pypi/{NAME}@{VER}" | |
| LEAF = f"{NAME}-{VER}-py2.py3-none-any.whl" | |
| LOG = os.path.join(HERE, "requests.log") | |
| def git_sha256(b): | |
| return hashlib.sha256(b"blob %d\0" % len(b) + b).hexdigest() | |
| def build(): | |
| src = zipfile.ZipFile(os.path.join(HERE, LEAF)) | |
| members = [] | |
| dist = f"{NAME}-{VER}.dist-info" | |
| for info in src.infolist(): | |
| if info.filename == f"{dist}/RECORD": | |
| continue | |
| data = src.read(info.filename) | |
| if info.filename == "six.py": | |
| orig = data | |
| data = data + SUFFIX | |
| members.append((info.filename, data)) | |
| rec = "" | |
| for n, d in members: | |
| dig = base64.urlsafe_b64encode(hashlib.sha256(d).digest()).rstrip(b"=").decode() | |
| rec += f"{n},sha256={dig},{len(d)}\n" | |
| rec += f"{dist}/RECORD,,\n" | |
| members.append((f"{dist}/RECORD", rec.encode())) | |
| buf = io.BytesIO() | |
| with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z: | |
| for n, d in members: | |
| zi = zipfile.ZipInfo(n, date_time=(2020, 1, 1, 0, 0, 0)); zi.compress_type = zipfile.ZIP_DEFLATED | |
| z.writestr(zi, d) | |
| return orig, orig + SUFFIX, buf.getvalue() | |
| ORIG, PATCHED, WHEEL = build() | |
| SHA = hashlib.sha256(WHEEL).hexdigest() | |
| BASE = f"http://127.0.0.1:{PORT}" | |
| ART_PATH = f"/patch/pypi/{NAME}/{VER}/{TOKEN}/{UUID}/{LEAF}" | |
| ART = BASE + ART_PATH | |
| VIEW = { | |
| "uuid": UUID, "purl": PURL, "publishedAt": "Fri, 27 Mar 2026 00:00:00 GMT", | |
| "files": {"six.py": {"beforeHash": git_sha256(ORIG), "afterHash": git_sha256(PATCHED), | |
| "blobContent": base64.b64encode(PATCHED).decode()}}, | |
| "vulnerabilities": {"GHSA-uvbu-ildv-ex01": {"cves": ["CVE-2026-7101"], "summary": "s", | |
| "severity": "high", "description": "d"}}, | |
| "description": "uv bughunt patch", "license": "MIT", "tier": "free", | |
| } | |
| class H(BaseHTTPRequestHandler): | |
| def log_message(self, *a): | |
| pass | |
| def send(self, code, body, ctype="application/json"): | |
| if not isinstance(body, bytes): | |
| body = json.dumps(body).encode() | |
| self.send_response(code) | |
| self.send_header("Content-Type", ctype) | |
| self.send_header("Content-Length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def route(self): | |
| with open(LOG, "a") as f: | |
| f.write(f"{self.command} {self.path}\n") | |
| p = self.path.split("?")[0] | |
| n = int(self.headers.get("Content-Length") or 0) | |
| body = self.rfile.read(n) if n else b"" | |
| if p == ART_PATH: | |
| if self.command == "HEAD": | |
| self.send_response(200); self.send_header("Content-Length", str(len(WHEEL))); self.end_headers(); return | |
| return self.send(200, WHEEL, "application/octet-stream") | |
| if re.fullmatch(rf"/v0/orgs/{ORG}/patches/batch", p) or p.endswith("/patch/batch"): | |
| try: | |
| comps = json.loads(body or b"{}").get("components", []) | |
| except Exception: | |
| comps = [] | |
| purls = [c.get("purl", "") for c in comps] if comps else [PURL] | |
| if not any(pu.lower().startswith(f"pkg:pypi/{NAME}@{VER}") for pu in purls): | |
| return self.send(200, {"packages": [], "canAccessPaidPatches": False}) | |
| return self.send(200, {"packages": [{"purl": PURL, "patches": [{ | |
| "uuid": UUID, "purl": PURL, "tier": "free", "cveIds": ["CVE-2026-7101"], | |
| "ghsaIds": ["GHSA-uvbu-ildv-ex01"], "severity": "HIGH", "title": "uv bughunt"}]}], | |
| "canAccessPaidPatches": False}) | |
| if "/patches/by-package/" in p or "/patch/by-package/" in p: | |
| return self.send(200, {"patches": [{"uuid": UUID, "purl": PURL, | |
| "publishedAt": "2026-09-01T00:00:00Z", "description": "uv bughunt", | |
| "license": "MIT", "tier": "free", "vulnerabilities": {}}], | |
| "canAccessPaidPatches": False}) | |
| if p.endswith("/patches/package") or p.endswith("/patch/package"): | |
| return self.send(200, {"results": {UUID: {"status": "granted", "url": ART, "purl": PURL, | |
| "artifacts": [{"kind": "tarball", "url": ART, "integrity": {"sha256": SHA, "sha512": "sha512-" + base64.b64encode(hashlib.sha512(WHEEL).digest()).decode()}}], | |
| "registryOverride": None}}}) | |
| if p.endswith(f"/view/{UUID}"): | |
| return self.send(200, VIEW) | |
| if "/blob/" in p: | |
| h = p.rsplit("/", 1)[1] | |
| if h == git_sha256(PATCHED): | |
| return self.send(200, PATCHED, "application/octet-stream") | |
| if h == git_sha256(ORIG): | |
| return self.send(200, ORIG, "application/octet-stream") | |
| return self.send(404, {"error": "not found", "path": p}) | |
| do_GET = route | |
| do_POST = route | |
| do_HEAD = route | |
| if __name__ == "__main__": | |
| print(json.dumps({"art": ART, "sha": SHA, "patched_py_sha": hashlib.sha256(PATCHED).hexdigest(), | |
| "orig_py_sha": hashlib.sha256(ORIG).hexdigest()}), flush=True) | |
| ThreadingHTTPServer(("127.0.0.1", PORT), H).serve_forever() | |
| PYEOF | |
| cat > "$RUNNER_TEMP/repro.sh" <<'SHEOF' | |
| #!/usr/bin/env bash | |
| # usage: repro.sh <uv-binary> <workdir> <hosted|vendored> <lf|crlf> | |
| set -u | |
| UVB=$1; D=$2; MODE=$3; EOL=$4; mkdir -p "$D" && cd "$D" || exit 1 | |
| W=$(pwd -W 2>/dev/null || pwd) | |
| PY() { if [ -x .venv/Scripts/python.exe ]; then .venv/Scripts/python.exe "$@"; else .venv/bin/python "$@"; fi; } | |
| ORC() { PY -c "import six; print('PATCHED' if getattr(six, 'SOCKET_PATCHED', 0) else 'PRISTINE')" 2>&1 | tail -1; } | |
| printf 'six==1.16.0\nidna==3.7\n' > requirements.in | |
| "$UVB" pip compile -q --generate-hashes requirements.in -o requirements.txt || { echo "compile failed"; exit 0; } | |
| if [ "$EOL" = crlf ]; then python -c "import sys;p='requirements.txt';b=open(p,'rb').read().replace(b'\r\n',b'\n').replace(b'\n',b'\r\n');open(p,'wb').write(b)"; fi | |
| cp requirements.txt orig.txt | |
| "$UVB" venv -q .venv; VIRTUAL_ENV="$W/.venv" "$UVB" pip sync -q requirements.txt | |
| $SP scan --mode $MODE --json --yes $API > scan.json 2>scan.err; rc=$? | |
| python -c "import json;d=json.load(open('scan.json'));r=d.get('redirect') or {};v=d.get('vendor') or {};print('scan rc=$rc',d.get('status'),r.get('redirected'),[w['code'] for w in r.get('warnings',[])],[(e['action'],e.get('errorCode')) for e in v.get('events',[])])" || cat scan.err | |
| grep -n "six\|socket" requirements.txt | head -3 | |
| rm -rf .venv; "$UVB" venv -q .venv | |
| VIRTUAL_ENV="$W/.venv" UV_CACHE_DIR="$W/cache1" "$UVB" pip sync --require-hashes requirements.txt 2>&1 | grep -iE "error|six" | head -3 | |
| echo "fresh install after $MODE scan: $(ORC)" | |
| if [ $MODE = hosted ]; then $SP rollback --json --yes $API > rb.json 2>/dev/null; else $SP vendor --revert --json --yes $API > rb.json 2>/dev/null; fi | |
| python -c "import json;d=json.load(open('rb.json'));print('unwind',d['status'],str(d.get('hosted') or d.get('events'))[:300],d.get('warnings'))" | |
| cmp -s orig.txt requirements.txt && echo "unwind BYTE-IDENTICAL" || { echo "unwind DRIFT"; diff orig.txt requirements.txt | head -6; } | |
| VIRTUAL_ENV="$W/.venv" "$UVB" pip sync requirements.txt 2>&1 | tail -1 | |
| echo "after unwind + uv pip sync: $(ORC)" | |
| SHEOF | |
| python -m pip download --no-deps six==1.16.0 -d "$RUNNER_TEMP/mock" | |
| - name: Run probe | |
| run: | | |
| python "$RUNNER_TEMP/mock/mock.py" > "$RUNNER_TEMP/mock.out" 2>&1 & | |
| sleep 3; cat "$RUNNER_TEMP/mock.out" | |
| export SP="$PWD/target/release/socket-patch"; [ -f "$SP.exe" ] && SP="$SP.exe" | |
| export API="--api-url http://127.0.0.1:18080 --api-token fake-token --org test-org --patch-server-url http://127.0.0.1:18080" | |
| export SOCKET_TELEMETRY_DISABLED=1 SOCKET_NO_CONFIG=1 | |
| for v in 0.1.44 0.5.31 0.12.21; do | |
| python -m venv "$RUNNER_TEMP/uv-$v" | |
| if [ -d "$RUNNER_TEMP/uv-$v/Scripts" ]; then B="$RUNNER_TEMP/uv-$v/Scripts"; else B="$RUNNER_TEMP/uv-$v/bin"; fi | |
| "$B/python" -m pip install -q "uv==$v" | |
| UVB="$B/uv"; [ -f "$UVB.exe" ] && UVB="$UVB.exe" | |
| echo "### RESULT uv $v on ${{ matrix.os }}" | |
| for m in hosted vendored; do for e in lf crlf; do | |
| echo "### CELL uv $v ${{ matrix.os }} $m $e" | |
| bash "$RUNNER_TEMP/repro.sh" "$UVB" "$RUNNER_TEMP/work/r-$v-$m-$e" $m $e || true | |
| done; done | |
| done |