Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
a9effc7000 fix(process): handle Object.prototype methods on Windows process.env
On Windows, process.env is wrapped in a Proxy for case-insensitive access.
The get trap previously converted all property names to uppercase and looked
them up in the env map, which meant inherited methods like hasOwnProperty
returned undefined (or threw an error when called).

This fix checks if the property is an inherited Object.prototype method
first (before looking up env vars), and returns a wrapper function that
invokes the method with the proxy as `this` so it works correctly.

Uses $Object.prototype, $isCallable, and $call intrinsics per code review
feedback to avoid relying on potentially mutated prototypes.

Fixes #26315

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 04:08:59 +00:00
2 changed files with 110 additions and 2 deletions

View File

@@ -386,9 +386,27 @@ export function windowsEnv(
return { ...internalEnv };
};
const objectProto = $Object.prototype;
return new Proxy(internalEnv, {
get(_, p) {
return typeof p === "string" ? internalEnv[p.toUpperCase()] : undefined;
get(target, p, receiver) {
if (typeof p === "string") {
// First check if it's an inherited Object.prototype method (case-sensitive)
// This prevents env vars from shadowing methods like hasOwnProperty
if (p in objectProto) {
const method = objectProto[p];
if ($isCallable(method)) {
// Return a wrapper that invokes the method with the proxy as `this`
// Using $call avoids relying on potentially mutated Function.prototype.bind
return function (...args) {
return method.$call(receiver, ...args);
};
}
return method;
}
// Otherwise, look up the env var (case-insensitive)
return internalEnv[p.toUpperCase()];
}
return undefined;
},
set(_, p, value) {
const k = String(p).toUpperCase();

View File

@@ -0,0 +1,90 @@
import { expect, test } from "bun:test";
// https://github.com/oven-sh/bun/issues/26315
// process.env.hasOwnProperty() throws on Windows because the Windows proxy
// doesn't handle inherited Object.prototype methods
test("process.env.hasOwnProperty works", () => {
// hasOwnProperty should be a function
expect(typeof process.env.hasOwnProperty).toBe("function");
// Use a temporary env var to test hasOwnProperty
const testKey = "__TEST_HAS_OWN_PROPERTY_26315__";
const originalValue = process.env[testKey];
try {
process.env[testKey] = "test_value";
// Should return true for existing env vars
expect(process.env.hasOwnProperty(testKey)).toBe(true);
delete process.env[testKey];
// Should return false for non-existent env vars
expect(process.env.hasOwnProperty(testKey)).toBe(false);
} finally {
if (originalValue !== undefined) {
process.env[testKey] = originalValue;
} else {
delete process.env[testKey];
}
}
});
test("process.env.propertyIsEnumerable works", () => {
expect(typeof process.env.propertyIsEnumerable).toBe("function");
// Use a temporary env var to test propertyIsEnumerable
const testKey = "__TEST_PROP_IS_ENUM_26315__";
const originalValue = process.env[testKey];
try {
process.env[testKey] = "test_value";
// Should be enumerable for existing env vars
expect(process.env.propertyIsEnumerable(testKey)).toBe(true);
delete process.env[testKey];
// Non-existent vars should not be enumerable
expect(process.env.propertyIsEnumerable(testKey)).toBe(false);
} finally {
if (originalValue !== undefined) {
process.env[testKey] = originalValue;
} else {
delete process.env[testKey];
}
}
});
test("process.env.toString works", () => {
expect(typeof process.env.toString).toBe("function");
expect(process.env.toString()).toBe("[object Object]");
});
test("process.env.valueOf works", () => {
expect(typeof process.env.valueOf).toBe("function");
// valueOf should return the proxy itself
expect(process.env.valueOf()).toBe(process.env);
});
test("process.env.isPrototypeOf works", () => {
expect(typeof process.env.isPrototypeOf).toBe("function");
expect(process.env.isPrototypeOf({})).toBe(false);
});
test("env var named after Object.prototype method is accessible", () => {
// If someone sets an env var named "HASOWNPROPERTY", that should be accessible
// via the uppercase name, while the lowercase still returns the method (like Node.js)
const originalValue = process.env.HASOWNPROPERTY;
try {
process.env.HASOWNPROPERTY = "custom_value";
// The method is still accessible via lowercase (matching Node.js behavior)
expect(typeof process.env.hasOwnProperty).toBe("function");
// The env var is accessible via uppercase
expect(process.env.HASOWNPROPERTY).toBe("custom_value");
} finally {
if (originalValue !== undefined) {
process.env.HASOWNPROPERTY = originalValue;
} else {
delete process.env.HASOWNPROPERTY;
}
}
});