diff --git a/CHANGELOG.md b/CHANGELOG.md index ac44cccc..eb5c1efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,38 @@ into the new version's section — see docs/releasing.md. ### Added +- **Poetry projects take hosted patches, and vendored patches now cover every + `poetry.lock` generation.** `scan --mode hosted` rewrites `poetry.lock` to a + `[package.source] type = "url"` pointing at the Socket-hosted, SHA-256-pinned + wheel (Poetry 1.0 through 2.x; Poetry 0.12 ignores URL sources and is refused + with `redirect_poetry_lock_unsupported`), and `scan --mode vendored` accepts + the legacy `[metadata.hashes]` (0.12) and `[metadata.files]` (lock 1.0/1.1) + layouts next to the 2.x `files` arrays, CRLF locks included. The rewrite keeps + every other byte of the lock — dependency metadata, groups, markers, extras + and the pyproject `content-hash` — and records independent rollback fragments + per patch, so `rollback` restores the recorded originals in any order. + Verified end-to-end against real Poetry 0.12.17, 1.0.10, 1.1.15, 1.2.2, + 1.3.2, 1.4.2, 1.5.1, 1.6.1, 1.7.1, 1.8.5, 2.0.1, 2.1.4, 2.2.1, 2.3.4 and + 2.4.3 in hosted, vendored and agent mode — see + `docs/testing/poetry-compatibility.md` and `scripts/backtest-poetry.py`. + Poetry releases before 1.4 neither verify local wheel hashes nor replace an + already-installed package at the same version; both modes surface that as an + advisory (`pypi_poetry_integrity_unverified`, `redirect_poetry_stale_install_risk`) + keyed on the lock's writer, and the hosted rewriter warns + `redirect_poetry_entry_not_found` when a lock has no entry for a granted + patch (uv parity). A rotated grant token or republished patch supersedes the + earlier hosted URL in place instead of being refused as a foreign source, + a future `lock-version = "2."` is rewritten like 2.1 on every path + (the vendored loader already accepted it), and a malformed + `[metadata.files]` / `[metadata.hashes]` value is refused instead of + panicking the scan. Rollback stays invertible across Poetry's own relocks: + the recorded package fragment carries its boundary header, so a unit that + Poetry 1.1/1.2 re-laid (source kept, inserted `files` line dropped) is + refused rather than mistaken for an already-reverted lock, a lock-1.0 + redirect restored by hand converges instead of refusing, and a re-scan + after such a relock REBASES the ledger's edits (pristine → current) + instead of appending a chain whose older links match nothing — which made + `rollback` and `remove` refuse forever. (#241) - **Python patches survive uv lockfiles in both hosted and vendored modes.** `scan --mode hosted|vendored` now rewrites native `uv.lock` together with the paired `pyproject.toml` source and metadata, PEP 723 script locks @@ -168,6 +200,32 @@ into the new version's section — see docs/releasing.md. ### Fixed +- Hosted Python redirects now warn when installed files still contain upstream + or modified bytes and omit those packages from same-run VEX. The read-only + probe covers Poetry virtualenvs, repeats on re-scans, and uses persisted patch + records if fetching fresh records fails. + +- **Agent mode finds Poetry's out-of-tree virtualenv.** Poetry keeps a + project's virtualenv under `{cache-dir}/virtualenvs/--py` + by default, so after a plain `poetry install` the crawler saw no + `VIRTUAL_ENV` / `.venv` / `venv` and fell through to the global interpreter: + `scan --mode agent` patched nothing for the project's dependencies (or the + wrong interpreter) while reporting success, and a bare `rollback` pruned the + manifest while the venv stayed patched. The crawler now reproduces Poetry's + own placement — `virtualenvs.create` / `in-project` / `path` and `cache-dir` + from `POETRY_*`, the project's `poetry.toml` and the user `config.toml`, the + platform default cache dir, and Poetry's env-name hash — without running + Poetry, and scans every `-py` sibling. `poetry run socket-patch …` and + `VIRTUAL_ENV` keep working as before. +- **`scan --mode vendored` works from a lock-only Poetry checkout.** The + `poetry.lock` inventory was discovery-only, so a fresh clone with nothing + installed was skipped with `vendor_fetch_unverifiable` even though the lock + records the wheel's sha256 (uv's lock vendored fine in the same scenario). + The inventory now carries the pure-Python wheel's sha256 from `files` (lock + 2.x) or `[metadata.files]` (lock 1.0/1.1), and the pypi fetcher resolves a + hash-only entry through PyPI's JSON API by that digest (verified again after + download; `SOCKET_PYPI_JSON_API` overrides the endpoint). Poetry 0.12's bare + `[metadata.hashes]` names no wheel and still needs an installed copy. - **`remove` no longer drops the manifest entry of a drift-kept vendored purl.** When the vendored revert keeps the artifact (`kept_artifact` — the lockfile drifted), the manifest entry is now kept too diff --git a/README.md b/README.md index 093b07dc..5f2d91af 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ The same patched bytes can reach your build three different ways. The modes diff |------|----------------------|--------------------------|-----------| | **agent** — `scan --mode agent` (or [`apply`](#apply)) | `.socket/` manifest + blobs, committed; the CLI re-applies after each install | The `socket-patch` CLI must run (install hook via [`setup`](#setup), or an `apply` step in CI) | Small repo footprint (per-file blobs, not whole packages); no lockfile edits; the only mode that needs CI / install-hook changes | | **vendored** — `scan --mode vendored` (or [`vendor`](#vendor)) | Patched packages committed under `.socket/vendor/`; the lockfile is rewired to consume them | **None** — the package manager installs the committed bytes | Fully airgapped and hermetic, at the cost of repo size | -| **hosted** — `scan --mode hosted` | No patched bytes in your repo: the lockfile is rewritten so **only** the patched dependencies resolve to Socket-hosted, integrity-pinned packages on `patch.socket.dev`; the edits + patch records are ledgered in `.socket/vendor/redirect-state.json` (commit it — [`vex`](#vex) reads it, and it records the pre-redirect originals a future revert feature will need; hosted has no CLI revert yet, see [Undo things](#undo-things)) | Installs must be able to reach `patch.socket.dev` (no CLI, no install hook) | Smallest possible diff (lockfile + ledger); not for airgapped installs | +| **hosted** — `scan --mode hosted` | No patched bytes in your repo: the lockfile is rewritten so **only** the patched dependencies resolve to Socket-hosted, integrity-pinned packages on `patch.socket.dev`; the edits + patch records are ledgered in `.socket/vendor/redirect-state.json` (commit it — [`vex`](#vex) reads it, and [`rollback`](#rollback) replays its recorded pre-redirect originals to unwind the redirect, see [Undo things](#undo-things)) | Installs must be able to reach `patch.socket.dev` (no CLI, no install hook) | Smallest possible diff (lockfile + ledger); not for airgapped installs | Every mode pins the patched bytes: in agent mode the CLI verifies every file on each apply; vendored and hosted modes lean on your package manager's own lockfile integrity @@ -356,11 +356,12 @@ and repair; pick by what you want back: And `setup --remove` reverts the install hooks that `setup` added. -> Hosted mode has no CLI revert yet: `scan --mode hosted` makes plain lockfile / -> registry-config edits, so undo them with your version control (e.g. -> `git checkout -- `) and delete the `.socket/vendor/redirect-state.json` -> ledger — once you've reverted by hand, its recorded original fragments are stale, and -> a leftover ledger would still let [`vex`](#vex) attest the removed redirects. +> Hosted mode is unwound by [`rollback`](#rollback), which replays the original +> lockfile / registry-config fragments recorded in `.socket/vendor/redirect-state.json` +> and drops the redirect records. If you revert a hosted edit by hand instead (e.g. +> `git checkout -- `), also delete that ledger — its recorded originals are +> then stale, and a leftover ledger would still let [`vex`](#vex) attest the removed +> redirects. ## Command reference diff --git a/crates/socket-patch-cli/CLI_CONTRACT.md b/crates/socket-patch-cli/CLI_CONTRACT.md index b0a730a1..95c6ec83 100644 --- a/crates/socket-patch-cli/CLI_CONTRACT.md +++ b/crates/socket-patch-cli/CLI_CONTRACT.md @@ -120,7 +120,7 @@ For a **9.0 root lock**, the CLI ensures `pnpm-workspace.yaml` carries `trustLoc `scan --mode hosted` (== `--redirect`) swaps the in-place apply for the registry-redirect pipeline: discover → resolve hosted-patch references (grant token + integrity + per-dep registry override) → rewrite ONLY the patched dependencies' lockfile / registry-config entries to point at the hosted packages. A dep counts as **redirected** only when its hosted-artifact URL (or per-dep registry index URL, or — for golang — the `patch.socket.dev/gopatch/` module path) actually landed in a project file — a granted reference whose rewriter found nothing to edit is neither recorded nor attested. Re-runs over already-rewritten output record zero new edits. JSON output gains a `redirect` sub-object: `{ mode: "hosted", redirected, rewrittenFiles, skipped, warnings, dryRun }` (`mode` is additive so consumers can dispatch without inferring it). Rewriter warnings carry stable `redirect_*` codes (e.g. `redirect_npm_no_lockfile`, `redirect_gradle_manual_snippet`, `redirect_golang_unsupported`); new codes are additive (MINOR). Refusals stay fail-closed with a diagnosis that names the actual cause: a yarn-berry lock entry resolving through a non-`npm:` protocol keeps `redirect_yarn_berry_unsupported_protocol` with the entry's ACTUAL protocol in the detail — except socket-patch's OWN vendored wiring (a `file:` range into `.socket/vendor/`), which gets the distinct `redirect_yarn_berry_vendored_entry` code whose detail names the retirement path (`remove ` per package, or `vendor --revert` which unwinds every vendored package, then re-run `scan --mode hosted`). Both leave the entry byte-identical; neither changes exit code or status. -The rewriter reads a fixed set of candidate files from the project root: the npm-family locks (`package-lock.json`, `npm-shrinkwrap.json`, `pnpm-lock.yaml`, `shrinkwrap.yaml`, `yarn.lock`, plus `.yarnrc.yml` for the berry cache-config gate and `bun.lock`), `requirements.txt` / `uv.lock`, `Cargo.toml` / `Cargo.lock` / `.cargo/config.toml` (plus the legacy extensionless `.cargo/config` — cargo reads that spelling in preference when both exist, so the managed `[registries.…]` block is written into whichever one is present), `composer.lock`, `nuget.config` / `packages.lock.json`, `Gemfile` / `Gemfile.lock`, `pom.xml` (+ `.mvn/maven.config` / `.mvn/checksums/checksums.sha256` for maven Trusted Checksums merge, and the Gradle build scripts read only to trigger the manual-snippet warning). **npm-family flavor coverage**: package-lock / npm-shrinkwrap, pnpm (root OR any nested `*/pnpm-lock.yaml`), yarn classic, **yarn berry** (`yarn.lock` entry only — `resolution: ::__archiveUrl=` + `yarnBerry10c0` checksum; cacheKey `10c0` and `.yarnrc.yml compressionLevel 0` gated by `redirect_yarn_berry_cache_unsupported`), and **bun** (text `bun.lock` lockfileVersion 1 or 2 — bun 1.3/1.4 share one emitted grammar; a binary `bun.lockb` with no text lock is auto-migrated to text via `bun install --save-text-lockfile --frozen-lockfile --lockfile-only` before the read, recorded as a `removed` FileEdit; `redirect_bun_lockb_would_migrate` on `--dry-run`, `redirect_bun_lockb_unsupported` when the migration is unavailable). **Rush monorepos**: when `rush.json` is present the rewriter also reads `common/config/rush/pnpm-lock.yaml` and each `common/config/subspaces//pnpm-lock.yaml` (sorted for determinism) under their repo-relative keys and repoints them in place; editing them emits `redirect_rush_repo_state_stale` when `common/config/rush/repo-state.json` exists (the `pnpmShrinkwrapHash` desync is refreshed by `rush update`, which the redirect survives). **maven** is fail-closed via version suffixing: a `mavenSuffixedVersion` + `mavenPomSha256` override pins the Socket-only `-socket.` by rewriting the literal `` (`redirect_maven_dep_version`) or adding a `` entry (`redirect_maven_dep_management_added`), plus optional Trusted Checksums (`redirect_maven_trusted_checksums`, conflicts as `redirect_maven_trusted_checksums_conflict`); a `${property}` version is refused (`redirect_maven_dep_unpinned`), a non-matching literal skipped (`redirect_maven_dep_version_mismatch`), and an override without a suffixed version falls back to same-GAV repository injection (`redirect_maven_same_gav_fallback`, NOT fail-closed). +The rewriter reads a fixed set of candidate files from the project root: the npm-family locks (`package-lock.json`, `npm-shrinkwrap.json`, `pnpm-lock.yaml`, `shrinkwrap.yaml`, `yarn.lock`, plus `.yarnrc.yml` for the berry cache-config gate and `bun.lock`), `requirements.txt` / `uv.lock` / `poetry.lock` (every Poetry lock generation from 1.0 on — the 0.12 `[metadata.hashes]` layout is refused because that installer ignores URL sources; a Poetry < 1.4 writer additionally gets `redirect_poetry_stale_install_risk`, see `docs/testing/poetry-compatibility.md`), `Cargo.toml` / `Cargo.lock` / `.cargo/config.toml` (plus the legacy extensionless `.cargo/config` — cargo reads that spelling in preference when both exist, so the managed `[registries.…]` block is written into whichever one is present), `composer.lock`, `nuget.config` / `packages.lock.json`, `Gemfile` / `Gemfile.lock`, `pom.xml` (+ `.mvn/maven.config` / `.mvn/checksums/checksums.sha256` for maven Trusted Checksums merge, and the Gradle build scripts read only to trigger the manual-snippet warning). **npm-family flavor coverage**: package-lock / npm-shrinkwrap, pnpm (root OR any nested `*/pnpm-lock.yaml`), yarn classic, **yarn berry** (`yarn.lock` entry only — `resolution: ::__archiveUrl=` + `yarnBerry10c0` checksum; cacheKey `10c0` and `.yarnrc.yml compressionLevel 0` gated by `redirect_yarn_berry_cache_unsupported`), and **bun** (text `bun.lock` lockfileVersion 1 or 2 — bun 1.3/1.4 share one emitted grammar; a binary `bun.lockb` with no text lock is auto-migrated to text via `bun install --save-text-lockfile --frozen-lockfile --lockfile-only` before the read, recorded as a `removed` FileEdit; `redirect_bun_lockb_would_migrate` on `--dry-run`, `redirect_bun_lockb_unsupported` when the migration is unavailable). **Rush monorepos**: when `rush.json` is present the rewriter also reads `common/config/rush/pnpm-lock.yaml` and each `common/config/subspaces//pnpm-lock.yaml` (sorted for determinism) under their repo-relative keys and repoints them in place; editing them emits `redirect_rush_repo_state_stale` when `common/config/rush/repo-state.json` exists (the `pnpmShrinkwrapHash` desync is refreshed by `rush update`, which the redirect survives). **maven** is fail-closed via version suffixing: a `mavenSuffixedVersion` + `mavenPomSha256` override pins the Socket-only `-socket.` by rewriting the literal `` (`redirect_maven_dep_version`) or adding a `` entry (`redirect_maven_dep_management_added`), plus optional Trusted Checksums (`redirect_maven_trusted_checksums`, conflicts as `redirect_maven_trusted_checksums_conflict`); a `${property}` version is refused (`redirect_maven_dep_unpinned`), a non-matching literal skipped (`redirect_maven_dep_version_mismatch`), and an override without a suffixed version falls back to same-GAV repository injection (`redirect_maven_same_gav_fallback`, NOT fail-closed). **Gem stale-install guard (additive warning — the canonical narrative; other mentions point here)**: the gem hosted rewrite is pure Gemfile/lock text, so a gem ALREADY materialized under the project's bundle paths keeps its upstream bytes — the next `bundle install` prints `Using ` and never refetches, on **every** bundler major (live-verified 2026-08-19 on 1.17.3 / 2.7.2 / 4.0.18: bundler 4's CHECKSUMS verify at download time only, and nothing is downloaded; `bundle install --force`/`--redownload` re-install from the stale cached `.gem` instead of re-fetching — bundler 1 silently, bundler 4 with an exit-37 checksum refusal that still leaves the upstream bytes installed; the **verified** remedy is removing the installed dir + cache `.gem` + `specifications` entry, then `bundle install`). After the rewrite, a hosted run therefore probes the installed-gem discovery paths (the same ruby-crawler discovery `apply` uses, honoring `--global`/`--global-prefix` like scan's own discovery) for each confirmed gem redirect and judges the materialization against the patch record's `afterHash` file map. Judgment rules: records are found **by uuid** — this run's fetched records first, then the redirect ledger's persisted ones, so a transiently failed `/patches/view` fetch cannot retire the warning (it re-fires on every re-scan until the stale materialization is gone); a materialization with every file at `afterHash` is already patched and never warns (an agent→hosted migration stays quiet by construction), and when several confirmed variant purls resolve to one installed dir, ANY of them judging it patched keeps it quiet; staleness needs **positive evidence** — at least one record file whose bytes were actually read and hash to neither state's expectation — so missing or unreadable files never produce a warning. Warnings emit `redirect_gem_stale_install` (JSON `redirect.warnings[]` + a code-tagged stderr line) in three flavors: a PROJECT-LOCAL dir gets the verified delete-list remedy (installed dir, cache `.gem`, `specifications` entry — plus the project's committed `vendor/cache/.gem` when present and not proven to be the patched artifact, since bundler installs from `vendor/cache` in preference to fetching); a SHARED gem-env home gets a caveat that the home is shared machine-wide and prefers migrating the project to a local bundle path over deleting shared files; and a committed `vendor/cache` archive whose sha256 differs from the patched artifact's warns standalone even with no installed dir at all (a fresh checkout with a committed stale cache re-materializes the upstream bytes forever). A stale-flagged purl is additionally **excluded from the same run's `--vex` `assume_applied` set** — the envelope must never attest a CVE its own warning says is live; the purl falls back to normal installed-tree verification (a patched install still attests, a stale one is omitted). The probe is read-only (nothing is deleted) and skipped on `--dry-run` — deliberately explicit, since nothing was rewritten but the ledger fallback could otherwise judge an already-redirected project. Exit code and `status` are unchanged (warning-only, the hosted-refusal posture); a same-run `--vex` may still fail on "nothing to attest" per the embedded-VEX contract. @@ -140,6 +140,8 @@ The rewriter reads a fixed set of candidate files from the project root: the npm The hidden alias `--no-apply` on `get --save-only` is **part of the contract** — it does not appear in `--help` but is widely used in existing scripts. +**Python stale-install guard**: after a hosted redirect, `scan` / `get` use the Python crawler to inspect every matching installed package, including Poetry's out-of-tree virtualenvs and `--global-prefix`. A readable file that differs from the patch's `afterHash` emits `redirect_pypi_stale_install` in JSON `redirect.warnings[]` and human stderr. The probe changes no installed files, re-runs on idempotent scans, and falls back to persisted patch records when fresh record fetching fails. Missing/unreadable files alone do not prove staleness; lock-only checkouts stay quiet. Dry runs skip the probe. Same-run VEX excludes positively stale Python packages (qualifier-insensitive), even with `--vex-no-verify` or a healthy copy in another interpreter; if nothing remains to attest, the command exits 1 with `no_applicable_patches`. Reinstall from the rewritten lock in the affected interpreter and verify with `socket-patch vex`. + ### Embedded VEX (`apply --vex` / `scan --vex` / `vendor --vex`) `--vex ` folds OpenVEX 0.2.0 generation into `apply`, `scan`, and `vendor`: on a successful run the command writes the document to `` using the same engine as the standalone `vex` command. The `--vex-*` flags mirror `vex`'s `--product` / `--no-verify` / `--doc-id` / `--compact` knobs (namespaced to avoid colliding with the host command), and reuse the standalone env vars (`SOCKET_VEX_PRODUCT`, etc.). They are inert unless `--vex` is set. @@ -296,7 +298,7 @@ the model is **not uniform** today: One repo-root invocation discovers and configures every member. *Single level only* — see property 9's nested-workspace gap. - **cwd-only (single project):** gem, pypi, composer. The crawler inspects only the project - rooted at `--cwd` (pypi looks at `/.venv`; composer at the vendor tree); it does **not** + rooted at `--cwd` (pypi looks at `$VIRTUAL_ENV`, `/.venv` / `venv`, then a Poetry project's out-of-tree virtualenv(s) under Poetry's `virtualenvs.path`; composer at the vendor tree); it does **not** descend into sibling subprojects. A monorepo with several independent lockfiles in subdirectories (`backend/Gemfile.lock` + `frontend/Gemfile.lock`, multiple `.venv`, multiple `go.mod` / `composer.json`) is handled by invoking the tool **once per subproject** (`--cwd` each), as a @@ -573,7 +575,7 @@ to **six flavors**. | composer | package dir `/@/` | `composer.lock` only: entry's `dist` → `{type: "path", url, reference: null}`, `source` removed, `transport-options: {symlink: false}` added. `content-hash` unaffected; `composer.json` untouched | `composer install` (from the lock alone, real copy not symlink, works under `--network none`). `composer update ` reverts it | | gem | gem dir `-/` + gemspec materialized from `specifications/` | **Gemfile + Gemfile.lock pair**: the `gem` line gains `path:` (or a managed block for transitive deps); the lock's spec block moves GEM→PATH and the DEPENDENCIES entry becomes ` (= )!`, in bundler's exact canonical form | `bundle install` (normal **and** `BUNDLE_FROZEN=true`), byte-stable lock. Lock-only edits are a silent unpatch — hence the mandatory pair | | pypi / uv (uv.lock) | rebuilt wheel (canonical PEP 427 filename; RECORD regenerated) | `[tool.uv.sources] = {path}` in pyproject + surgical uv.lock rewrite; transitive deps via `[tool.uv] override-dependencies` | `uv sync --locked` / `--frozen --offline` (hash-verified, byte-stable lock) | -| pypi / poetry (poetry.lock 2.0/2.1) | (rebuilt wheel) | lock-only: the target `[[package]]` gets `[package.source] type="file"` + `files = [{file, hash: sha256-of-our-wheel}]`. pyproject + `metadata.content-hash` untouched | `poetry check --lock && poetry sync`, cold cache (hash fail-closed; byte-stable lock) | +| pypi / poetry (poetry.lock: legacy `[metadata.hashes]`, lock 1.0/1.1 `[metadata.files]`, 2.x `files`) | (rebuilt wheel) | lock-only: the target `[[package]]` gets `[package.source] type="file"` (+ `reference = ""` on the 0.12/1.0 layouts, which read it unconditionally) and the single `{file, hash: sha256-of-our-wheel}` entry in whichever table the generation keeps it. pyproject + `metadata.content-hash` untouched; CRLF locks keep their line endings. A lock written by Poetry < 1.4 emits `pypi_poetry_integrity_unverified` (that installer verifies no local hashes and skips an already-installed version) | `poetry check --lock && poetry sync`, cold cache (hash fail-closed from Poetry 1.4; byte-stable lock) — see `docs/testing/poetry-compatibility.md` | | pypi / pdm (pdm.lock) | (rebuilt wheel) | lock-only: the `[[package]]` gains the local-file `path` + `files[]` hash. pyproject + `content_hash` untouched. Non-fixture `[metadata] strategy` / hash-less locks refused | `pdm sync` (+ `pdm install --check`), cold cache | | pypi / pipenv (Pipfile.lock) | (rebuilt wheel) | lock-only: the `default`/`develop` entry → `{file, hashes:[sha256-of-our-wheel]}`. Pipfile + `_meta.hash` untouched. Emits `vendor_integrity_unverified` — pipenv does not hash-check file entries; the committed wheel bytes are the protection | `pipenv install --deploy` (+ `pipenv verify`), cold cache | | pypi / requirements.txt (pip / `uv pip`) | (rebuilt wheel) | pin line → `./ --hash=sha256:` (markers carried over; transitive deps appended) | `pip install -r` / `uv pip install -r` **run from the project root** (both resolve bare paths against the CWD) | @@ -606,7 +608,7 @@ worse, lets a warm cache silently serve unpatched bytes): | npm / bun | the packages-entry trailing `sha512-…` | recomputed from the tarball; tamper fails the frozen install | | gem | `CHECKSUMS` section (bundler ≥ 2.6 opt-in) | the vendored gem's entry rewritten to bundler's own path-gem form (bare `name (ver)`, sha256 token stripped) so re-locks stay byte-stable; original line in the ledger | | pypi / uv | `wheels[].hash`, `sdist.hash`, requires-dist specifiers | single `{filename, hash: sha256-of-our-wheel}`; sdist dropped; dropped specifiers ledgered for revert | -| pypi / poetry | `files = [{file, hash}]` | replaced with a single `{file, hash: sha256-of-our-wheel}` (poetry verifies the artifact against one listed hash; stale registry hashes removed) | +| pypi / poetry | `files = [{file, hash}]` (2.x) / `[metadata.files]` entry (1.0/1.1) / `[metadata.hashes]` entry (0.12) | replaced with a single `{file, hash: sha256-of-our-wheel}` (or the bare hash for 0.12) in the generation's own table (Poetry ≥ 1.4 verifies the artifact against one listed hash; older writers are flagged `pypi_poetry_integrity_unverified`; stale registry hashes removed) | | pypi / pdm | `[[package]].files[]` hashes | replaced with our wheel's sha256; hash-less locks refused (`pypi_pdm_lock_no_hashes`) | | pypi / pipenv | per-entry `hashes[]` | replaced with `["sha256:"]` — but pipenv does **not** enforce hashes on file entries (`vendor_integrity_unverified` warning); the committed wheel bytes are the actual protection | | pypi / requirements | `--hash=sha256:` | fresh hash of the rebuilt wheel always emitted (turns on pip's hash-checking for the line) | @@ -1058,7 +1060,7 @@ Every `--json` invocation emits a single JSON object that follows the **unified | `vendor_override_conflict` | `failed` | vendor (pnpm/yarn-berry): a user-authored override/resolution for the package already exists. | | `vendor_integrity_unverified` | `skipped` (warning) | vendor (pipenv): the lockfile format does not hash-check file entries; the committed wheel bytes are the protection. | | `vendor_content_mismatch_overwritten` | `skipped` (warning) | vendor: a staged file matched NEITHER beforeHash nor afterHash (patch built against different bytes, or local edits); the stage was overwritten with the verified patched content and the vendor succeeded. | -| `vendor_fetched_missing` | `skipped` (warning) | vendor: the package was not installed; its pristine artifact was fetched per the lockfile resolution (or staged from the committed vendor artifact), integrity-verified, and vendored — the project tree was not touched. | +| `vendor_fetched_missing` | `skipped` (warning) | vendor: the package was not installed; its pristine artifact was fetched per the lockfile resolution (or staged from the committed vendor artifact), integrity-verified, and vendored — the project tree was not touched. For `poetry.lock` (which records hashes but no URLs) the pure-Python wheel's sha256 selects the file through PyPI's JSON API (`SOCKET_PYPI_JSON_API` overrides the endpoint); Poetry 0.12's bare `[metadata.hashes]` names no wheel, so those locks still need an installed copy (`vendor_fetch_unverifiable`). | | `vendor_fetch_failed` | `failed` | vendor: the lockfile-resolved fetch was attempted and failed (HTTP error, size cap, integrity mismatch, or a PRESENT-but-corrupt committed artifact — pointed at `socket-patch repair`). A MISSING committed artifact no longer lands here: it falls through to the ledger-recovered registry fetch. Suppresses the duplicate `package_not_installed` skip. | | `vendor_fetch_unverifiable` | `skipped` (warning) | vendor: the lockfile records no usable integrity for the missing package; nothing was fetched (fail-closed) and the `package_not_installed` skip follows. | | `vendor_artifact_missing` | `skipped` (warning) / `failed` | vendor: the committed artifact is gone — the registry resolution is recovered from the ledger and the artifact rebuilt (warning); repair `--offline` with no local source surfaces it as the per-entry failure instead. | @@ -1069,8 +1071,12 @@ Every `--json` invocation emits a single JSON object that follows the **unified | `vendor_uuid_mismatch` | `skipped` | repair: the manifest's patch uuid moved past the vendored artifact — a re-vendor (`vendor` / `scan --vendor`) is pending; repair does not cross patch generations. | | `content_mismatch_overwritten` | `skipped` (warning) | apply (default policy): a file matched NEITHER beforeHash nor afterHash and was overwritten with the full verified patched content. `--strict` turns this case into a `failed` event instead. | | `vendor_lock_checksums_unsupported` / `vendor_stale_lock_checksum` | `failed` | vendor (gem): an ambiguous/platform CHECKSUMS entry, or a v1-wired lock whose stale token blocks the hot path (run `vendor --revert` + re-vendor). | +| `redirect_pypi_stale_install` | `redirect.warnings[]` (warning) | Hosted Python redirect: readable installed files differ from patched hashes. Read-only, repeated on re-scan, and excludes the package from same-run VEX. See the "Python stale-install guard" section. | | `redirect_gem_stale_install` | `redirect.warnings[]` (warning) | scan `--mode hosted` (gem): a stale UNPATCHED materialization (installed gem, or committed `vendor/cache` archive) that `bundle install` will reuse instead of fetching the redirected patch; the detail carries the verified remedy. Full rules and flavors: the "Gem stale-install guard" section. | | `pypi_{poetry,pdm,pipenv}_no_lockfile` | `failed` | vendor (pypi): a lock-less tool marker with no `requirements.txt` fallback — run ` lock`. | +| `pypi_poetry_integrity_unverified` | `skipped` (warning) | vendor (pypi / poetry): the lock was written by Poetry < 1.4 (0.12 `[metadata.hashes]`, lock 1.0/1.1, or a 2.0 lock without a `@generated by Poetry X.Y.Z` header — 1.3 wrote those). That installer does not verify local wheel hashes (the committed wheel bytes are the protection) and does not replace an already-installed package at the same version; recreate the virtualenv or `pip uninstall` the package before `poetry install`, or upgrade Poetry. | +| `redirect_poetry_stale_install_risk` | `redirect.warnings[]` (warning) | scan `--mode hosted` (poetry): same writer test as above — a warm virtualenv keeps the upstream package after the redirect on Poetry < 1.4 (1.4+ re-installs from the new source); fresh installs pick up the patched wheel. Emitted once per rewritten lock, only on the run that rewrites it. | +| `redirect_poetry_entry_not_found` / `redirect_poetry_missing_sha256` / `redirect_poetry_lock_unsupported` | `redirect.warnings[]` (warning) | scan `--mode hosted` (poetry): the lock has no `[[package]]` at the granted version (uv-parity twin of `redirect_uv_entry_not_found`); the grant carries no SHA-256 (gated once per dep, not per lock); the lock is refused — Poetry 0.12 layout (URL sources ignored), an unsupported `lock-version`, a forked package listed at several versions, a user-authored `[package.source]` on another origin (an earlier Socket URL for the same wheel is superseded in place), a malformed `[metadata.files]`/`[metadata.hashes]`, or a wheel whose filename does not match the locked package. Exit code and `status` unchanged (hosted-refusal posture). | | `vendor_prebuilt_stub_invalid` | `failed` / `skipped` (warning) | vendor (gem, `--vendor-source`): the served stub gemspec fails the rubygems `summary`/`authors` bar, so bundler would refuse the vendored path source at install time. `service`: refusal naming the missing attributes; `auto`: loud warning + local-build fallback — or, when the gem is also not installed locally (no stub to derive), a refusal naming the served defect and the install-the-gem remedy. | | `gem_spec_invalid` | `failed` | vendor (gem): the LOCAL `specifications/` stub gemspec fails the same rubygems `summary`/`authors` bar (a corrupted or hand-edited gem home); the refusal names the file — reinstall the gem (`gem pristine ` / fresh `bundle install`). | | `vendor_*` / `pypi_*` / `gemfile_*` / `lock_*` / `locked_version_mismatch` / `user_authored_*` / `native_extensions_unsupported` / `platform_gem_unsupported` | `failed`/`skipped` | vendor: per-ecosystem refusal + drift vocabulary; see the Vendor command contract section. New tags are additive (MINOR). | diff --git a/crates/socket-patch-cli/src/commands/scan/hosted.rs b/crates/socket-patch-cli/src/commands/scan/hosted.rs index 2060cac0..6419bb2d 100644 --- a/crates/socket-patch-cli/src/commands/scan/hosted.rs +++ b/crates/socket-patch-cli/src/commands/scan/hosted.rs @@ -11,8 +11,15 @@ use crate::commands::vex::generate_vex_from_manifest_path; use super::{discover_selected, ScanArgs}; +mod python; + /// Candidate lockfiles / registry configs the redirect rewriters may touch — /// read from the project when present and handed to `rewrite_registry_redirect`. +/// Fragment-edit kinds whose lockfile the package manager re-lays in place +/// (keeping the Socket source) — a re-scan REBASES their ledger edits instead +/// of appending; see the ledger merge below. +const REBASE_KINDS: &[&str] = &["redirect_poetry_lock_package"]; + const REDIRECT_CANDIDATE_FILES: &[&str] = &[ "package-lock.json", "npm-shrinkwrap.json", @@ -27,6 +34,7 @@ const REDIRECT_CANDIDATE_FILES: &[&str] = &[ "bun.lock", "requirements.txt", "uv.lock", + "poetry.lock", "pyproject.toml", "Cargo.toml", "Cargo.lock", @@ -386,14 +394,13 @@ fn build_redirect_json_envelope( result } -/// The gem stale-install probe's outcome: warnings for both output channels, +/// The installed-tree probes' outcome: warnings for both output channels, /// plus the stale purls STRUCTURALLY, so the same-run `--vex` can exclude /// them from `assume_applied` — an envelope must never attest a CVE its own -/// warnings say is live. Excluded purls fall back to `vex`'s normal -/// installed-tree verification: a patched install still attests (with hash -/// evidence), a stale one is omitted. +/// warnings say is live. Python also carries positive evidence through VEX +/// so a different, healthy interpreter cannot mask a stale installation. #[derive(Default)] -struct GemStaleOutcome { +struct StaleInstallOutcome { warnings: Vec, stale_purls: std::collections::BTreeSet, } @@ -487,13 +494,13 @@ fn gem_stale_cache_warning(purl: &str, cache_path: &Path) -> serde_json::Value { /// already-patched install must not produce a delete prescription. /// (`current_hash` is `Some` only when the bytes were really hashed, which /// also excludes the absent-new-file `Ready`.) -async fn gem_stale_positive_evidence( - gem_dir: &Path, +async fn installed_stale_positive_evidence( + package_dir: &Path, record: &socket_patch_core::manifest::schema::PatchRecord, ) -> bool { use socket_patch_core::patch::apply::{verify_file_patch, VerifyStatus}; for (file_name, info) in &record.files { - let result = verify_file_patch(gem_dir, file_name, info).await; + let result = verify_file_patch(package_dir, file_name, info).await; if matches!( result.status, VerifyStatus::Ready | VerifyStatus::HashMismatch @@ -527,7 +534,7 @@ async fn gem_stale_positive_evidence( /// Judgments are grouped BY INSTALLED DIR: platform-variant purls of one /// gem resolve to the same dir, and if ANY variant's record proves the /// dir patched, the dir is patched — never warned. -/// * STALE requires [`gem_stale_positive_evidence`] — never inferred from +/// * STALE requires [`installed_stale_positive_evidence`] — never inferred from /// missing/unreadable files. /// * A committed `vendor/cache/.gem` whose sha256 differs from the /// patched artifact's is stale too (bundler installs from it first, fresh @@ -547,14 +554,14 @@ async fn gem_stale_install_warnings( socket_patch_core::manifest::schema::PatchRecord, >, gem_artifact_shas: &std::collections::BTreeMap<(String, String), String>, -) -> GemStaleOutcome { +) -> StaleInstallOutcome { use socket_patch_core::crawlers::types::CrawlerOptions; use socket_patch_core::crawlers::RubyCrawler; use socket_patch_core::manifest::schema::PatchRecord; use socket_patch_core::vendor::file_sha256_hex; use socket_patch_core::vex::verify::verify_patch_record; - let mut out = GemStaleOutcome::default(); + let mut out = StaleInstallOutcome::default(); let find_record = |uuid: &str| -> Option<&PatchRecord> { records .values() @@ -618,7 +625,7 @@ async fn gem_stale_install_warnings( }); if verify_patch_record(&pkg.path, record).await.is_ok() { entry.patched = true; - } else if !entry.positive && gem_stale_positive_evidence(&pkg.path, record).await { + } else if !entry.positive && installed_stale_positive_evidence(&pkg.path, record).await { entry.positive = true; entry.purl = (*purl).to_string(); } @@ -1816,8 +1823,66 @@ pub(crate) async fn run_redirect_selected( ledger.mode = "hosted".to_string(); // The bun.lockb→bun.lock migration removal precedes the rewrite // edits so `--revert` unwinds it last (after restoring bun.lock). + // + // REBASE instead of append for fragment kinds whose file the + // package manager itself rewrites in place: when the ledger already + // holds edits for the same (path, kind, key) and the file no longer + // carried their `new` fragments before this run (Poetry 1.1/1.2 + // `poetry lock --no-update` keeps the Socket source but re-lays the + // unit and drops the inserted `files` line), appending this run's + // edits — recorded against the RELOCKED text — would build a chain + // whose older links match nothing: rollback and remove then refuse + // forever, and the refusal's own remedy ("re-run scan") is what + // lengthened the chain. Keeping the oldest `original` (the pristine + // fragment) and adopting the fresh `new` keeps the chain a single + // invertible link: replay swaps the fragment this run wrote back to + // the fragment the very first run found. + let mut rebased: Vec = Vec::new(); + for edit in rewrite.edits.iter().filter(|e| REBASE_KINDS.contains(&e.kind.as_str())) { + let siblings: Vec = ledger + .edits + .iter() + .enumerate() + .filter(|(_, old)| { + old.path == edit.path && old.kind == edit.kind && old.key == edit.key + }) + .map(|(i, _)| i) + .collect(); + let before = files.get(&edit.path).map(String::as_str).unwrap_or(""); + let drifted = !siblings.is_empty() + && siblings.iter().all(|&i| { + ledger.edits[i] + .new + .as_ref() + .and_then(serde_json::Value::as_str) + .is_none_or(|new| !before.contains(new)) + }); + if !drifted { + continue; + } + // Positional pairing: the rewriter emits a key's fragments in a + // fixed order (package unit, then the legacy integrity entry). + let nth = rewrite + .edits + .iter() + .filter(|e| e.path == edit.path && e.kind == edit.kind && e.key == edit.key) + .position(|e| std::ptr::eq(e, edit)) + .unwrap_or(0); + if let Some(&target) = siblings.get(nth) { + if !rebased.contains(&target) { + ledger.edits[target].new = edit.new.clone(); + ledger.edits[target].action = edit.action.clone(); + rebased.push(target); + } + } + } for edit in migration_edits.iter().chain(rewrite.edits.iter()) { - if !ledger.edits.contains(edit) { + let is_rebased = REBASE_KINDS.contains(&edit.kind.as_str()) + && rebased.iter().any(|&t| { + let old = &ledger.edits[t]; + old.path == edit.path && old.kind == edit.kind && old.key == edit.key && old.new == edit.new + }); + if !is_rebased && !ledger.edits.contains(edit) { ledger.edits.push(edit.clone()); } } @@ -1869,8 +1934,8 @@ pub(crate) async fn run_redirect_selected( // the probe's ledger-record fallback could still judge an // already-redirected project, so without this gate a dry-run would warn // about state the run did not (re)create. - let gem_stale: GemStaleOutcome = if common.dry_run { - GemStaleOutcome::default() + let gem_stale: StaleInstallOutcome = if common.dry_run { + StaleInstallOutcome::default() } else { // purl-coordinate → the PATCHED .gem artifact's sha256 (registry // override identifier, tarball integrity fallback) — judges a @@ -1899,6 +1964,12 @@ pub(crate) async fn run_redirect_selected( .await }; + let python_stale = if common.dry_run { + StaleInstallOutcome::default() + } else { + python::stale_install_warnings(common, &confirmed, &records, &ledger_records).await + }; + // Cross-mode takeover: a committed vendored ledger (`.socket/vendor/state.json`) // may still claim package(s) this project also has a hosted redirect ledger // for — their tarballs would then be orphaned and that ledger stale. But the @@ -1958,8 +2029,13 @@ pub(crate) async fn run_redirect_selected( params.assume_applied = confirmed .iter() .map(|(purl, _)| purl.clone()) - .filter(|purl| !gem_stale.stale_purls.contains(purl)) + .filter(|purl| { + !gem_stale.stale_purls.contains(purl) && !python_stale.stale_purls.contains(purl) + }) .collect(); + // A healthy copy in another interpreter must not override a stale + // Python tree found by the probe, including with --vex-no-verify. + params.known_stale = python_stale.stale_purls.iter().cloned().collect(); let manifest_path = common.resolved_manifest_path(); match generate_vex_from_manifest_path(common, ¶ms, &manifest_path).await { Ok(summary) => vex_statements = Some(summary.statements), @@ -1985,6 +2061,7 @@ pub(crate) async fn run_redirect_selected( warnings.extend(rush_warnings.iter().cloned()); warnings.extend(pnpm_warnings.iter().cloned()); warnings.extend(gem_stale.warnings.iter().cloned()); + warnings.extend(python_stale.warnings.iter().cloned()); warnings.extend(takeover_pre_warnings.iter().cloned()); warnings.extend(takeover_warnings.iter().cloned()); warnings.extend(prune_warnings.iter().cloned()); @@ -2062,7 +2139,7 @@ pub(crate) async fn run_redirect_selected( for w in &pnpm_warnings { eprintln!(" warning: {}", w["detail"].as_str().unwrap_or_default()); } - for w in &gem_stale.warnings { + for w in gem_stale.warnings.iter().chain(&python_stale.warnings) { // Code included: the stale-install hazard is a silent-CVE // state, so the stderr line must be greppable by its stable // code in CI logs, same as the JSON envelope. @@ -2132,7 +2209,7 @@ pub(crate) fn boxed_run_redirect_selected<'a>( mod tests { use super::{ build_redirect_json_envelope, gem_stale_cache_warning, gem_stale_install_warning, - gem_stale_install_warnings, gem_stale_positive_evidence, parse_purl_simple, + gem_stale_install_warnings, installed_stale_positive_evidence, parse_purl_simple, plan_workspace_trust, pnpm_heal_root, pnpm_lock_carries_hosted_redirect, pnpm_lock_version_major, pnpm_trust_configured_detail, pnpm_trust_legacy_detail, pnpm_trust_manual_guidance, pnpm_trust_workspace_unreadable_detail, @@ -2684,7 +2761,7 @@ mod tests { cwd: &std::path::Path, confirmed: &[(String, String)], records: &std::collections::BTreeMap, - ) -> super::GemStaleOutcome { + ) -> super::StaleInstallOutcome { gem_stale_install_warnings( cwd, false, @@ -2792,7 +2869,7 @@ mod tests { /// transiently unreadable file in a patched install must not produce /// a delete prescription. #[tokio::test] - async fn gem_stale_positive_evidence_requires_readable_mismatched_bytes() { + async fn installed_stale_positive_evidence_requires_readable_mismatched_bytes() { let tmp = tempfile::tempdir().unwrap(); let record = gem_record(); @@ -2800,30 +2877,30 @@ mod tests { let upstream = tmp.path().join("upstream"); std::fs::create_dir_all(upstream.join("lib")).unwrap(); std::fs::write(upstream.join("lib").join("stale_unit.rb"), GEM_UPSTREAM).unwrap(); - assert!(gem_stale_positive_evidence(&upstream, &record).await); + assert!(installed_stale_positive_evidence(&upstream, &record).await); // Tampered bytes (neither hash) → evidence. let tampered = tmp.path().join("tampered"); std::fs::create_dir_all(tampered.join("lib")).unwrap(); std::fs::write(tampered.join("lib").join("stale_unit.rb"), b"other").unwrap(); - assert!(gem_stale_positive_evidence(&tampered, &record).await); + assert!(installed_stale_positive_evidence(&tampered, &record).await); // Patched bytes → no evidence. let patched = tmp.path().join("patched"); std::fs::create_dir_all(patched.join("lib")).unwrap(); std::fs::write(patched.join("lib").join("stale_unit.rb"), GEM_PATCHED).unwrap(); - assert!(!gem_stale_positive_evidence(&patched, &record).await); + assert!(!installed_stale_positive_evidence(&patched, &record).await); // Missing file → no evidence (never a guess). let hollow = tmp.path().join("hollow"); std::fs::create_dir_all(hollow.join("lib")).unwrap(); - assert!(!gem_stale_positive_evidence(&hollow, &record).await); + assert!(!installed_stale_positive_evidence(&hollow, &record).await); // A DIRECTORY at the file path (the unreadable-NotFound class) → // no evidence. let blocked = tmp.path().join("blocked"); std::fs::create_dir_all(blocked.join("lib").join("stale_unit.rb")).unwrap(); - assert!(!gem_stale_positive_evidence(&blocked, &record).await); + assert!(!installed_stale_positive_evidence(&blocked, &record).await); // Absent new-file (empty beforeHash routes to Ready with NO // current_hash) → no evidence. @@ -2833,7 +2910,7 @@ mod tests { .get_mut("lib/stale_unit.rb") .expect("fixture file entry") .before_hash = String::new(); - assert!(!gem_stale_positive_evidence(&hollow, &new_file).await); + assert!(!installed_stale_positive_evidence(&hollow, &new_file).await); } /// The probe end to end over a real deployment layout: a STALE diff --git a/crates/socket-patch-cli/src/commands/scan/hosted/python.rs b/crates/socket-patch-cli/src/commands/scan/hosted/python.rs new file mode 100644 index 00000000..37433a6c --- /dev/null +++ b/crates/socket-patch-cli/src/commands/scan/hosted/python.rs @@ -0,0 +1,172 @@ +//! Read-only installed-byte checks for hosted Python redirects. + +use std::collections::{BTreeMap, BTreeSet}; + +use socket_patch_core::crawlers::{types::CrawlerOptions, PythonCrawler}; +use socket_patch_core::manifest::schema::PatchRecord; +use socket_patch_core::utils::purl::strip_purl_qualifiers; +use socket_patch_core::vex::verify::verify_patch_record; + +use super::{installed_stale_positive_evidence, StaleInstallOutcome}; + +/// A lock rewrite cannot prove a warm virtualenv has installed the wheel. +/// Use the same discovery as apply (including Poetry's out-of-tree venvs), +/// and inspect every interpreter rather than deduplicating by package name. +/// Missing/unreadable files are not positive evidence of stale bytes. +pub(super) async fn stale_install_warnings( + common: &crate::args::GlobalArgs, + confirmed: &[(String, String)], + records: &BTreeMap, + ledger_records: &BTreeMap, +) -> StaleInstallOutcome { + let mut out = StaleInstallOutcome::default(); + let candidates: Vec<_> = confirmed + .iter() + .filter(|(purl, _)| purl.starts_with("pkg:pypi/")) + .filter_map(|(purl, uuid)| { + records + .values() + .chain(ledger_records.values()) + .find(|record| &record.uuid == uuid) + .filter(|record| !record.files.is_empty()) + .map(|record| (purl, record)) + }) + .collect(); + if candidates.is_empty() { + return out; + } + + let crawler = PythonCrawler::new(); + let paths = crawler + .get_site_packages_paths(&CrawlerOptions { + cwd: common.cwd.clone(), + global: common.global, + global_prefix: common.global_prefix.clone(), + }) + .await + .unwrap_or_default(); + + #[derive(Default)] + struct Judgment { + purls: BTreeSet, + patched: bool, + stale: bool, + } + // Python distributions share site-packages, so the key must include + // the package identity. Any matching artifact variant can prove this + // package patched; a healthy *different* package cannot. + let mut judgments = BTreeMap::new(); + for (purl, record) in candidates { + let base = strip_purl_qualifiers(purl).to_string(); + for site in &paths { + let found = crawler + .find_by_purls(site, std::slice::from_ref(&base)) + .await + .unwrap_or_default(); + let Some(pkg) = found.get(&base) else { + continue; + }; + let judgment: &mut Judgment = judgments + .entry((pkg.path.clone(), pkg.name.clone(), pkg.version.clone())) + .or_default(); + judgment.purls.insert(purl.clone()); + if verify_patch_record(&pkg.path, record).await.is_ok() { + judgment.patched = true; + } else if installed_stale_positive_evidence(&pkg.path, record).await { + judgment.stale = true; + } + } + } + for ((site, _, _), judgment) in judgments { + if judgment.patched || !judgment.stale { + continue; + } + for purl in judgment.purls { + out.warnings.push(serde_json::json!({ + "code": "redirect_pypi_stale_install", + "detail": format!( + "{purl} was redirected to a hosted patch, but installed files in {} \ + still differ from the patched hashes. Reinstall from the rewritten \ + lock in this interpreter and verify with `socket-patch vex`. \ + Poetry before 1.4 may keep same-version packages: recreate the \ + project virtualenv or uninstall this package before installing. \ + The installed files were left unchanged.", + site.display() + ), + })); + out.stale_purls.insert(purl); + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + use socket_patch_core::hash::git_sha256::compute_git_sha256_from_bytes; + use socket_patch_core::manifest::schema::PatchFileInfo; + + fn record(uuid: &str, file: &str, patched: &[u8]) -> PatchRecord { + PatchRecord { + uuid: uuid.to_string(), + exported_at: "2026-09-17T00:00:00Z".to_string(), + files: [( + file.to_string(), + PatchFileInfo { + before_hash: compute_git_sha256_from_bytes(b"upstream"), + after_hash: compute_git_sha256_from_bytes(patched), + }, + )] + .into(), + vulnerabilities: Default::default(), + description: String::new(), + license: String::new(), + tier: "free".to_string(), + } + } + + #[tokio::test] + async fn prefix_probe_groups_variants_by_package_and_uses_ledger_records() { + let tmp = tempfile::tempdir().unwrap(); + let site = tmp.path().join("custom-site-packages"); + for name in ["first", "second"] { + std::fs::create_dir_all(site.join(format!("{name}-1.0.dist-info"))).unwrap(); + } + std::fs::write(site.join("first.py"), b"upstream").unwrap(); + std::fs::write(site.join("second.py"), b"patched").unwrap(); + let common = crate::args::GlobalArgs { + cwd: tmp.path().to_path_buf(), + global_prefix: Some(site.clone()), + ..Default::default() + }; + let first = "pkg:pypi/first@1.0?artifact_id=one"; + let second = "pkg:pypi/second@1.0"; + let mut confirmed = vec![ + (first.to_string(), "first-uuid".to_string()), + (second.to_string(), "second-uuid".to_string()), + ]; + // Deliberately unrelated keys: lookup must use each record's UUID. + let mut ledger = BTreeMap::from([ + ("one".into(), record("first-uuid", "first.py", b"patched")), + ("two".into(), record("second-uuid", "second.py", b"patched")), + ]); + let out = stale_install_warnings(&common, &confirmed, &BTreeMap::new(), &ledger).await; + assert_eq!(out.stale_purls, BTreeSet::from([first.to_string()])); + assert_eq!(out.warnings.len(), 1); + assert!(out.warnings[0]["detail"] + .as_str() + .unwrap() + .contains(&site.display().to_string())); + + // An installed package can match a different wheel variant. That + // variant, unlike the healthy sibling distribution, proves it patched. + confirmed.push(( + "pkg:pypi/first@1.0?artifact_id=two".into(), + "variant".into(), + )); + ledger.insert("three".into(), record("variant", "first.py", b"upstream")); + let out = stale_install_warnings(&common, &confirmed, &BTreeMap::new(), &ledger).await; + assert!(out.stale_purls.is_empty()); + assert!(out.warnings.is_empty()); + } +} diff --git a/crates/socket-patch-cli/src/commands/vex.rs b/crates/socket-patch-cli/src/commands/vex.rs index beda9b55..fdfecdf5 100644 --- a/crates/socket-patch-cli/src/commands/vex.rs +++ b/crates/socket-patch-cli/src/commands/vex.rs @@ -157,6 +157,7 @@ impl VexEmbedArgs { doc_id: self.vex_doc_id.clone(), compact: self.vex_compact, assume_applied: Vec::new(), + known_stale: Vec::new(), } } } @@ -180,6 +181,9 @@ pub(crate) struct VexBuildParams { /// standalone `vex` passes an empty list so redirected patches are then /// hash-verified against the installed tree like any applied patch. pub assume_applied: Vec, + /// Hosted probes positively identified unpatched installed bytes. These + /// PURLs cannot be attested by another interpreter or --no-verify. + pub known_stale: Vec, } /// Successful result of [`generate_vex`]. @@ -233,6 +237,7 @@ pub async fn run(args: VexArgs) -> i32 { doc_id: args.doc_id.clone(), compact: args.compact, assume_applied: Vec::new(), + known_stale: Vec::new(), }; let manifest_path = args.common.resolved_manifest_path(); @@ -397,16 +402,47 @@ async fn generate_vex( // record the run did not re-confirm (a reverted lockfile or a withdrawn // patch must not keep attesting). if !params.assume_applied.is_empty() { - let exempt: std::collections::HashSet<&str> = - params.assume_applied.iter().map(|s| s.as_str()).collect(); - outcome.failed.retain(|f| !exempt.contains(f.purl.as_str())); - for purl in ¶ms.assume_applied { - if manifest.patches.contains_key(purl) && !outcome.applied.iter().any(|p| p == purl) { - outcome.applied.push(purl.clone()); + use socket_patch_core::utils::purl::strip_purl_qualifiers; + // The confirmed purls come from the grant reference (unqualified — + // `pkg:pypi/urllib3@1.26.18`) while the ledger records the API's + // artifact-qualified purl (`…?artifact_id=py2-py3-none-any-whl`), so + // match on the qualifier-stripped form: a lock-only pypi redirect used + // to attest nothing and fail the same-run `--vex` with + // `no_applicable_patches`. + let exempt: std::collections::HashSet<&str> = params + .assume_applied + .iter() + .map(|s| strip_purl_qualifiers(s)) + .collect(); + let is_exempt = |purl: &str| exempt.contains(strip_purl_qualifiers(purl)); + outcome.failed.retain(|f| !is_exempt(&f.purl)); + for key in manifest.patches.keys() { + if is_exempt(key) && !outcome.applied.iter().any(|p| p == key) { + outcome.applied.push(key.clone()); } } } + // Positive evidence from a hosted probe takes precedence over an + // assumed redirect or a healthy copy found in a different interpreter. + if !params.known_stale.is_empty() { + use socket_patch_core::utils::purl::strip_purl_qualifiers; + let stale: std::collections::HashSet<&str> = params + .known_stale + .iter() + .map(|purl| strip_purl_qualifiers(purl)) + .collect(); + let is_stale = |purl: &str| stale.contains(strip_purl_qualifiers(purl)); + outcome.applied.retain(|purl| !is_stale(purl)); + outcome.failed.retain(|failure| !is_stale(&failure.purl)); + outcome.failed.extend(manifest.patches.keys().filter(|purl| is_stale(purl)).map( + |purl| FailedPatch { + purl: purl.clone(), + reason: "stale_install".to_string(), + }, + )); + } + // Vendored disclosure: the committed artifact verified (the attestation // stands — the committables are what the lockfile consumes) but the LIVE // installed tree is present and running different bytes. Say so — a diff --git a/crates/socket-patch-cli/tests/in_process_redirect_poetry.rs b/crates/socket-patch-cli/tests/in_process_redirect_poetry.rs new file mode 100644 index 00000000..5daf55a7 --- /dev/null +++ b/crates/socket-patch-cli/tests/in_process_redirect_poetry.rs @@ -0,0 +1,534 @@ +//! In-process CLI test for `scan --mode hosted` on a Poetry project: mocks the +//! API (discovery + reference + view) via wiremock, lays down a native +//! `poetry.lock` (the committed Poetry 2.4.3 fixture) with NO installed +//! package — the lock-only fresh-checkout / CI shape — and asserts the lock is +//! repointed at the hosted wheel, the redirect ledger is written, the same-run +//! `--vex` attests the redirect, a re-scan is idempotent, and `rollback` +//! restores every byte. The rewriter bytes themselves are pinned by the core +//! `poetry_hosted` tests; this covers the CLI wiring around them. + +use std::path::Path; + +use serial_test::serial; +use socket_patch_cli::args::GlobalArgs; +use socket_patch_cli::commands::rollback::{self, RollbackArgs}; +use socket_patch_cli::commands::scan::{run, ScanArgs}; +use socket_patch_cli::commands::vex::VexEmbedArgs; +use socket_patch_core::hash::git_sha256::compute_git_sha256_from_bytes; +use wiremock::matchers::{method, path, path_regex}; +use wiremock::{Mock, MockServer, ResponseTemplate}; + +const ORG: &str = "test-org"; +/// Discovery names the base purl (the lockfile supplement's spelling)… +const PURL: &str = "pkg:pypi/urllib3@1.26.18"; +/// …while the patch record carries the API's artifact-qualified purl, which +/// is what the redirect ledger is keyed by. +const RECORD_PURL: &str = "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl"; +const UUID: &str = "e828efa5-5c6d-43f3-9909-03f5ac232b98"; +const HOSTED_URL: &str = "http://patch.test/patch/pypi/urllib3/1.26.18/22222222-2222-4222-8222-222222222222/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl"; +const GHSA: &str = "GHSA-gm62-xv2j-4w53"; +const UPSTREAM: &[u8] = b"upstream response implementation\n"; +const PATCHED: &[u8] = b"patched response implementation\n"; + +const LOCK: &str = include_str!("../../socket-patch-core/tests/fixtures/poetry/2.4.3/poetry.lock"); +/// Poetry 1.2.2's native lock (lock-version 1.1, populated `[metadata.files]`). +const LOCK_1_1: &str = + include_str!("../../socket-patch-core/tests/fixtures/poetry/1.2.2/poetry.lock"); +const PYPROJECT: &str = + include_str!("../../socket-patch-core/tests/fixtures/poetry/2.4.3/pyproject.toml"); + +fn sha256() -> String { + "c".repeat(64) +} + +fn global(cwd: &Path, api_url: String) -> GlobalArgs { + GlobalArgs { + cwd: cwd.to_path_buf(), + org: Some(ORG.to_string()), + api_token: Some("fake".to_string()), + api_url: Some(api_url), + json: true, + yes: true, + ..GlobalArgs::default() + } +} + +fn hosted_args(cwd: &Path, api_url: String, vex: Option<&Path>) -> ScanArgs { + ScanArgs { + paths: Vec::new(), + common: global(cwd, api_url), + batch_size: 100, + apply: false, + prune: false, + sync: false, + vendor: false, + detached: false, + redirect: true, + mode: None, + all_releases: false, + vex: VexEmbedArgs { + vex: vex.map(Path::to_path_buf), + ..Default::default() + }, + } +} + +async fn mock_api(server: &MockServer) { + Mock::given(method("POST")) + .and(path(format!("/v0/orgs/{ORG}/patches/batch"))) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "packages": [{ + "purl": PURL, + "patches": [{ + "uuid": UUID, "purl": RECORD_PURL, "tier": "free", + "cveIds": ["CVE-2025-66418"], "ghsaIds": [GHSA], "severity": "HIGH", + "title": "poetry redirect fixture" + }] + }], + "canAccessPaidPatches": false, + }))) + .mount(server) + .await; + Mock::given(method("GET")) + .and(path_regex(format!( + "^/v0/orgs/{ORG}/patches/by-package/.+$" + ))) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "patches": [{ + "uuid": UUID, "purl": RECORD_PURL, + "publishedAt": "2026-07-29T20:20:47Z", + "description": "x", "license": "MIT", "tier": "free", + "vulnerabilities": {} + }], + "canAccessPaidPatches": false, + }))) + .mount(server) + .await; + Mock::given(method("POST")) + .and(path(format!("/v0/orgs/{ORG}/patches/package"))) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "results": { + UUID: { + "status": "granted", + "url": HOSTED_URL, + "purl": PURL, + "artifacts": [{ + "kind": "tarball", + "url": HOSTED_URL, + "integrity": { "sha256": sha256() } + }], + "registryOverride": null + } + } + }))) + .mount(server) + .await; + Mock::given(method("GET")) + .and(path(format!("/v0/orgs/{ORG}/patches/view/{UUID}"))) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "uuid": UUID, + "purl": RECORD_PURL, + "publishedAt": "2026-07-29T20:20:47Z", + "files": { + "urllib3/response.py": { + "beforeHash": compute_git_sha256_from_bytes(UPSTREAM), + "afterHash": compute_git_sha256_from_bytes(PATCHED), + } + }, + "vulnerabilities": { + GHSA: { + "cves": ["CVE-2025-66418"], + "summary": "poetry redirect vex fixture", + "severity": "HIGH", + "description": "d" + } + }, + "description": "x", "license": "MIT", "tier": "free" + }))) + .mount(server) + .await; +} + +/// A Poetry project with nothing installed: the lock is the only source of +/// the dependency. An EMPTY in-project venv keeps the crawl hermetic (without +/// it the project-marker fallback would walk this machine's global +/// interpreters). +fn write_project(root: &Path) { + std::fs::write(root.join("pyproject.toml"), PYPROJECT).unwrap(); + std::fs::write(root.join("poetry.lock"), LOCK).unwrap(); + let site = if cfg!(windows) { + root.join(".venv").join("Lib").join("site-packages") + } else { + root.join(".venv") + .join("lib") + .join("python3.12") + .join("site-packages") + }; + std::fs::create_dir_all(site).unwrap(); +} + +fn read(path: &Path) -> String { + std::fs::read_to_string(path).unwrap() +} + +#[tokio::test] +#[serial] +async fn lock_only_poetry_project_redirects_attests_rescans_and_rolls_back() { + let server = MockServer::start().await; + mock_api(&server).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path()); + let lock_path = tmp.path().join("poetry.lock"); + let vex_path = tmp.path().join("out.vex.json"); + + // 1. Hosted redirect with same-run --vex on the lock-only checkout. + let code = run(hosted_args(tmp.path(), server.uri(), Some(&vex_path))).await; + assert_eq!(code, 0, "hosted redirect + same-run vex must succeed"); + let redirected = read(&lock_path); + assert!(redirected.contains(HOSTED_URL), "{redirected}"); + assert!( + redirected.contains(&format!("hash = \"sha256:{}\"", sha256())), + "{redirected}" + ); + assert!(redirected.contains("type = \"url\""), "{redirected}"); + assert_eq!( + read(&tmp.path().join("pyproject.toml")), + PYPROJECT, + "pyproject untouched" + ); + let ledger: serde_json::Value = serde_json::from_str(&read( + &tmp.path().join(".socket/vendor/redirect-state.json"), + )) + .unwrap(); + assert!( + ledger["records"][RECORD_PURL].is_object(), + "ledger keyed by the artifact-qualified purl: {ledger}" + ); + assert_eq!( + ledger["edits"][0]["kind"].as_str(), + Some("redirect_poetry_lock_package"), + "{ledger}" + ); + // The redirect is attested from the ledger (assume_applied) even though + // the base purl the run confirmed differs from the record's qualified + // purl only by its `?artifact_id=` qualifier. + let vex: serde_json::Value = serde_json::from_str(&read(&vex_path)).unwrap(); + let statements = vex["statements"].as_array().expect("statements"); + assert_eq!(statements.len(), 1, "{vex}"); + assert_eq!( + statements[0]["vulnerability"]["name"].as_str(), + Some(GHSA), + "{vex}" + ); + + // 2. Idempotent re-scan: no further edits, lock byte-identical. + let code = run(hosted_args(tmp.path(), server.uri(), None)).await; + assert_eq!(code, 0); + assert_eq!( + read(&lock_path), + redirected, + "re-scan must not touch the lock" + ); + + // 3. rollback unwinds the redirect and drops the record. + let code = rollback::run(RollbackArgs { + targets: Vec::new(), + common: global(tmp.path(), server.uri()), + one_off: false, + preserve_state: false, + }) + .await; + assert_eq!(code, 0, "rollback must succeed"); + assert_eq!( + read(&lock_path), + LOCK, + "rollback must restore the pristine lock byte for byte" + ); + let ledger_path = tmp.path().join(".socket/vendor/redirect-state.json"); + if ledger_path.exists() { + let ledger: serde_json::Value = serde_json::from_str(&read(&ledger_path)).unwrap(); + assert!( + ledger["records"] + .as_object() + .is_none_or(|records| records.is_empty()), + "no redirect record may survive rollback: {ledger}" + ); + } +} + +/// What `poetry lock --no-update` on Poetry 1.1 / 1.2 does to a redirected +/// lock-1.1 unit: keeps `[package.source]`, drops the inserted package-level +/// `files` line, and re-lays the `[metadata.files]` entry from the CLI's +/// inline table into Poetry's multi-line array. +fn simulate_poetry_1x_relock(lock: &str) -> String { + let newline = if lock.contains("\r\n") { "\r\n" } else { "\n" }; + let mut out = String::new(); + for line in lock.lines() { + if line.starts_with("files = [{ file = ") { + continue; + } + if let Some(rest) = line.strip_prefix("urllib3 = [{ ") { + let inner = rest.trim_end_matches(" }]"); + out.push_str("urllib3 = [\n {"); + out.push_str(inner); + out.push_str("},\n]\n"); + continue; + } + out.push_str(line); + out.push('\n'); + } + out.replace("\n", newline) +} + +/// Relock → re-scan → rollback must still land on the pristine lock. The +/// re-scan REBASES the ledger's edits (pristine → freshly written) instead of +/// appending edits recorded against the relocked text, whose older links +/// would match nothing and make rollback (and remove) refuse forever. +#[tokio::test] +#[serial] +async fn relock_then_rescan_keeps_rollback_invertible() { + // Checkout settings must not decide which newline shape this test covers. + for newline in ["\n", "\r\n"] { + let lock = LOCK_1_1.replace("\r\n", "\n").replace("\n", newline); + assert_relock_roundtrip(&lock).await; + } +} + +async fn assert_relock_roundtrip(lock: &str) { + let server = MockServer::start().await; + mock_api(&server).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path()); + let lock_path = tmp.path().join("poetry.lock"); + std::fs::write(&lock_path, lock).unwrap(); + + assert_eq!(run(hosted_args(tmp.path(), server.uri(), None)).await, 0); + let redirected = read(&lock_path); + let ledger_path = tmp.path().join(".socket/vendor/redirect-state.json"); + let ledger: serde_json::Value = serde_json::from_str(&read(&ledger_path)).unwrap(); + assert_eq!( + ledger["edits"].as_array().unwrap().len(), + 2, + "package + metadata fragments" + ); + + let relocked = simulate_poetry_1x_relock(&redirected); + assert_ne!(relocked, redirected); + assert!(relocked.contains(HOSTED_URL), "relock keeps the source"); + std::fs::write(&lock_path, &relocked).unwrap(); + + // Re-scan: the unit lost its `files` line, so the rewriter writes again. + assert_eq!(run(hosted_args(tmp.path(), server.uri(), None)).await, 0); + let rescanned = read(&lock_path); + assert_ne!( + rescanned, relocked, + "the re-scan must restore the package files entry" + ); + let ledger: serde_json::Value = serde_json::from_str(&read(&ledger_path)).unwrap(); + let edits = ledger["edits"].as_array().unwrap(); + assert_eq!(edits.len(), 2, "rebased, not appended: {ledger}"); + for edit in edits { + let original = edit["original"].as_str().unwrap(); + assert!( + !original.contains(HOSTED_URL), + "originals stay pristine: {original}" + ); + let new = edit["new"].as_str().unwrap(); + assert!( + rescanned.contains(new), + "new fragments describe the current lock" + ); + } + + let code = rollback::run(RollbackArgs { + targets: Vec::new(), + common: global(tmp.path(), server.uri()), + one_off: false, + preserve_state: false, + }) + .await; + assert_eq!(code, 0, "rollback after relock + re-scan must succeed"); + assert_eq!( + read(&lock_path), + lock, + "pristine lock restored byte for byte" + ); +} + +fn install_package(root: &Path, venv: &str, bytes: &[u8]) -> std::path::PathBuf { + let site = if cfg!(windows) { + root.join(venv).join("Lib").join("site-packages") + } else { + root.join(venv) + .join("lib") + .join("python3.12") + .join("site-packages") + }; + std::fs::create_dir_all(site.join("urllib3-1.26.18.dist-info")).unwrap(); + std::fs::create_dir_all(site.join("urllib3")).unwrap(); + let file = site.join("urllib3").join("response.py"); + std::fs::write(&file, bytes).unwrap(); + file +} + +async fn scan_output(root: &Path, server: &MockServer, extra: &[&str]) -> std::process::Output { + let mut cmd = tokio::process::Command::new(env!("CARGO_BIN_EXE_socket-patch")); + for (key, _) in std::env::vars_os() { + let name = key.to_string_lossy(); + if name.starts_with("SOCKET_") || name.starts_with("POETRY_") || name == "VIRTUAL_ENV" { + cmd.env_remove(key); + } + } + cmd.env("SOCKET_TELEMETRY_DISABLED", "1") + .args(["scan", "--mode", "hosted", "--yes", "--cwd"]) + .arg(root) + .args([ + "--api-url", + &server.uri(), + "--org", + ORG, + "--api-token", + "fake", + ]) + .args(extra); + cmd.output().await.unwrap() +} + +fn envelope(out: &std::process::Output) -> serde_json::Value { + serde_json::from_slice(&out.stdout).unwrap_or_else(|error| { + panic!( + "{error}: stdout={} stderr={}", + String::from_utf8_lossy(&out.stdout), + String::from_utf8_lossy(&out.stderr) + ) + }) +} + +fn stale_warning(value: &serde_json::Value) -> bool { + value["redirect"]["warnings"] + .as_array() + .unwrap() + .iter() + .any(|warning| warning["code"] == "redirect_pypi_stale_install") +} + +/// Both upstream and locally modified warm installs must be diagnosed on +/// every scan. Merely rewriting the lock must never attest their live bytes. +#[tokio::test] +async fn stale_python_install_warns_and_cannot_attest_even_on_rescan() { + for bytes in [UPSTREAM, b"local modification\n".as_slice()] { + let server = MockServer::start().await; + mock_api(&server).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path()); + let installed = install_package(tmp.path(), ".venv", bytes); + let out = scan_output(tmp.path(), &server, &["--json"]).await; + let json = envelope(&out); + assert!(out.status.success(), "{json}"); + assert_eq!(json["redirect"]["redirected"], 1, "{json}"); + assert!(stale_warning(&json), "{json}"); + let redirected = read(&tmp.path().join("poetry.lock")); + let vex = tmp.path().join("out.vex.json"); + for extra in [ + vec!["--json", "--vex", vex.to_str().unwrap()], + vec!["--json", "--vex", vex.to_str().unwrap(), "--vex-no-verify"], + ] { + let out = scan_output(tmp.path(), &server, &extra).await; + let json = envelope(&out); + assert_eq!(out.status.code(), Some(1), "{json}"); + assert!(stale_warning(&json), "{json}"); + assert_eq!(json["error"]["code"], "no_applicable_patches", "{json}"); + assert!(!vex.exists(), "stale bytes cannot produce a VEX file"); + } + // A failed fresh record fetch must not bypass the persisted evidence. + Mock::given(method("GET")) + .and(path(format!("/v0/orgs/{ORG}/patches/view/{UUID}"))) + .respond_with(ResponseTemplate::new(404)) + .with_priority(1) + .mount(&server) + .await; + let out = scan_output(tmp.path(), &server, &[]).await; + assert!( + out.status.success(), + "{}", + String::from_utf8_lossy(&out.stderr) + ); + assert!(String::from_utf8_lossy(&out.stderr).contains("redirect_pypi_stale_install")); + assert_eq!( + std::fs::read(installed).unwrap(), + bytes, + "probe is read-only" + ); + assert_eq!(read(&tmp.path().join("poetry.lock")), redirected); + } +} + +#[tokio::test] +async fn patched_python_install_attests_without_a_stale_warning() { + let server = MockServer::start().await; + mock_api(&server).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path()); + let installed = install_package(tmp.path(), ".venv", PATCHED); + let vex = tmp.path().join("out.vex.json"); + let out = scan_output( + tmp.path(), + &server, + &["--json", "--vex", vex.to_str().unwrap()], + ) + .await; + let json = envelope(&out); + assert!(out.status.success(), "{json}"); + assert!(!stale_warning(&json), "{json}"); + assert_eq!(json["vex"]["statements"], 1, "{json}"); + assert_eq!(std::fs::read(installed).unwrap(), PATCHED); +} + +#[tokio::test] +async fn healthy_interpreter_cannot_mask_a_stale_python_install() { + let server = MockServer::start().await; + mock_api(&server).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path()); + install_package(tmp.path(), ".venv", PATCHED); + install_package(tmp.path(), "venv", UPSTREAM); + let vex = tmp.path().join("out.vex.json"); + let out = scan_output( + tmp.path(), + &server, + &["--json", "--vex", vex.to_str().unwrap()], + ) + .await; + let json = envelope(&out); + assert_eq!(out.status.code(), Some(1), "{json}"); + assert!(stale_warning(&json), "{json}"); + assert!(!vex.exists()); +} + +#[tokio::test] +async fn python_probe_does_not_guess_staleness_or_run_on_dry_run() { + let server = MockServer::start().await; + mock_api(&server).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path()); + let installed = install_package(tmp.path(), ".venv", UPSTREAM); + let out = scan_output(tmp.path(), &server, &["--json", "--dry-run"]).await; + let json = envelope(&out); + assert!(out.status.success(), "{json}"); + assert!(!stale_warning(&json), "{json}"); + assert_eq!(read(&tmp.path().join("poetry.lock")), LOCK); + assert!(!tmp + .path() + .join(".socket/vendor/redirect-state.json") + .exists()); + std::fs::remove_file(&installed).unwrap(); + for unreadable in [false, true] { + if unreadable { + std::fs::create_dir(&installed).unwrap(); + } + let out = scan_output(tmp.path(), &server, &["--json"]).await; + let json = envelope(&out); + assert!(out.status.success(), "{json}"); + assert!(!stale_warning(&json), "{json}"); + } +} diff --git a/crates/socket-patch-core/src/crawlers/python_crawler.rs b/crates/socket-patch-core/src/crawlers/python_crawler.rs index 88384486..be22628d 100644 --- a/crates/socket-patch-core/src/crawlers/python_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/python_crawler.rs @@ -244,6 +244,8 @@ async fn find_site_packages_under( /// 1. `VIRTUAL_ENV` environment variable /// 2. `.venv` directory in `cwd` /// 3. `venv` directory in `cwd` +/// 4. Poetry's out-of-tree virtualenv(s) for a Poetry project (see +/// [`find_poetry_virtualenv_site_packages`]) pub async fn find_local_venv_site_packages(cwd: &Path) -> Vec { let mut results = Vec::new(); @@ -264,6 +266,320 @@ pub async fn find_local_venv_site_packages(cwd: &Path) -> Vec { results.extend(matches); } + // 3. Poetry keeps its virtualenv OUTSIDE the project by default, so a plain + // `poetry install` leaves nothing above to find and the crawl used to fall + // through to the global interpreter (patching the wrong site-packages, or + // nothing, and reporting success). + if results.is_empty() { + results.extend(find_poetry_virtualenv_site_packages(cwd).await); + } + + results +} + +/// Poetry's `virtualenvs.*` settings that decide where a project's virtualenv +/// lives. Precedence mirrors Poetry's: `POETRY_VIRTUALENVS_*` environment +/// variables, then the project-local `poetry.toml`, then the user +/// `config.toml`, then the defaults. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct PoetryVirtualenvConfig { + /// `virtualenvs.create` — `false` means Poetry installs into the + /// interpreter it runs under (a container's system Python), which the + /// project-marker global fallback already covers. + create: Option, + /// `virtualenvs.in-project` — `true` means `./.venv`, already probed. + in_project: Option, + /// `virtualenvs.path` — may carry Poetry's `{cache-dir}` / + /// `{project-dir}` placeholders and a leading `~`. + path: Option, + /// `cache-dir` — the parent of the default `virtualenvs` root. + cache_dir: Option, +} + +impl PoetryVirtualenvConfig { + /// Layer `other` (lower precedence) under `self`: only unset keys take + /// the lower layer's value. + fn or(mut self, other: PoetryVirtualenvConfig) -> Self { + self.create = self.create.or(other.create); + self.in_project = self.in_project.or(other.in_project); + self.path = self.path.or(other.path); + self.cache_dir = self.cache_dir.or(other.cache_dir); + self + } + + fn from_env(var: impl Fn(&str) -> Option) -> Self { + let flag = |name: &str| { + var(name).map(|v| { + let v = v.trim().to_ascii_lowercase(); + matches!(v.as_str(), "1" | "true" | "yes" | "on") + }) + }; + Self { + create: flag("POETRY_VIRTUALENVS_CREATE"), + in_project: flag("POETRY_VIRTUALENVS_IN_PROJECT"), + path: var("POETRY_VIRTUALENVS_PATH").filter(|v| !v.trim().is_empty()), + cache_dir: var("POETRY_CACHE_DIR").filter(|v| !v.trim().is_empty()), + } + } + + /// `[virtualenvs]` (poetry.toml / config.toml) and the top-level + /// `cache-dir` key. A file that does not parse contributes nothing. + fn from_toml(text: &str) -> Self { + let Ok(doc) = text.parse::() else { + return Self::default(); + }; + let venvs = doc.get("virtualenvs").and_then(toml_edit::Item::as_table_like); + let get_bool = |key: &str| { + venvs.and_then(|t| t.get(key)).and_then(|item| { + item.as_bool().or_else(|| { + item.as_str().map(|s| { + matches!(s.trim().to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on") + }) + }) + }) + }; + Self { + create: get_bool("create"), + in_project: get_bool("in-project"), + path: venvs + .and_then(|t| t.get("path")) + .and_then(toml_edit::Item::as_str) + .map(str::to_string), + cache_dir: doc + .get("cache-dir") + .and_then(toml_edit::Item::as_str) + .map(str::to_string), + } + } +} + +/// The user-level Poetry config file, per Poetry's own lookup: +/// `$POETRY_CONFIG_DIR/config.toml`, else the platform config dir +/// (`~/Library/Application Support/pypoetry` on macOS, `$XDG_CONFIG_HOME` or +/// `~/.config` + `/pypoetry` elsewhere on unix, `%APPDATA%\pypoetry` on +/// Windows). +fn poetry_user_config_path(var: &impl Fn(&str) -> Option) -> Option { + if let Some(dir) = var("POETRY_CONFIG_DIR").filter(|v| !v.trim().is_empty()) { + return Some(PathBuf::from(dir).join("config.toml")); + } + let home = var("HOME").or_else(|| var("USERPROFILE")).map(PathBuf::from); + let dir = if cfg!(windows) { + var("APPDATA") + .map(PathBuf::from) + .or_else(|| home.map(|h| h.join("AppData").join("Roaming")))? + .join("pypoetry") + } else if cfg!(target_os = "macos") { + home?.join("Library").join("Application Support").join("pypoetry") + } else { + var("XDG_CONFIG_HOME") + .filter(|v| !v.trim().is_empty()) + .map(PathBuf::from) + .or_else(|| home.map(|h| h.join(".config")))? + .join("pypoetry") + }; + Some(dir.join("config.toml")) +} + +/// Poetry's default `cache-dir`: `~/Library/Caches/pypoetry` (macOS), +/// `$XDG_CACHE_HOME`/`~/.cache` + `/pypoetry` (other unix), +/// `%LOCALAPPDATA%\pypoetry\Cache` (Windows). +fn poetry_default_cache_dir(var: &impl Fn(&str) -> Option) -> Option { + let home = var("HOME").or_else(|| var("USERPROFILE")).map(PathBuf::from); + if cfg!(windows) { + Some( + var("LOCALAPPDATA") + .map(PathBuf::from) + .or_else(|| home.map(|h| h.join("AppData").join("Local")))? + .join("pypoetry") + .join("Cache"), + ) + } else if cfg!(target_os = "macos") { + Some(home?.join("Library").join("Caches").join("pypoetry")) + } else { + Some( + var("XDG_CACHE_HOME") + .filter(|v| !v.trim().is_empty()) + .map(PathBuf::from) + .or_else(|| home.map(|h| h.join(".cache")))? + .join("pypoetry"), + ) + } +} + +/// Poetry's virtualenv directory name for a project, minus the `-py` +/// suffix — `EnvManager.generate_env_name(name, cwd)`, unchanged from Poetry +/// 1.0 through 2.x: the lowercased project name with shell-hostile characters +/// replaced by `_` and truncated to 42 chars, a dash, then the first 8 chars +/// of the URL-safe base64 sha256 of `os.path.normcase(os.path.realpath(cwd))`. +/// `realpath` is resolved by the caller (`normalized_cwd` is the already +/// canonical path) so the pure function stays testable with fixed vectors. +fn poetry_env_name_prefix(project_name: &str, normalized_cwd: &str) -> String { + use base64::Engine as _; + use sha2::{Digest, Sha256}; + + let lowered = project_name.to_lowercase(); + let sanitized: String = lowered + .chars() + .map(|c| { + if matches!(c, ' ' | '$' | '`' | '!' | '*' | '@' | '"' | '\\' | '\r' | '\n' | '\t') { + '_' + } else { + c + } + }) + .take(42) + .collect(); + let digest = Sha256::digest(normalized_cwd.as_bytes()); + let hash = base64::engine::general_purpose::URL_SAFE.encode(digest); + format!("{sanitized}-{}", &hash[..8]) +} + +/// `os.path.normcase(os.path.realpath(cwd))` as Poetry hashes it: symlinks +/// resolved; on Windows lowercased with forward slashes turned into +/// backslashes, elsewhere unchanged. +fn poetry_normalized_cwd(cwd: &Path) -> String { + let real = std::fs::canonicalize(cwd).unwrap_or_else(|_| cwd.to_path_buf()); + let text = real.to_string_lossy().into_owned(); + if cfg!(windows) { + text.replace('/', "\\").to_lowercase() + } else { + text + } +} + +/// The project name Poetry derives its virtualenv name from: `[tool.poetry] +/// name`, else PEP 621 `[project] name`. Poetry canonicalizes it (PEP 503) — +/// both spellings are returned so a lock written before that normalization +/// still matches. +fn poetry_project_names(pyproject: &str) -> Vec { + let Ok(doc) = pyproject.parse::() else { + return Vec::new(); + }; + let raw = doc + .get("tool") + .and_then(|t| t.get("poetry")) + .and_then(|p| p.get("name")) + .and_then(toml_edit::Item::as_str) + .or_else(|| { + doc.get("project") + .and_then(|p| p.get("name")) + .and_then(toml_edit::Item::as_str) + }); + let Some(raw) = raw else { + return Vec::new(); + }; + let mut names = vec![canonicalize_pypi_name(raw)]; + if !names.contains(&raw.to_string()) { + names.push(raw.to_string()); + } + names +} + +/// The root directory Poetry would place this project's virtualenvs under, +/// or `None` when Poetry would not create one (`virtualenvs.create = false`, +/// `virtualenvs.in-project = true`, or no home to resolve the default against). +fn poetry_virtualenvs_root( + cwd: &Path, + config: &PoetryVirtualenvConfig, + var: &impl Fn(&str) -> Option, +) -> Option { + if config.create == Some(false) || config.in_project == Some(true) { + return None; + } + let cache_dir = config + .cache_dir + .as_deref() + .map(|c| expand_home(c, var)) + .or_else(|| poetry_default_cache_dir(var))?; + match config.path.as_deref() { + Some(template) => { + let expanded = template + .replace("{cache-dir}", &cache_dir.to_string_lossy()) + .replace("{project-dir}", &cwd.to_string_lossy()); + let path = expand_home(&expanded, var); + Some(if path.is_absolute() { + path + } else { + cwd.join(path) + }) + } + None => Some(cache_dir.join("virtualenvs")), + } +} + +fn expand_home(raw: &str, var: &impl Fn(&str) -> Option) -> PathBuf { + if let Some(rest) = raw.strip_prefix("~/").or_else(|| raw.strip_prefix("~\\")) { + if let Some(home) = var("HOME").or_else(|| var("USERPROFILE")) { + return PathBuf::from(home).join(rest); + } + } + if raw == "~" { + if let Some(home) = var("HOME").or_else(|| var("USERPROFILE")) { + return PathBuf::from(home); + } + } + PathBuf::from(raw) +} + +/// `site-packages` of every virtualenv Poetry created for the project at +/// `cwd` under its `virtualenvs.path` (`--py`; one per +/// interpreter minor the user ran `poetry env use` with). Empty for +/// non-Poetry projects and whenever Poetry's configuration says the +/// virtualenv is in-project, disabled, or unresolvable. Read-only: nothing is +/// executed, no `poetry` binary is needed. +pub async fn find_poetry_virtualenv_site_packages(cwd: &Path) -> Vec { + let var = |name: &str| std::env::var(name).ok(); + let has = |leaf: &str| cwd.join(leaf).is_file(); + let pyproject = match tokio::fs::read_to_string(cwd.join("pyproject.toml")).await { + Ok(text) => text, + Err(_) => return Vec::new(), + }; + let poetry_project = has("poetry.lock") || has("poetry.toml") || pyproject.contains("[tool.poetry"); + if !poetry_project { + return Vec::new(); + } + let names = poetry_project_names(&pyproject); + if names.is_empty() { + return Vec::new(); + } + let local = match tokio::fs::read_to_string(cwd.join("poetry.toml")).await { + Ok(text) => PoetryVirtualenvConfig::from_toml(&text), + Err(_) => PoetryVirtualenvConfig::default(), + }; + let user = match poetry_user_config_path(&var) { + Some(path) => match tokio::fs::read_to_string(&path).await { + Ok(text) => PoetryVirtualenvConfig::from_toml(&text), + Err(_) => PoetryVirtualenvConfig::default(), + }, + None => PoetryVirtualenvConfig::default(), + }; + let config = PoetryVirtualenvConfig::from_env(var).or(local).or(user); + let Some(root) = poetry_virtualenvs_root(cwd, &config, &var) else { + return Vec::new(); + }; + let normalized = poetry_normalized_cwd(cwd); + let prefixes: Vec = names + .iter() + .map(|name| format!("{}-py", poetry_env_name_prefix(name, &normalized))) + .collect(); + let Ok(mut entries) = tokio::fs::read_dir(&root).await else { + return Vec::new(); + }; + let mut venvs = Vec::new(); + while let Ok(Some(entry)) = entries.next_entry().await { + let file_name = entry.file_name(); + let Some(name) = file_name.to_str() else { + continue; + }; + if prefixes.iter().any(|prefix| name.starts_with(prefix)) { + venvs.push(entry.path()); + } + } + venvs.sort(); + let mut results = Vec::new(); + for venv in venvs { + results.extend(find_site_packages_under(&venv, "site-packages").await); + } results } @@ -739,6 +1055,181 @@ mod tests { use super::*; use crate::utils::purl::parse_pypi_purl; + // ── Poetry out-of-tree virtualenv discovery ───────────────────────────── + + /// Known-answer vectors computed with Poetry's own algorithm + /// (`EnvManager.generate_env_name`): lowercase, shell-hostile characters + /// to `_`, 42-char cap, `-`, first 8 chars of url-safe-b64(sha256(cwd)). + #[test] + fn poetry_env_name_prefix_matches_poetry_generate_env_name() { + assert_eq!( + poetry_env_name_prefix("poetry-patch-fixture", "/tmp/socket-patch-poetry-fixture"), + "poetry-patch-fixture-SmzYEVFn" + ); + assert_eq!( + poetry_env_name_prefix("My.Project", "/Users/dev/My Project"), + "my.project-0aTnNZ0w" + ); + let long = "a".repeat(60); + let prefix = poetry_env_name_prefix(&format!("we ird$name`{long}"), "/x"); + assert!(prefix.starts_with("we_ird_name_")); + assert_eq!(prefix.rsplit_once('-').unwrap().0.chars().count(), 42); + assert_eq!(prefix.rsplit_once('-').unwrap().1.len(), 8); + } + + #[test] + fn poetry_project_names_prefer_tool_poetry_and_return_both_spellings() { + assert_eq!( + poetry_project_names("[tool.poetry]\nname = \"Flask_Login\"\n[project]\nname = \"other\"\n"), + vec!["flask-login".to_string(), "Flask_Login".to_string()] + ); + assert_eq!( + poetry_project_names("[project]\nname = \"my-app\"\n[tool.poetry]\npackage-mode = false\n"), + vec!["my-app".to_string()] + ); + assert!(poetry_project_names("[tool.poetry]\nversion = \"1\"\n").is_empty()); + assert!(poetry_project_names("not toml [").is_empty()); + } + + #[test] + fn poetry_virtualenv_config_layers_and_templates() { + let local = PoetryVirtualenvConfig::from_toml( + "[virtualenvs]\nin-project = false\npath = \"{cache-dir}/venvs\"\n", + ); + let user = PoetryVirtualenvConfig::from_toml("cache-dir = \"/srv/poetry-cache\"\n[virtualenvs]\ncreate = false\n"); + let env = PoetryVirtualenvConfig::from_env(|k| match k { + "POETRY_VIRTUALENVS_CREATE" => Some("true".into()), + _ => None, + }); + let merged = env.or(local).or(user); + assert_eq!(merged.create, Some(true), "env beats config.toml"); + assert_eq!(merged.in_project, Some(false)); + assert_eq!(merged.path.as_deref(), Some("{cache-dir}/venvs")); + assert_eq!(merged.cache_dir.as_deref(), Some("/srv/poetry-cache")); + let var = |k: &str| match k { + "HOME" => Some("/home/dev".to_string()), + _ => None, + }; + let cwd = Path::new("/home/dev/proj"); + assert_eq!( + poetry_virtualenvs_root(cwd, &merged, &var), + Some(PathBuf::from("/srv/poetry-cache/venvs")) + ); + let project_local = PoetryVirtualenvConfig { + path: Some("{project-dir}/.envs".into()), + ..Default::default() + }; + assert_eq!( + poetry_virtualenvs_root(cwd, &project_local, &var), + Some(PathBuf::from("/home/dev/proj/.envs")) + ); + let tilde = PoetryVirtualenvConfig { + path: Some("~/venvs".into()), + ..Default::default() + }; + assert_eq!(poetry_virtualenvs_root(cwd, &tilde, &var), Some(PathBuf::from("/home/dev/venvs"))); + for disabled in [ + PoetryVirtualenvConfig { create: Some(false), ..Default::default() }, + PoetryVirtualenvConfig { in_project: Some(true), ..Default::default() }, + ] { + assert_eq!(poetry_virtualenvs_root(cwd, &disabled, &var), None); + } + // Defaults resolve against the platform cache dir; with no home at all + // there is nothing to resolve against. + let default = PoetryVirtualenvConfig::default(); + assert!(poetry_virtualenvs_root(cwd, &default, &var).is_some()); + assert_eq!(poetry_virtualenvs_root(cwd, &default, &|_: &str| None), None); + } + + /// End to end against the filesystem: a Poetry project with no `.venv` + /// finds the virtualenv(s) Poetry placed under `virtualenvs.path`, every + /// interpreter minor, and stops looking once the project opts into + /// `in-project` venvs. + #[tokio::test] + #[serial_test::serial] + async fn poetry_out_of_tree_virtualenvs_are_discovered_without_a_dot_venv() { + struct Guard(Vec<(&'static str, Option)>); + impl Guard { + fn new(overrides: &[(&'static str, Option<&str>)]) -> Self { + let prev = overrides + .iter() + .map(|(k, v)| { + let old = std::env::var(k).ok(); + match v { + Some(v) => std::env::set_var(k, v), + None => std::env::remove_var(k), + } + (*k, old) + }) + .collect(); + Guard(prev) + } + } + impl Drop for Guard { + fn drop(&mut self) { + for (k, old) in &self.0 { + match old { + Some(v) => std::env::set_var(k, v), + None => std::env::remove_var(k), + } + } + } + } + let tmp = tempfile::tempdir().unwrap(); + let project = tmp.path().join("proj"); + std::fs::create_dir_all(&project).unwrap(); + std::fs::write( + project.join("pyproject.toml"), + "[tool.poetry]\nname = \"Poetry_Patch.Fixture\"\nversion = \"0.1.0\"\n", + ) + .unwrap(); + std::fs::write(project.join("poetry.lock"), "[[package]]\nname = \"six\"\nversion = \"1.16.0\"\n[metadata]\nlock-version = \"2.1\"\n").unwrap(); + let venvs = tmp.path().join("venvs"); + let prefix = poetry_env_name_prefix("poetry-patch-fixture", &poetry_normalized_cwd(&project)); + let site = |venv: &Path, minor: &str| { + if cfg!(windows) { + venv.join("Lib").join("site-packages") + } else { + venv.join("lib").join(format!("python{minor}")).join("site-packages") + } + }; + let venv312 = venvs.join(format!("{prefix}-py3.12")); + let venv311 = venvs.join(format!("{prefix}-py3.11")); + let other = venvs.join("someone-else-AAAAAAAA-py3.12"); + for (venv, minor) in [(&venv312, "3.12"), (&venv311, "3.11"), (&other, "3.12")] { + std::fs::create_dir_all(site(venv, minor)).unwrap(); + } + let _guard = Guard::new(&[ + ("VIRTUAL_ENV", None), + ("POETRY_VIRTUALENVS_PATH", Some(venvs.to_str().unwrap())), + ("POETRY_VIRTUALENVS_IN_PROJECT", None), + ("POETRY_VIRTUALENVS_CREATE", None), + ("POETRY_CACHE_DIR", None), + ("POETRY_CONFIG_DIR", Some(tmp.path().join("no-config").to_str().unwrap())), + ]); + + let found = find_local_venv_site_packages(&project).await; + assert_eq!(found, vec![site(&venv311, "3.11"), site(&venv312, "3.12")], "{found:?}"); + + // A project-local `.venv` wins and the out-of-tree probe is skipped. + std::fs::create_dir_all(site(&project.join(".venv"), "3.12")).unwrap(); + assert_eq!(find_local_venv_site_packages(&project).await, vec![site(&project.join(".venv"), "3.12")]); + std::fs::remove_dir_all(project.join(".venv")).unwrap(); + + // `poetry.toml` opting into in-project venvs (or disabling creation) + // means Poetry never used the shared root: nothing is probed. + std::fs::write(project.join("poetry.toml"), "[virtualenvs]\nin-project = true\n").unwrap(); + assert!(find_local_venv_site_packages(&project).await.is_empty()); + std::fs::write(project.join("poetry.toml"), "[virtualenvs]\ncreate = false\n").unwrap(); + assert!(find_local_venv_site_packages(&project).await.is_empty()); + std::fs::remove_file(project.join("poetry.toml")).unwrap(); + + // Not a Poetry project (no lock, no [tool.poetry]): untouched. + std::fs::write(project.join("pyproject.toml"), "[project]\nname = \"poetry-patch-fixture\"\n").unwrap(); + std::fs::remove_file(project.join("poetry.lock")).unwrap(); + assert!(find_local_venv_site_packages(&project).await.is_empty()); + } + #[test] fn test_canonicalize_pypi_name_basic() { assert_eq!(canonicalize_pypi_name("Requests"), "requests"); diff --git a/crates/socket-patch-core/src/patch/apply_lock.rs b/crates/socket-patch-core/src/patch/apply_lock.rs index 518a1315..abd19f6f 100644 --- a/crates/socket-patch-core/src/patch/apply_lock.rs +++ b/crates/socket-patch-core/src/patch/apply_lock.rs @@ -360,8 +360,8 @@ mod tests { /// lock exists to prevent. Re-opening the path on every retry keeps /// the waiter honest about whatever file `apply.lock` names now. /// - /// The choreography below can lose two *benign* races on a loaded - /// runner (both observed on CI's macos-latest), so it retries: the + /// The choreography below can lose benign races on a loaded runner + /// (observed on macOS and Windows CI), so it retries: the /// regressed bug double-holds on essentially every iteration, while /// the benign losses need an unlucky deschedule and almost never /// repeat. One clean iteration proves the re-open behavior; a full @@ -430,6 +430,28 @@ mod tests { (Ok(_fresh_guard), Ok(_waiter_guard)) => { benign.push("double hold via the open->flock window"); } + // Windows can keep an unlinked file delete-pending until + // its last handle closes. CreateFile then returns + // ERROR_ACCESS_DENIED (5), including when the waiter is + // reopening while the fresh acquire races that cleanup. + // Neither an I/O refusal nor Held grants a second lock. + // Retry this choreography; still require a clean Held + // iteration above, and never relax acquire's I/O errors. + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + #[cfg(windows)] + (Ok(_) | Err(LockError::Held), Err(LockError::Io { source, .. })) + | (Err(LockError::Io { source, .. }), Ok(_) | Err(LockError::Held)) + if source.raw_os_error() == Some(5) => + { + benign.push("open raced Windows delete-pending handle"); + } + #[cfg(windows)] + ( + Err(LockError::Io { source: first, .. }), + Err(LockError::Io { source: second, .. }), + ) if first.raw_os_error() == Some(5) && second.raw_os_error() == Some(5) => { + benign.push("both opens raced Windows delete-pending handle"); + } (fresh, waiter_result) => panic!( "unexpected lock outcome: fresh={:?} waiter={:?}", fresh.map(|_| "Ok(guard)"), diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index d291c7b9..e604b749 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -25,6 +25,7 @@ use crate::vendor::yarn_berry_lock::yarnrc_compression_level; pub mod golang_local; mod pnpm; +mod poetry; mod replay; mod requirements; mod state; @@ -215,6 +216,7 @@ pub fn rewrite_registry_redirect_with_python_metadata( rewrite_bun_lock(files, overrides, &mut result); rewrite_pypi_requirements(files, overrides, &mut result); rewrite_uv_lock(files, overrides, python_metadata, &mut result); + poetry::rewrite_poetry(files, overrides, &mut result); rewrite_cargo(files, overrides, &mut result); rewrite_composer_lock(files, overrides, &mut result); rewrite_nuget(files, overrides, &mut result); diff --git a/crates/socket-patch-core/src/patch/redirect/poetry.rs b/crates/socket-patch-core/src/patch/redirect/poetry.rs new file mode 100644 index 00000000..5e9b7d23 --- /dev/null +++ b/crates/socket-patch-core/src/patch/redirect/poetry.rs @@ -0,0 +1,140 @@ +//! Hosted (URL-source) redirects for `poetry.lock`, every lock generation +//! Poetry 1.0+ has written. Poetry 0.12 (format `"0"`) ignores URL sources and +//! is refused by the shared rewriter. See `crate::utils::poetry_lock`. + +use std::collections::BTreeMap; + +use serde_json::Value; +use toml_edit::DocumentMut; + +use super::{DepOverride, FileEdit, RewriteResult, RewriteWarning}; +use crate::utils::poetry_lock::{ + generated_by_version, lock_version, poetry_lock_edits, rewrite_poetry_lock, +}; + +/// Whether the lock was written by a Poetry release older than 1.4. Those +/// installers neither verify hashes for local wheels nor replace an +/// already-installed package at the same version, so a warm virtualenv keeps +/// serving the upstream bytes after the redirect. Formats `0`/`1.0`/`1.1` are +/// only written by such releases; lock `2.0` is written by 1.3 through 1.8, so +/// the `@generated by Poetry X.Y.Z` header (present from 1.4) decides there. +fn pre_1_4_writer(lock_text: &str) -> Option<&'static str> { + let lock = lock_text.parse::().ok()?; + match lock_version(&lock) { + Ok("0") => Some("0"), + Ok("1.0") => Some("1.0"), + Ok("1.1") => Some("1.1"), + Ok("2.0") if !matches!(generated_by_version(lock_text), Some(v) if v >= (1, 4)) => { + Some("2.0") + } + _ => None, + } +} + +pub(super) fn rewrite_poetry( + files: &BTreeMap, + overrides: &[DepOverride], + result: &mut RewriteResult, +) { + let locks: Vec<(&String, &String)> = files + .iter() + .filter(|(path, _)| path.as_str() == "poetry.lock" || path.ends_with("/poetry.lock")) + .collect(); + if locks.is_empty() { + return; + } + // Intake gate ONCE per dep, not once per lock file (uv parity). + let mut usable: Vec<(&DepOverride, &str)> = Vec::new(); + for dep in overrides.iter().filter(|dep| dep.ecosystem == "pypi") { + match dep.integrity.sha256.as_deref() { + Some(sha256) => usable.push((dep, sha256)), + None => result.warnings.push(RewriteWarning { + code: "redirect_poetry_missing_sha256".into(), + detail: format!("{} has no SHA-256 integrity", dep.name), + }), + } + } + for (path, original) in locks { + let mut content = original.clone(); + let mut stale_warned = false; + for &(dep, sha256) in &usable { + let filename = dep.artifact_url.rsplit('/').next().unwrap_or(""); + match rewrite_poetry_lock( + &content, + &dep.name, + &dep.version, + "url", + &dep.artifact_url, + filename, + sha256, + ) { + Ok(Some(rewritten)) if rewritten != content => { + match poetry_lock_edits(&content, &rewritten, &dep.name) { + Ok(edits) => { + for (original, new) in edits { + result.edits.push(FileEdit { + path: path.clone(), + kind: "redirect_poetry_lock_package".into(), + action: "rewritten".into(), + key: Some(format!("{}@{}", dep.name, dep.version)), + original: Some(Value::String(original)), + new: Some(Value::String(new)), + }); + } + } + Err(detail) => { + result.warnings.push(RewriteWarning { + code: "redirect_poetry_lock_unsupported".into(), + detail: format!("{path}: {detail}"), + }); + continue; + } + } + content = rewritten; + if !stale_warned { + if let Some(format) = pre_1_4_writer(&content) { + stale_warned = true; + // Poetry 1.0 installs url sources through pip, which reads + // the hash from the URL fragment; pip 22.3–23.0 take the + // rest of the fragment (`&#egg=`, appended by Poetry) + // as part of the digest and refuse the install (measured; + // pip <= 22.2 and >= 23.1 install and verify). + let pip_note = if format == "1.0" { + " Poetry 1.0 installs through pip: pip 22.3–23.0 misparse the \ + hash fragment and refuse the install (fail-closed) — use pip \ + <= 22.2 or >= 23.1 in the virtualenv." + } else { + "" + }; + result.warnings.push(RewriteWarning { + code: "redirect_poetry_stale_install_risk".into(), + detail: format!( + "{path} was written by Poetry < 1.4, which does not replace \ + an already-installed package at the same version: an \ + existing virtualenv keeps the upstream {} until it is \ + recreated (or the package is `pip uninstall`ed) before \ + `poetry install`; fresh installs pick up the patched \ + wheel.{pip_note}", + dep.name + ), + }); + } + } + } + // Already redirected to this artifact (idempotent re-scan). + Ok(Some(_)) => {} + Ok(None) => result.warnings.push(RewriteWarning { + code: "redirect_poetry_entry_not_found".into(), + detail: format!("no {path} entry for {}@{}", dep.name, dep.version), + }), + Err(detail) => result.warnings.push(RewriteWarning { + code: "redirect_poetry_lock_unsupported".into(), + detail: format!("{path}: {detail}"), + }), + } + } + if content != *original { + result.files.insert(path.clone(), content); + } + } +} diff --git a/crates/socket-patch-core/src/patch/redirect/replay.rs b/crates/socket-patch-core/src/patch/redirect/replay.rs index af29f644..a35f29cd 100644 --- a/crates/socket-patch-core/src/patch/redirect/replay.rs +++ b/crates/socket-patch-core/src/patch/redirect/replay.rs @@ -86,7 +86,9 @@ enum Inverse { /// Gemfile.lock) revert together or not at all. fn classify(kind: &str, action: &str) -> (&'static str, Inverse) { match kind { - "redirect_requirements_line" | "redirect_uv_lock_wheel" => ("pypi", Inverse::ReplaceFragment), + "redirect_requirements_line" | "redirect_uv_lock_wheel" | "redirect_poetry_lock_package" => { + ("pypi", Inverse::ReplaceFragment) + } "redirect_composer_dist" => ("composer", Inverse::ReplaceFragment), "redirect_cargo_toml_dep" | "redirect_cargo_lock_entry" => ("cargo", Inverse::ReplaceFragment), "redirect_cargo_registry" => ( diff --git a/crates/socket-patch-core/src/utils/mod.rs b/crates/socket-patch-core/src/utils/mod.rs index d50be2dd..e83802f9 100644 --- a/crates/socket-patch-core/src/utils/mod.rs +++ b/crates/socket-patch-core/src/utils/mod.rs @@ -1,6 +1,7 @@ pub mod env_compat; pub mod fs; pub(crate) mod http; +pub mod poetry_lock; pub mod process; pub mod purl; pub mod python_lock; diff --git a/crates/socket-patch-core/src/utils/poetry_lock.rs b/crates/socket-patch-core/src/utils/poetry_lock.rs new file mode 100644 index 00000000..b04e3b36 --- /dev/null +++ b/crates/socket-patch-core/src/utils/poetry_lock.rs @@ -0,0 +1,579 @@ +//! Shared `poetry.lock` rewriter for hosted (URL source) and vendored (file +//! source) patches across every lock generation Poetry has written: +//! +//! * format `"0"` — Poetry 0.12: no `lock-version`, hashes in `[metadata.hashes]` +//! * `"1.0"` / `"1.1"` — Poetry 1.0–1.2: per-package files in `[metadata.files]` +//! * `"2.x"` — Poetry 1.3+: `files = [...]` inside each `[[package]]` +//! +//! Every edit is computed on a parsed `toml_edit` document and then spliced +//! back into the ORIGINAL text as verbatim fragment replacements, so untouched +//! bytes (formatting, comments, line endings) survive and rollback can replay +//! the recorded fragments. A malformed lock (user-editable input) must never +//! panic: every table access here is guarded and degrades to an `Err`, which +//! callers surface as a refusal warning. + +use toml_edit::{value, Array, DocumentMut, InlineTable, Item, Table, Value}; + +use crate::crawlers::python_crawler::canonicalize_pypi_name; + +/// The lock generation: `"0"`, `"1.0"`, `"1.1"`, or any `"2."` (Poetry +/// bumps the minor additively — 2.0 → 2.1 kept every shape we rewrite, and the +/// vendored loader already accepts newer minors with an advisory; the hosted +/// path must not refuse what the vendored path accepts). +pub fn lock_version(lock: &DocumentMut) -> Result<&str, String> { + let metadata = lock + .get("metadata") + .filter(|item| item.is_table_like()) + .ok_or("missing Poetry lock metadata")?; + match metadata.get("lock-version").and_then(Item::as_str) { + Some(version @ ("1.0" | "1.1")) => Ok(version), + Some(version) if is_2x(version) => Ok(version), + Some(version) => Err(format!( + "unsupported Poetry lock-version {version:?} (supported: legacy metadata.hashes, 1.0, \ + 1.1 and 2.x)" + )), + None if metadata + .get("hashes") + .and_then(Item::as_table_like) + .is_some() => + { + Ok("0") + } + None => Err("poetry.lock has neither a [metadata] lock-version nor a [metadata.hashes] table".into()), + } +} + +fn is_2x(version: &str) -> bool { + version + .strip_prefix("2.") + .is_some_and(|minor| !minor.is_empty() && minor.bytes().all(|b| b.is_ascii_digit())) +} + +/// The `# This file is automatically @generated by Poetry X.Y.Z …` header +/// Poetry writes from 1.4 on (1.3 writes the sentence without a version). +/// Advisory only — a lock can be consumed by a different Poetry than the one +/// that wrote it — but it lets warnings about the WRITER's installer be +/// precise instead of blaming every lock-2.0 project for Poetry 1.3. +pub fn generated_by_version(lock_text: &str) -> Option<(u64, u64)> { + let first = lock_text.lines().next()?; + let rest = first.split("@generated by Poetry ").nth(1)?; + let version = rest.split_whitespace().next()?; + let mut parts = version.split('.'); + let major = parts.next()?.parse().ok()?; + let minor = parts.next()?.parse().ok()?; + Some((major, minor)) +} + +/// Whether `existing` is an earlier hosted redirect of the SAME artifact: +/// same origin (`scheme://host[:port]`) and same trailing filename as the +/// current artifact URL, fragments ignored. Grant tokens and patch uuids live +/// in the path between them, so a rotated token or a republished patch is +/// superseded in place instead of stranding the pin on a URL that no longer +/// serves (the bun / requirements / cargo rewriters make the same call). +fn is_prior_hosted_url(existing: &str, current: &str) -> bool { + fn origin_and_leaf(url: &str) -> Option<(&str, &str)> { + if !url.starts_with("https://") && !url.starts_with("http://") { + return None; + } + let url = url.split('#').next()?; + let scheme_end = url.find("://")? + 3; + let path_start = url[scheme_end..].find('/')? + scheme_end; + let leaf = url[path_start..] + .rsplit('/') + .next() + .filter(|leaf| !leaf.is_empty())?; + Some((&url[..path_start], leaf)) + } + match (origin_and_leaf(existing), origin_and_leaf(current)) { + (Some(old), Some(new)) => old == new, + _ => false, + } +} + +/// Rewrite the `[[package]]` for `name`@`version` to install the wheel at +/// `source_url` (`source_type` `"url"` for hosted, `"file"` for vendored), +/// pinning `sha256`. Returns `Ok(None)` when the lock has no such entry (or +/// resolves another version) — the caller decides whether that is a warning. +/// Returns `Err` for inputs the rewriter must refuse without writing. +pub fn rewrite_poetry_lock( + text: &str, + name: &str, + version: &str, + source_type: &str, + source_url: &str, + filename: &str, + sha256: &str, +) -> Result, String> { + if !matches!(source_type, "file" | "url") + || sha256.len() != 64 + || !sha256.bytes().all(|byte| byte.is_ascii_hexdigit()) + { + return Err("invalid Poetry artifact source or SHA-256".into()); + } + // Poetry compares the lock's `sha256:` against `hashlib`'s lowercase + // hexdigest as strings, so an uppercase digest would fail every install. + let sha256 = sha256.to_ascii_lowercase(); + if filename.contains(['/', '\\']) || !filename.ends_with(".whl") { + return Err("Poetry patch wheel does not match the locked package".into()); + } + let parts: Vec<_> = filename.split('-').collect(); + if !matches!(parts.len(), 5 | 6) + || canonicalize_pypi_name(parts[0]) != canonicalize_pypi_name(name) + || parts[1] != version + { + return Err("Poetry patch wheel does not match the locked package".into()); + } + let mut lock: DocumentMut = text + .parse() + .map_err(|e| format!("invalid Poetry lock: {e}"))?; + let format = lock_version(&lock)?.to_string(); + if format == "0" && source_type == "url" { + return Err("Poetry 0.x ignores URL sources; hosted patches require Poetry >= 1.0".into()); + } + let effective_url = if format == "1.0" && source_type == "url" { + // Poetry 1.0 hands pip `#egg=` unconditionally; the trailing + // `&` keeps `sha256=` a complete fragment parameter when `#egg=` + // is appended (pip >= 22 would otherwise read `#egg=` as the + // digest and hard-fail the install). + format!("{source_url}#sha256={sha256}&") + } else { + source_url.to_string() + }; + let packages = lock + .get_mut("package") + .and_then(Item::as_array_of_tables_mut) + .ok_or("missing Poetry packages")?; + let indices: Vec<_> = packages + .iter() + .enumerate() + .filter(|(_, package)| { + package + .get("name") + .and_then(Item::as_str) + .is_some_and(|candidate| { + canonicalize_pypi_name(candidate) == canonicalize_pypi_name(name) + }) + }) + .map(|(index, _)| index) + .collect(); + if indices.is_empty() { + return Ok(None); + } + if indices.len() != 1 { + return Err("forked Poetry package requires an unambiguous source".into()); + } + let package = packages + .get_mut(indices[0]) + .ok_or("missing Poetry package")?; + if package.get("version").and_then(Item::as_str) != Some(version) { + return Ok(None); + } + let package_name = package + .get("name") + .and_then(Item::as_str) + .unwrap_or(name) + .to_string(); + if let Some(source) = package.get("source") { + let existing_type = source.get("type").and_then(Item::as_str); + let existing_url = source.get("url").and_then(Item::as_str).unwrap_or(""); + let same_target = existing_type == Some(source_type) && existing_url == effective_url; + let prior_hosted = source_type == "url" + && existing_type == Some("url") + && is_prior_hosted_url(existing_url, &effective_url); + if !same_target && !prior_hosted { + return Err(format!( + "refusing to replace an existing Poetry source ({} {}) for {package_name}", + existing_type.unwrap_or("unknown"), + existing_url + )); + } + } + let mut entry = InlineTable::new(); + entry.insert("file", Value::from(filename)); + entry.insert("hash", Value::from(format!("sha256:{sha256}"))); + let mut files = Array::new(); + files.push(entry); + let mut source = Table::new(); + source.insert("type", value(source_type)); + source.insert("url", value(effective_url)); + if matches!(format.as_str(), "0" | "1.0") { + // Poetry 0.12 / 1.0 read `source.reference` unconditionally (KeyError + // without it), even for archive sources. + source.insert("reference", value("")); + } + package.insert("source", Item::Table(source)); + if matches!(format.as_str(), "1.0" | "1.1") && source_type == "url" { + // Poetry >= 1.2 verifies url sources against the package's own + // `files` (it never reads `metadata.files` hashes for them), Poetry + // 1.0/1.1 against `metadata.files` — write both so whichever installer + // consumes this legacy lock enforces the patched hash. Poetry 1.0/1.1 + // ignore the extra package key (measured on 1.0.10; 1.2.2 rejects a + // tampered package `files` hash on a lock-1.0 file only when it is + // present). + package.insert("files", value(files.clone())); + } + if format.starts_with('2') { + package.insert("files", value(files)); + } else { + let field = if format == "0" { "hashes" } else { "files" }; + let table = lock + .get_mut("metadata") + .and_then(Item::as_table_like_mut) + .ok_or("missing Poetry lock metadata")? + .get_mut(field) + .ok_or_else(|| format!("missing Poetry integrity table [metadata.{field}]"))? + .as_table_like_mut() + .ok_or_else(|| format!("[metadata.{field}] is not a table"))?; + if format == "0" { + let mut hashes = Array::new(); + hashes.push(sha256.as_str()); + table.insert(&package_name, value(hashes)); + } else { + table.insert(&package_name, value(files)); + } + } + let mut rewritten = lock.to_string(); + if text.contains("\r\n") { + rewritten = rewritten.replace("\r\n", "\n").replace('\n', "\r\n"); + } + let edits = poetry_lock_edits(text, &rewritten, name)?; + let mut result = text.to_string(); + for (original, replacement) in edits { + result = result.replacen(&original, &replacement, 1); + } + Ok(Some(result)) +} + +/// End (exclusive, before its line break) of the first top-level TOML header +/// line at or after `from`, skipping blank lines; `text.len()` at EOF; `from` +/// itself when the next non-blank line is not a header (a shape Poetry never +/// writes — the fragment then ends where it used to). +fn next_header_end(text: &str, from: usize) -> usize { + let mut pos = from; + for line in text[from..].split_inclusive('\n') { + let content = line.trim_end_matches(['\r', '\n']); + // Blank lines and comments sit between units (toml_edit clones carry + // the file's leading comment as decor); they belong to the boundary. + if content.trim().is_empty() || content.trim_start().starts_with('#') { + pos += line.len(); + continue; + } + if content.starts_with('[') { + return pos + content.len(); + } + return from; + } + text.len() +} + +/// The verbatim `(original, replacement)` fragments that turn `original` into +/// `rewritten` for `name`: the package's `[[package]]` unit (with its +/// sub-tables) and, for legacy formats, its `[metadata.files]` / +/// `[metadata.hashes]` entry. Each fragment must occur exactly once on both +/// sides so a textual splice — and its rollback — can never hit the wrong +/// place. +pub fn poetry_lock_edits( + original: &str, + rewritten: &str, + name: &str, +) -> Result, String> { + fn fragments(text: &str, name: &str) -> Result, String> { + let lock = toml_edit::Document::parse(text).map_err(|e| e.to_string())?; + let package = lock + .get("package") + .and_then(Item::as_array_of_tables) + .and_then(|packages| { + packages.iter().find(|package| { + package + .get("name") + .and_then(Item::as_str) + .is_some_and(|value| { + canonicalize_pypi_name(value) == canonicalize_pypi_name(name) + }) + }) + }) + .ok_or("missing Poetry package")?; + fn extend_span(table: &Table, span: &mut std::ops::Range) { + if let Some(own) = table.span() { + span.start = span.start.min(own.start); + span.end = span.end.max(own.end); + } + for (_, item) in table.iter() { + if let Some(own) = item.span() { + span.start = span.start.min(own.start); + span.end = span.end.max(own.end); + } + if let Some(child) = item.as_table() { + extend_span(child, span); + } + } + } + let mut span = package.span().ok_or("missing Poetry package span")?; + extend_span(package, &mut span); + span.end += text[span.end..] + .find(['\r', '\n']) + .unwrap_or(text.len() - span.end); + // Carry the unit's BOUNDARY: the blank line(s) after it plus the next + // top-level header (`[[package]]`, `[metadata]`, `[extras]`, …) or + // EOF. The rewrite APPENDS `[package.source]` to the unit, so without + // the boundary the pristine fragment would be a strict prefix of every + // rewritten (or later relocked) unit: rollback's "already converged" + // check could never fire for lock 1.0, and a relock that dropped the + // inserted `files` line but kept the source block would match the + // pristine prefix and report a successful rollback while the lock + // still redirected. With the header included, the pristine fragment + // matches only a unit that really ends where it ended. + span.end = next_header_end(text, span.end); + let mut result = vec![text[span].to_string()]; + let metadata = lock + .get("metadata") + .filter(|item| item.is_table_like()) + .ok_or("missing Poetry metadata")?; + let format = metadata + .get("lock-version") + .and_then(Item::as_str) + .unwrap_or("0"); + if !format.starts_with('2') { + let field = if format == "0" { "hashes" } else { "files" }; + let table = metadata + .get(field) + .and_then(Item::as_table) + .ok_or_else(|| format!("missing Poetry integrity table [metadata.{field}]"))?; + let package_name = package + .get("name") + .and_then(Item::as_str) + .ok_or("missing package name")?; + let key_start = table + .key(package_name) + .and_then(|key| key.span()) + .ok_or("missing Poetry integrity key")? + .start; + // Anchor the fragment at the preceding line break so a + // suffix-named sibling entry (`pyurllib3 = […]` vs `urllib3 = […]`) + // holding the same value can never contain it. + let start = text[..key_start].rfind('\n').unwrap_or(key_start); + let end = table + .get(package_name) + .and_then(Item::span) + .ok_or("missing Poetry integrity span")? + .end; + result.push(text[start..end].to_string()); + } + Ok(result) + } + let before = fragments(original, name)?; + let after = fragments(rewritten, name)?; + let mut edits = Vec::new(); + for (old, new) in before.into_iter().zip(after) { + if old == new { + continue; + } + if original.matches(&old).count() != 1 || rewritten.matches(&new).count() != 1 { + return Err("ambiguous Poetry rollback fragment".into()); + } + edits.push((old, new)); + } + Ok(edits) +} + +#[cfg(test)] +mod tests { + use super::*; + + const WHEEL: &str = "urllib3-1.26.18-py2.py3-none-any.whl"; + const URL: &str = "https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl"; + + fn fixture(version: &str) -> String { + std::fs::read_to_string(format!( + "{}/tests/fixtures/poetry/{version}/poetry.lock", + env!("CARGO_MANIFEST_DIR") + )) + .unwrap() + .replace("\r\n", "\n") + } + + fn sha() -> String { + "a".repeat(64) + } + + fn hosted(text: &str) -> Result, String> { + rewrite_poetry_lock(text, "urllib3", "1.26.18", "url", URL, WHEEL, &sha()) + } + + /// A user-editable lock with a malformed integrity table must be refused, + /// never panic (it used to `IndexMut` straight into `metadata.files`). + #[test] + fn malformed_integrity_tables_are_refused_without_panicking() { + let lock = fixture("1.2.2"); + let array_of_tables = lock.replace("[metadata.files]", "[[metadata.files]]"); + let scalar = { + let start = lock.find("[metadata.files]").unwrap(); + format!("{}files = \"oops\"\n", &lock[..start]) + }; + let missing = { + let start = lock.find("\n[metadata.files]").unwrap(); + lock[..start + 1].to_string() + }; + let metadata_array = lock.replace("[metadata]", "[[metadata]]"); + for (label, text) in [ + ("array-of-tables", array_of_tables), + ("scalar", scalar), + ("missing", missing), + ("metadata-array", metadata_array), + ] { + match hosted(&text) { + Err(err) => assert!(!err.is_empty(), "{label}"), + Ok(other) => panic!("{label}: expected a refusal, got {other:?}"), + } + // The vendored (file-source) spelling takes the same guarded path. + match rewrite_poetry_lock(&text, "urllib3", "1.26.18", "file", ".socket/vendor/pypi/x/urllib3-1.26.18-py2.py3-none-any.whl", WHEEL, &sha()) { + Err(err) => assert!(!err.is_empty(), "{label}"), + Ok(other) => panic!("{label}: expected a refusal, got {other:?}"), + } + } + } + + /// Poetry bumps the 2.x minor additively; the vendored loader accepts a + /// newer minor with an advisory, so the shared rewriter must too — the + /// same lock used to be applied (LF vendored), hard-failed (CRLF vendored) + /// and silently skipped (hosted) depending only on the path taken. + #[test] + fn newer_2x_minor_is_rewritten_like_2_1() { + let lock = fixture("2.4.3").replace("lock-version = \"2.1\"", "lock-version = \"2.2\""); + let rewritten = hosted(&lock).unwrap().unwrap(); + assert!(rewritten.contains(URL)); + assert!(rewritten.contains("lock-version = \"2.2\"")); + for bad in ["3.0", "2", "2.x", "1.2"] { + let lock = fixture("2.4.3").replace("lock-version = \"2.1\"", &format!("lock-version = \"{bad}\"")); + let err = hosted(&lock).unwrap_err(); + assert!(err.contains(bad), "{bad}: {err}"); + } + } + + /// An earlier hosted redirect of the same wheel (rotated grant token, + /// republished patch) is superseded in place; a foreign url source is not. + #[test] + fn prior_hosted_url_is_superseded_but_foreign_sources_are_refused() { + let lock = fixture("2.4.3"); + let first = hosted(&lock).unwrap().unwrap(); + let rotated = URL.replace("7e52b8b6", "00000000"); + let second = + rewrite_poetry_lock(&first, "urllib3", "1.26.18", "url", &rotated, WHEEL, &sha()) + .unwrap() + .unwrap(); + assert!(second.contains(&rotated) && !second.contains(URL)); + // Idempotent: the same URL again changes nothing. + assert_eq!(hosted(&first).unwrap().unwrap(), first); + // Poetry 1.0 carries a `#sha256=…&` fragment; the comparison ignores it. + let lock10 = fixture("1.0.10"); + let first10 = hosted(&lock10).unwrap().unwrap(); + let second10 = rewrite_poetry_lock(&first10, "urllib3", "1.26.18", "url", &rotated, WHEEL, &"b".repeat(64)) + .unwrap() + .unwrap(); + assert!(second10.contains(&format!("{rotated}#sha256={}&", "b".repeat(64)))); + // A user's own url source on another origin stays untouched. + let foreign = first.replace("https://patch.socket.dev", "https://mirror.example"); + assert!(hosted(&foreign).unwrap_err().contains("existing Poetry source")); + // A vendored file source is never taken over by the hosted path here. + let vendored = rewrite_poetry_lock(&lock, "urllib3", "1.26.18", "file", ".socket/vendor/pypi/x/urllib3-1.26.18-py2.py3-none-any.whl", WHEEL, &sha()) + .unwrap() + .unwrap(); + assert!(hosted(&vendored).unwrap_err().contains("existing Poetry source")); + } + + #[test] + fn sha256_is_written_lowercase() { + let lock = fixture("2.4.3"); + let upper = "A".repeat(64); + let rewritten = + rewrite_poetry_lock(&lock, "urllib3", "1.26.18", "url", URL, WHEEL, &upper) + .unwrap() + .unwrap(); + assert!(rewritten.contains(&format!("sha256:{}", "a".repeat(64)))); + assert!(!rewritten.contains(&upper)); + } + + /// The `[metadata.files]` fragment is anchored at its line break, so a + /// sibling whose key ends in the same characters and holds the same value + /// cannot make the splice ambiguous. + #[test] + fn suffix_named_sibling_with_identical_integrity_does_not_refuse() { + let lock = fixture("1.2.2"); + let files_entry = { + let start = lock.find("urllib3 = [\n").unwrap(); + let end = lock[start..].find("\n]\n").unwrap() + start + 3; + lock[start..end].to_string() + }; + let sibling = files_entry.replacen("urllib3 = [", "pyurllib3 = [", 1); + let lock = format!("{lock}{sibling}"); + let rewritten = hosted(&lock).unwrap().unwrap(); + assert!(rewritten.contains(URL)); + assert!(rewritten.contains(&sibling), "sibling entry must survive verbatim"); + let edits = poetry_lock_edits(&lock, &rewritten, "urllib3").unwrap(); + assert_eq!(edits.len(), 2); + assert!(edits[1].0.starts_with('\n')); + } + + /// The package fragment ends with the NEXT top-level header, so the + /// pristine fragment is never a prefix of the rewritten one (the source + /// block sits between the unit and that header). + #[test] + fn package_fragment_carries_its_boundary_header() { + for version in ["1.0.10", "1.2.2", "2.4.3"] { + let lock = fixture(version); + let rewritten = hosted(&lock).unwrap().unwrap(); + let edits = poetry_lock_edits(&lock, &rewritten, "urllib3").unwrap(); + let (original, new) = &edits[0]; + assert!( + original.ends_with("[metadata]") || original.ends_with("[extras]"), + "{version}: {original:?}" + ); + assert!(new.ends_with("[metadata]") || new.ends_with("[extras]")); + assert!(!new.contains(original.as_str()), "{version}: pristine must not be a prefix of new"); + // A relock that keeps `[package.source]` but drops the inserted + // `files` line must NOT contain the pristine fragment either. + let drifted: String = rewritten + .lines() + .filter(|l| !l.starts_with("files = [{ file")) + .collect::>() + .join("\n"); + assert!(!drifted.contains(original.as_str()), "{version}"); + } + // Two adjacent packages: the first fragment ends with the second's + // header, the second starts with it; both splice independently. + let lock = fixture("2.4.3"); + let mut doc: DocumentMut = lock.parse().unwrap(); + let mut second = doc["package"].as_array_of_tables().unwrap().get(0).unwrap().clone(); + second["name"] = value("six"); + second["version"] = value("1.16.0"); + second.set_position(None); + second.remove("extras"); + doc["package"].as_array_of_tables_mut().unwrap().push(second); + let two = doc.to_string(); + let first = hosted(&two).unwrap().unwrap(); + let edits = poetry_lock_edits(&two, &first, "urllib3").unwrap(); + assert!(edits[0].0.ends_with("[[package]]"), "{:?}", edits[0].0); + assert_eq!(two.matches(edits[0].0.as_str()).count(), 1); + } + + #[test] + fn absent_or_other_version_yields_none_not_error() { + let lock = fixture("2.4.3"); + assert_eq!( + rewrite_poetry_lock(&lock, "six", "1.16.0", "url", &URL.replace("urllib3", "six").replace("1.26.18", "1.16.0"), "six-1.16.0-py2.py3-none-any.whl", &sha()).unwrap(), + None + ); + assert_eq!( + rewrite_poetry_lock(&lock, "urllib3", "1.26.17", "url", &URL.replace("1.26.18", "1.26.17"), "urllib3-1.26.17-py2.py3-none-any.whl", &sha()).unwrap(), + None + ); + } + + #[test] + fn generated_by_header_is_parsed_when_present() { + assert_eq!(generated_by_version(&fixture("1.8.5")), Some((1, 8))); + assert_eq!(generated_by_version(&fixture("2.4.3")), Some((2, 4))); + assert_eq!(generated_by_version(&fixture("1.3.2")), None); + assert_eq!(generated_by_version(&fixture("1.2.2")), None); + } +} diff --git a/crates/socket-patch-core/src/vendor/common.rs b/crates/socket-patch-core/src/vendor/common.rs index 89d303c1..2e23077d 100644 --- a/crates/socket-patch-core/src/vendor/common.rs +++ b/crates/socket-patch-core/src/vendor/common.rs @@ -395,6 +395,42 @@ pub(crate) async fn revert_lock_fragment_splice( lock_file: &str, kind: &str, flavor: &str, +) -> RevertOutcome { + revert_lock_fragment_splice_inner( + entry, root, dry_run, lock_file, kind, flavor, false, + ) + .await +} + +/// [`revert_lock_fragment_splice`] for backends whose records are COUPLED +/// (poetry's legacy formats write the `[package.source]` table and the +/// `[metadata.files]` entry as two fragments): when any recorded fragment has +/// drifted, nothing is written — a half-restored lock (registry hashes with a +/// vendored source, or the reverse) is worse than the wired one. Records this +/// build does not recognize are skipped with a warning as usual and do not +/// hold the write. +pub(crate) async fn revert_lock_fragment_splice_atomic( + entry: &VendorEntry, + root: &Path, + dry_run: bool, + lock_file: &str, + kind: &str, + flavor: &str, +) -> RevertOutcome { + revert_lock_fragment_splice_inner( + entry, root, dry_run, lock_file, kind, flavor, true, + ) + .await +} + +async fn revert_lock_fragment_splice_inner( + entry: &VendorEntry, + root: &Path, + dry_run: bool, + lock_file: &str, + kind: &str, + flavor: &str, + atomic: bool, ) -> RevertOutcome { use tokio::io::AsyncReadExt as _; @@ -413,6 +449,13 @@ pub(crate) async fn revert_lock_fragment_splice( Err(e) => return RevertOutcome::failed(format!("cannot read {lock_file}: {e}")), }; let mut warnings: Vec = Vec::new(); + // Set when a recorded fragment is neither present nor already restored: + // the only condition under which the atomic flavor must hold the write + // (restoring the source table while its integrity entry stays patched, or + // vice versa, would leave a lock Poetry cannot install). A record this + // build does not understand (foreign file, unknown kind) is skipped with a + // warning but must not veto restoring the fragments it does understand. + let mut drifted = false; for rec in entry.wiring.iter().rev() { // SECURITY: `rec.file` comes verbatim from the committed, tamper-able @@ -454,6 +497,7 @@ pub(crate) async fn revert_lock_fragment_splice( if original_text.is_some_and(|orig| lock_text.contains(orig)) { continue; } + drifted = true; warnings.push(VendorWarning::new( "vendor_lock_entry_drifted", format!( @@ -465,7 +509,7 @@ pub(crate) async fn revert_lock_fragment_splice( } } - if !dry_run { + if !dry_run && (!atomic || !drifted) { // Mode-preserving: the lock is a user-owned file we merely edit, so // the swapped-in inode must keep its permission bits rather than // reset them to umask defaults. diff --git a/crates/socket-patch-core/src/vendor/lock_inventory.rs b/crates/socket-patch-core/src/vendor/lock_inventory.rs index 8ee9a9d0..5ba45798 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory.rs @@ -1249,12 +1249,65 @@ fn python_lock_inventory(text: &str) -> Option> { Some(out) } -/// poetry.lock: `[[package]]` blocks with `name`/`version` — discovery -/// only (file hashes exist but carry no URLs and no platform choice). +/// The sha256 of each package's pure-Python (`-none-any.whl`) wheel as the +/// lock records it — `files = [...]` inside `[[package]]` (lock 2.x) or the +/// `[metadata.files]` entry (lock 1.0/1.1). Poetry 0.12's `[metadata.hashes]` +/// lists bare digests without filenames, so no wheel can be chosen there. +/// Keyed by canonical name. An unparseable lock contributes nothing (the +/// line-based name/version walk below still runs). +fn poetry_pure_wheel_hashes(text: &str) -> HashMap { + fn pure_wheel_sha(files: &Item) -> Option { + let files = files.as_array()?; + files + .iter() + .filter_map(TomlValue::as_inline_table) + .find_map(|entry| { + let file = entry.get("file")?.as_str()?; + if !file.ends_with("-none-any.whl") { + return None; + } + let sha = entry.get("hash")?.as_str()?.strip_prefix("sha256:")?; + is_hex_of_len(sha, 64).then(|| sha.to_ascii_lowercase()) + }) + } + let mut out = HashMap::new(); + let Ok(document) = text.parse::() else { + return out; + }; + if let Some(packages) = document.get("package").and_then(Item::as_array_of_tables) { + for package in packages.iter() { + let Some(name) = package.get("name").and_then(Item::as_str) else { + continue; + }; + if let Some(sha) = package.get("files").and_then(pure_wheel_sha) { + out.entry(canonicalize_pypi_name(name)).or_insert(sha); + } + } + } + if let Some(files) = document + .get("metadata") + .and_then(|m| m.get("files")) + .and_then(Item::as_table_like) + { + for (name, entry) in files.iter() { + if let Some(sha) = pure_wheel_sha(entry) { + out.entry(canonicalize_pypi_name(name)).or_insert(sha); + } + } + } + out +} + +/// poetry.lock: `[[package]]` blocks with `name`/`version`. The lock records +/// file hashes but no URLs and no platform choice, so an entry carries the +/// pure-Python wheel's sha256 when the lock lists one (the pypi fetcher then +/// resolves the matching file through PyPI's JSON API) and stays +/// discovery-only otherwise. async fn inventory_poetry_lock(project_root: &Path) -> Option> { let text = read_regular_to_string(&project_root.join("poetry.lock")) .await .ok()?; + let hashes = poetry_pure_wheel_hashes(&text); let mut out = Vec::new(); let mut in_package = false; let mut name: Option = None; @@ -1280,13 +1333,17 @@ async fn inventory_poetry_lock(project_root: &Path) -> Option if path_safety::is_safe_single_segment(&n) && path_safety::is_safe_single_segment(&v) { + let integrity = hashes + .get(&n) + .map(|sha| LockIntegrity::Sha256Hex(sha.clone())) + .unwrap_or(LockIntegrity::None); out.push(LockfileEntry { ecosystem: "pypi", purl: format!("pkg:pypi/{n}@{v}"), name: n, version: v, resolved: None, - integrity: LockIntegrity::None, + integrity, }); } } @@ -3225,6 +3282,43 @@ source = { editable = "." } assert_eq!(entries[0].purl, "pkg:pypi/requests@2.28.0"); } + /// A lock that lists a pure-Python wheel carries its sha256 (lock 2.x + /// `files`, lock 1.x `[metadata.files]`), so a lock-only checkout can + /// vendor like uv does; platform wheels only, or 0.12's bare + /// `[metadata.hashes]`, stay discovery-only. + #[tokio::test] + async fn poetry_lock_carries_the_pure_wheel_sha256_when_listed() { + let sha = "34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"; + let lock2 = format!( + "[[package]]\nname = \"urllib3\"\nversion = \"1.26.18\"\nfiles = [\n {{file = \"urllib3-1.26.18.tar.gz\", hash = \"sha256:{}\"}},\n {{file = \"urllib3-1.26.18-py2.py3-none-any.whl\", hash = \"sha256:{sha}\"}},\n]\n\n[[package]]\nname = \"numpy\"\nversion = \"2.0.0\"\nfiles = [\n {{file = \"numpy-2.0.0-cp312-cp312-macosx_11_0_arm64.whl\", hash = \"sha256:{}\"}},\n]\n\n[metadata]\nlock-version = \"2.1\"\n", + "f".repeat(64), + "e".repeat(64) + ); + let tmp = tempfile::tempdir().unwrap(); + write(tmp.path(), "poetry.lock", &lock2).await; + let entries = inventory_pypi_locks(tmp.path()).await.unwrap(); + assert_eq!(entry(&entries, "urllib3").integrity, LockIntegrity::Sha256Hex(sha.into())); + assert_eq!(entry(&entries, "urllib3").resolved, None); + assert_eq!(entry(&entries, "numpy").integrity, LockIntegrity::None); + + let lock1 = format!( + "[[package]]\nname = \"urllib3\"\nversion = \"1.26.18\"\n\n[metadata]\nlock-version = \"1.1\"\n\n[metadata.files]\nurllib3 = [\n {{file = \"urllib3-1.26.18-py2.py3-none-any.whl\", hash = \"sha256:{}\"}},\n]\n", + sha.to_uppercase() + ); + let tmp = tempfile::tempdir().unwrap(); + write(tmp.path(), "poetry.lock", &lock1).await; + let entries = inventory_pypi_locks(tmp.path()).await.unwrap(); + assert_eq!(entry(&entries, "urllib3").integrity, LockIntegrity::Sha256Hex(sha.into()), "lowercased"); + + let lock0 = format!( + "[[package]]\nname = \"urllib3\"\nversion = \"1.26.18\"\n\n[metadata]\ncontent-hash = \"x\"\n\n[metadata.hashes]\nurllib3 = [\"{sha}\"]\n" + ); + let tmp = tempfile::tempdir().unwrap(); + write(tmp.path(), "poetry.lock", &lock0).await; + let entries = inventory_pypi_locks(tmp.path()).await.unwrap(); + assert_eq!(entry(&entries, "urllib3").integrity, LockIntegrity::None, "bare digests name no wheel"); + } + #[tokio::test] async fn pnp_layouts_propagate_the_diagnosis_instead_of_yielding_none() { // PnP marker wins over any lockfile — and the diagnosis must diff --git a/crates/socket-patch-core/src/vendor/pypi_poetry.rs b/crates/socket-patch-core/src/vendor/pypi_poetry.rs index 164984fd..c2c7e736 100644 --- a/crates/socket-patch-core/src/vendor/pypi_poetry.rs +++ b/crates/socket-patch-core/src/vendor/pypi_poetry.rs @@ -1,21 +1,4 @@ -//! poetry-project wiring: a lock-ONLY `[[package]]` splice (poetry.lock -//! lock-versions 2.0 and 2.1). -//! -//! Unlike uv (whose sources entry must be paired into pyproject.toml), poetry -//! installs are 100% lock-driven and `metadata.content-hash` covers ONLY the -//! pyproject — so the vendored wheel is wired by rewriting just the target -//! `[[package]]` unit (files[] → the single patched-wheel hash, plus a -//! `[package.source] type = "file"` table) and touching nothing else. The -//! spike proved this splice passes `poetry install`/`sync`/`check --lock` -//! byte-stably on BOTH supported majors (Poetry 2.4.1 = lock 2.1, Poetry -//! 1.8.5 = lock 2.0), is hash-fail-closed against a tampered wheel, and works -//! for direct AND transitive deps — see `spikes/poetry/` and the poetry -//! section of `spikes/PHASE0-V2-FINDINGS.txt`. -//! -//! Drift caveat (spike P5): `poetry update `, 2.x `poetry lock -//! --regenerate` and 1.x plain `poetry lock` silently revert the splice with -//! exit 0; the lock's files[] hash is the drift oracle. `pyproject.toml` and -//! `metadata.content-hash` are NEVER written by this backend. +//! Poetry lock wiring preserves the pyproject content hash and records reversible edits. use std::path::Path; @@ -25,7 +8,7 @@ use crate::crawlers::python_crawler::canonicalize_pypi_name; use crate::utils::fs::atomic_write_bytes_preserving_mode; use super::common::{ - item_get, lock_units_named, pep621_declared_names, record, revert_lock_fragment_splice, + item_get, lock_units_named, pep621_declared_names, record, revert_lock_fragment_splice_atomic, unit_has_canon_name, }; use super::path::parse_vendor_path; @@ -108,16 +91,21 @@ pub(super) async fn load_poetry_project( .and_then(|m| item_get(m, "lock-version")) .and_then(Item::as_str) .map(str::to_string) + .or_else(|| { + crate::utils::poetry_lock::lock_version(&lock) + .ok() + .map(str::to_string) + }) .ok_or_else(|| { ( "pypi_poetry_lock_version_unsupported", - format!("{LOCK_FILE} has no [metadata] lock-version; only 2.x locks are supported"), + format!("{LOCK_FILE} has no [metadata] lock-version; supported locks are legacy hashes, 1.0, 1.1, and 2.x"), ) })?; let mut warnings = Vec::new(); match lock_version.as_str() { // The fixture-tested versions (Poetry 1.8.x writes 2.0, 2.x writes 2.1). - "2.0" | "2.1" => {} + "0" | "1.0" | "1.1" | "2.0" | "2.1" => {} // A newer 2.x minor keeps the shapes we rewrite (additive schema), so // it warns instead of refusing; `poetry check --lock` is the backstop. v if is_newer_2x(v) => warnings.push(VendorWarning::new( @@ -131,12 +119,37 @@ pub(super) async fn load_poetry_project( return Err(( "pypi_poetry_lock_version_unsupported", format!( - "poetry.lock lock-version {v:?} is not a supported 2.x lock; re-lock with \ + "poetry.lock lock-version {v:?} is not a supported lock; re-lock with \ Poetry >= 1.3" ), )) } } + // Poetry < 1.4 neither verifies local wheel hashes nor replaces an + // already-installed package at the same version (a warm virtualenv keeps + // the upstream bytes after the lock is rewired). Formats 0/1.0/1.1 are + // only written by those releases; lock 2.0 is written by 1.3 through 1.8, + // so the `@generated by Poetry X.Y.Z` header (present from 1.4) decides. + let pre_1_4_writer = match lock_version.as_str() { + "0" | "1.0" | "1.1" => true, + "2.0" => !matches!( + crate::utils::poetry_lock::generated_by_version(&lock_text), + Some(v) if v >= (1, 4) + ), + _ => false, + }; + if pre_1_4_writer { + warnings.push(VendorWarning::new( + "pypi_poetry_integrity_unverified", + format!( + "{LOCK_FILE} was written by Poetry < 1.4: that installer does not verify local \ + wheel hashes (the committed wheel bytes are the protection — review them) and \ + does not replace an already-installed package at the same version — upgrade to \ + Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) \ + before `poetry install`" + ), + )); + } let pyproject_text = read_regular_to_string(&root.join("pyproject.toml")) .await @@ -303,14 +316,39 @@ pub(super) async fn wire_poetry( PoetryTarget::Fresh => {} } - let (old_unit, new_unit) = rewrite_target_package_unit( - &p.lock_text, - canon_name, - rel_wheel, - wheel_file_name, - wheel_sha256_hex, - )?; - let new_lock = p.lock_text.replacen(&old_unit, &new_unit, 1); + let edits = + if matches!(p.lock_version.as_str(), "0" | "1.0" | "1.1") || p.lock_text.contains("\r\n") { + let rewritten = crate::utils::poetry_lock::rewrite_poetry_lock( + &p.lock_text, + canon_name, + version, + "file", + rel_wheel, + wheel_file_name, + wheel_sha256_hex, + ) + .map_err(|detail| ("pypi_poetry_lock_parse_failed", detail))? + .ok_or_else(|| { + ( + "pypi_poetry_lock_package_missing", + format!("no {canon_name}@{version} in {LOCK_FILE}"), + ) + })?; + crate::utils::poetry_lock::poetry_lock_edits(&p.lock_text, &rewritten, canon_name) + .map_err(|detail| ("pypi_poetry_lock_parse_failed", detail))? + } else { + vec![rewrite_target_package_unit( + &p.lock_text, + canon_name, + rel_wheel, + wheel_file_name, + wheel_sha256_hex, + )?] + }; + let mut new_lock = p.lock_text.clone(); + for (old_unit, new_unit) in &edits { + new_lock = new_lock.replacen(old_unit, new_unit, 1); + } // Mode-preserving: the lock is a user-owned file we merely edit, so the // swapped-in inode must keep its permission bits rather than reset them // to umask defaults (same class as the revert leg in common.rs). @@ -323,14 +361,19 @@ pub(super) async fn wire_poetry( ) })?; - let wiring = vec![record( - LOCK_FILE, - KIND_LOCK_PACKAGE, - WiringAction::Rewritten, - canon_name, - Some(old_unit), - new_unit, - )]; + let wiring = edits + .into_iter() + .map(|(old_unit, new_unit)| { + record( + LOCK_FILE, + KIND_LOCK_PACKAGE, + WiringAction::Rewritten, + canon_name, + Some(old_unit), + new_unit, + ) + }) + .collect(); let meta = PoetryMeta { dep_class: classify_dependency(p, canon_name).to_string(), lock_version: p.lock_version.clone(), @@ -346,7 +389,8 @@ pub(super) async fn revert_poetry( root: &Path, dry_run: bool, ) -> RevertOutcome { - revert_lock_fragment_splice(entry, root, dry_run, LOCK_FILE, KIND_LOCK_PACKAGE, "poetry").await + revert_lock_fragment_splice_atomic(entry, root, dry_run, LOCK_FILE, KIND_LOCK_PACKAGE, "poetry") + .await } // ── helpers ────────────────────────────────────────────────────────────── @@ -824,6 +868,232 @@ content-hash = "4b42a89b7ff7b26511b06acdc458dbd85312e5083db8f212b017482bc68cdd01 .unwrap() } + /// The pre-1.4 advisory follows the WRITER: every 0/1.0/1.1 lock, and a + /// 2.0 lock without a `@generated by Poetry X.Y.Z` header (1.3 wrote + /// those); 1.4–1.8 also write 2.0 but stamp their version, and verify + /// hashes / reinstall over a warm venv, so they must not be blamed. + #[tokio::test] + async fn integrity_advisory_tracks_pre_1_4_writers_not_the_bare_format() { + for (version, expect) in [ + ("0.12.17", true), + ("1.0.10", true), + ("1.1.15", true), + ("1.2.2", true), + ("1.3.2", true), + ("1.4.2", false), + ("1.8.5", false), + ("2.0.1", false), + ("2.4.3", false), + ] { + let native = std::fs::read_to_string(format!( + "{}/tests/fixtures/poetry/{version}/poetry.lock", + env!("CARGO_MANIFEST_DIR") + )) + .unwrap(); + let tmp = write_project(&native, PYPROJECT_DIRECT).await; + let project = load_poetry_project(tmp.path()).await.unwrap(); + let fired = project + .warnings + .iter() + .any(|w| w.code == "pypi_poetry_integrity_unverified"); + assert_eq!(fired, expect, "{version}: {:?}", project.warnings); + } + } + + /// A newer ledger's record this build cannot replay (unknown kind / + /// foreign file) is skipped with a warning; it must NOT hold the atomic + /// write hostage — the fragments this build does understand are restored. + #[tokio::test] + async fn atomic_revert_ignores_unknown_records_but_holds_on_drift() { + let native = + include_str!("../../tests/fixtures/poetry/1.2.2/poetry.lock").replace("\r\n", "\n"); + let tmp = write_project(&native, PYPROJECT_DIRECT).await; + let project = load_poetry_project(tmp.path()).await.unwrap(); + let wheel = "urllib3-1.26.18-py2.py3-none-any.whl"; + let path = format!(".socket/vendor/pypi/{UUID}/{wheel}"); + let (mut wiring, meta) = wire_poetry( + &project, + tmp.path(), + "urllib3", + "1.26.18", + &path, + wheel, + WHEEL_SHA, + UUID, + ) + .await + .unwrap(); + assert_eq!(wiring.len(), 2, "source table + metadata.files entry"); + wiring.push(record( + "poetry.lock", + "poetry_future_kind", + WiringAction::Rewritten, + "urllib3", + Some("x".into()), + "y".into(), + )); + wiring.push(record( + "pyproject.toml", + KIND_LOCK_PACKAGE, + WiringAction::Rewritten, + "urllib3", + Some("x".into()), + "y".into(), + )); + let outcome = revert_poetry(&entry_for(wiring.clone(), meta.clone()), tmp.path(), false).await; + assert!(outcome.success); + assert_eq!(outcome.warnings.len(), 2, "{:?}", outcome.warnings); + assert!(outcome + .warnings + .iter() + .all(|w| w.code == "vendor_lock_entry_drifted")); + assert_eq!(read_lock(tmp.path()).await, native, "known fragments restored"); + + // Re-wire, then drift one fragment: now the atomic write must hold. + let project = load_poetry_project(tmp.path()).await.unwrap(); + let (wiring, meta) = wire_poetry( + &project, + tmp.path(), + "urllib3", + "1.26.18", + &path, + wheel, + WHEEL_SHA, + UUID, + ) + .await + .unwrap(); + let drifted = read_lock(tmp.path()) + .await + .replace("HTTP library", "Edited description"); + tokio::fs::write(tmp.path().join("poetry.lock"), &drifted) + .await + .unwrap(); + let outcome = revert_poetry(&entry_for(wiring, meta), tmp.path(), false).await; + assert!(!outcome.warnings.is_empty()); + assert_eq!(read_lock(tmp.path()).await, drifted, "half-restore refused"); + } + + #[tokio::test] + async fn legacy_revert_keeps_source_and_hash_together_on_drift() { + let native = + include_str!("../../tests/fixtures/poetry/1.1.15/poetry.lock").replace("\r\n", "\n"); + let lock = native.replace("urllib3 = []", &format!("urllib3 = [{{file = \"urllib3-1.26.18-py2.py3-none-any.whl\", hash = \"sha256:{}\"}}]", "b".repeat(64))); + for crlf in [false, true] { + let pristine = if crlf { + lock.replace('\n', "\r\n") + } else { + lock.clone() + }; + let tmp = write_project(&pristine, PYPROJECT_DIRECT).await; + let project = load_poetry_project(tmp.path()).await.unwrap(); + let wheel = "urllib3-1.26.18-py2.py3-none-any.whl"; + let path = format!(".socket/vendor/pypi/{UUID}/{wheel}"); + let (wiring, meta) = wire_poetry( + &project, + tmp.path(), + "urllib3", + "1.26.18", + &path, + wheel, + WHEEL_SHA, + UUID, + ) + .await + .unwrap(); + let drifted = read_lock(tmp.path()) + .await + .replace("HTTP library", "Edited description"); + tokio::fs::write(tmp.path().join("poetry.lock"), &drifted) + .await + .unwrap(); + let outcome = revert_poetry(&entry_for(wiring, meta), tmp.path(), false).await; + assert!(!outcome.warnings.is_empty()); + assert_eq!(read_lock(tmp.path()).await, drifted); + } + } + + #[tokio::test] + async fn legacy_and_crlf_patches_revert_independently() { + for version in ["0.12.17", "1.0.10", "1.1.15", "1.2.2", "1.8.5", "2.4.3"] { + for crlf in [false, true] { + for reverse in [false, true] { + let native = std::fs::read_to_string(format!( + "{}/tests/fixtures/poetry/{version}/poetry.lock", + env!("CARGO_MANIFEST_DIR") + )) + .unwrap(); + let mut lock: DocumentMut = native.parse().unwrap(); + let packages = lock["package"].as_array_of_tables_mut().unwrap(); + let mut second = packages.get(0).unwrap().clone(); + second["name"] = toml_edit::value("six"); + second["version"] = toml_edit::value("1.16.0"); + second.set_position(None); + second.remove("extras"); + packages.push(second); + for field in ["files", "hashes"] { + if let Some(entries) = lock["metadata"].get_mut(field) { + if let Some(value) = entries.get("urllib3").cloned() { + entries["six"] = value; + } + } + } + let pristine = lock.to_string(); + let pristine = if crlf { + pristine.replace('\n', "\r\n") + } else { + pristine + }; + let tmp = write_project(&pristine, PYPROJECT_DIRECT).await; + let mut entries = Vec::new(); + for (name, version, wheel) in [ + ("urllib3", "1.26.18", "urllib3-1.26.18-py2.py3-none-any.whl"), + ("six", "1.16.0", WHEEL_NAME), + ] { + let project = load_poetry_project(tmp.path()).await.unwrap(); + let path = format!(".socket/vendor/pypi/{UUID}/{wheel}"); + let (wiring, meta) = wire_poetry( + &project, + tmp.path(), + name, + version, + &path, + wheel, + WHEEL_SHA, + UUID, + ) + .await + .unwrap(); + entries.push(entry_for(wiring, meta)); + } + if reverse { + entries.reverse(); + } + let comment = if crlf { + "# retained edit\r\n" + } else { + "# retained edit\n" + }; + tokio::fs::write( + tmp.path().join("poetry.lock"), + format!("{comment}{}", read_lock(tmp.path()).await), + ) + .await + .unwrap(); + for entry in entries { + let outcome = revert_poetry(&entry, tmp.path(), false).await; + assert!( + outcome.success && outcome.warnings.is_empty(), + "{version}: {:?}", + outcome.warnings + ); + } + assert_eq!(read_lock(tmp.path()).await, format!("{comment}{pristine}")); + } + } + } + } + /// The load-bearing oracle: wiring the registry lock must produce the /// spliced evidence-lockonly lock BYTE-IDENTICALLY (per lock version, /// direct and transitive), leaving pyproject and content-hash untouched. @@ -982,35 +1252,18 @@ content-hash = "4b42a89b7ff7b26511b06acdc458dbd85312e5083db8f212b017482bc68cdd01 ); } - /// A CRLF lock (git autocrlf checkout) parses fine, but the splice - /// fragment is re-derived via `str::lines()` (which strips `\r`) and can - /// never byte-match the file — the replacen would silently no-op while - /// still reporting success and recording a rewrite that never landed. - /// Wiring must refuse instead. #[tokio::test] - async fn crlf_lock_refuses_instead_of_silently_wiring_nothing() { + async fn crlf_lock_wires_and_reverts_without_changing_line_endings() { let crlf = LOCK21_DIRECT_REGISTRY.replace('\n', "\r\n"); let tmp = write_project(&crlf, PYPROJECT_DIRECT).await; - let p = load_poetry_project(tmp.path()).await.unwrap(); - assert_eq!( - check_target_guards(&p, "six", "1.16.0", UUID).unwrap(), - PoetryTarget::Fresh - ); - - let err = wire_poetry( - &p, - tmp.path(), - "six", - "1.16.0", - REL_WHEEL, - WHEEL_NAME, - WHEEL_SHA, - UUID, - ) - .await - .unwrap_err(); - assert_eq!(err.0, "pypi_poetry_lock_parse_failed"); - assert_eq!(read_lock(tmp.path()).await, crlf, "refusal writes nothing"); + let project = load_poetry_project(tmp.path()).await.unwrap(); + let (wiring, meta) = wire_default(&project, tmp.path()).await; + let rewritten = read_lock(tmp.path()).await; + assert!(rewritten.contains(REL_WHEEL)); + assert!(!rewritten.replace("\r\n", "").contains('\n')); + let outcome = revert_poetry(&entry_for(wiring, meta), tmp.path(), false).await; + assert!(outcome.success); + assert_eq!(read_lock(tmp.path()).await, crlf); } /// Valid TOML poetry itself never emits — `name="six"` with no spaces @@ -1093,7 +1346,7 @@ content-hash = "4b42a89b7ff7b26511b06acdc458dbd85312e5083db8f212b017482bc68cdd01 let tmp = write_project("[[package]]\nname = \"six\"\n", PYPROJECT_DIRECT).await; let err = load_poetry_project(tmp.path()).await.unwrap_err(); assert_eq!(err.0, "pypi_poetry_lock_version_unsupported"); - for bad in ["1.1", "3.0"] { + for bad in ["1.2", "3.0"] { let lock = LOCK21_DIRECT_REGISTRY.replace( "lock-version = \"2.1\"", &format!("lock-version = \"{bad}\""), diff --git a/crates/socket-patch-core/src/vendor/registry_fetch.rs b/crates/socket-patch-core/src/vendor/registry_fetch.rs index c87b4d22..c67b7048 100644 --- a/crates/socket-patch-core/src/vendor/registry_fetch.rs +++ b/crates/socket-patch-core/src/vendor/registry_fetch.rs @@ -290,16 +290,85 @@ async fn fetch_gem( /// wheel IS a site-packages layout (package dirs + `.dist-info/RECORD` at /// the root), which is exactly the shape the pypi vendor backend stages /// from. +/// PyPI's JSON API base; override with `SOCKET_PYPI_JSON_API` (tests point it +/// at a mock). Used only to turn a lock's file hash into a download URL for +/// locks that record hashes without URLs (poetry.lock). +pub const DEFAULT_PYPI_JSON_API: &str = "https://pypi.org/pypi"; + +fn pypi_json_api_base() -> String { + std::env::var("SOCKET_PYPI_JSON_API") + .ok() + .map(|v| v.trim_end_matches('/').to_string()) + .filter(|v| !v.is_empty()) + .unwrap_or_else(|| DEFAULT_PYPI_JSON_API.to_string()) +} + +/// Resolve the download URL of the release file whose sha256 the lock +/// records, via `GET ///json` → `urls[].digests.sha256`. +/// The hash, not the filename, selects the file, so a lock that names a wheel +/// PyPI has since re-uploaded under the same name cannot be satisfied by +/// different bytes — the download is still verified against the same hash. +async fn resolve_pypi_url_by_hash( + entry: &LockfileEntry, + sha256: &str, + client: &reqwest::Client, +) -> Result { + let api = format!( + "{}/{}/{}/json", + pypi_json_api_base(), + entry.name, + entry.version + ); + let resp = client.get(&api).send().await.map_err(|e| { + FetchError::Failed(format!("PyPI JSON API request for {} failed: {e}", entry.purl)) + })?; + if !resp.status().is_success() { + return Err(FetchError::Failed(format!( + "PyPI JSON API returned HTTP {} for {}", + resp.status(), + entry.purl + ))); + } + let body: serde_json::Value = resp.json().await.map_err(|e| { + FetchError::Failed(format!("PyPI JSON API response for {} is not JSON: {e}", entry.purl)) + })?; + body.get("urls") + .and_then(serde_json::Value::as_array) + .into_iter() + .flatten() + .find(|file| { + file.get("digests") + .and_then(|d| d.get("sha256")) + .and_then(serde_json::Value::as_str) + .is_some_and(|d| d.eq_ignore_ascii_case(sha256)) + }) + .and_then(|file| file.get("url").and_then(serde_json::Value::as_str)) + .map(str::to_string) + .ok_or_else(|| { + FetchError::Unverifiable(format!( + "no PyPI release file for {}@{} matches the lockfile's sha256 {sha256}", + entry.name, entry.version + )) + }) +} + async fn fetch_pypi( entry: &LockfileEntry, client: &reqwest::Client, ) -> Result { - let Some(url) = entry.resolved.clone() else { - return Err(FetchError::Unverifiable(format!( - "the lockfile records no platform-independent wheel URL for {}@{} (only uv.lock \ - carries fetchable wheel resolutions today)", - entry.name, entry.version - ))); + let url = match (&entry.resolved, &entry.integrity) { + (Some(url), _) => url.clone(), + // poetry.lock records the wheel's hash but no URL: look the file up + // by that hash (verified again after download). + (None, LockIntegrity::Sha256Hex(sha256)) => { + resolve_pypi_url_by_hash(entry, sha256, client).await? + } + (None, _) => { + return Err(FetchError::Unverifiable(format!( + "the lockfile records no platform-independent wheel URL or sha256 for {}@{}", + entry.name, entry.version + ))); + } }; let bytes = download(client, &url).await.map_err(FetchError::Failed)?; verify_integrity(&bytes, &entry.integrity)?; @@ -1747,14 +1816,71 @@ mod tests { .join("requests-2.28.0.dist-info/RECORD") .is_file()); - // No recorded wheel URL (poetry/requirements) → Unverifiable. + } + + /// poetry.lock records wheel hashes but no URLs: the fetcher resolves the + /// file through PyPI's JSON API by sha256 and still verifies the bytes. + #[tokio::test] + #[serial_test::serial] + async fn pypi_hash_only_entry_is_resolved_through_the_json_api() { + let wheel = make_zip(&[ + ("requests/__init__.py", b"__version__ = '2.28.0'\n"), + ("requests-2.28.0.dist-info/RECORD", b"requests/__init__.py,sha256=abc,24\n"), + ]); + let sha = hex::encode(Sha256::digest(&wheel)); + let mock = MockServer::start().await; + Mock::given(method("GET")) + .and(url_path("/packages/requests-2.28.0-py3-none-any.whl")) + .respond_with(ResponseTemplate::new(200).set_body_bytes(wheel)) + .mount(&mock) + .await; + Mock::given(method("GET")) + .and(url_path("/pypi/requests/2.28.0/json")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "urls": [ + {"filename": "requests-2.28.0.tar.gz", "url": format!("{}/packages/requests-2.28.0.tar.gz", mock.uri()), "digests": {"sha256": "0".repeat(64)}}, + {"filename": "requests-2.28.0-py3-none-any.whl", "url": format!("{}/packages/requests-2.28.0-py3-none-any.whl", mock.uri()), "digests": {"sha256": sha.to_uppercase()}}, + ] + }))) + .mount(&mock) + .await; + let saved = std::env::var("SOCKET_PYPI_JSON_API").ok(); + std::env::set_var("SOCKET_PYPI_JSON_API", format!("{}/pypi/", mock.uri())); + let restore = || match &saved { + Some(v) => std::env::set_var("SOCKET_PYPI_JSON_API", v), + None => std::env::remove_var("SOCKET_PYPI_JSON_API"), + }; let entry = LockfileEntry { + ecosystem: "pypi", + name: "requests".into(), + version: "2.28.0".into(), + purl: "pkg:pypi/requests@2.28.0".into(), resolved: None, - integrity: LockIntegrity::Sha256Hex("0".repeat(64)), + integrity: LockIntegrity::Sha256Hex(sha.clone()), + }; + let fetched = fetch_and_stage(&entry, &build_registry_client()).await; + // A hash no release file carries is refused before any download. + let unknown = LockfileEntry { + integrity: LockIntegrity::Sha256Hex("1".repeat(64)), + ..entry.clone() + }; + let missing = fetch_and_stage(&unknown, &build_registry_client()).await; + // No hash at all: nothing to resolve by. + let bare = LockfileEntry { + integrity: LockIntegrity::Sri("sha512-x".into()), ..entry }; - match fetch_and_stage(&entry, &build_registry_client()).await { - Err(FetchError::Unverifiable(msg)) => assert!(msg.contains("wheel"), "{msg}"), + let bare_result = fetch_and_stage(&bare, &build_registry_client()).await; + restore(); + let fetched = fetched.unwrap(); + assert!(fetched.dir().join("requests/__init__.py").is_file()); + assert!(fetched.url.ends_with("requests-2.28.0-py3-none-any.whl")); + match missing { + Err(FetchError::Unverifiable(msg)) => assert!(msg.contains("matches"), "{msg}"), + other => panic!("expected Unverifiable, got {other:?}"), + } + match bare_result { + Err(FetchError::Unverifiable(msg)) => assert!(msg.contains("sha256"), "{msg}"), other => panic!("expected Unverifiable, got {other:?}"), } } @@ -1928,13 +2054,15 @@ mod tests { #[tokio::test] async fn pypi_no_wheel_url_message_is_single_spaced() { + // No URL and no sha256 to resolve one by (a sha256 would consult the + // PyPI JSON API — `pypi_hash_only_entry_is_resolved_through_the_json_api`). let entry = LockfileEntry { ecosystem: "pypi", name: "requests".into(), version: "2.28.0".into(), purl: "pkg:pypi/requests@2.28.0".into(), resolved: None, - integrity: LockIntegrity::Sha256Hex("0".repeat(64)), + integrity: LockIntegrity::Sri("sha512-x".into()), }; match fetch_and_stage(&entry, &build_registry_client()).await { Err(FetchError::Unverifiable(msg)) => assert!( diff --git a/crates/socket-patch-core/tests/fixtures/poetry/0.12.17/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/0.12.17/poetry.lock new file mode 100644 index 00000000..b9148ce1 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/0.12.17/poetry.lock @@ -0,0 +1,14 @@ +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +version = "1.26.18" + +[metadata] +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" +python-versions = ">=3.8" + +[metadata.hashes] +urllib3 = [] diff --git a/crates/socket-patch-core/tests/fixtures/poetry/0.12.17/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/0.12.17/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/0.12.17/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.0.10/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.0.10/poetry.lock new file mode 100644 index 00000000..617f6844 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.0.10/poetry.lock @@ -0,0 +1,20 @@ +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +version = "1.26.18" + +[package.extras] +brotli = ["brotlicffi (>=0.8.0)", "brotli (1.0.9)", "brotlipy (>=0.6.0)", "brotli (>=1.0.9)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[metadata] +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" +lock-version = "1.0" +python-versions = ">=3.8" + +[metadata.files] +urllib3 = [] diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.0.10/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.0.10/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.0.10/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.1.15/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.1.15/poetry.lock new file mode 100644 index 00000000..afb7e058 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.1.15/poetry.lock @@ -0,0 +1,20 @@ +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" + +[metadata.files] +urllib3 = [] diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.1.15/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.1.15/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.1.15/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.2.2/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.2.2/poetry.lock new file mode 100644 index 00000000..81885b17 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.2.2/poetry.lock @@ -0,0 +1,23 @@ +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" + +[metadata.files] +urllib3 = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.2.2/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.2.2/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.2.2/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.3.2/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.3.2/poetry.lock new file mode 100644 index 00000000..1fc3bf45 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.3.2/poetry.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.3.2/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.3.2/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.3.2/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.4.2/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.4.2/poetry.lock new file mode 100644 index 00000000..6cf04876 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.4.2/poetry.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.4.2/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.4.2/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.4.2/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.5.1/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.5.1/poetry.lock new file mode 100644 index 00000000..5fc7fd61 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.5.1/poetry.lock @@ -0,0 +1,22 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.5.1/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.5.1/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.5.1/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.6.1/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.6.1/poetry.lock new file mode 100644 index 00000000..fa84f4d0 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.6.1/poetry.lock @@ -0,0 +1,22 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.6.1/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.6.1/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.6.1/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.7.1/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.7.1/poetry.lock new file mode 100644 index 00000000..f2eab4d2 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.7.1/poetry.lock @@ -0,0 +1,22 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.7.1/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.7.1/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.7.1/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.8.5/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/1.8.5/poetry.lock new file mode 100644 index 00000000..4ccb63f6 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.8.5/poetry.lock @@ -0,0 +1,22 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/1.8.5/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/1.8.5/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/1.8.5/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.0.1/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/2.0.1/poetry.lock new file mode 100644 index 00000000..43ce61ce --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.0.1/poetry.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +groups = ["main"] +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.0.1/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/2.0.1/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.0.1/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.1.4/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/2.1.4/poetry.lock new file mode 100644 index 00000000..ce51e702 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.1.4/poetry.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +groups = ["main"] +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.1.4/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/2.1.4/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.1.4/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.2.1/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/2.2.1/poetry.lock new file mode 100644 index 00000000..5fca1f1e --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.2.1/poetry.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +groups = ["main"] +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.2.1/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/2.2.1/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.2.1/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.3.4/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/2.3.4/poetry.lock new file mode 100644 index 00000000..b7e609fd --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.3.4/poetry.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Poetry 2.3.4 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +groups = ["main"] +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.3.4/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/2.3.4/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.3.4/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.4.3/poetry.lock b/crates/socket-patch-core/tests/fixtures/poetry/2.4.3/poetry.lock new file mode 100644 index 00000000..efa06eee --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.4.3/poetry.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Poetry 2.4.3 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +groups = ["main"] +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.8" +content-hash = "4f9ffb08e662523420bf03909c6f496ad5f6d44d5107bda8fb5955ce0dfe3ab2" diff --git a/crates/socket-patch-core/tests/fixtures/poetry/2.4.3/pyproject.toml b/crates/socket-patch-core/tests/fixtures/poetry/2.4.3/pyproject.toml new file mode 100644 index 00000000..4e9d7ee4 --- /dev/null +++ b/crates/socket-patch-core/tests/fixtures/poetry/2.4.3/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" diff --git a/crates/socket-patch-core/tests/poetry_hosted.rs b/crates/socket-patch-core/tests/poetry_hosted.rs new file mode 100644 index 00000000..17173e65 --- /dev/null +++ b/crates/socket-patch-core/tests/poetry_hosted.rs @@ -0,0 +1,478 @@ +use socket_patch_core::patch::redirect::{ + revert_remaining_redirect_edits, rewrite_registry_redirect, DepOverride, Integrity, + RedirectState, +}; +use socket_patch_core::utils::poetry_lock::rewrite_poetry_lock; +use std::collections::BTreeMap; + +const VERSIONS: &[&str] = &[ + "0.12.17", "1.0.10", "1.1.15", "1.2.2", "1.3.2", "1.4.2", "1.5.1", "1.6.1", "1.7.1", "1.8.5", + "2.0.1", "2.1.4", "2.2.1", "2.3.4", "2.4.3", +]; +const WHEEL: &str = "urllib3-1.26.18-py2.py3-none-any.whl"; +const URL: &str = "https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl"; + +fn original(version: &str) -> String { + std::fs::read_to_string(format!( + "{}/tests/fixtures/poetry/{version}/poetry.lock", + env!("CARGO_MANIFEST_DIR") + )) + .unwrap() + .replace("\r\n", "\n") +} + +fn patch() -> DepOverride { + DepOverride { + ecosystem: "pypi".into(), + name: "urllib3".into(), + namespace: None, + version: "1.26.18".into(), + token: "7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e".into(), + patch_uuid: "e828efa5-5c6d-43f3-9909-03f5ac232b98".into(), + artifact_url: URL.into(), + berry_zip_url: None, + registry_override: None, + integrity: Integrity { + sha256: Some("a".repeat(64)), + ..Integrity::default() + }, + } +} + +#[tokio::test] +async fn native_lock_generations_redirect_idempotently_and_restore_every_byte() { + for version in VERSIONS { + for crlf in [false, true] { + let pristine = if crlf { + original(version).replace('\n', "\r\n") + } else { + original(version) + }; + let files = BTreeMap::from([("poetry.lock".into(), pristine.clone())]); + let result = rewrite_registry_redirect(&files, &[patch()]); + if *version == "0.12.17" { + assert!(result.files.is_empty()); + assert!(result.edits.is_empty()); + assert!(result + .warnings + .iter() + .any(|warning| warning.detail.contains("ignores URL sources"))); + continue; + } + // Poetry < 1.4 writers (0/1.0/1.1 locks, and 1.3's unstamped 2.0 lock) + // get the warm-virtualenv advisory; nothing else may warn. + let pre_1_4 = matches!(*version, "1.0.10" | "1.1.15" | "1.2.2" | "1.3.2"); + let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); + assert_eq!( + codes, + if pre_1_4 { vec!["redirect_poetry_stale_install_risk"] } else { vec![] }, + "{version}: {:?}", + result.warnings + ); + let redirected = &result.files["poetry.lock"]; + assert!(redirected.contains(URL)); + let again = rewrite_registry_redirect(&result.files, &[patch()]); + assert!(again.warnings.is_empty(), "{version}: {:?}", again.warnings); + assert!(again.files.is_empty()); + assert!(again.edits.is_empty()); + let directory = tempfile::tempdir().unwrap(); + tokio::fs::write(directory.path().join("poetry.lock"), redirected) + .await + .unwrap(); + let mut state = RedirectState { + edits: result.edits, + ..RedirectState::default() + }; + let outcome = + revert_remaining_redirect_edits(directory.path(), &mut state, false).await; + assert!(outcome.fully_reverted(), "{:?}", outcome.refusals); + assert_eq!( + tokio::fs::read_to_string(directory.path().join("poetry.lock")) + .await + .unwrap(), + pristine + ); + } + } +} + +/// The per-generation hosted shapes real Poetry releases were measured to +/// need: lock 1.0 gets `reference = ""` and the `#sha256=&` fragment +/// (Poetry 1.0 reads `reference` unconditionally and appends `#egg=`); lock +/// 1.1 gets the patched hash in BOTH the package `files` (what Poetry 1.2 +/// verifies) and `[metadata.files]` (what Poetry 1.1 verifies); 2.x gets the +/// package `files` only, with `[metadata]` untouched. +#[test] +fn hosted_shapes_match_each_lock_generations_installer() { + let sha = "a".repeat(64); + let lock10 = rewrite_registry_redirect( + &BTreeMap::from([("poetry.lock".to_string(), original("1.0.10"))]), + &[patch()], + ) + .files["poetry.lock"] + .clone(); + assert!(lock10.contains(&format!("url = \"{URL}#sha256={sha}&\"")), "{lock10}"); + assert!(lock10.contains("reference = \"\""), "{lock10}"); + assert!(lock10.contains(&format!("urllib3 = [{{ file = \"{WHEEL}\", hash = \"sha256:{sha}\" }}]")), "{lock10}"); + // Poetry >= 1.2 consuming this 1.0 lock verifies the package `files` + // entry, so it is written too (1.0 ignores the extra key). + assert_eq!(lock10.matches(&format!("sha256:{sha}")).count(), 2, "{lock10}"); + let doc: toml_edit::DocumentMut = lock10.parse().unwrap(); + assert!(doc["package"][0]["files"].is_array(), "{lock10}"); + + let lock11 = rewrite_registry_redirect( + &BTreeMap::from([("poetry.lock".to_string(), original("1.2.2"))]), + &[patch()], + ) + .files["poetry.lock"] + .clone(); + assert_eq!(lock11.matches(&format!("sha256:{sha}")).count(), 2, "package files + metadata.files:\n{lock11}"); + assert!(lock11.contains(&format!("url = \"{URL}\"")), "no fragment on 1.1"); + assert!(!lock11.contains("reference"), "{lock11}"); + let doc: toml_edit::DocumentMut = lock11.parse().unwrap(); + assert!(doc["package"][0]["files"].is_array()); + assert!(doc["metadata"]["files"]["urllib3"].is_array()); + + let lock21 = rewrite_registry_redirect( + &BTreeMap::from([("poetry.lock".to_string(), original("2.4.3"))]), + &[patch()], + ) + .files["poetry.lock"] + .clone(); + assert_eq!(lock21.matches(&format!("sha256:{sha}")).count(), 1, "{lock21}"); + assert!(!lock21.contains("reference")); + let pristine: toml_edit::DocumentMut = original("2.4.3").parse().unwrap(); + let doc: toml_edit::DocumentMut = lock21.parse().unwrap(); + assert_eq!(doc["metadata"].to_string(), pristine["metadata"].to_string(), "[metadata] untouched on 2.x"); +} + +#[test] +fn every_native_lock_generation_supports_file_sources() { + for version in VERSIONS { + let pristine = original(version); + let path = format!(".socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/{WHEEL}"); + let rewritten = rewrite_poetry_lock( + &pristine, + "URLLib3", + "1.26.18", + "file", + &path, + WHEEL, + &"a".repeat(64), + ) + .unwrap() + .unwrap(); + let lock: toml_edit::DocumentMut = rewritten.parse().unwrap(); + let package = lock["package"] + .as_array_of_tables() + .unwrap() + .get(0) + .unwrap(); + assert_eq!(package["source"]["url"].as_str(), Some(path.as_str())); + assert_eq!( + rewrite_poetry_lock( + &rewritten, + "urllib3", + "1.26.18", + "file", + &path, + WHEEL, + &"a".repeat(64) + ) + .unwrap() + .unwrap(), + rewritten + ); + let old: toml_edit::DocumentMut = pristine.parse().unwrap(); + assert_eq!( + old["metadata"]["content-hash"].as_str(), + lock["metadata"]["content-hash"].as_str() + ); + } +} + +#[test] +fn invalid_inputs_never_produce_edits() { + let pristine = original("2.4.3"); + let source = "\n[package.source]\ntype='git'\nurl='https://example.test/urllib3'\n"; + let fork = "\n[[package]]\nname='urllib3'\nversion='1.26.18'\n"; + for lock in [ + pristine.replace("[metadata]", &format!("{source}\n[metadata]")), + format!("{pristine}{fork}"), + pristine.replace("lock-version = \"2.1\"", "lock-version = \"3.0\""), + "[[invalid".into(), + ] { + let files = BTreeMap::from([("poetry.lock".into(), lock)]); + let result = rewrite_registry_redirect(&files, &[patch()]); + assert!(result.files.is_empty()); + assert!(result.edits.is_empty()); + assert!(!result.warnings.is_empty()); + } + for (filename, hash) in [ + ("requests-1.26.18-py3-none-any.whl", "a".repeat(64)), + ("urllib3-1.0-py3-none-any.whl", "a".repeat(64)), + (WHEEL, "bad".into()), + (WHEEL, "z".repeat(64)), + ] { + assert!( + rewrite_poetry_lock(&pristine, "urllib3", "1.26.18", "url", URL, filename, &hash) + .is_err() + ); + } + let files = BTreeMap::from([("poetry.lock".into(), pristine)]); + let mut missing_hash = patch(); + missing_hash.integrity.sha256 = None; + let result = rewrite_registry_redirect(&files, &[missing_hash]); + assert!(result.files.is_empty()); + assert_eq!(result.warnings[0].code, "redirect_poetry_missing_sha256"); +} + +#[tokio::test] +async fn drift_keeps_the_lock_and_rollback_ledger() { + let files = BTreeMap::from([("poetry.lock".into(), original("1.1.15"))]); + let result = rewrite_registry_redirect(&files, &[patch()]); + let changed = result.files["poetry.lock"].replace("sha256:aaaa", "sha256:bbbb"); + let directory = tempfile::tempdir().unwrap(); + tokio::fs::write(directory.path().join("poetry.lock"), &changed) + .await + .unwrap(); + let mut state = RedirectState { + edits: result.edits, + ..RedirectState::default() + }; + let outcome = revert_remaining_redirect_edits(directory.path(), &mut state, false).await; + assert!(!outcome.fully_reverted()); + assert!(!state.edits.is_empty()); + assert_eq!( + tokio::fs::read_to_string(directory.path().join("poetry.lock")) + .await + .unwrap(), + changed + ); +} + +#[tokio::test] +async fn either_patch_reverts_independently_with_unrelated_edits() { + for version in VERSIONS.iter().filter(|version| **version != "0.12.17") { + for crlf in [false, true] { + for first_name in ["urllib3", "six"] { + let mut lock: toml_edit::DocumentMut = original(version).parse().unwrap(); + let packages = lock["package"].as_array_of_tables_mut().unwrap(); + let mut second = packages.get(0).unwrap().clone(); + second["name"] = toml_edit::value("six"); + second["version"] = toml_edit::value("1.16.0"); + second.set_position(None); + second.remove("extras"); + packages.push(second); + if let Some(files) = lock["metadata"].get_mut("files") { + if let Some(value) = files.get("urllib3").cloned() { + files["six"] = value; + } + } + let pristine = lock.to_string(); + let pristine = if crlf { + pristine.replace('\n', "\r\n") + } else { + pristine + }; + let second_patch = DepOverride { + name: "six".into(), + version: "1.16.0".into(), + artifact_url: URL.replace("urllib3", "six").replace("1.26.18", "1.16.0"), + ..patch() + }; + let files = BTreeMap::from([("poetry.lock".into(), pristine.clone())]); + let first = rewrite_registry_redirect(&files, &[patch()]); + assert!( + !first.files.is_empty(), + "{version}: {:?}\n{pristine}", + first.warnings + ); + let second = rewrite_registry_redirect(&first.files, &[second_patch]); + assert!( + !second.files.is_empty(), + "{version}: {:?}\n{}", + second.warnings, + first.files["poetry.lock"] + ); + assert!( + second + .warnings + .iter() + .all(|w| w.code == "redirect_poetry_stale_install_risk"), + "{:?}", + second.warnings + ); + let directory = tempfile::tempdir().unwrap(); + let unrelated = if crlf { + "# retained user edit\r\n" + } else { + "# retained user edit\n" + }; + tokio::fs::write( + directory.path().join("poetry.lock"), + format!("{unrelated}{}", second.files["poetry.lock"]), + ) + .await + .unwrap(); + let mut states: Vec<_> = [first, second] + .into_iter() + .map(|result| RedirectState { + edits: result.edits, + ..RedirectState::default() + }) + .collect(); + if first_name == "six" { + states.reverse(); + } + for state in &mut states { + let outcome = + revert_remaining_redirect_edits(directory.path(), state, false).await; + assert!( + outcome.fully_reverted(), + "{version}: {:?}", + outcome.refusals + ); + } + assert_eq!( + tokio::fs::read_to_string(directory.path().join("poetry.lock")) + .await + .unwrap(), + format!("{unrelated}{pristine}") + ); + } + } + } +} + +#[test] +fn absent_entries_warn_once_and_missing_sha256_is_gated_once_per_dep() { + let files = BTreeMap::from([ + ("poetry.lock".to_string(), original("2.4.3")), + ("packages/app/poetry.lock".to_string(), original("1.8.5")), + ]); + let mut six = patch(); + six.name = "six".into(); + six.version = "1.16.0".into(); + six.artifact_url = URL.replace("urllib3", "six").replace("1.26.18", "1.16.0"); + let result = rewrite_registry_redirect(&files, &[six]); + assert!(result.files.is_empty()); + let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); + assert_eq!( + codes, + vec!["redirect_poetry_entry_not_found", "redirect_poetry_entry_not_found"] + ); + let mut missing_hash = patch(); + missing_hash.integrity.sha256 = None; + let result = rewrite_registry_redirect(&files, &[missing_hash]); + assert!(result.files.is_empty()); + let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); + assert_eq!(codes, vec!["redirect_poetry_missing_sha256"], "gated once, not once per lock"); +} + +/// A future Poetry that bumps the lock minor (2.2) is rewritten like 2.1 in +/// hosted mode — the vendored loader already accepts it with an advisory, and +/// the same lock must not be a silent no-op on one path and applied on another. +#[tokio::test] +async fn newer_2x_minor_redirects_and_reverts() { + let lock = original("2.4.3").replace("lock-version = \"2.1\"", "lock-version = \"2.2\""); + let files = BTreeMap::from([("poetry.lock".to_string(), lock.clone())]); + let result = rewrite_registry_redirect(&files, &[patch()]); + assert!(result.warnings.is_empty(), "{:?}", result.warnings); + assert!(result.files["poetry.lock"].contains(URL)); + let directory = tempfile::tempdir().unwrap(); + tokio::fs::write(directory.path().join("poetry.lock"), &result.files["poetry.lock"]) + .await + .unwrap(); + let mut state = RedirectState { + edits: result.edits, + ..RedirectState::default() + }; + let outcome = revert_remaining_redirect_edits(directory.path(), &mut state, false).await; + assert!(outcome.fully_reverted(), "{:?}", outcome.refusals); + assert_eq!( + tokio::fs::read_to_string(directory.path().join("poetry.lock")).await.unwrap(), + lock + ); +} + +/// A rotated grant token (or republished patch) supersedes the earlier hosted +/// URL in place; rollback of the SECOND run restores the FIRST run's fragment, +/// exactly as the ledger records it. +#[test] +fn rotated_grant_token_supersedes_the_prior_hosted_url() { + let files = BTreeMap::from([("poetry.lock".to_string(), original("1.8.5"))]); + let first = rewrite_registry_redirect(&files, &[patch()]); + let mut rotated = patch(); + rotated.token = "00000000-0000-4000-8000-000000000000".into(); + rotated.artifact_url = URL.replace("7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e", "00000000-0000-4000-8000-000000000000"); + let second = rewrite_registry_redirect(&first.files, &[rotated.clone()]); + assert!(second.warnings.is_empty(), "{:?}", second.warnings); + let lock = &second.files["poetry.lock"]; + assert!(lock.contains(&rotated.artifact_url) && !lock.contains(URL)); + assert_eq!(second.edits.len(), 1); + assert!(second.edits[0].original.as_ref().unwrap().as_str().unwrap().contains(URL)); +} + +/// A relock (or hand edit) that drops the inserted `files` line but keeps +/// `[package.source]` must be REFUSED by rollback, never mistaken for an +/// already-reverted lock: before the fragment carried its boundary header the +/// pristine unit matched as a prefix, replay reported success, deleted the +/// ledger, and left the lock redirecting with upstream hashes in +/// `[metadata.files]`. +#[tokio::test] +async fn dropped_files_line_with_source_kept_is_refused_not_converged() { + for version in ["1.2.2", "2.4.3"] { + let pristine = original(version); + let files = BTreeMap::from([("poetry.lock".to_string(), pristine.clone())]); + let result = rewrite_registry_redirect(&files, &[patch()]); + let redirected = &result.files["poetry.lock"]; + let drifted: String = redirected + .lines() + .filter(|line| !line.starts_with("files = [{ file = ")) + .collect::>() + .join("\n") + + "\n"; + assert_ne!(drifted, *redirected, "{version}: the files line must have been removed"); + assert!(drifted.contains("[package.source]")); + let directory = tempfile::tempdir().unwrap(); + tokio::fs::write(directory.path().join("poetry.lock"), &drifted).await.unwrap(); + let mut state = RedirectState { + edits: result.edits.clone(), + ..RedirectState::default() + }; + let outcome = revert_remaining_redirect_edits(directory.path(), &mut state, false).await; + assert!(!outcome.fully_reverted(), "{version}: must refuse, not report success"); + assert_eq!( + tokio::fs::read_to_string(directory.path().join("poetry.lock")).await.unwrap(), + drifted, + "{version}: a refused revert writes nothing" + ); + assert!(!state.edits.is_empty(), "{version}: the ledger keeps its edits for a re-scan"); + } +} + +/// Lock 1.0 only APPENDS `[package.source]` to the unit. A lock restored to +/// pristine by hand (or by Poetry 1.0's own bare `poetry lock`, which drops +/// the source) must let rollback converge and clear the ledger instead of +/// refusing with a spurious drift. +#[tokio::test] +async fn lock_1_0_rollback_converges_on_a_hand_restored_lock() { + let pristine = original("1.0.10"); + let files = BTreeMap::from([("poetry.lock".to_string(), pristine.clone())]); + let result = rewrite_registry_redirect(&files, &[patch()]); + assert!(!result.edits.is_empty()); + let directory = tempfile::tempdir().unwrap(); + tokio::fs::write(directory.path().join("poetry.lock"), &pristine).await.unwrap(); + let mut state = RedirectState { + edits: result.edits, + ..RedirectState::default() + }; + let outcome = revert_remaining_redirect_edits(directory.path(), &mut state, false).await; + assert!(outcome.fully_reverted(), "{:?}", outcome.refusals); + assert!(state.edits.is_empty()); + assert_eq!( + tokio::fs::read_to_string(directory.path().join("poetry.lock")).await.unwrap(), + pristine + ); +} diff --git a/docs/ecosystems.md b/docs/ecosystems.md index 17291b25..2361843b 100644 --- a/docs/ecosystems.md +++ b/docs/ecosystems.md @@ -15,7 +15,7 @@ The backticked slug in each row is the value `-e`/`--ecosystems` accepts (e.g. | Ecosystem | agent (`--mode agent`) | vendored (`--mode vendored`) | hosted (`--mode hosted`) | |-----------|------------------------|------------------------------|--------------------------| | npm (`npm`) — pnpm / yarn / berry / bun | ✅ any install layout; `setup` postinstall hook | ✅ six lockfile flavors: package-lock, yarn classic, yarn berry (node-modules linker; PnP refused), pnpm v9, pnpm legacy v5.4/v6.0 (`pnpm 7/8` — frozen installs are path-bound because those majors absolutize `file:` override specifiers; moved checkouts run one `pnpm install --offline --no-frozen-lockfile`, surfaced as `vendor_pnpm_legacy_absolute_specifier`), bun `bun.lock` (binary `bun.lockb` refused with a `--save-text-lockfile` pointer). Rush monorepos refused (`vendor_rush_unsupported`) — see [Rush notes](#npm-rush-monorepos) | ✅ package-lock / npm-shrinkwrap, pnpm-lock.yaml and legacy shrinkwrap.yaml (pnpm majors 1–12; block and flow resolutions), yarn classic, yarn berry, bun — pnpm, berry, and bun carry constraints, see [npm hosted-mode notes](#npm-hosted-mode-notes) | -| PyPI (`pypi`) — uv / poetry / pdm / pipenv / pip | ✅ `.pth` startup hook via `setup` | ✅ uv project/script locks, PEP 751 `pylock.toml` / `pylock..toml`, poetry, pdm, pipenv (lock rewired, but pipenv doesn't hash-check file entries — `vendor_integrity_unverified` warning; the committed wheel bytes are the protection), and requirements.txt. Native uv vendoring requires uv ≥ 0.2.35 (the `[[package]]` lock grammar); hosted mode covers native `uv.lock` from uv 0.1.45 (the first release whose `uv lock` writes one) and requirements from uv 0.0.5; see [uv compatibility](testing/uv-compatibility.md). | ✅ requirements.txt including hash continuations, uv project/script locks, and PEP 751 locks. Version/source ambiguity is refused; see [uv compatibility](testing/uv-compatibility.md). **poetry / pdm / pipenv locks are not rewritten** — use vendored | +| PyPI (`pypi`) — uv / poetry / pdm / pipenv / pip | ✅ `.pth` startup hook via `setup` | ✅ uv project/script locks, PEP 751 `pylock.toml` / `pylock..toml`, poetry, pdm, pipenv (lock rewired, but pipenv doesn't hash-check file entries — `vendor_integrity_unverified` warning; the committed wheel bytes are the protection), and requirements.txt. Native uv vendoring requires uv ≥ 0.2.35 (the `[[package]]` lock grammar); hosted mode covers native `uv.lock` from uv 0.1.45 (the first release whose `uv lock` writes one) and requirements from uv 0.0.5; see [uv compatibility](testing/uv-compatibility.md). | ✅ requirements.txt including hash continuations, uv project/script locks, and PEP 751 locks. Version/source ambiguity is refused; see [uv compatibility](testing/uv-compatibility.md). Poetry 1.x and 2.x locks are supported; Poetry 0.x ignores URL sources and is refused. See [Poetry compatibility](testing/poetry-compatibility.md). **pdm / pipenv locks are not rewritten** — use vendored | | Cargo (`cargo`) | ✅ in-place + `.cargo-checksum.json` rewrite (shared registry-cache caveat — see [Cargo: shared registry cache](#cargo-shared-registry-cache)) | ✅ `[patch.crates-io]` path entry | ✅ per-patch sparse registry (`[registries.socket-patch-]` + Cargo.lock source/checksum) | | RubyGems (`gem`) | ✅ Bundler plugin via `setup` — needs bundler ≥ 2.2 (1.x cannot load `plugin ... path:` directives; `setup` refuses below the floor and `setup --check` red-flags a wired 1.x project) | ✅ Gemfile + Gemfile.lock path pair (`Gemfile` spelling only — a `gems.rb` project cannot vendor yet) | ✅ per-dep `source` block — edits `gems.rb` + `gems.locked` when present (bundler prefers them over `Gemfile`; spellings that diverge beyond Socket's own edits fail closed with `redirect_gem_gemfile_spellings_diverge`); the `CHECKSUMS` pin needs bundler ≥ 2.6 (older locks get a `redirect_gem_no_checksums_section` warning); a stale pre-redirect materialization that `bundle install` would reuse instead of refetching is flagged `redirect_gem_stale_install` with a prescriptive remedy (see CLI_CONTRACT.md's "Gem stale-install guard") | | Go (`golang`) | ✅ `go.mod` `replace` → `.socket/go-patches/` — see [Go: directory replaces and go.sum](#go-directory-replaces-and-gosum) | ✅ `replace` → the committed vendor tree | ✅ (free tier) fork-style `replace` → `patch.socket.dev/gopatch/` + committed `go.sum` pin; see [golang-hosted.md](design/golang-hosted.md). Paid tier stays ❌ ([golang-hosted-no-go.md](design/golang-hosted-no-go.md)); `redirect_golang_unsupported` names the vendored remedy | diff --git a/docs/testing/hosted-production-e2e.md b/docs/testing/hosted-production-e2e.md index b0817063..53aae87b 100644 --- a/docs/testing/hosted-production-e2e.md +++ b/docs/testing/hosted-production-e2e.md @@ -101,9 +101,13 @@ history of this demotion shows every piece to restore (catalog constants, preflight registration, the install-proof leg, and these tables) in both production suites. -PyPI's poetry / pdm / pipenv locks are **not** rewritten by hosted mode (see the +PyPI's pdm / pipenv locks are **not** rewritten by hosted mode (see the [matrix](../ecosystems.md#mode--ecosystem-matrix)); those flavors are vendored-mode -only, so there is no hosted leg to write for them. +only, so there is no hosted leg to write for them. `poetry.lock` IS rewritten +(Poetry 1.0+); its live coverage is the per-release matrix in +[poetry-compatibility.md](poetry-compatibility.md) (`scripts/backtest-poetry.py`), +which installs the redirected lock with every Poetry release rather than one +pinned installer, so it is not duplicated as a leg here. Two supported hosted shapes are deliberately **not** covered here: diff --git a/docs/testing/poetry-compatibility.md b/docs/testing/poetry-compatibility.md new file mode 100644 index 00000000..381c9419 --- /dev/null +++ b/docs/testing/poetry-compatibility.md @@ -0,0 +1,210 @@ +# Poetry compatibility and production backtests + +`socket-patch` supports hosted and vendored Python patches in every +`poetry.lock` generation Poetry has written, and agent mode (in-place +patching of the project's virtualenv) on every Poetry release. The tests use +real Poetry releases bootstrapped with uv, real PyPI artifacts, and the public +Socket patch service. Successful rewriting alone is not an installation result: +the backtest reinstalls from the rewritten lock and compares the installed bytes +with the published patch. + +This supplements the [hosted](hosted-production-e2e.md) and +[vendored](vendored-production-e2e.md) production suites and mirrors the +[uv matrix](uv-compatibility.md). See the +[ecosystem matrix](../ecosystems.md#mode--ecosystem-matrix) for other package +managers. + +## Formats and rewrite behavior + +| Lock generation (writer) | Hosted (`scan --mode hosted`) | Vendored (`scan --mode vendored`) | +| --- | --- | --- | +| `[metadata.hashes]`, no `lock-version` (Poetry 0.12) | **Refused** (`redirect_poetry_lock_unsupported`): the installer ignores `[package.source] type = "url"` and installs the registry artifact, so a rewrite would attest a patch that never lands. | `[package.source] type = "file"` + `reference = ""` (read unconditionally by 0.12) and the wheel's SHA-256 in `[metadata.hashes]`. | +| `lock-version = "1.0"` (Poetry 1.0) | `type = "url"` + `reference = ""`; the URL carries `#sha256=&` because Poetry 1.0 appends `#egg=` unconditionally (see the pip caveat below). pip verifies the fragment; the patched hash is also written to `[metadata.files]` (consistency) and to the package's own `files` (what Poetry ≥ 1.2 verifies when it consumes a 1.0 lock; 1.0 ignores the extra key). | `type = "file"` + `reference = ""`; `[metadata.files]` entry replaced. | +| `lock-version = "1.1"` (Poetry 1.1, 1.2) | `type = "url"`; the patched hash is written to BOTH `[metadata.files]` (what Poetry 1.1 verifies) and the package's own `files` (what Poetry 1.2 verifies — it drops URL hashes from `[metadata.files]`). | `type = "file"`; `[metadata.files]` entry replaced. | +| `lock-version = "2.0"` / `"2.1"` / any `"2."` (Poetry 1.3+) | `type = "url"`; `files = [{file, hash}]` replaced with the single patched wheel. | `type = "file"`; `files` replaced. LF 2.x locks keep Poetry's own multi-line `files` formatting; CRLF locks and legacy formats go through the shared toml_edit rewriter, which writes a single-line inline array. Both are valid TOML and byte-stable under `poetry check --lock`. | + +Both modes retain the package version, dependencies, groups, markers, extras +and the pyproject `content-hash`; no pyproject edit is required. A repeated +scan leaves the lock unchanged. Rollback restores the recorded original +fragments — one per patch (plus the integrity-table entry on legacy formats), +so either of two patches can be rolled back first and unrelated edits survive. +Refused before any write: a `[[package]]` listed at several versions (marker +fork), a user-authored `[package.source]` on another origin (an earlier Socket +URL for the same wheel is superseded in place, e.g. after a grant-token +rotation), an unsupported `lock-version`, a malformed `[metadata.files]` / +`[metadata.hashes]` value, and a wheel whose filename does not match the +locked package. + +## Installer boundaries (measured) + +| Poetry | Hosted | Vendored | Verifies the lock hash on install | Replaces an already-installed same-version package | +| --- | --- | --- | --- | --- | +| 0.12 | refused (URL sources ignored) | supported | no | no | +| 1.0 | supported (`#sha256=…&` fragment; pip ≤ 22.2 or ≥ 23.1) | supported | hosted: yes (pip fragment); vendored: no | no | +| 1.1 – 1.3 | supported | supported | hosted: yes; vendored: no | **no** | +| 1.4 – 1.8 | supported | supported | yes / yes | yes | +| 2.0 – 2.4 | supported | supported | yes / yes | yes | + +Two consequences for Poetry releases before 1.4: + +- A **warm virtualenv keeps the upstream package** after the lock is rewritten: + `poetry install` compares installed packages by name and version only and + prints "No dependencies to install or update". Recreate the virtualenv (or + `pip uninstall` the package) before installing, or upgrade Poetry. Fresh + installs pick up the patched wheel on every release. The CLI flags this as + `redirect_poetry_stale_install_risk` (hosted) and + `pypi_poetry_integrity_unverified` (vendored), keyed on the lock's writer: + formats 0 / 1.0 / 1.1 are only written by pre-1.4 releases, and a lock 2.0 + whose header lacks a `@generated by Poetry X.Y.Z` version was written by 1.3 + (1.4+ stamp their version). A 2.0 lock stamped 1.4–1.8 is not flagged. +- **Poetry 1.0 installs URL sources through pip**, which reads the hash from the + URL fragment. Poetry appends `#egg=` to every URL, so the rewrite ends the + fragment with `&` to keep `sha256=` intact. Measured across pip releases: + pip 20.3 – 22.2 and 23.1+ install and verify the patched wheel with that + spelling; pip 22.3 and 23.0 (the pip Python 3.8's `ensurepip` seeds) treat the + trailing `&#egg=…` as part of the digest and refuse the install (fail-closed, + nothing installed) — no fragment spelling satisfies them, so upgrade or + downgrade pip inside the virtualenv. Without the `&`, every pip ≥ 22.3 refuses. + The hosted advisory names this window for lock 1.0. +- Local (vendored) wheel hashes are **not verified**; the committed wheel bytes + are the protection — review them. Hosted URL hashes are verified on every + release from 1.0 on by the default installer (Poetry's deprecated pip backend, + `experimental.new-installer = false`, verifies nothing). + +Hosted mode also checks the installed package's bytes, independently of the lock's +writer version. `redirect_pypi_stale_install` means readable files in a discovered +interpreter still differ from the patched hashes. This read-only check repeats on +re-scans and excludes the package from same-run VEX, even with +`--vex-no-verify` or a healthy copy in another interpreter. If no patches remain +to attest, `--vex` fails with `no_applicable_patches`. Reinstall in the affected +interpreter, then verify with `socket-patch vex`. A lock-only checkout has no +installed bytes to judge and keeps the existing lock-based attestation behavior. + +Other measured details: + +- `poetry lock --no-update` (1.1–1.8) and bare `poetry lock` (2.x) keep the + patch source. Bare `poetry lock` on 0.12 / 1.0 (no `--no-update`), + `poetry lock --regenerate` (2.x), `poetry update` (with or without `--lock`) + and `poetry update ` — even when the version does not + change — drop the source and restore the registry hashes on every release. + `metadata.content-hash` is unchanged by that, so `poetry check --lock` / + `poetry lock --check` cannot detect the loss: re-run + `socket-patch scan --mode …` after any of them, or gate CI on `socket-patch vex`. +- `poetry lock --no-update` on Poetry 1.1 / 1.2 keeps the hosted source but + rewrites the entry in its own lock-1.1 shape, dropping the package-level + `files` the rewrite added for Poetry ≥ 1.2's hash check; a lock relocked by + 1.1 and then installed by 1.2+ installs the hosted wheel unverified. Re-run + `socket-patch scan --mode hosted` after relocking on those releases: the + re-scan restores the entry and rebases the ledger's recorded edits onto the + relocked text (pristine → current, never an appended chain), so `rollback` + still lands on the pristine lock afterwards. A relocked-but-not-rescanned + lock is refused by `rollback` (its recorded fragments match nothing) rather + than reported as already reverted. +- Poetry 0.12 and 1.0 resolve a relative `type = "file"` path against the + shell's working directory, not the project root; run `poetry install` from + the project root on those releases. +- Poetry ≤ 1.1 stores its HTTP cache under the user cache directory regardless + of `POETRY_CACHE_DIR`, behind one lockfile that wedges parallel runs and stays + wedged after a SIGKILL; the harness gives each legacy case its own `HOME`. + +## Mode notes + +- **Agent mode** patches the interpreter the crawler finds: `VIRTUAL_ENV`, + `./.venv`, `./venv`, then — for a Poetry project — the virtualenv(s) Poetry + placed under its `virtualenvs.path` (`--py`; every + interpreter minor), reproducing Poetry's own placement from `POETRY_*`, the + project's `poetry.toml`, the user `config.toml` and the platform default + cache dir without running Poetry; else — for a project directory — the global + interpreter's site-packages. So a bare `socket-patch scan --mode agent` / + `rollback` in a default-configured Poetry checkout works; `poetry run + socket-patch …`, `VIRTUAL_ENV=$(poetry env info -p)` and + `--global-prefix ` keep working. `virtualenvs.create = false` + (containers) means Poetry installed into the system interpreter, which the + project-marker global fallback covers. Patched bytes survive + `poetry install`, `poetry sync` and `poetry install --sync` on every release + (same version → no reinstall). +- **Vendored mode works lock-only** for locks that list a pure-Python + (`-none-any.whl`) wheel for the package — lock 2.x `files`, lock 1.0/1.1 + `[metadata.files]`: the inventory carries that sha256 and the fetcher + resolves the file through PyPI's JSON API by digest, verifying the bytes + again, exactly like uv's lock-only path. Poetry 0.12's bare + `[metadata.hashes]` names no wheel, and a package that ships only platform + wheels has no platform-independent choice: those still need the package + installed in the discovered virtualenv (`vendor_fetch_unverifiable` + + `package_not_installed`). Fresh clones of the committed `.socket/vendor/` + tree and rewired lock install from the committed wheel with no CLI at all + (the matrix's fresh-clone leg). +- **Hosted mode works lock-only** (`redirected: 1` with no virtualenv). + +## Running the matrix + +```sh +cargo build -p socket-patch-cli +cp target/debug/socket-patch /tmp/socket-patch-under-test # rebuilds must not swap it mid-run +python3 scripts/backtest-poetry.py \ + --cli /tmp/socket-patch-under-test \ + --cli-revision "$(git rev-parse --short HEAD)" \ + --output /tmp/socket-patch-poetry-backtest \ + --modes hosted vendored agent agent-oot setup \ + --shapes direct populated crlf pep621 +python3 scripts/backtest-poetry.py --render-doc-table /tmp/socket-patch-poetry-backtest/summary.json +``` + +The harness bootstraps every release in `VERSIONS` with uv (Python 3.8.20 for +0.12–1.1, 3.12.13 for 1.2+; Poetry 1.2.2 needs `cleo==1.0.0a5`), generates a +native lock for a one-dependency project (`urllib3 = "1.26.18"`, whose public +free-tier patch needs no token), and for each mode checks: exactly one patch +applied, pyproject untouched, idempotent re-scan, `poetry install` into an +emptied virtualenv installs bytes matching the patch record's `afterHash`, +`poetry check --lock`, a fresh clone of the committed state installs the patch, +`socket-patch vex` attests it, a corrupted hash is rejected where the installer +verifies, Poetry's own relock keeps the source, and `rollback` restores every +byte and clears the ledgers. Shapes: `direct` (the committed native fixture), +`populated` (legacy locks with real upstream hashes filled in — today's PyPI +JSON API leaves old Poetry's `[metadata.files]` empty), `crlf`, and `pep621` +(2.x `[project]` tables with `package-mode = false`). Modes `agent-oot` +(Poetry's default out-of-tree virtualenv via `poetry run`) and `setup` +(`socket-patch setup` on a Poetry project, then `poetry lock`) are +informational. + +Rust coverage of the rewriters: `cargo test -p socket-patch-core --lib +utils::poetry_lock vendor::pypi_poetry` and `cargo test -p socket-patch-core +--test poetry_hosted`. The committed native locks under +`crates/socket-patch-core/tests/fixtures/poetry//` are the harness's +`original/` inputs (same pyproject, same `content-hash`). + +## Results + + +| Poetry | hosted | vendored | agent (in-project venv) | agent (`poetry run`, out-of-tree venv) | tamper rejected (hosted / vendored) | warm venv re-installed (hosted / vendored) | relock keeps patch (hosted / vendored) | lock-only vendored | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0.12.17 | refused (0.x ignores URL sources) | pass (crlf,direct,populated) | pass (direct) | n/a | n/a / no | n/a / false | n/a / false | refused | +| 1.0.10 | pass (crlf,direct,populated) | pass (crlf,direct,populated) | pass (direct) | pass (direct) | yes / no | false / false | false / false | refused (crlf), refused (direct), yes (populated) | +| 1.1.15 | pass (crlf,direct,populated) | pass (crlf,direct,populated) | pass (direct) | pass (direct) | yes / no | false / false | true / true | refused (crlf), refused (direct), yes (populated) | +| 1.2.2 | pass (crlf,direct) | pass (crlf,direct) | pass (direct) | pass (direct) | yes / no | false / false | true / true | yes | +| 1.3.2 | pass (crlf,direct) | pass (crlf,direct) | pass (direct) | pass (direct) | yes / no | false / false | true / true | yes | +| 1.4.2 | pass (crlf,direct) | pass (crlf,direct) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 1.5.1 | pass (crlf,direct) | pass (crlf,direct) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 1.6.1 | pass (crlf,direct) | pass (crlf,direct) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 1.7.1 | pass (crlf,direct) | pass (crlf,direct) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 1.8.5 | pass (crlf,direct) | pass (crlf,direct) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 2.0.1 | pass (crlf,direct,pep621) | pass (crlf,direct,pep621) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 2.1.4 | pass (crlf,direct,pep621) | pass (crlf,direct,pep621) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 2.2.1 | pass (crlf,direct,pep621) | pass (crlf,direct,pep621) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 2.3.4 | pass (crlf,direct,pep621) | pass (crlf,direct,pep621) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | +| 2.4.3 | pass (crlf,direct,pep621) | pass (crlf,direct,pep621) | pass (direct) | pass (direct) | yes / yes | true / true | true / true | yes | + + +Captured 2026-09-17 on macOS arm64 against the fix-branch head; 108 cases, +all passing (the `pass`/`refused` cells are the asserted outcomes; the +`tamper` / `warm venv` / `relock` / `lock-only vendored` columns are the +measured installer facts the sections above describe). The +[machine-readable results](poetry-compatibility/results.json) carry every +check, the CLI envelopes' relevant fields and the per-step exit codes. +`poetry lock` on 0.12 / 1.0 is bare (no `--no-update`), hence +`relock keeps patch = false` there; `lock-only vendored = refused` on 0.12 and +on the unpopulated 1.0/1.1 fixtures (`urllib3 = []`) because those locks name +no wheel hash. The companion SBOM annotation work and its own capture set live +in SocketDev/depscan (`tools/pipeline/poetry-patch-backtest.py`). diff --git a/docs/testing/poetry-compatibility/results.json b/docs/testing/poetry-compatibility/results.json new file mode 100644 index 00000000..d85d8915 --- /dev/null +++ b/docs/testing/poetry-compatibility/results.json @@ -0,0 +1,11925 @@ +{ + "errors": [], + "provenance": { + "capturedAt": "2026-09-17T20:22:39.127492+00:00", + "cliRevision": "75d49ac", + "cliSha256": "40a16bf5f5fe0f58e738710b88283a6dd412de0e6e23f958b324d9debacf201d", + "host": "Darwin arm64", + "modes": [ + "hosted", + "vendored", + "agent", + "agent-oot", + "setup" + ], + "note": "Single harness run on the fix-branch head; the Poetry 1.1.15 `setup` case was re-run alone after its first attempt hit Poetry <= 1.1's shared HTTP-cache lock (the CLI-spawned `poetry lock` now also gets the case-isolated HOME).", + "poetryVersions": [ + "0.12.17", + "1.0.10", + "1.1.15", + "1.2.2", + "1.3.2", + "1.4.2", + "1.5.1", + "1.6.1", + "1.7.1", + "1.8.5", + "2.0.1", + "2.1.4", + "2.2.1", + "2.3.4", + "2.4.3" + ], + "shapes": [ + "direct", + "populated", + "crlf", + "pep621" + ] + }, + "results": [ + { + "checks": { + "lockUnchanged": true, + "noLedger": true, + "pyprojectUnchanged": true, + "refusedWithWarning": true + }, + "expected": "refused: Poetry 0.x ignores URL sources", + "info": { + "applied": 0, + "refusedWithWarning": [ + { + "code": "redirect_poetry_lock_unsupported", + "detail": "poetry.lock: Poetry 0.x ignores URL sources; hosted patches require Poetry >= 1.0" + } + ], + "scanExit": 0, + "warnings": [ + { + "code": "redirect_poetry_lock_unsupported", + "detail": "poetry.lock: Poetry 0.x ignores URL sources; hosted patches require Poetry >= 1.0" + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "0.12.17", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": false, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "lockOnlyVendorApplies": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n - Installing poetry-patch-fixture (0.1.0)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNothing to install or update\n\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNothing to install or update\n\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "0.12.17", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "0.12.17", + "shape": "direct" + }, + { + "checks": { + "lockUnchanged": true, + "noLedger": true, + "pyprojectUnchanged": true, + "refusedWithWarning": true + }, + "expected": "refused: Poetry 0.x ignores URL sources", + "info": { + "applied": 0, + "refusedWithWarning": [ + { + "code": "redirect_poetry_lock_unsupported", + "detail": "poetry.lock: Poetry 0.x ignores URL sources; hosted patches require Poetry >= 1.0" + } + ], + "scanExit": 0, + "warnings": [ + { + "code": "redirect_poetry_lock_unsupported", + "detail": "poetry.lock: Poetry 0.x ignores URL sources; hosted patches require Poetry >= 1.0" + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "0.12.17", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": false, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "lockOnlyVendorApplies": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n - Installing poetry-patch-fixture (0.1.0)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNothing to install or update\n\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNothing to install or update\n\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "0.12.17", + "shape": "direct" + }, + { + "checks": { + "lockUnchanged": true, + "noLedger": true, + "pyprojectUnchanged": true, + "refusedWithWarning": true + }, + "expected": "refused: Poetry 0.x ignores URL sources", + "info": { + "applied": 0, + "refusedWithWarning": [ + { + "code": "redirect_poetry_lock_unsupported", + "detail": "poetry.lock: Poetry 0.x ignores URL sources; hosted patches require Poetry >= 1.0" + } + ], + "scanExit": 0, + "warnings": [ + { + "code": "redirect_poetry_lock_unsupported", + "detail": "poetry.lock: Poetry 0.x ignores URL sources; hosted patches require Poetry >= 1.0" + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "0.12.17", + "shape": "populated" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": false, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "lockOnlyVendorApplies": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n - Installing poetry-patch-fixture (0.1.0)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNothing to install or update\n\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNothing to install or update\n\n - Installing poetry-patch-fixture (0.1.0)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "0.12.17", + "shape": "populated" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel. Poetry 1.0 installs through pip: pip 22.3\u201323.0 misparse the hash fragment and refuse the install (fail-closed) \u2014 use pip <= 22.2 or >= 23.1 in the virtualenv." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl#sha256=ccc9a9e0b18a5efc7038c504cfc580e47d2e02e5390f2e29cad833cbccb956b6&)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl#sha256=ccc9a9e0b18a5efc7038c504cfc580e47d2e02e5390f2e29cad833cbccb956b6&)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel. Poetry 1.0 installs through pip: pip 22.3\u201323.0 misparse the hash fragment and refuse the install (fail-closed) \u2014 use pip <= 22.2 or >= 23.1 in the virtualenv." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.0.10", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": false, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "lockOnlyVendorApplies": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.0.10", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.0.10", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 3, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 3 + }, + "ootVenv": "/matrix-final2/captures/1.0.10-direct-agent-oot/venvs/poetry-patch-fixture-OOp1MSO--py3.8", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.0.10", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel. Poetry 1.0 installs through pip: pip 22.3\u201323.0 misparse the hash fragment and refuse the install (fail-closed) \u2014 use pip <= 22.2 or >= 23.1 in the virtualenv." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl#sha256=ccc9a9e0b18a5efc7038c504cfc580e47d2e02e5390f2e29cad833cbccb956b6&)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl#sha256=ccc9a9e0b18a5efc7038c504cfc580e47d2e02e5390f2e29cad833cbccb956b6&)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel. Poetry 1.0 installs through pip: pip 22.3\u201323.0 misparse the hash fragment and refuse the install (fail-closed) \u2014 use pip <= 22.2 or >= 23.1 in the virtualenv." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.0.10", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": false, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "lockOnlyVendorApplies": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.0.10", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel. Poetry 1.0 installs through pip: pip 22.3\u201323.0 misparse the hash fragment and refuse the install (fail-closed) \u2014 use pip <= 22.2 or >= 23.1 in the virtualenv." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl#sha256=ccc9a9e0b18a5efc7038c504cfc580e47d2e02e5390f2e29cad833cbccb956b6&)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl#sha256=ccc9a9e0b18a5efc7038c504cfc580e47d2e02e5390f2e29cad833cbccb956b6&)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel. Poetry 1.0 installs through pip: pip 22.3\u201323.0 misparse the hash fragment and refuse the install (fail-closed) \u2014 use pip <= 22.2 or >= 23.1 in the virtualenv." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.0.10", + "shape": "populated" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 .socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": false, + "pyprojectUnchanged": true, + "tail": "Updating dependencies\nResolving dependencies...\n\nWriting lock file\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.0.10", + "shape": "populated" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanAfterRelockApplies": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n\nWriting lock file\n" + }, + "rescanAfterRelock": { + "applied": 1, + "exit": 0, + "lockChanged": true + }, + "rescanAfterRelockApplies": { + "applied": 1, + "exit": 0 + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.1.15", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": false, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.1.15-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "lockOnlyVendorApplies": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.1.15-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.1.15", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.1.15", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 4, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 4 + }, + "ootVenv": "/matrix-final2/captures/1.1.15-direct-agent-oot/venvs/poetry-patch-fixture-Ynsj_tRb-py3.8", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.1.15", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanAfterRelockApplies": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n\nWriting lock file\n" + }, + "rescanAfterRelock": { + "applied": 1, + "exit": 0, + "lockChanged": true + }, + "rescanAfterRelockApplies": { + "applied": 1, + "exit": 0 + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.1.15", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": false, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.1.15-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "lockOnlyVendorApplies": { + "applied": 0, + "codes": [ + "package_not_installed", + "vendor_fetch_unverifiable" + ], + "exit": 1 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.1.15-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.1.15", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanAfterRelockApplies": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n\nWriting lock file\n" + }, + "rescanAfterRelock": { + "applied": 1, + "exit": 0, + "lockChanged": true + }, + "rescanAfterRelockApplies": { + "applied": 1, + "exit": 0 + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.1.15", + "shape": "populated" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.1.15-populated-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": null + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.1.15-populated-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nNo dependencies to install or update\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.1.15", + "shape": "populated" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanAfterRelockApplies": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n\nWriting lock file\n" + }, + "rescanAfterRelock": { + "applied": 1, + "exit": 0, + "lockChanged": true + }, + "rescanAfterRelockApplies": { + "applied": 1, + "exit": 0 + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.2.2", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.2.2-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.2.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.2.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.2.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.2.2", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.2.2", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 4, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 4 + }, + "ootVenv": "/matrix-final2/captures/1.2.2-direct-agent-oot/venvs/poetry-patch-fixture--o8xsesc-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.2.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanAfterRelockApplies": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": false, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n\nWriting lock file\n" + }, + "rescanAfterRelock": { + "applied": 1, + "exit": 0, + "lockChanged": true + }, + "rescanAfterRelockApplies": { + "applied": 1, + "exit": 0 + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.2.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.2.2-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.2.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.2.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.2.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.2.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.3.2", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.3.2-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.3.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.3.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.3.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.3.2", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.3.2", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 4, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 4 + }, + "ootVenv": "/matrix-final2/captures/1.3.2-direct-agent-oot/venvs/poetry-patch-fixture-nqgm3NcM-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.3.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "code": "redirect_poetry_stale_install_risk", + "detail": "poetry.lock was written by Poetry < 1.4, which does not replace an already-installed package at the same version: an existing virtualenv keeps the upstream urllib3 until it is recreated (or the package is `pip uninstall`ed) before `poetry install`; fresh installs pick up the patched wheel." + } + ] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.3.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": false + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.3.2-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "pypi_poetry_integrity_unverified", + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.3.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "tamperBehaviorAsDocumented": { + "expectsReject": false, + "installExit": 0, + "installedPatchedAnyway": true + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": false, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.3.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": false, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.3.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "pypi_poetry_integrity_unverified", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "poetry.lock was written by Poetry < 1.4: that installer does not verify local wheel hashes (the committed wheel bytes are the protection \u2014 review them) and does not replace an already-installed package at the same version \u2014 upgrade to Poetry >= 1.4, or recreate the virtualenv (or `pip uninstall` the package) before `poetry install`" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.3.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.4.2", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.4.2-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.4.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.4.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.4.2-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.4.2", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.4.2", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 4, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 4 + }, + "ootVenv": "/matrix-final2/captures/1.4.2-direct-agent-oot/venvs/poetry-patch-fixture-7SNtuRHk-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.4.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.4.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.4.2-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.4.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.4.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.4.2-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.4.2", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.5.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.5.1-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.5.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.5.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.5.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.5.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.5.1", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/1.5.1-direct-agent-oot/venvs/poetry-patch-fixture-_zoUJMnU-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.5.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.5.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.5.1-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "lock --check", + "exit": 0, + "tail": "poetry.lock is consistent with pyproject.toml.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.5.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.5.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.5.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.5.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.6.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.6.1-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.6.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.6.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.6.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.6.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.6.1", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/1.6.1-direct-agent-oot/venvs/poetry-patch-fixture-3oGutE4e-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.6.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.6.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.6.1-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.6.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.6.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.6.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.6.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.7.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.7.1-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.7.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.7.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "\u2022 Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.7.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.7.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.7.1", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/1.7.1-direct-agent-oot/venvs/poetry-patch-fixture-h-NPhXP2-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.7.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n \u2022 Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.7.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.7.1-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n \u2022 Installing urllib3 (1.26.18 /matrix-final2/captures/1.7.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.7.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.7.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.7.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.8.5", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/1.8.5-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/1.8.5-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.8.5-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.8.5-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.8.5", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "1.8.5", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/1.8.5-direct-agent-oot/venvs/poetry-patch-fixture-A0_pGI6P-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "1.8.5", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "1.8.5", + "shape": "direct" + }, + { + "checks": {}, + "expected": "informational: setup edits pyproject; poetry must resolve socket-patch[hook]", + "info": { + "lockChanged": true, + "poetryLockAfterSetup": { + "exit": 0, + "tail": "Resolving dependencies...\nCreating virtualenv poetry-patch-fixture in /matrix-final2/captures/1.8.5-direct-setup/project/.venv\n" + }, + "pyprojectChanged": true, + "pyprojectDiff": "[tool.poetry]\nname = \"poetry-patch-fixture\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Socket \"]\n\n[tool.poetry.dependencies]\npython = \">=3.8\"\nurllib3 = \"1.26.18\"\nsocket-patch = { version = \"*\", extras = [\"hook\"] }\n", + "setupCheckExit": 0, + "setupEnvelope": { + "alreadyConfigured": 0, + "errors": 0, + "files": [ + { + "error": null, + "kind": "pth", + "path": "/matrix-final2/captures/1.8.5-direct-setup/project/pyproject.toml", + "status": "updated" + } + ], + "packageManager": "npm", + "pythonPackageManager": "poetry", + "status": "success", + "updated": 1 + }, + "setupExit": 0 + }, + "mode": "setup", + "passed": true, + "poetry": "1.8.5", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/1.8.5-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/1.8.5-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock --no-update -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.8.5-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/1.8.5-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "1.8.5", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.0.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.0.1-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.0.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.0.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.0.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.0.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "2.0.1", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/2.0.1-direct-agent-oot/venvs/poetry-patch-fixture-0k24feQr-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "2.0.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.0.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.0.1-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.0.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.0.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.0.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.0.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.0.1", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.0.1-pep621-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.0.1-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.0.1-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.0.1-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.0.1", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.1.4", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.1.4-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.1.4-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.1.4-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.1.4-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.1.4", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "2.1.4", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/2.1.4-direct-agent-oot/venvs/poetry-patch-fixture-V-8iOFCt-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "2.1.4", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.1.4", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.1.4-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.1.4-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.1.4-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.1.4-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.1.4", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.1.4", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.1.4-pep621-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.1.4-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.1.4-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.1.4-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.1.4", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.2.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.2.1-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.2.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.2.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.2.1-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.2.1", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "2.2.1", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/2.2.1-direct-agent-oot/venvs/poetry-patch-fixture-GNeKz0YO-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "2.2.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.2.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.2.1-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.2.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.2.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.2.1-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.2.1", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.2.1", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.2.1-pep621-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.2.1-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.2.1-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.2.1-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.2.1", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.3.4", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.3.4-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.3.4-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.3.4-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.3.4-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.3.4", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "2.3.4", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/2.3.4-direct-agent-oot/venvs/poetry-patch-fixture-a0nSdvf5-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "2.3.4", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.3.4", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.3.4-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.3.4-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.3.4-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.3.4-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.3.4", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.3.4", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.3.4-pep621-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.3.4-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.3.4-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.3.4-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.3.4", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.4.3", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "crlfPreserved": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.4.3-crlf-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.4.3-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.4.3-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "- Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.4.3-crlf-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.4.3", + "shape": "crlf" + }, + { + "checks": { + "appliedExactlyOne": true, + "installedBytesPatched": true, + "lockUnchanged": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "rollbackRestoresUpstreamBytes": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 1, + "status": "success", + "vendoredReverted": [] + }, + "rollbackRestoresUpstreamBytes": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + }, + "scanExit": 0, + "survivesRepeatInstall": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "warnings": [] + }, + "mode": "agent", + "passed": true, + "poetry": "2.4.3", + "shape": "direct" + }, + { + "checks": { + "bareScanSeesPoetryVenv": true, + "patchedViaPoetryRun": true, + "poetryRunScanApplied": true, + "rollbackClearsManifest": true, + "rollbackRestoresUpstream": true, + "survivesRepeatInstall": true, + "survivesSync": true + }, + "expected": "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass", + "info": { + "applyPath": "bare", + "bareScan": { + "exit": 0, + "packageDirs": [], + "packagesWithPatches": 1, + "paths": [], + "scannedPackages": 2, + "urllib3Found": true + }, + "bareScanSeesPoetryVenv": { + "found": 1, + "scannedPackages": 2 + }, + "ootVenv": "/matrix-final2/captures/2.4.3-direct-agent-oot/venvs/poetry-patch-fixture-8DuawaLX-py3.12", + "patchedViaPoetryRun": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "poetryRunScanApplied": { + "applied": 1, + "exit": 0, + "path": "bare" + }, + "rollbackRestoresUpstream": { + "exit": 0, + "oracle": { + "urllib3/response.py": "ea86196bb5cb3f20fee709d7507cd6b9b2c98017050f3a70033c27f47051ce06" + } + }, + "survivesRepeatInstall": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "survivesSync": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + } + } + }, + "mode": "agent-oot", + "passed": true, + "poetry": "2.4.3", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.4.3", + "shape": "direct" + }, + { + "checks": {}, + "expected": "informational: setup edits pyproject; poetry must resolve socket-patch[hook]", + "info": { + "lockChanged": true, + "poetryLockAfterSetup": { + "exit": 0, + "tail": "Resolving dependencies...\nCreating virtualenv poetry-patch-fixture in /matrix-final2/captures/2.4.3-direct-setup/project/.venv\n" + }, + "pyprojectChanged": true, + "pyprojectDiff": "[tool.poetry]\nname = \"poetry-patch-fixture\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Socket \"]\n\n[tool.poetry.dependencies]\npython = \">=3.8\"\nurllib3 = \"1.26.18\"\nsocket-patch = { version = \"*\", extras = [\"hook\"] }\n", + "setupCheckExit": 0, + "setupEnvelope": { + "alreadyConfigured": 0, + "errors": 0, + "files": [ + { + "error": null, + "kind": "pth", + "path": "/matrix-final2/captures/2.4.3-direct-setup/project/pyproject.toml", + "status": "updated" + } + ], + "packageManager": "npm", + "pythonPackageManager": "poetry", + "status": "success", + "updated": 1 + }, + "setupExit": 0 + }, + "mode": "setup", + "passed": true, + "poetry": "2.4.3", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.4.3-direct-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "dynamic].\nIf you want to set the version dynamically via `poetry build --local-version` or you are using a plugin, which sets the version dynamically, you should define the version in [tool.poetry] and add 'version' to [project.dynamic].\nWarning: [tool.poetry.description] is deprecated. Use [project.description] instead.\nWarning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.4.3-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.4.3-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.4.3-direct-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.4.3", + "shape": "direct" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasUrlSource": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackClearsRedirectLedger": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 1, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 1, + "failed": [], + "reverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Installing dependencies from lock file\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n - Updating urllib3 (1.26.18 -> 1.26.18 https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [] + }, + "mode": "hosted", + "passed": true, + "poetry": "2.4.3", + "shape": "pep621" + }, + { + "checks": { + "appliedExactlyOne": true, + "freshCloneInstallsPatch": true, + "installedBytesPatched": true, + "lockHasFileSource": true, + "lockOnlyVendorApplies": true, + "lockRewritten": true, + "lockUnchangedByInstall": true, + "poetryInstallExit0": true, + "pyprojectUnchanged": true, + "recordHasFiles": true, + "rescanIdempotent": true, + "rollbackClearsManifest": true, + "rollbackExit0": true, + "rollbackKeepsPyproject": true, + "rollbackRemovesVendoredWheel": true, + "rollbackRestoresLockBytes": true, + "tamperBehaviorAsDocumented": true, + "vendoredWheelPresent": true, + "warmInstallReplacesUpstream": true + }, + "info": { + "applied": 1, + "appliedExactlyOne": { + "applied": 1, + "status": "success", + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "freshCloneInstallsPatch": { + "exit": 0, + "oracle": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "tail": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.4.3-pep621-vendored/fresh/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "installedBytesPatched": { + "urllib3/response.py": "9027726cab26bb63b978e6af295b22ec6172b100cda66d9b91cdc30bebc03730" + }, + "lockCheck": { + "cmd": "check --lock", + "exit": 0, + "tail": "All set!\n" + }, + "lockOnlyVendor": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "lockOnlyVendorApplies": { + "applied": 1, + "codes": [ + "vendor_fetched_missing", + "vendor_prebuilt_downloaded" + ], + "exit": 0 + }, + "poetryInstallExit0": "Installing dependencies from lock file\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n - Installing urllib3 (1.26.18 /matrix-final2/captures/2.4.3-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n", + "relock": { + "cmd": "lock -n", + "exit": 0, + "lockBytesUnchanged": true, + "patchSourceKept": true, + "pyprojectUnchanged": true, + "tail": "Resolving dependencies...\n" + }, + "rescanIdempotent": { + "applied": 0, + "exit": 0, + "status": "success" + }, + "rollbackEnvelope": { + "failed": 0, + "hosted": { + "editedFiles": 0, + "failed": [], + "reverted": [], + "unsupported": [] + }, + "manifest": { + "preserved": false, + "removedEntries": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "rolledBack": 0, + "status": "success", + "vendoredReverted": [ + "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + ] + }, + "scanExit": 0, + "tamper": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "tamperBehaviorAsDocumented": { + "expectsReject": true, + "installExit": 1, + "installedPatchedAnyway": false + }, + "uuid": "e828efa5-5c6d-43f3-9909-03f5ac232b98", + "vex": { + "exit": 0, + "statements": 1 + }, + "warmInstall": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.4.3-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warmInstallReplacesUpstream": { + "exit": 0, + "patched": true, + "tail": "Updating urllib3 (1.26.18 -> 1.26.18 /matrix-final2/captures/2.4.3-pep621-vendored/project/.socket/vendor/pypi/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)\n" + }, + "warnings": [ + { + "action": "applied", + "files": [ + { + "path": "urllib3/response.py", + "verified": true + } + ], + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl" + }, + { + "action": "skipped", + "errorCode": "vendor_prebuilt_downloaded", + "purl": "pkg:pypi/urllib3@1.26.18?artifact_id=py2-py3-none-any-whl", + "reason": "vendored the wheel for pkg:pypi/urllib3@1.26.18 from the patch service (https://patch.socket.dev/patch/pypi/urllib3/1.26.18/7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e/e828efa5-5c6d-43f3-9909-03f5ac232b98/urllib3-1.26.18-py2.py3-none-any.whl)" + } + ] + }, + "mode": "vendored", + "passed": true, + "poetry": "2.4.3", + "shape": "pep621" + }, + { + "checks": {}, + "expected": "informational: setup edits pyproject; poetry must resolve socket-patch[hook]", + "info": { + "lockChanged": true, + "poetryLockAfterSetup": { + "exit": 0, + "tail": "Creating virtualenv poetry-patch-fixture in /matrix-final2-setup/captures/1.1.15-direct-setup/project/.venv\nResolving dependencies...\n" + }, + "pyprojectChanged": true, + "pyprojectDiff": "[tool.poetry]\nname = \"poetry-patch-fixture\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Socket \"]\n\n[tool.poetry.dependencies]\npython = \">=3.8\"\nurllib3 = \"1.26.18\"\nsocket-patch = { version = \"*\", extras = [\"hook\"] }\n", + "setupCheckExit": 0, + "setupEnvelope": { + "alreadyConfigured": 0, + "errors": 0, + "files": [ + { + "error": null, + "kind": "pth", + "path": "/matrix-final2-setup/captures/1.1.15-direct-setup/project/pyproject.toml", + "status": "updated" + } + ], + "packageManager": "npm", + "pythonPackageManager": "poetry", + "status": "success", + "updated": 1 + }, + "setupExit": 0 + }, + "mode": "setup", + "passed": true, + "poetry": "1.1.15", + "shape": "direct" + } + ] +} \ No newline at end of file diff --git a/scripts/backtest-poetry.py b/scripts/backtest-poetry.py new file mode 100755 index 00000000..61c80c23 --- /dev/null +++ b/scripts/backtest-poetry.py @@ -0,0 +1,777 @@ +#!/usr/bin/env python3 +"""Drive the real socket-patch CLI and real Poetry releases through hosted, +vendored and agent mode on native poetry.lock generations. + +For every Poetry version the harness bootstraps that exact release with uv, +generates a native lock for a one-dependency project (urllib3 1.26.18, which +has a public free-tier Socket patch), then for each mode: + + hosted scan --mode hosted -> poetry install -> installed bytes == patch + vendored scan --mode vendored -> poetry install -> installed bytes == patch + agent poetry install -> scan --mode agent -> installed bytes == patch + +and checks idempotent re-scans, unchanged pyproject, lock-driven installs in a +FRESH clone of the committed state, tampered-hash rejection, what Poetry's own +relock does to the patch source, `poetry check --lock`, `vex`, and `rollback` +restoring every byte. Extra modes: `agent-oot` (Poetry's default out-of-tree +venv) and `setup`. Shapes: `direct` (native lock), `populated` (legacy locks +with real hashes filled in, as 2020-era locks have), `crlf`, `pep621` (2.x). + +Needs network (PyPI + patch.socket.dev), uv, and no Socket token. +""" + +import argparse +import concurrent.futures +import hashlib +import json +import os +import re +import shutil +import subprocess +import sys +import traceback +import urllib.request +from datetime import datetime, timezone +from pathlib import Path + +VERSIONS = [ + "0.12.17", + "1.0.10", + "1.1.15", + "1.2.2", + "1.3.2", + "1.4.2", + "1.5.1", + "1.6.1", + "1.7.1", + "1.8.5", + "2.0.1", + "2.1.4", + "2.2.1", + "2.3.4", + "2.4.3", +] +MODES = ["hosted", "vendored", "agent", "agent-oot", "setup"] +SHAPES = ["direct", "populated", "crlf", "pep621"] + +PROJECT = """[tool.poetry] +name = "poetry-patch-fixture" +version = "0.1.0" +description = "" +authors = ["Socket "] + +[tool.poetry.dependencies] +python = ">=3.8" +urllib3 = "1.26.18" +""" +PROJECT_PEP621 = """[project] +name = "poetry-patch-fixture" +version = "0.1.0" +requires-python = ">=3.8" +dependencies = ["urllib3==1.26.18"] + +[tool.poetry] +package-mode = false +""" +ORACLE = """import hashlib,json,pathlib,sys,sysconfig +root=pathlib.Path(sysconfig.get_paths()['purelib']) +out={} +for name in json.loads(sys.argv[1]): + p=root/name + if p.is_file(): + d=p.read_bytes(); out[name]=hashlib.sha256(('blob %d\\0'%len(d)).encode()+d).hexdigest() + else: + out[name]=None +print(json.dumps(out)) +""" +PATCH_UUID = "e828efa5-5c6d-43f3-9909-03f5ac232b98" +PURL_BASE = "pkg:pypi/urllib3@1.26.18" + + +def vtuple(v): + return tuple(int(x) for x in v.split(".")) + + +def save(path, data): + path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n") + + +DEFAULT_TIMEOUT = int(os.environ.get("BACKTEST_TIMEOUT", "900")) + + +class Run: + def __init__(self, cmd, cwd, env, log, timeout=None): + timeout = timeout or DEFAULT_TIMEOUT + self.cmd = [str(c) for c in cmd] + try: + r = subprocess.run( + self.cmd, + cwd=cwd, + env=env, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + timeout=timeout, + ) + self.rc, self.out, self.err = r.returncode, r.stdout, r.stderr + except subprocess.TimeoutExpired as e: + def text(b): + return b.decode("utf-8", "replace") if isinstance(b, bytes) else (b or "") + self.rc, self.out, self.err = 124, text(e.stdout), text(e.stderr) + "\nTIMEOUT after %ss" % timeout + Path(log).write_text( + "$ " + " ".join(self.cmd) + f"\n# exit {self.rc}\n--- stdout\n{self.out}\n--- stderr\n{self.err}" + ) + + def ok(self): + return self.rc == 0 + + def json(self): + i = self.out.find("{") + if i < 0: + raise RuntimeError("no JSON in output: " + (self.out + self.err)[-2000:]) + return json.loads(self.out[i:]) + + def json_or_empty(self): + try: + return self.json() + except Exception: + return {} + + +def require(r, what): + if not r.ok(): + raise RuntimeError(f"{what} failed (exit {r.rc}):\n{(r.out + r.err)[-4000:]}") + return r + + +def base_env(): + env = { + k: v + for k, v in os.environ.items() + if not k.startswith(("PYTHON", "PIP_", "POETRY_", "SOCKET_", "UV_")) and k != "VIRTUAL_ENV" + } + env.update( + SOCKET_NO_CONFIG="1", + SOCKET_TELEMETRY_DISABLED="1", + PIP_CONFIG_FILE=os.devnull, + PIP_DISABLE_PIP_VERSION_CHECK="1", + PYTHONDONTWRITEBYTECODE="1", + ) + return env + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--cli", required=True, type=Path) + ap.add_argument("--cli-revision", required=True) + ap.add_argument("--output", required=True, type=Path) + ap.add_argument("--versions", nargs="+", default=VERSIONS) + ap.add_argument("--modes", nargs="+", default=MODES, choices=MODES) + ap.add_argument("--shapes", nargs="+", default=["direct", "populated", "crlf"], choices=SHAPES) + ap.add_argument("--jobs", type=int, default=4) + ap.add_argument("--render-doc-table", type=Path, metavar="SUMMARY_JSON") + args = ap.parse_args() + if args.render_doc_table: + summary = json.loads(args.render_doc_table.read_text()) + print(render_doc_table(summary)) + print() + print(render_table(summary)) + return + root = args.output.resolve() + root.mkdir(parents=True, exist_ok=True) + cli = args.cli.resolve() + env = base_env() + provenance = { + "capturedAt": datetime.now(timezone.utc).isoformat(), + "cliRevision": args.cli_revision, + "cliSha256": hashlib.sha256(cli.read_bytes()).hexdigest(), + "poetryVersions": args.versions, + "modes": args.modes, + "shapes": args.shapes, + "host": os.uname().sysname + " " + os.uname().machine, + } + save(root / "provenance.json", provenance) + + # Real hashes of the upstream artifacts, for the `populated` legacy shape. + with urllib.request.urlopen("https://pypi.org/pypi/urllib3/1.26.18/json", timeout=60) as r: + pypi = json.load(r) + upstream_files = [ + {"file": u["filename"], "hash": "sha256:" + u["digests"]["sha256"]} + for u in pypi["urls"] + if u["filename"].endswith((".whl", ".tar.gz")) + ] + + def python_for(version): + return "3.8.20" if version.startswith(("0.", "1.0.", "1.1.")) else "3.12.13" + + def prepare_tool(version): + tool = root / "tools" / version + if not (tool / "bin/poetry").exists(): + require(Run(["uv", "venv", "-q", "--python", python_for(version), tool], root, env, root / f"tool-{version}-venv.log"), "uv venv") + pkgs = ["poetry==" + version, "pip==24.0", "setuptools==69.5.1"] + if version == "1.2.2": + pkgs.append("cleo==1.0.0a5") + require(Run(["uv", "pip", "install", "-q", "--python", tool / "bin/python", *pkgs], root, env, root / f"tool-{version}-bootstrap.log"), "poetry bootstrap") + return tool + + log_lock = __import__("threading").Lock() + + def say(*a): + with log_lock: + print(*a, flush=True) + + def poetry_env(project, venv=None, cache=None, home=None): + e = dict(env) + e["POETRY_VIRTUALENVS_IN_PROJECT"] = "true" + e["POETRY_CACHE_DIR"] = str(cache or (project / ".poetry-cache")) + # Poetry <= 1.1 keeps its HTTP cache under ~/Library/Caches/pypoetry + # regardless of POETRY_CACHE_DIR, behind a single lockfile that wedges + # every parallel run (and stays wedged after a SIGKILL). Give each case + # its own HOME so legacy releases never share that lock. + h = home or (project.parent / "home") + h.mkdir(parents=True, exist_ok=True) + e["HOME"] = str(h) + if venv is not None: + e["VIRTUAL_ENV"] = str(venv) + return e + + def native_lock(version, tool, shape): + """Generate (once) the native lock for `version`/`shape`; return dir.""" + key = "pep621" if shape == "pep621" else "direct" + original = root / "original" / version / key + if not (original / "poetry.lock").exists(): + original.mkdir(parents=True, exist_ok=True) + (original / "pyproject.toml").write_text(PROJECT_PEP621 if key == "pep621" else PROJECT) + (original / "poetry_patch_fixture").mkdir(exist_ok=True) + (original / "poetry_patch_fixture/__init__.py").touch() + require(Run([tool / "bin/poetry", "lock", "-n"], original, poetry_env(original), original / "generation.log"), f"poetry {version} lock") + return original + + def derive_shape(version, original, shape, dest): + dest.mkdir(parents=True, exist_ok=True) + for f in ["pyproject.toml", "poetry.lock"]: + shutil.copyfile(original / f, dest / f) + (dest / "poetry_patch_fixture").mkdir(exist_ok=True) + (dest / "poetry_patch_fixture/__init__.py").touch() + lock = (dest / "poetry.lock").read_text() + if shape == "populated": + if version.startswith("0."): + hashes = ", ".join(json.dumps(f["hash"].split(":", 1)[1]) for f in upstream_files) + lock = re.sub(r"(?m)^urllib3 = \[\]$", f"urllib3 = [{hashes}]", lock) + else: + entries = ",\n".join( + " {file = %s, hash = %s}" % (json.dumps(f["file"]), json.dumps(f["hash"])) for f in upstream_files + ) + lock = re.sub(r"(?m)^urllib3 = \[\]$", f"urllib3 = [\n{entries},\n]", lock) + if "urllib3 = []" in lock: + raise RuntimeError("populated shape: could not fill hashes") + (dest / "poetry.lock").write_text(lock) + if shape == "crlf": + for f in ["pyproject.toml", "poetry.lock"]: + p = dest / f + p.write_bytes(p.read_text().replace("\r\n", "\n").replace("\n", "\r\n").encode()) + + def make_venv(tool, venv, cwd, log, packages=()): + require(Run(["uv", "venv", "-q", "--python", tool / "bin/python", venv], cwd, env, log), "uv venv") + pkgs = ["pip==24.0", "setuptools==69.5.1", *packages] + require(Run(["uv", "pip", "install", "-q", "--python", venv / "bin/python", *pkgs], cwd, env, str(log) + ".pip"), "venv bootstrap") + + def oracle(python, names, cwd, log): + r = Run([python, "-c", ORACLE, json.dumps(names)], cwd, env, log) + return json.loads(r.out) if r.ok() and r.out.strip() else {} + + def record_hashes(project, mode): + if mode == "hosted": + ledger = json.loads((project / ".socket/vendor/redirect-state.json").read_text()) + recs = ledger["records"] + else: + recs = json.loads((project / ".socket/manifest.json").read_text())["patches"] + rec = next(iter(recs.values())) + return ( + {n: i["afterHash"] for n, i in rec["files"].items()}, + {n: i["beforeHash"] for n, i in rec["files"].items() if i.get("beforeHash")}, + rec.get("uuid"), + ) + + def poetry_install_cmd(version, poetry): + cmd = [poetry, "install", "-n"] + if not version.startswith("0."): + cmd.append("--no-root") + return cmd + + def cli_cmd(project, *rest): + return [cli, *rest, "--cwd", project, "--json", "--yes", "--no-telemetry"] + + def applied_count(mode, envelope): + if mode == "hosted": + return envelope.get("redirect", {}).get("redirected", 0) + if mode == "vendored": + return envelope.get("vendor", {}).get("summary", {}).get("applied", 0) + return envelope.get("apply", {}).get("applied", 0) + + def lock_check(version, poetry, project, penv, log): + """Poetry's own lock consistency check, whichever spelling exists.""" + v = vtuple(version) + if v >= (1, 6): + r = Run([poetry, "check", "--lock", "-n"], project, penv, log) + return {"cmd": "check --lock", "exit": r.rc, "tail": (r.out + r.err)[-400:]} + if v >= (1, 2): + r = Run([poetry, "lock", "--check", "-n"], project, penv, log) + return {"cmd": "lock --check", "exit": r.rc, "tail": (r.out + r.err)[-400:]} + return {"cmd": None} + + def relock(version, poetry, project, penv, log): + v = vtuple(version) + if (1, 1) <= v < (2, 0): + cmd = [poetry, "lock", "--no-update", "-n"] + else: + cmd = [poetry, "lock", "-n"] + r = Run(cmd, project, penv, log, timeout=600) + return {"cmd": " ".join(cmd[1:]), "exit": r.rc, "tail": (r.out + r.err)[-400:]} + + def sync_cmd(version, poetry): + v = vtuple(version) + if v >= (2, 0): + return [poetry, "sync", "-n", "--no-root"] + if v >= (1, 2): + return [poetry, "install", "-n", "--no-root", "--sync"] + return None + + def backtest(job): + version, shape, mode = job + tool = root / "tools" / version + poetry = tool / "bin/poetry" + case = root / "captures" / f"{version}-{shape}-{mode}" + if case.exists(): + shutil.rmtree(case) + case.mkdir(parents=True) + original = native_lock(version, tool, shape) + pristine_dir = case / "pristine" + derive_shape(version, original, shape, pristine_dir) + project = case / "project" + shutil.copytree(pristine_dir, project) + pristine_lock = (pristine_dir / "poetry.lock").read_bytes() + pristine_pyproject = (pristine_dir / "pyproject.toml").read_bytes() + row = {"poetry": version, "shape": shape, "mode": mode, "checks": {}, "info": {}, "passed": None} + checks, info = row["checks"], row["info"] + v = vtuple(version) + + def check(name, value, note=None): + checks[name] = bool(value) + if note is not None: + info[name] = note + return bool(value) + + venv = project / ".venv" + python = venv / "bin/python" + + # ------------------------------------------------------------ setup + if mode == "setup": + senv = dict(env) + senv["PATH"] = str(tool / "bin") + os.pathsep + senv.get("PATH", "") + # `setup` shells out to that Poetry; give it the case's isolated + # HOME too (Poetry <= 1.1's shared HTTP-cache lock, see poetry_env). + senv["HOME"] = poetry_env(project)["HOME"] + r = Run(cli_cmd(project, "setup"), project, senv, case / "setup.log") + info["setupExit"] = r.rc + try: + info["setupEnvelope"] = r.json() + except Exception: + info["setupOutput"] = (r.out + r.err)[-1500:] + info["pyprojectChanged"] = (project / "pyproject.toml").read_bytes() != pristine_pyproject + info["pyprojectDiff"] = (project / "pyproject.toml").read_text() + info["lockChanged"] = (project / "poetry.lock").read_bytes() != pristine_lock + # Can Poetry itself resolve the committed hook dependency? + rl = Run([poetry, "lock", "-n"] + (["--no-update"] if (1, 1) <= v < (2, 0) else []), project, poetry_env(project), case / "setup-relock.log") + info["poetryLockAfterSetup"] = {"exit": rl.rc, "tail": (rl.out + rl.err)[-600:]} + chk = Run(cli_cmd(project, "setup", "--check"), project, senv, case / "setup-check.log") + info["setupCheckExit"] = chk.rc + row["passed"] = r.rc == 0 and info["pyprojectChanged"] and rl.rc == 0 + row["expected"] = "informational: setup edits pyproject; poetry must resolve socket-patch[hook]" + return row + + # -------------------------------------------------------- agent-oot + if mode == "agent-oot": + penv = dict(env) + penv["POETRY_VIRTUALENVS_IN_PROJECT"] = "false" + penv["POETRY_VIRTUALENVS_PATH"] = str(case / "venvs") + penv["POETRY_CACHE_DIR"] = str(case / "poetry-cache") + require(Run(poetry_install_cmd(version, poetry), project, penv, case / "install-upstream.log"), "poetry install (out-of-tree)") + ep = Run([poetry, "env", "info", "-p"], project, penv, case / "env-info.log") + oot_venv = Path(ep.out.strip().splitlines()[-1]) if ep.ok() and ep.out.strip() else None + info["ootVenv"] = str(oot_venv) + if not oot_venv or not (oot_venv / "bin/python").exists(): + raise RuntimeError("could not locate Poetry's out-of-tree venv: " + ep.out + ep.err) + # 1. bare scan from the project dir, no VIRTUAL_ENV: does the CLI see the venv? + # The CLI inherits the user's Poetry configuration (the custom + # virtualenvs path below is configuration, not an activation) but + # not VIRTUAL_ENV — that is exactly what `poetry run` would add. + bare_env = dict(env) + for key in ("POETRY_VIRTUALENVS_PATH", "POETRY_CACHE_DIR", "POETRY_VIRTUALENVS_IN_PROJECT"): + bare_env[key] = penv[key] + r1 = Run(cli_cmd(project, "scan", "--mode", "agent", "--dry-run"), project, bare_env, case / "scan-bare-dryrun.log") + e1 = r1.json_or_empty() + paths = [p for p in (e1.get("paths") or [])] + pkgs = e1.get("packages") or [] + info["bareScan"] = { + "exit": r1.rc, + "scannedPackages": e1.get("scannedPackages"), + "packagesWithPatches": e1.get("packagesWithPatches"), + "paths": paths[:10], + "urllib3Found": any("urllib3" in (p.get("purl") or "") for p in pkgs), + "packageDirs": [pth for p in pkgs for pth in (p.get("paths") or [])][:10], + } + # Did the bare dry-run see the package inside Poetry's venv (not + # merely list it lockfile-only)? A crawler that finds the venv reports + # urllib3 as installed with a patch to add; one that does not falls + # through to the global interpreter and reports it not installed. + sees = any( + "urllib3" in (p.get("purl") or "") and not p.get("notInstalled") + for p in pkgs + ) and e1.get("apply", {}).get("found", 0) >= 1 and not any( + ev.get("errorCode") == "package_not_installed" for ev in e1.get("apply", {}).get("patches", []) + ) + check("bareScanSeesPoetryVenv", sees, {"scannedPackages": e1.get("scannedPackages"), "found": e1.get("apply", {}).get("found")}) + # 2. apply for real: BARE when the crawler found the venv (the fixed + # CLI), else via `poetry run` (Poetry exports VIRTUAL_ENV). + bare = bool(sees) + info["applyPath"] = "bare" if bare else "poetry run" + cmd = cli_cmd(project, "scan", "--mode", "agent") if bare else [poetry, "run", *cli_cmd(project, "scan", "--mode", "agent")] + r2 = Run(cmd, project, bare_env if bare else penv, case / "scan-apply.log") + e2 = r2.json_or_empty() + check("poetryRunScanApplied", applied_count("agent", e2) == 1, {"exit": r2.rc, "applied": applied_count("agent", e2), "path": info["applyPath"]}) + after, before, _ = record_hashes(project, "agent") if (project / ".socket/manifest.json").exists() else ({}, {}, None) + res = oracle(oot_venv / "bin/python", list(after), project, case / "oracle-1.log") + check("patchedViaPoetryRun", bool(after) and all(res.get(n) == h for n, h in after.items()), res) + # 3. a repeat `poetry install` must not revert the in-place patch + require(Run(poetry_install_cmd(version, poetry), project, penv, case / "install-again.log"), "poetry install again") + res = oracle(oot_venv / "bin/python", list(after), project, case / "oracle-2.log") + check("survivesRepeatInstall", bool(after) and all(res.get(n) == h for n, h in after.items()), res) + sc = sync_cmd(version, poetry) + if sc: + rs = Run(sc, project, penv, case / "sync.log") + res = oracle(oot_venv / "bin/python", list(after), project, case / "oracle-3.log") + check("survivesSync", rs.ok() and bool(after) and all(res.get(n) == h for n, h in after.items()), {"exit": rs.rc, "oracle": res}) + # 4. rollback the same way the patch was applied (bare when the + # crawler sees the venv; a bare rollback that cannot see it would + # prune the manifest while the venv stays patched) + rb = Run(cli_cmd(project, "rollback") if bare else [poetry, "run", *cli_cmd(project, "rollback")], project, bare_env if bare else penv, case / "rollback.log") + res = oracle(oot_venv / "bin/python", list(after), project, case / "oracle-4.log") + check("rollbackRestoresUpstream", rb.ok() and bool(before) and all(res.get(n) == h for n, h in before.items()), {"exit": rb.rc, "oracle": res}) + check("rollbackClearsManifest", not (project / ".socket/manifest.json").exists() or json.loads((project / ".socket/manifest.json").read_text()).get("patches") == {}) + row["passed"] = all(checks[k] for k in checks if k != "bareScanSeesPoetryVenv") + row["expected"] = "bareScanSeesPoetryVenv is informational (known crawler gap); the rest must pass" + return row + + # ------------------------------------------------- hosted / vendored / agent + if mode == "vendored": + # Fresh-clone scenario first: nothing installed, lock only. + r0 = Run(cli_cmd(project, "scan", "--mode", "vendored"), project, env, case / "scan-lockonly.log") + e0 = r0.json_or_empty() + events = e0.get("vendor", {}).get("events", []) + info["lockOnlyVendor"] = { + "exit": r0.rc, + "applied": applied_count("vendored", e0), + "codes": sorted({ev.get("errorCode") for ev in events if ev.get("errorCode")}), + } + check("lockOnlyVendorApplies", applied_count("vendored", e0) == 1, info["lockOnlyVendor"]) + # reset any partial state + shutil.rmtree(project / ".socket", ignore_errors=True) + (project / "poetry.lock").write_bytes(pristine_lock) + + make_venv(tool, venv, project, case / "venv.log", packages=["urllib3==1.26.18"] if mode != "agent" else ()) + penv = poetry_env(project, venv=venv) + if mode == "agent": + require(Run(poetry_install_cmd(version, poetry), project, penv, case / "install-upstream.log"), "poetry install (upstream)") + + r = Run(cli_cmd(project, "scan", "--mode", mode), project, env, case / "scan.log") + info["scanExit"] = r.rc + try: + envelope = r.json() + except Exception as e: + raise RuntimeError(f"scan produced no JSON: {e}") + save(case / "cli-output.json", envelope) + applied = applied_count(mode, envelope) + info["applied"] = applied + warnings = envelope.get("redirect", {}).get("warnings", []) if mode == "hosted" else envelope.get("vendor", {}).get("events", []) + info["warnings"] = warnings[:8] + lock_after = (project / "poetry.lock").read_bytes() + check("pyprojectUnchanged", (project / "pyproject.toml").read_bytes() == pristine_pyproject) + + if mode == "hosted" and version.startswith("0."): + row["expected"] = "refused: Poetry 0.x ignores URL sources" + check("refusedWithWarning", applied == 0 and any("ignores URL sources" in json.dumps(w) for w in warnings), warnings[:3]) + check("lockUnchanged", lock_after == pristine_lock) + check("noLedger", not (project / ".socket/vendor/redirect-state.json").exists()) + row["passed"] = all(checks.values()) + return row + + check("appliedExactlyOne", applied == 1, {"applied": applied, "status": envelope.get("status"), "warnings": warnings[:4]}) + if not checks["appliedExactlyOne"]: + row["passed"] = False + return row + if mode == "agent": + check("lockUnchanged", lock_after == pristine_lock) + else: + check("lockRewritten", lock_after != pristine_lock) + if shape == "crlf": + check("crlfPreserved", b"\n" not in lock_after.replace(b"\r\n", b"")) + after, before, uuid = record_hashes(project, mode) + info["uuid"] = uuid + check("recordHasFiles", bool(after)) + if mode == "vendored": + wheel_dir = project / ".socket/vendor/pypi" / (uuid or "") + check("vendoredWheelPresent", wheel_dir.is_dir() and any(wheel_dir.glob("*.whl"))) + check("lockHasFileSource", b'type = "file"' in lock_after) + if mode == "hosted": + check("lockHasUrlSource", b'type = "url"' in lock_after and b"patch.socket.dev" in lock_after) + + # idempotent re-scan + r2 = Run(cli_cmd(project, "scan", "--mode", mode), project, env, case / "rescan.log") + e2 = r2.json_or_empty() + check("rescanIdempotent", r2.ok() and (project / "poetry.lock").read_bytes() == lock_after and (project / "pyproject.toml").read_bytes() == pristine_pyproject, {"exit": r2.rc, "applied": applied_count(mode, e2), "status": e2.get("status")}) + + if mode == "agent": + res = oracle(python, list(after), project, case / "oracle-1.log") + check("installedBytesPatched", all(res.get(n) == h for n, h in after.items()), res) + # repeat install must not revert; sync too + ri = Run(poetry_install_cmd(version, poetry), project, penv, case / "install-again.log") + res = oracle(python, list(after), project, case / "oracle-2.log") + check("survivesRepeatInstall", ri.ok() and all(res.get(n) == h for n, h in after.items()), {"exit": ri.rc, "oracle": res}) + sc = sync_cmd(version, poetry) + if sc: + rs = Run(sc, project, penv, case / "sync.log") + res = oracle(python, list(after), project, case / "oracle-3.log") + check("survivesSync", rs.ok() and all(res.get(n) == h for n, h in after.items()), {"exit": rs.rc, "oracle": res}) + else: + # Warm venv: upstream urllib3 is already installed. Does the + # redirected lock make Poetry replace it? (Poetry <= 1.1 compares + # name+version only and leaves the vulnerable copy in place.) + warm = Run(poetry_install_cmd(version, poetry), project, penv, case / "install-warm.log") + wres = oracle(python, list(after), project, case / "oracle-warm.log") + info["warmInstall"] = {"exit": warm.rc, "patched": bool(after) and all(wres.get(n) == h for n, h in after.items()), "tail": (warm.out + warm.err)[-300:]} + check("warmInstallReplacesUpstream", warm.ok() and info["warmInstall"]["patched"], info["warmInstall"]) + # Lock-driven install into the (now emptied) venv. + require(Run(["uv", "pip", "uninstall", "-q", "--python", python, "urllib3"], project, env, case / "uninstall.log"), "uninstall") + inst = Run(poetry_install_cmd(version, poetry), project, penv, case / "install.log") + res = oracle(python, list(after), project, case / "oracle-1.log") + check("poetryInstallExit0", inst.ok(), (inst.out + inst.err)[-600:]) + check("installedBytesPatched", all(res.get(n) == h for n, h in after.items()), res) + check("lockUnchangedByInstall", (project / "poetry.lock").read_bytes() == lock_after) + info["lockCheck"] = lock_check(version, poetry, project, penv, case / "lock-check.log") + # Fresh clone of the committed state (no venv, no caches) + fresh = case / "fresh" + shutil.copytree(project, fresh, ignore=shutil.ignore_patterns(".venv", ".poetry-cache", "__pycache__")) + make_venv(tool, fresh / ".venv", fresh, case / "fresh-venv.log") + fenv = poetry_env(fresh, venv=fresh / ".venv", cache=fresh / ".poetry-cache") + finst = Run(poetry_install_cmd(version, poetry), fresh, fenv, case / "fresh-install.log") + fres = oracle(fresh / ".venv/bin/python", list(after), fresh, case / "fresh-oracle.log") + check("freshCloneInstallsPatch", finst.ok() and all(fres.get(n) == h for n, h in after.items()), {"exit": finst.rc, "oracle": fres, "tail": (finst.out + finst.err)[-500:]}) + # vex over the installed, redirected/vendored tree + vx = Run([cli, "vex", "--cwd", project, "--no-telemetry"], project, env, case / "vex.log") + try: + vdoc = json.loads(vx.out[vx.out.find("{"):]) if vx.ok() else {} + info["vex"] = {"exit": vx.rc, "statements": len(vdoc.get("statements", []))} + except Exception: + info["vex"] = {"exit": vx.rc, "tail": (vx.out + vx.err)[-400:]} + # Tamper: corrupt the recorded hash, install must fail where the installer verifies. + if shape != "crlf": + require(Run(["uv", "pip", "uninstall", "-q", "--python", python, "urllib3"], project, env, case / "tamper-uninstall.log"), "uninstall") + corrupt = re.sub(rb"sha256[:=][a-f0-9]{64}", lambda m: m[0][:7] + b"0" * 64, lock_after) + if version.startswith("0."): + corrupt = re.sub(rb'"[a-f0-9]{64}"', b'"' + b"0" * 64 + b'"', corrupt) + (project / "poetry.lock").write_bytes(corrupt) + tam = Run(poetry_install_cmd(version, poetry), project, poetry_env(project, venv=venv, cache=case / "tamper-cache"), case / "tamper-install.log") + tres = oracle(python, list(after), project, case / "tamper-oracle.log") + (project / "poetry.lock").write_bytes(lock_after) + expects_reject = mode == "hosted" or v >= (1, 4) + info["tamper"] = {"installExit": tam.rc, "installedPatchedAnyway": all(tres.get(n) == h for n, h in after.items()), "expectsReject": expects_reject} + check("tamperBehaviorAsDocumented", (tam.rc != 0) == expects_reject, info["tamper"]) + # reinstall the good state for the remaining steps + Run(["uv", "pip", "uninstall", "-q", "--python", python, "urllib3"], project, env, case / "tamper-uninstall2.log") + require(Run(poetry_install_cmd(version, poetry), project, penv, case / "reinstall.log"), "reinstall") + # Relock: does Poetry's own relock keep the patch source? (informational) + rl = relock(version, poetry, project, penv, case / "relock.log") + relocked = (project / "poetry.lock").read_bytes() + marker = b"patch.socket.dev" if mode == "hosted" else b".socket/vendor/pypi" + rl.update(lockBytesUnchanged=relocked == lock_after, patchSourceKept=marker in relocked, pyprojectUnchanged=(project / "pyproject.toml").read_bytes() == pristine_pyproject) + info["relock"] = rl + if rl["patchSourceKept"] and not rl["lockBytesUnchanged"]: + # Poetry re-laid the unit around the kept source (1.1/1.2 drop + # the inserted `files` line). The documented recovery is a + # re-scan; it must restore the entry and leave a ledger that + # the final rollback below can still invert to pristine bytes. + rs = Run(cli_cmd(project, "scan", "--mode", mode), project, env, case / "rescan-after-relock.log") + ers = rs.json_or_empty() + check("rescanAfterRelockApplies", rs.ok() and applied_count(mode, ers) >= 0 and marker in (project / "poetry.lock").read_bytes(), {"exit": rs.rc, "applied": applied_count(mode, ers)}) + info["rescanAfterRelock"] = {"exit": rs.rc, "applied": applied_count(mode, ers), "lockChanged": (project / "poetry.lock").read_bytes() != relocked} + else: + (project / "poetry.lock").write_bytes(lock_after) + + # Rollback restores every byte and clears the ledgers. + rb = Run(cli_cmd(project, "rollback"), project, env, case / "rollback.log") + erb = rb.json_or_empty() + check("rollbackExit0", rb.ok(), (rb.out + rb.err)[-600:] if not rb.ok() else None) + check("rollbackRestoresLockBytes", (project / "poetry.lock").read_bytes() == pristine_lock) + check("rollbackKeepsPyproject", (project / "pyproject.toml").read_bytes() == pristine_pyproject) + if mode == "hosted": + check("rollbackClearsRedirectLedger", not (project / ".socket/vendor/redirect-state.json").exists() or not json.loads((project / ".socket/vendor/redirect-state.json").read_text()).get("records")) + if mode == "vendored": + check("rollbackRemovesVendoredWheel", not (project / ".socket/vendor/pypi" / (uuid or "x")).exists()) + if mode == "agent": + res = oracle(python, list(after), project, case / "oracle-rollback.log") + check("rollbackRestoresUpstreamBytes", bool(before) and all(res.get(n) == h for n, h in before.items()), res) + mf = project / ".socket/manifest.json" + check("rollbackClearsManifest", not mf.exists() or json.loads(mf.read_text()).get("patches") in ({}, None)) + info["rollbackEnvelope"] = {k: erb.get(k) for k in ("status", "rolledBack", "failed", "hosted", "vendoredReverted", "manifest") if k in erb} + informational = {"lockOnlyVendorApplies", "warmInstallReplacesUpstream"} + row["passed"] = all(val for k, val in checks.items() if k not in informational) + return row + + prepared = {} + with concurrent.futures.ThreadPoolExecutor(max_workers=args.jobs) as pool: + futs = {pool.submit(prepare_tool, v): v for v in args.versions} + for f in concurrent.futures.as_completed(futs): + v = futs[f] + try: + prepared[v] = f.result() + say("bootstrapped poetry", v) + except Exception as e: + say("BOOTSTRAP FAILED", v, str(e)[-800:]) + + def wanted(version, shape, mode): + v = vtuple(version) + if version not in prepared: + return False + if shape == "populated" and not version.startswith(("0.", "1.0.", "1.1.")): + return False + if shape == "pep621" and v < (2, 0): + return False + if shape in ("crlf", "pep621") and mode in ("agent", "agent-oot", "setup"): + return False + if shape == "populated" and mode in ("agent", "agent-oot", "setup"): + return False + if mode == "agent-oot" and version.startswith("0."): + return False + if mode == "setup" and version not in ("1.1.15", "1.8.5", "2.4.3"): + return False + return True + + jobs = [(v, s, m) for v in args.versions for s in args.shapes for m in args.modes if wanted(v, s, m)] + say(f"{len(jobs)} cases") + results, errors = [], [] + # Generate native locks serially per version first (the pool would race on the shared dir). + for v in args.versions: + if v in prepared: + for shape in {("pep621" if s == "pep621" else "direct") for s in args.shapes if any(wanted(v, s, m) for m in args.modes)}: + try: + native_lock(v, prepared[v], shape) + except Exception as e: + say("LOCK GENERATION FAILED", v, shape, str(e)[-800:]) + errors.append({"poetry": v, "shape": shape, "error": "lock generation: " + str(e)[-1500:]}) + with concurrent.futures.ThreadPoolExecutor(max_workers=args.jobs) as pool: + pending = {pool.submit(backtest, job): job for job in jobs} + for fut in concurrent.futures.as_completed(pending): + job = pending[fut] + try: + row = fut.result() + results.append(row) + failed = [k for k, ok in row["checks"].items() if not ok] + say(*job, "PASS" if row["passed"] else "FAIL", ",".join(failed)) + except Exception as e: + errors.append({"poetry": job[0], "shape": job[1], "mode": job[2], "error": str(e)[-3000:], "trace": traceback.format_exc()[-1500:]}) + say(*job, "ERROR", str(e)[-300:].replace("\n", " ")) + save(root / "summary.json", {"provenance": provenance, "results": sorted(results, key=lambda r: (vtuple(r["poetry"]), r["shape"], r["mode"])), "errors": errors}) + summary = json.loads((root / "summary.json").read_text()) if (root / "summary.json").exists() else {"provenance": provenance, "results": results, "errors": errors} + (root / "summary.md").write_text(render_table(summary)) + say(render_table(summary)) + if errors or any(not r["passed"] for r in results): + sys.exit(1) + + +def render_doc_table(summary): + """Per-version compatibility table for docs/testing/poetry-compatibility.md.""" + rows = summary["results"] + by = {} + for r in rows: + by.setdefault(r["poetry"], []).append(r) + lines = [ + "| Poetry | hosted | vendored | agent (in-project venv) | agent (`poetry run`, out-of-tree venv) | tamper rejected (hosted / vendored) | warm venv re-installed (hosted / vendored) | relock keeps patch (hosted / vendored) | lock-only vendored |", + "| --- | --- | --- | --- | --- | --- | --- | --- | --- |", + ] + + def cell(cases): + if not cases: + return "n/a" + ok = sum(1 for c in cases if c["passed"]) + shapes = ",".join(sorted({c["shape"] for c in cases})) + return ("pass" if ok == len(cases) else f"{ok}/{len(cases)}") + f" ({shapes})" + + def flag(cases, key, sub=None): + vals = set() + for c in cases: + i = c.get("info", {}).get(key) + if isinstance(i, dict): + vals.add(i.get(sub)) + vals.discard(None) + return "/".join(sorted(str(v).lower() for v in vals)) or "n/a" + + for version in sorted(by, key=vtuple): + cs = by[version] + m = lambda mode: [c for c in cs if c["mode"] == mode] + hosted, vendored = m("hosted"), m("vendored") + refused = any(c.get("expected", "").startswith("refused") for c in hosted) + hosted_cell = "refused (0.x ignores URL sources)" if refused else cell(hosted) + direct_h = [c for c in hosted if c["shape"] == "direct" and "tamper" in c["info"]] + direct_v = [c for c in vendored if c["shape"] == "direct" and "tamper" in c["info"]] + tamper = f"{'yes' if any(c['info']['tamper']['installExit'] != 0 for c in direct_h) else ('n/a' if not direct_h else 'no')} / {'yes' if any(c['info']['tamper']['installExit'] != 0 for c in direct_v) else ('n/a' if not direct_v else 'no')}" + relock = f"{flag([c for c in hosted if c['shape']=='direct'], 'relock', 'patchSourceKept')} / {flag([c for c in vendored if c['shape']=='direct'], 'relock', 'patchSourceKept')}" + warm = f"{flag([c for c in hosted if c['shape']=='direct'], 'warmInstall', 'patched')} / {flag([c for c in vendored if c['shape']=='direct'], 'warmInstall', 'patched')}" + # Per shape: the unpopulated legacy fixtures (`urllib3 = []`) name no + # wheel hash, so they stay refused while the populated ones vendor. + per_shape = {} + for c in vendored: + lo = c.get("info", {}).get("lockOnlyVendor") + if lo is not None: + per_shape[c["shape"]] = "yes" if lo.get("applied") == 1 else "refused" + if not per_shape: + lockonly = "n/a" + elif len(set(per_shape.values())) == 1: + lockonly = next(iter(per_shape.values())) + else: + lockonly = ", ".join(f"{v} ({k})" for k, v in sorted(per_shape.items())) + lines.append( + f"| {version} | {hosted_cell} | {cell(vendored)} | {cell(m('agent'))} | {cell(m('agent-oot'))} | {tamper} | {warm} | {relock} | {lockonly} |" + ) + return "\n".join(lines) + + +def render_table(summary): + rows = summary["results"] + lines = ["| Poetry | shape | mode | passed | failed checks | notes |", "| --- | --- | --- | --- | --- | --- |"] + for r in sorted(rows, key=lambda r: (vtuple(r["poetry"]), r["shape"], r["mode"])): + failed = ", ".join(k for k, ok in r["checks"].items() if not ok) + notes = [] + info = r.get("info", {}) + if "relock" in info: + notes.append(f"relock({info['relock'].get('cmd')}) exit {info['relock'].get('exit')} keeps patch={info['relock'].get('patchSourceKept')}") + if "lockCheck" in info and info["lockCheck"].get("cmd"): + notes.append(f"{info['lockCheck']['cmd']} exit {info['lockCheck']['exit']}") + if "tamper" in info: + notes.append(f"tamper install exit {info['tamper']['installExit']} (expects reject={info['tamper']['expectsReject']})") + if "lockOnlyVendor" in info: + notes.append(f"lock-only vendor applied={info['lockOnlyVendor']['applied']} {info['lockOnlyVendor']['codes']}") + if "bareScan" in info: + notes.append(f"bare scan sees venv={r['checks'].get('bareScanSeesPoetryVenv')}") + if "vex" in info: + notes.append(f"vex exit {info['vex'].get('exit')} stmts={info['vex'].get('statements')}") + if "poetryLockAfterSetup" in info: + notes.append(f"poetry lock after setup exit {info['poetryLockAfterSetup']['exit']}") + lines.append(f"| {r['poetry']} | {r['shape']} | {r['mode']} | {'PASS' if r['passed'] else 'FAIL'} | {failed} | {'; '.join(notes)} |") + for e in summary.get("errors", []): + lines.append(f"| {e.get('poetry')} | {e.get('shape')} | {e.get('mode')} | ERROR | {e['error'][-160:].replace(chr(10), ' ')} | |") + return "\n".join(lines) + + +if __name__ == "__main__": + main()