Skip to content

Make switch npm registry and authentication configurable#269

Open
bgotink wants to merge 1 commit intomainfrom
feat/switch-registry
Open

Make switch npm registry and authentication configurable#269
bgotink wants to merge 1 commit intomainfrom
feat/switch-registry

Conversation

@bgotink
Copy link
Member

@bgotink bgotink commented Mar 15, 2026

I'm not sure where & how best to add tests for this, would love some pointers!
Code works with a local verdaccio setup.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 15, 2026

Confidence Score: 2/5

  • Not safe to merge without addressing the cache key issue — cached binaries from one registry will silently be reused when a different registry is configured.
  • The HTTP and auth logic is sound, but the cache key not incorporating the registry URL is a meaningful correctness issue: a binary downloaded from a private registry will be served from cache even when the default registry is expected, and vice versa. This undermines the core purpose of the feature.
  • packages/zpm-switch/src/cache.rs — the CacheKey struct and its hash need to incorporate the registry URL to correctly scope cached binaries to their source registry.

Important Files Changed

Filename Overview
packages/zpm-switch/src/cache.rs Removed the hardcoded https://registry.npmjs.org/ prefix from to_npm_url() to return a relative path, enabling the registry to be configured externally — but the cache key still doesn't include the registry URL, so cached binaries from different registries will be incorrectly shared.
packages/zpm-switch/src/http.rs Refactored fetch into create_request + send_request helpers, and added fetch_from_npm which reads the registry URL and authentication credentials (YARN_SWITCH_NPM_AUTH_TOKEN / YARN_SWITCH_NPM_AUTH_IDENT) from environment variables. Base64 encoding logic for Basic auth is functionally correct but could benefit from a clarifying comment.
packages/zpm-switch/src/install.rs Minimal change: imports fetch_from_npm and uses it instead of fetch for npm registry downloads. Correct and straightforward.
packages/zpm-switch/Cargo.toml Adds the base64 workspace dependency needed for Basic auth encoding in the new HTTP authentication logic.
Cargo.lock Auto-generated lock file update adding base64 to the zpm-switch dependency list.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/zpm-switch/src/http.rs
Line: 67-71

Comment:
**`auth_ident` base64 detection encodes on wrong condition**

The intent here appears to be: encode raw `username:password` strings, but pass through pre-encoded base64 tokens as-is. However, the condition is inverted for this intent — it base64-encodes when `:` is present (raw credentials) and passes through as-is when `:` is absent (presumably already encoded).

This happens to be correct behavior because RFC 7617 Basic auth requires base64-encoding of `user:password` (which contains `:`), and a pre-encoded base64 string will never contain `:` (base64 uses A–Z, a–z, 0–9, `+`, `/`, `=`). So mechanically the code works.

That said, the logic would be clearer and more intentional if the comment explained that `:` presence distinguishes raw credentials from pre-encoded tokens — to avoid a future reader interpreting this as a bug and "fixing" it by reversing the condition.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/zpm-switch/src/cache.rs
Line: 33

Comment:
**Cache key does not include registry URL**

The cache key (`CacheKey`) is hashed from `cache_version`, `version`, and `platform` only. It does not include the registry URL (`YARN_SWITCH_NPM_REGISTRY`). This means:

- If a user downloads Yarn from a private/custom registry (e.g., one serving a patched binary), the result is cached under the same key as the official package.
- On the next invocation — even if the user has unset `YARN_SWITCH_NPM_REGISTRY` or changed it back to the default — the cached binary from the private registry will be served.

Conversely, a clean cache populated from the default registry will be reused when a private registry is configured, completely bypassing the custom registry.

Since the registry can now serve genuinely different binaries at the same version number (the whole point of this PR), the registry URL should be part of the cache key to ensure cache entries are scoped to their source.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 5b68f86

@bgotink bgotink force-pushed the feat/switch-registry branch from c620476 to 5b68f86 Compare March 15, 2026 22:01
Comment on lines +67 to +71
if let Ok(token) = std::env::var("YARN_SWITCH_NPM_AUTH_TOKEN") {
request = request.bearer_auth(token);
} else if let Ok(mut auth_ident) = std::env::var("YARN_SWITCH_NPM_AUTH_IDENT") {
if auth_ident.contains(':') {
auth_ident = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, auth_ident);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auth_ident base64 detection encodes on wrong condition

The intent here appears to be: encode raw username:password strings, but pass through pre-encoded base64 tokens as-is. However, the condition is inverted for this intent — it base64-encodes when : is present (raw credentials) and passes through as-is when : is absent (presumably already encoded).

This happens to be correct behavior because RFC 7617 Basic auth requires base64-encoding of user:password (which contains :), and a pre-encoded base64 string will never contain : (base64 uses A–Z, a–z, 0–9, +, /, =). So mechanically the code works.

That said, the logic would be clearer and more intentional if the comment explained that : presence distinguishes raw credentials from pre-encoded tokens — to avoid a future reader interpreting this as a bug and "fixing" it by reversing the condition.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/zpm-switch/src/http.rs
Line: 67-71

Comment:
**`auth_ident` base64 detection encodes on wrong condition**

The intent here appears to be: encode raw `username:password` strings, but pass through pre-encoded base64 tokens as-is. However, the condition is inverted for this intent — it base64-encodes when `:` is present (raw credentials) and passes through as-is when `:` is absent (presumably already encoded).

This happens to be correct behavior because RFC 7617 Basic auth requires base64-encoding of `user:password` (which contains `:`), and a pre-encoded base64 string will never contain `:` (base64 uses A–Z, a–z, 0–9, `+`, `/`, `=`). So mechanically the code works.

That said, the logic would be clearer and more intentional if the comment explained that `:` presence distinguishes raw credentials from pre-encoded tokens — to avoid a future reader interpreting this as a bug and "fixing" it by reversing the condition.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


if self.version >= first_npm_release {
return Some(format!("https://registry.npmjs.org/@yarnpkg/yarn-{}/-/yarn-{}-{}.tgz", self.platform, self.platform, self.version.to_file_string()));
return Some(format!("@yarnpkg/yarn-{}/-/yarn-{}-{}.tgz", self.platform, self.platform, self.version.to_file_string()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache key does not include registry URL

The cache key (CacheKey) is hashed from cache_version, version, and platform only. It does not include the registry URL (YARN_SWITCH_NPM_REGISTRY). This means:

  • If a user downloads Yarn from a private/custom registry (e.g., one serving a patched binary), the result is cached under the same key as the official package.
  • On the next invocation — even if the user has unset YARN_SWITCH_NPM_REGISTRY or changed it back to the default — the cached binary from the private registry will be served.

Conversely, a clean cache populated from the default registry will be reused when a private registry is configured, completely bypassing the custom registry.

Since the registry can now serve genuinely different binaries at the same version number (the whole point of this PR), the registry URL should be part of the cache key to ensure cache entries are scoped to their source.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/zpm-switch/src/cache.rs
Line: 33

Comment:
**Cache key does not include registry URL**

The cache key (`CacheKey`) is hashed from `cache_version`, `version`, and `platform` only. It does not include the registry URL (`YARN_SWITCH_NPM_REGISTRY`). This means:

- If a user downloads Yarn from a private/custom registry (e.g., one serving a patched binary), the result is cached under the same key as the official package.
- On the next invocation — even if the user has unset `YARN_SWITCH_NPM_REGISTRY` or changed it back to the default — the cached binary from the private registry will be served.

Conversely, a clean cache populated from the default registry will be reused when a private registry is configured, completely bypassing the custom registry.

Since the registry can now serve genuinely different binaries at the same version number (the whole point of this PR), the registry URL should be part of the cache key to ensure cache entries are scoped to their source.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the registry can now serve genuinely different binaries at the same version number (the whole point of this PR), the registry URL should be part of the cache key to ensure cache entries are scoped to their source.

The point of this PR isn't that different registries can serve different results, it's that not everyone has open internet access from their device. The NPM registry is explicitly blocked on our corporate network.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense for your use case. If the custom registry is purely a proxy/mirror serving identical binaries, then the cache key doesn't need to change.

However, the implementation doesn't enforce that constraint — it accepts any registry URL via the environment variable. If someone (intentionally or accidentally) points YARN_SWITCH_NPM_REGISTRY at a registry serving different packages, the cache would still serve binaries from the wrong source.

Two options:

  1. Accept the risk — document that custom registries should be mirrors/proxies only, and cache invalidation is the user's responsibility if they switch registries
  2. Add a safeguard — include the registry URL in the cache key to ensure cache entries are isolated by source, even if that's not the primary use case

Which approach fits your project's expectations better?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant