GeezerVison is showing github.com — fix(vendor): vendor pure-ruby (?platform=ruby) gems, gate on the purl not the staging dir by mikolalysenko · Pull Request #172 · SocketDev/socket-patch
Conversation
Problem
Vendoring a pure-ruby gem whose production patch purl is platform-qualified — pkg:gem/[email protected]?platform=ruby — was refused with platform_gem_unsupported ("installed dir gem does not equal activestorage-7.0.2.2"). The ruby platform is the DEFAULT, portable RubyGems platform, not a native build, so this refusal is wrong.
The gem vendor gate used installed_dir.file_name() != "<name>-<version>" as a proxy for "platform-specific build". That proxy breaks on the registry auto-fetch ladder: a lockfile-resolvable-but-not-installed gem is staged into a private tempdir named literally gem (registry_fetch::fetch_gem), so dir_name is gem and every pure-ruby auto-fetched gem tripped the gate.
What #157 already fixed vs. what remained (verified against current main, real production API + rubygems.org, SOCKET_NO_CONFIG=true):
Fix
Gate on the authoritative signal — the purl's own ?platform= qualifier — instead of the staging dir name:
?platform=ruby (and a bare, unqualified purl) is the portable build and vendors normally.
- Only a genuine native platform (
x86_64-linux, arm64-darwin, java, x64-mingw32, …) is refused, with the same clear platform_gem_unsupported code.
- Defense in depth: a locally-resolved platform-suffixed install dir (
<name>-<version>-<suffix>) is still refused (preserving the downstream invariant that platform installs never reach the copy/lock edit). The auto-fetch gem dir is not such a suffix, so it passes.
Adds a small reusable purl_qualifier(purl, key) to utils::purl (the other ecosystems only ever strip qualifiers) rather than hand-rolling the parse. Change is scoped to the gem vendor path.
Test
- New hermetic unit tests in the gem vendor module (RED before / GREEN after — both fail on the old gate, the first reproducing the exact
installed dir gem does not equal rack-3.2.6 symptom):
test_platform_ruby_gem_from_autofetch_staging_dir_vendors — a ?platform=ruby gem from a gem-named auto-fetch staging dir now vendors successfully.
test_refuses_native_platform_qualifier — a native ?platform=x86_64-linux purl is still refused even from a clean leaf dir.
test_refuses_platform_suffixed_dir (existing) stays green via the defense-in-depth dir check.
test_purl_qualifier — unit coverage for the new helper.
- Real end-to-end (rebuilt binary,
SOCKET_NO_CONFIG=true, live prod + rubygems.org):
- The previously-broken auto-fetch scenario now vendors cleanly 4/4 runs (
applied: 1, exit 0), via vendor_fetched_missing → vendor_prebuilt_downloaded.
- Installed-gem scenario still clean 8/8.
- Delivery proof: a cold, frozen
bundle install from the committed .socket/vendor/ tree succeeds (exit 0) and bundle info activestorage resolves 7.0.2.2 from the vendored (patched) path.
cargo build + cargo clippy (both crates) clean; the only clippy warnings are pre-existing doc_lazy_continuation in e2e_vendored_production.rs (untouched).
Note (separate, pre-existing, out of scope): with the default --vendor-source auto/service, the patch-service gem-stub-gemspec for activestorage omits s.summary, which RubyGems' validation rejects on bundle install. This is a converter/service-side artifact defect, not in the gem vendor CLI path — it affects the installed and auto-fetch paths equally and was simply never reached before because vendoring failed first. Delivery via the local-build stub (a valid gemspec) works, as shown above. The live-ignored gem_bundler_vendored_known_platform_defect e2e leg will now take its built-in "vendor succeeds" branch; promoting it to a full frozen-install delivery proof is a follow-up gated on that stub-summary fix.
🤖 Generated with Claude Code
Note Medium Risk
Changes gem vendor gating logic on production patch paths; scope is limited to platform detection with added tests, but incorrect classification could still allow or block vendoring incorrectly.
Overview
Fixes incorrect platform_gem_unsupported refusals for pure-ruby gems whose patch PURL is ?platform=ruby (or unqualified), especially on the registry auto-fetch path where content is staged in a tempdir named gem rather than <name>-<version>.
Adds purl_qualifier in utils/purl to read qualifier values (case-insensitive keys, stops at #subpath). Gem vendor logic now treats ?platform=ruby and bare purls as portable and vendors them; only non-empty, non-ruby platform values (e.g. x86_64-linux) are refused via platform_gem_unsupported.
Defense in depth: platform-suffixed install dirs (<name>-<version>-<suffix>) are still refused via dir_name.starts_with("{leaf}-") instead of dir_name != leaf, so the literal gem staging dir is not mistaken for a native build.
New unit tests cover purl_qualifier, native platform refusal from a clean leaf dir, and the auto-fetch gem staging regression.
Reviewed by Cursor Bugbot for commit ed3688f. Configure here.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
crates/socket-patch-core/src/vendor/gem.rs:215 — maybe allowlist?
if dir_name != leaf && dir_name != "gem" {
Only the two legitimate names pass (<name>-<version> when installed, gem when staged by registry_fetch::fetch_gem), so an unexpected dir still fails closed. The current suffix check admits any other name. Non-blocking.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
… not the staging dir
The gem vendor gate refused any install whose dir name was not
`<name>-<version>`, using that as a proxy for "platform-specific build".
That proxy is wrong for the registry auto-fetch ladder: a lockfile-
resolvable-but-not-installed gem is staged into a private tempdir named
literally `gem` (registry_fetch::fetch_gem), so a pure-ruby gem like
`activestorage` (whose production patch purl is `?platform=ruby`, the
DEFAULT portable platform) was refused with `platform_gem_unsupported`.
gems the crawler finds installed (qualified `?platform=ruby` now maps to
the installed base dir), so `scan --mode vendored` vendors cleanly when the
gem is installed under `vendor/bundle` or a gem home. But the auto-fetch
path (fresh clone / uninstalled but lock-resolvable) still hit the bug with
the identical error.
Gate on the authoritative signal instead: the purl's own `?platform=`
qualifier. `ruby` (and a bare, unqualified purl) is the portable build and
vendors; only a genuine native platform (`x86_64-linux`, `arm64-darwin`,
`java`, `x64-mingw32`, …) is refused. A defense-in-depth dir check still
refuses a locally-resolved platform-suffixed install dir
(`<name>-<version>-<suffix>`), which the auto-fetch `gem` dir is not.
Adds a reusable `purl_qualifier(purl, key)` to utils::purl (the other
ecosystems only ever strip qualifiers) and hermetic regression tests:
`?platform=ruby` from a `gem`-named staging dir now vendors; a native
`?platform=x86_64-linux` is refused even from a clean leaf dir.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Per review (Tanmay182003): the defense-in-depth install-dir check was a
suffix match (`dir_name != leaf && dir_name.starts_with("{leaf}-")`), which
refuses `<name>-<version>-<platform>` builds but ADMITS any other unexpected
dir name. Switch to an allowlist — only the two legitimate names pass: the
installed `<name>-<version>` leaf and the literal `gem` staging dir from the
registry auto-fetch ladder (registry_fetch::fetch_gem). Everything else fails
closed. Generalize the refusal message accordingly (kept errorCode
platform_gem_unsupported); update the one dir-check assertion that keyed on
the old "platform-suffixed" wording to "platform-specific", and add
test_refuses_unexpected_install_dir_name (a `random-unrelated` dir the old
suffix check would have admitted is now refused).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The pinned gem patch [email protected] (2535d43d) was intentionally
unpublished 2026-08-14 pending a corrected republish. Its record still
resolves via /patch/view/<uuid>, but the discovery endpoints
(/patch/batch, /patch/by-package) now return zero patches for it, so
preflight_required_patches_are_published, the advisory canary, and the
gem redirect leg fail for reasons unrelated to the CLI — red-lighting the
required hosted-e2e check on every PR and on main.
Add a single GEM_E2E_DISABLED switch (defaulting true) that skips those
three gem legs while keeping npm/PyPI/cargo enforced. The gem redirect
leg returns unconditionally (NOT soft_skip!, which panics under STRICT),
so it skips cleanly in CI too. Flip the const back to false (and re-point
GEM_UUID if the replacement differs) once the corrected patch is live.
Verified under SOCKET_PATCH_HOSTED_E2E_STRICT=1 against real production:
preflight + canary pass on the three remaining ecosystems, the gem leg
prints SKIP and returns; 3 passed, 0 failed.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Uh oh!
There was an error while loading. Please reload this page.
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Aug 19, 2026
…ish (#217)
Production republished the rubygems patch catalog on 2026-08-18: 15 free
activestorage patches (5.2.0-6.0.3, GHSA-m42x-37p3-fv5w / CVE-2020-8162).
The old pin [email protected] (2535d43d-…) no longer exists, and the
hosted registry's compact index now serves the full dependency list — the
server defect that motivated the temporary gem shutdown is fixed.
- e2e_hosted_production: delete the GEM_E2E_DISABLED kill switch (all 3
sites) and the probe-based install tolerance per its own auto-retire
design (gem_registry_base/http_probe helpers now dead, removed); re-pin
to [email protected] / 15e960b5-…; the leg is renamed
gem_bundler_hosted_install_proof and asserts the full chain
unconditionally (redirect rewrite, CHECKSUMS pin swap to the patched
sha, real install through patch.socket.dev, per-file afterHash
verification). Verified live: 3/3 green incl. preflight + canary.
- e2e_vendored_production: re-pin; the platform_gem_unsupported tolerance
is vestigial (gap fixed in #172) and the leg's doc comment now says so.
The full delivery-proof upgrade is deferred to a stacked fix PR: the
served gem-stub-gemspec artifact is currently invalid (missing
summary/authors; bundler rejects the path source — discovered
2026-08-19). Verified live: preflight + gem leg green.
- e2e_gem: re-pin the 5.2.0 lifecycle tests to the republished patch
(efc8d8ca-…, CVE-2020-8162); GEM_PURL now carries ?platform=ruby
because `get` keys the manifest by the view response's qualified purl.
Verified live: 3/3 green.
- in_process_vendor: new test pinning the qualified-purl
(?platform=ruby) path through scan --vendor — the regression that
would silently re-open the #172 gap (ledger keyed by qualified purl,
basePurl bare).
- docs/testing/{hosted,vendored}-production-e2e.md: catalogs re-pinned,
defect sections converted to FIXED-history style, new OPEN section for
the invalid served stub gemspec.
Co-authored-by: Claude Fable 5 <[email protected]>
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Aug 19, 2026
…c is invalid (#221)
* test(gem): restore production e2e after the 2026-08-18 catalog republish
Production republished the rubygems patch catalog on 2026-08-18: 15 free
activestorage patches (5.2.0-6.0.3, GHSA-m42x-37p3-fv5w / CVE-2020-8162).
The old pin [email protected] (2535d43d-…) no longer exists, and the
hosted registry's compact index now serves the full dependency list — the
server defect that motivated the temporary gem shutdown is fixed.
- e2e_hosted_production: delete the GEM_E2E_DISABLED kill switch (all 3
sites) and the probe-based install tolerance per its own auto-retire
design (gem_registry_base/http_probe helpers now dead, removed); re-pin
to [email protected] / 15e960b5-…; the leg is renamed
gem_bundler_hosted_install_proof and asserts the full chain
unconditionally (redirect rewrite, CHECKSUMS pin swap to the patched
sha, real install through patch.socket.dev, per-file afterHash
verification). Verified live: 3/3 green incl. preflight + canary.
- e2e_vendored_production: re-pin; the platform_gem_unsupported tolerance
is vestigial (gap fixed in #172) and the leg's doc comment now says so.
The full delivery-proof upgrade is deferred to a stacked fix PR: the
served gem-stub-gemspec artifact is currently invalid (missing
summary/authors; bundler rejects the path source — discovered
2026-08-19). Verified live: preflight + gem leg green.
- e2e_gem: re-pin the 5.2.0 lifecycle tests to the republished patch
(efc8d8ca-…, CVE-2020-8162); GEM_PURL now carries ?platform=ruby
because `get` keys the manifest by the view response's qualified purl.
Verified live: 3/3 green.
- in_process_vendor: new test pinning the qualified-purl
(?platform=ruby) path through scan --vendor — the regression that
would silently re-open the #172 gap (ledger keyed by qualified purl,
basePurl bare).
- docs/testing/{hosted,vendored}-production-e2e.md: catalogs re-pinned,
defect sections converted to FIXED-history style, new OPEN section for
the invalid served stub gemspec.
Co-Authored-By: Claude Fable 5 <[email protected]>
* test(gem): adversarial-review hardening — self-adapting UUID pin, frozen converged-lock install proof, hermeticity
Round-2 fixes on the gem e2e restoration:
* hosted gem leg: GEM_UUIDS any-of set (PYPI_UUIDS precedent) — parse the
UUID actually wired into the rewritten Gemfile's patch-registry URL,
assert membership, and afterHash-verify against that exact patch's
/patch/view; hard-assert the BUNDLE_PATH wipe; reinstall under
BUNDLE_FROZEN=true with converged-lock assertions (patch-registry GEM
remote + `activestorage (= 6.0.3)!` DEPENDENCIES pin), verified live
* mimemagic hazard: USE_FREEDESKTOP_PLACEHOLDER=true on every
gem-installing leg in e2e_hosted_production.rs and e2e_gem.rs
* published_patch_advisory_counts: HTTP-status guard like its siblings
* truthing: qualifiers are normalized by the SERVER, nothing client-side
strips (consts comments + hosted doc coverage row + vendored doc
REPLACED-pin history keeping the withdrawn 7.0.2.2/2535d43d ids findable)
* e2e_redirect_gem_build.rs: module doc reframed historical, dangling
is_known_defect pointer fixed to the docs history section
* e2e_gem.rs: GHSA-or-CVE advisory assertion, full SOCKET_* scrub +
SOCKET_NO_CONFIG, assert_after_hashes delete-entry branch
* in_process_vendor.rs: qualified gem test at full parity with its bare
twin (PATH remote, stub gemspec, downloaded==1, positive applied event),
exact percent-encoded by-package mock paths, new detached-qualified
revert test
Co-Authored-By: Claude Fable 5 <[email protected]>
* fix(vendor): fall back to local build when the served gem stub gemspec is invalid
Defense-in-depth for defect D4 from the 2026-08-19 gem live-matrix campaign
(verified 6/6 live across bundler 1.17/2.7/4.0): production's gem-stub-gemspec
secondary artifact omits the rubygems-required `summary` and `authors`
attributes (and `licenses`). The CLI sha512-verified the stub and wrote it
verbatim as the vendored path-source `<name>.gemspec`, and every bundler major
validates path-source gemspecs, so any post-vendor or fresh-checkout
`bundle install` exited 1 with `missing value for attribute summary`. A
depscan-side fix for the stub generator is in flight, but every
currently-published gem stub is invalid, so the CLI hardens now.
An INVALID served stub now follows the existing MISSING-stub policy, under its
own code (additive/MINOR):
- `--vendor-source auto`: loud `vendor_prebuilt_stub_invalid` warning naming
the missing attributes, then fall back to the local build (installed gem +
locally derived stub);
- `--vendor-source service` (explicit): refuse with
`vendor_prebuilt_stub_invalid`, naming the attributes and the remedy, before
anything is written (no partial artifacts).
Validation is a conservative textual heuristic (assignment-line presence for
`summary` / `authors`|`author`, obviously-empty spellings rejected — no ruby
parsing); a legitimate stub always passes and is still written byte-verbatim.
A missing `licenses` is only mentioned in the message (rubygems warns, not
fails). CLI_CONTRACT.md documents the new code in the fallback ladder and the
PatchAction vocabulary.
Tests:
- hermetic (wiremock, RED->GREEN): auto+invalid-stub falls back to the local
build with the loud warning and the LOCAL stub on disk; service+invalid-stub
refuses with `vendor_prebuilt_stub_invalid` leaving no partial artifacts and
an untouched lock; the valid-stub byte-verbatim write is pinned (the
service-success fixture now carries the required attributes); plus a unit
table for the heuristic's spellings.
- live regression leg: `e2e_vendored_production.rs`'s gem leg is upgraded to
`gem_bundler_vendored_install_proof` — a full fresh-dir frozen
`bundle install` delivery proof with the failure tolerance and the
`SOCKET_PATCH_VENDORED_E2E_GEM_STRICT` knob deleted. It passes against real
production TODAY via the auto fallback (the served stub is still invalid),
and exercises the service artifact directly once the depscan fix deploys and
the artifacts rebuild. The leg installs in bundler's deployment layout
(`vendor/bundle` inside the project) so the crawler-visible install can feed
the local-build fallback its stub gemspec.
Verified live against production: scan --mode vendored applied=1 via the
fallback with the `vendor_prebuilt_stub_invalid` event, vendored gemspec
carries real summary/authors; --vendor-source service refuses with the new
code and leaves no .socket/vendor.
Stacked on #217 (test/gem-e2e-restore).
Co-Authored-By: Claude Fable 5 <[email protected]>
* fix(vendor): review round — gem-home guard, empirical rubygems bar, both-arm stub validation, truthful dead-ends, on-disk heal
Adversarial-review fix round for the D4 stub-hardening PR.
SECURITY (1): the local-stub derivation walked two parents up from
installed_dir unconditionally. For a registry auto-fetch staging dir
(<private tempdir>/<name>-<version>) that escapes into the SHARED temp
root, making $TMPDIR/specifications/<leaf>.gemspec a predictable,
attacker-plantable path whose contents would be committed and eval'd as
Ruby by every later `bundle install`. The derivation now requires
installed_dir's parent to be a literal `gems/` dir (a real gem-home
layout); staging dirs have no local stub. Test plants a valid spec at
the old derivation target and proves it is never consumed.
Scanner rewrite (2): comment-stripping at `#` truncated inside string
literals (s.summary = "#1 Ruby web server" judged missing → valid stubs
refused under service). The scanner now examines raw lines anchored to
the line start (receiver ident + .attr + assignment), where a preceding
comment marker is impossible. The emptiness policy is now EMPIRICAL,
verified against rubygems 3.3/3.5/3.6 in the bundler 1.17/2.7/4.0 era
images: summary hard-fails only when never assigned (nil/"" are
writer-coerced, warning at most); authors hard-fails when never
assigned or collapsing to no String elements ([], nil, [nil], %w[] —
while [""] passes). The old code refused rubygems-tolerated stubs and
passed %w[] which hard-fails.
Both-arm validation (3): the local-build arm wrote the local stub
verbatim; it now validates at the same write choke point and refuses
`gem_spec_invalid` naming the file (new CLI_CONTRACT vocabulary row).
The GEMSPEC/GEMSPEC_318/GEMSPEC_PUMA and CLI-suite fixtures now carry
summary+authors like every healthy rubygems-written stub.
Truthful dead-end (4): auto + invalid served stub + gem not installed
used to refuse gem_spec_missing with circular advice ("use
--vendor-source=service" <-> service says "use auto") and the D4
diagnostic never reached the envelope (Refused carries no warnings).
The FallBack variant now carries the served-stub defect; the refusal is
`vendor_prebuilt_stub_invalid`, names the defect, and advises
installing the gem. Tests cover auto and service, both not-installed.
Heal existing victims (5): the idempotent hot path only checked the
vendored gemspec EXISTS, silently re-blessing pre-fix invalid stubs.
copy_ok now re-validates the on-disk stub; invalid routes into the
existing artifact-only rebuild (test: pre-seeded invalid stub on disk →
re-scan rewrites a valid one, pair edit + ledger untouched).
Dedupe (11 + addendum B): one shared attr_mention line-scanner under
both gemspec_declares_extensions and the attr checks; the miss closure
widened with the (hard code, remedy) pair instead of a re-implemented
branch; licenses/license alias fan-out folded into the alias-list
helper; stub_text bound once.
e2e leg robustness (6-10): bundler invocations scrub ambient
BUNDLE_*/GEM_*/RUBYOPT (sibling-suite pattern) and set
USE_FREEDESKTOP_PLACEHOLDER=true (mimemagic shared-mime-info hazard,
mirrors #217 round 2); delivery proof asserts canonicalized
starts_with(fresh-dir) provenance and compares installed bytes against
captured pristine bytes; a route-attribution assertion requires exactly
one of {vendor_prebuilt_downloaded, vendor_prebuilt_stub_invalid} so
the leg auto-retires the fallback expectation when the depscan stub fix
deploys; applied/failed/idempotency/revert assertions are scoped to the
activestorage purl so future catalog additions cannot red the leg;
stale doc comments fixed.
Contract (addendum A): documented why the service-mode refusal on an
invalid stub rides a MINOR — the prior exit-0 wrote a stub bundler
rejects (an uninstallable project); the refusal is the bug fix.
Rebased on test/gem-e2e-restore @ 63531d9 (PR #217 review round 2).
Co-Authored-By: Claude Fable 5 <[email protected]>
---------
Co-authored-by: Claude Fable 5 <[email protected]>
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|