Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistency of prototype chain lookup for parameters #43401

Open
LiviaMedeiros opened this issue Jun 13, 2022 · 3 comments
Open

Consistency of prototype chain lookup for parameters #43401

LiviaMedeiros opened this issue Jun 13, 2022 · 3 comments

Comments

@LiviaMedeiros
Copy link
Contributor

Version

v19.0.0-pre

Platform

any

Subsystem

lib

What steps will reproduce the bug?

import fs from 'node:fs/promises';

const DIR = '/tmp/issue43401';
const defaultDirOptions = { recursive: true };

async function run(dirOptions) {
  await fs.rm(DIR, { recursive: true, force: true });
  console.log('dirOptions.recursive:', dirOptions.recursive);
  try {
    await fs.mkdir(`${DIR}/1/2/3/4`, dirOptions);
    await fs.rm(DIR, dirOptions);
    await fs.stat(DIR);
  } catch(e) {
    console.error(e);
  }
}

await run({});
await run({ recursive: true });
await run(Object.create(defaultDirOptions));

run() tries to create nested directories, then remove them, then access topmost one.

  1. When ran with {}, mkdir must fail with ENOENT because it's not recursive.
  2. When ran with { recursive: true }, stat must fail with ENOENT because directory was deleted.

Depending on if we care that each property is "own", Object.create(defaultDirOptions) should behave either as (1) or as (2).

However, currently rm fails with EISDIR. So, mkdir is recursive but rm is not.

How often does it reproduce? Is there a required condition?

It depends on methods, it depends on version (pretty sure that some of these are changed within semver-patch level).

For some methods is might even depend on which property we access or on something even less related.

What is the expected behavior?

Consistency.

Or a warning somewhere, that only ownProperties are guaranteed to work.

Or an explicit agreement that it's UB "by design".

What do you see instead?

Inconsistency.

Additional information

In most cases, inherited properties are dropped by copying (e.g. { ...options }).

@aduh95
Copy link
Contributor

aduh95 commented Jun 17, 2022

I think only the only use case that has been considered is user is passing a plain object; to me the "correct" answer should be "Node.js will use prototype inheritance and read non-enumerable properties", to be consistent with the ECMAScript spec.

@LiviaMedeiros
Copy link
Contributor Author

Dug up a bit: this problem was found, discussed and added to tsc-agenda Issues and PRs to discuss during the meetings of the TSC. some time ago:

However, I couldn't find a further сontinuation in terms of resolving or documenting it. /cc @BridgeAR

Looks like the main argument for prototype lookups is that it is consistent with the ECMAScript specs and therefore with how things in JS usually work; and the main argument against it is that in Node.js core it is actively prevented to guard against prototype pollution.

Personally I don't think that a strict and urgent goal of having consistent behaviour across all currently existing APIs is worth the effort, churn and potential breakage. Assuming that, two questions remain open:

  1. Can we decide which approach is preferable for new APIs (or refactorings in new PRs), when it's possible to choose?
  2. Can we consider any change in this behaviour (which might happen frequently and implicitly) to be non-breaking, and any tests for it to be unwanted?

@BridgeAR
Copy link
Member

@LiviaMedeiros thanks for reminding me.

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

No branches or pull requests

3 participants