Skip to content

isWeb() in fs.js incorrectly detects Bun as web environment, breaking keychain access #3535

@andrii-solokh

Description

@andrii-solokh

Summary

When running @salesforce/core under the Bun runtime, the isWeb() check in lib/fs/fs.js returns true, causing the library to use memfs (in-memory filesystem) instead of node:fs. This breaks all filesystem-dependent functionality, including keychain access for org authentication.

Root Cause

In lib/fs/fs.js, line 37:

const isWeb = () => process.env.FORCE_MEMFS === 'true' || 'window' in globalThis || 'self' in globalThis;

Bun exposes self in globalThis as an alias for globalThis itself (for Web API compatibility), similar to how browsers expose self. This causes 'self' in globalThis to return true, making isWeb() incorrectly identify Bun as a web/browser environment.

Impact

All fs operations go through memfs instead of the real filesystem. The first visible symptom is typically:

MissingCredentialProgramError: Unable to find required security software: /usr/bin/security

This happens because memfs.statSync('/usr/bin/security') throws (the file doesn't exist in the in-memory filesystem), even though /usr/bin/security exists on disk.

Reproduction

  1. Install a project that depends on @salesforce/core@^8.28.0
  2. Run it with Bun (e.g., bun run my-cli-command)
  3. Any operation that triggers keychain/crypto access will fail with MissingCredentialProgramError

Verification:

// Bun exposes self
bun -e "console.log('self' in globalThis)" // true

// Node.js does not
node -e "console.log('self' in globalThis)" // false

Suggested Fix

The isWeb() check should use a more reliable detection method. Options:

  1. Check for window (DOM) specifically, since self exists in both Web Workers and Bun:

    const isWeb = () => process.env.FORCE_MEMFS === 'true' || (typeof window !== 'undefined' && typeof document !== 'undefined');
  2. Exclude known server runtimes:

    const isWeb = () => process.env.FORCE_MEMFS === 'true' || (('window' in globalThis || 'self' in globalThis) && !('process' in globalThis && process.versions?.node || process.versions?.bun));
  3. Check for the absence of node:fs availability instead of checking globals.

Environment

  • @salesforce/core: 8.28.0
  • Runtime: Bun 1.3.9
  • OS: macOS (Darwin arm64)

Related Repository

The affected code lives in forcedotcom/sfdx-core (src/fs/fs.ts).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssue or pull request that identifies or fixes a bugvalidatedVersion information for this issue has been validated

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions