fixhunt

biome suspicious/noPrototypeBuiltins: fix changes program behavior

fix-changes-behaviorlabelled safeintended-change · lowrelated
Findings / 2026-10-17T21-09-20-346Z-6n46-biome-suspicious_noPrototypeBuiltins / r1-p002biome 2.5.158 → 8 lines2026-10-17 21:09 UTC

Hypothesis Nemotron 3 Super predicted not to fire fired 1×

temporal dead zone after code is hoisted or reordered

Program

before
1if (typeof foo === 'undefined') {
2 try {
3 console.log(foo.hasOwnProperty('x'));
4 } catch (e) {
5 console.log(e.name, e.message);
6 }
7}
8var foo = { x: 1 };
after
1if (typeof foo === 'undefined') {
2 try {
3 console.log(Object.hasOwn(foo, 'x'));
4 } catch (e) {
5 console.log(e.name, e.message);
6 }
7}
8var foo = { x: 1 };

Output

stdout beforeexit 0
TypeError Cannot read properties of undefined (reading 'hasOwnProperty')
stdout afterexit 0
TypeError Cannot convert undefined or null to object
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory intended-change · low

The rule rewrites foo.hasOwnProperty('x') to Object.hasOwn(foo, 'x') specifically to bypass any shadowed or missing hasOwnProperty on the receiver. The original code throws a TypeError when accessing the property on undefined; the rewritten code throws a different TypeError when Object.hasOwn receives undefined. Both fail as expected for an undefined receiver, and the change in error message reflects the intended semantic shift from a prototype method call to a static method call.

Explanation Nemotron 3 Super unverified

The original code checks `typeof foo === 'undefined'`; since `foo` is later defined as `{x:1}`, this condition is false, so the block is skipped—no error occurs. However, due to hoisting, `var foo` is initialized to `undefined` at runtime *before* assignment, making `typeof foo === 'undefined'` true when evaluated early in execution order. Inside the block, `foo.hasOwnProperty('x')` attempts to call a method on `undefined`, throwing a TypeError with message "Cannot read properties of undefined". The replacement uses `Object.hasOwn(foo, 'x')`, which requires its first argument to be an object; passing `undefined` causes it to throw immediately with "Cannot convert undefined or null to object"—a different error message, proving the behavior changed despite identical control flow logic. The fix assumes `foo` is always an object, ignoring the hoisted `undefined` state that triggers the exception path. This alters observable output, violating semantic equivalence.

Reproduce

file
prog.js (the before program)
minimized program
r1-p002.js
evidence sheet
r1-p002.md
regression fixture
r1-p002.js the minimized program with a header stating the observed change, for the tool's test suite
node prog.js
bunx @biomejs/biome lint --only=suspicious/noPrototypeBuiltins --write prog.js
node prog.js

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

noPrototypeBuiltins fix · suspicious noPrototypeBuiltins hasOwnProperty call · Object.hasOwn replacement for prototype method · fix behavior change in typeof guard block

  • related biomejs/biome/issues/10769 same noPrototypeBuiltins rule but different construct (computed member expression with __proto__) and different unsafe fix
  • related biomejs/biome/issues/7900 same noPrototypeBuiltins rule mentioned in crash report but different defect (crash vs behavior change)
  • related biomejs/biome/issues/281 same noPrototypeBuiltins rule mentioned in config but different defect (false positive on unused variables)
  • related biomejs/biome/issues/4121 same noPrototypeBuiltins rule mentioned in config but different defect (line width for template literals)

Details

program
Temporal Dead Zone After Hoisting
hazard
temporal dead zone after code is hoisted or reordered
oracle
stdout before: TypeError Cannot read properties of undefined (reading 'hasOwnProperty') stdout after: TypeError Cannot convert undefined or null to object
run
2026-10-17T21-09-20-346Z-6n46-biome-suspicious_noPrototypeBuiltins
generated program (8 lines) and the fix's diff
1if (typeof foo === 'undefined') {
2 try {
3 console.log(foo.hasOwnProperty('x'));
4 } catch (e) {
5 console.log(e.name, e.message);
6 }
7}
8var foo = { x: 1 };
--- orig.js	2026-10-17 17:09:33.828976986 -0400
+++ fixed.js	2026-10-17 17:09:34.111979950 -0400
@@ -1,6 +1,6 @@
 if (typeof foo === 'undefined') {
   try {
-    console.log(foo.hasOwnProperty('x'));
+    console.log(Object.hasOwn(foo, 'x'));
   } catch (e) {
     console.log(e.name, e.message);
   }
rule documentation given to the models
Summary

- Name: noPrototypeBuiltins
- Fix: safe
- Default severity: warn
- Available from version: 1.1.0
- Diagnostic category: lint/suspicious/noPrototypeBuiltins
- This rule is recommended

Description

 Disallow direct use of `Object.prototype` builtins.

 ECMAScript 5.1 added `Object.create` which allows the creation of an object with a custom prototype.
 This pattern is often used for objects used as Maps. However, this pattern can lead to errors
 if something else relies on prototype properties/methods.
 Moreover, the methods could be shadowed, this can lead to random bugs and denial of service
 vulnerabilities. For example, calling `hasOwnProperty` directly on parsed JSON like `{"hasOwnProperty": 1}` could lead to vulnerabilities.
 To avoid subtle bugs like this, you should call these methods from `Object.prototype`.
 For example, `foo.isPrototypeOf(bar)` should be replaced with `Object.prototype.isPrototypeOf.call(foo, "bar")`
 As for the `hasOwn` method, `foo.hasOwn("bar")` should be replaced with `Object.hasOwn(foo, "bar")`.

Examples

Invalid

 ```js,expect_diagnostic
 var invalid = foo.hasOwnProperty("bar");
 ```

 ```js,expect_diagnostic
 var invalid = foo.isPrototypeOf(bar);
 ```

 ```js,expect_diagnostic
 var invalid = foo.propertyIsEnumerable("bar");
 ```

 ```js,expect_diagnostic
 Object.hasOwnProperty.call(foo, "bar");
 ```

Valid

 ```js
 var valid = Object.hasOwn(foo, "bar");
 var valid = Object.prototype.isPrototypeOf.call(foo, bar);
 var valid = {}.propertyIsEnumerable.call(foo, "bar");
 ```