FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

2025-07-29, Version 24.5.0 (Current) by github-actions[bot] · Pull Request #59257 · nodejs/node · GitHub

Repository navigation

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
d555db2
lib: flag to conditionally modify proto on deprecate
RafaelGSS Jul 8, 2025
f753645
net: update net.blocklist to allow file save and file management
alphaleadership Jul 8, 2025
1cffcc0
test: add missing port=0 arg in test-debugger-extract-function-name
dario-piotrowicz Jul 8, 2025
04c7514
meta: add marco-ippolito to security release stewards
marco-ippolito Jul 9, 2025
cfaa299
lib: fix incorrect `ArrayBufferPrototypeGetDetached` primordial type
dario-piotrowicz Jul 9, 2025
c9ded6b
doc: add sponsorship link to RafaelGSS
RafaelGSS Jul 9, 2025
515b581
module: throw error when re-runing errored module jobs
joyeecheung Jul 9, 2025
3a3eb68
repl: improve REPL disabling completion on proxies and getters
dario-piotrowicz Jul 9, 2025
2178007
src: check import attributes value types as strings
legendecas Jul 9, 2025
85d6a28
inspector: initial support for Network.loadNetworkResource
islandryu Jul 10, 2025
cb95e18
doc: add scroll margin to links
silverwind Jul 10, 2025
c52aaac
dns: support max timeout
theanarkh Jul 10, 2025
3924c43
doc: add missing `<code>` blocks
aduh95 Jul 10, 2025
45ffdb3
doc: add stability index to the `--watch-kill-signal` flag
dario-piotrowicz Jul 10, 2025
83cdf17
test_runner: clean up promisified interval generation
Renegade334 Jul 10, 2025
9d02960
doc: add missing environment variables to manpage
HumaneLogic Jul 11, 2025
467dbd3
repl: fix repl crashing on variable declarations without init
dario-piotrowicz Jul 11, 2025
623e264
tools: ignore CVE mention when linting release proposals
aduh95 Jul 13, 2025
d2e7f8e
doc: remove unused import in `zlib.md`
coderaiser Jul 14, 2025
6cb5e0d
doc: refine `util.parseArgs` `default` definition
Slayer95 Jul 15, 2025
1940a2c
doc: fix typed list formatting
avivkeller Jul 15, 2025
999b5e5
doc: run license-builder
github-actions[bot] Jul 16, 2025
2d1dcb8
doc: clarify ERR_FS_FILE_TOO_LARGE to reflect fs.readFile() I/O limit
haramj Jul 16, 2025
0e547e0
tools,test: enforce best practices to detect never settling promises
aduh95 Jul 16, 2025
9b2e965
doc: add RafaelGSS as steward July 25
RafaelGSS Jul 16, 2025
34c4900
doc: add missing section for `setReturnArrays` in `sqlite.md`
geeksilva97 Jul 17, 2025
e3e729c
build: remove suppressions.supp
RafaelGSS Jul 17, 2025
a9511a6
test: mark test-inspector-network-fetch as flaky on Windows
joyeecheung Jul 17, 2025
16dc53c
test: move http proxy tests to test/client-proxy
joyeecheung Jul 3, 2025
7f654ce
http,https: add built-in proxy support in http/https.request and Agent
joyeecheung Jul 1, 2025
324d9fc
meta: enable jsdoc/check-tag-names rule
anonrig Jul 18, 2025
135fca5
crypto: avoid copying buffers to UTF-8 strings in `crypto.hash()`
Renegade334 Jul 14, 2025
15e6c28
node-api,doc: update links to ecma262 with section names
legendecas Jul 18, 2025
9791ff3
worker: add web locks api
IlyasShabi May 29, 2025
b1a318d
doc: update release key for aduh95
aduh95 Jul 18, 2025
eeeb40e
crypto: add tls.setDefaultCACertificates()
joyeecheung Jul 18, 2025
71bb6cd
esm: js-string Wasm builtins in ESM Integration
guybedford Jul 10, 2025
1a44265
repl: handle errors from getters during completion
islandryu Jul 19, 2025
ef89c2f
src,test: fix config file parsing for flags defaulted to true
geeksilva97 Jul 20, 2025
8d869e6
fs: fix return value of fs APIs
theanarkh Jul 20, 2025
82d4175
test: update `startCLI` to set `--port=0` by default
dario-piotrowicz Jul 20, 2025
7fc3143
benchmark: reduce N for diagnostics_channel subscribe benchmark
artimmy Jul 20, 2025
88e434e
doc: add new environment variables doc page
dario-piotrowicz Jul 20, 2025
6f81b27
doc: fix typo in writing-test.md
SeokhunEom Jul 21, 2025
4ffa756
doc: avoid suggesting testing fast api with intense loop
legendecas Jul 21, 2025
83023e5
test: skip tests that cause timeouts on IBM i
abmusse Jul 21, 2025
d89390f
src: add cache to nearest parent package json
IlyasShabi Jul 21, 2025
f122602
deps: update corepack to 0.34.0
nodejs-github-bot Jul 22, 2025
fb52d0d
deps: upgrade openssl sources to openssl-3.0.17
nodejs-github-bot Jul 20, 2025
3f813ea
deps: update archs files for openssl-3.0.17
nodejs-github-bot Jul 20, 2025
15d8cc9
test: update WPT for WebCryptoAPI to 19d82c57ab
nodejs-github-bot Jul 20, 2025
c607004
test: update WPT for WebCryptoAPI to ab08796857
nodejs-github-bot Jul 21, 2025
51b8dfd
doc: add Aditi-1400 to collaborators
Aditi-1400 Jul 22, 2025
2a5f35b
deps: V8: cherry-pick 3d750c2aa9ef
targos Jun 18, 2025
f67b686
perf_hooks: do not expose SafeMap via Histogram wrapper
Renegade334 Jul 22, 2025
20be901
tools: bump @eslint/plugin-kit from 0.3.1 to 0.3.3 in /tools/eslint
dependabot[bot] Jul 22, 2025
72adf52
deps: update ada to 3.2.6
nodejs-github-bot Jul 22, 2025
88b99ee
src: remove declarations of removed BaseObject static fns
addaleax Jul 22, 2025
b919fe0
esm: unflag --experimental-wasm-modules
guybedford Feb 13, 2025
bfe4781
deps: update googletest to 7e17b15
nodejs-github-bot Jul 22, 2025
a34e445
deps: update sqlite to 3.50.3
nodejs-github-bot Jul 22, 2025
af789d9
deps: update undici to 7.12.0
nodejs-github-bot Jul 23, 2025
2b5d451
deps: update amaro to 1.1.1
nodejs-github-bot Jul 23, 2025
96c9dd7
meta: move one or more collaborators to emeritus
nodejs-github-bot Jul 23, 2025
7e0a0fc
test: expand linting rules around `assert` w literal messages
addaleax Jul 23, 2025
86babf9
src: use C++20 `consteval` for `FastStringKey`
addaleax Jul 21, 2025
77649ad
src: use `FastStringKey` for `TrackV8FastApiCall`
addaleax Jul 21, 2025
075d196
util: respect nested formats in styleText
himself65 Jul 23, 2025
dfb72d1
doc: add constraints for mem leak to threat model
RafaelGSS Jul 24, 2025
06cd746
doc: improve onboarding instructions
joyeecheung Jul 24, 2025
51f578a
tools: add support for URLs to PR commits in `merge.sh`
aduh95 Jul 24, 2025
d5640ca
cli: support `${pid}` placeholder in --cpu-prof-name
haramj Jul 24, 2025
3da5bc0
doc: fix broken sentence in `URL.parse`
SuperchupuDev Jul 24, 2025
ad6757e
benchmark: fix sqlite-is-transaction
RafaelGSS Jul 24, 2025
09717eb
benchmark: drop misc/punycode benchmark
RafaelGSS Jul 24, 2025
a28d804
benchmark: small lint fix on _cli.js
RafaelGSS Jul 24, 2025
28538f2
benchmark: add --track to benchmark
RafaelGSS Jul 25, 2025
a0d22e9
test: remove timeout in test-https-proxy-request-handshake-failure
joyeecheung Jul 25, 2025
195d603
tools: clarify README linter error message
joyeecheung Jul 25, 2025
55838e7
src: call unmask after install signal handler
theanarkh Jul 25, 2025
5457c7a
benchmark: adjust configuration for string-decoder bench
RafaelGSS Jul 25, 2025
3d2f919
process: make execve's args argument optional
mureinik Jul 26, 2025
9a8d202
doc: copyedit SECURITY.md
Trott Jul 26, 2025
1c3aadb
test: fix flaky test-worker-message-port-transfer-filehandle test
himself65 Jul 26, 2025
55a90ee
deps: upgrade npm to 11.5.1
npm-cli-bot Jul 26, 2025
fe0195f
module: fix conditions override in synchronous resolve hooks
joyeecheung Jul 26, 2025
f281272
buffer: cache Environment::GetCurrent to avoid repeated calls
mertcanaltin Jul 26, 2025
f5da494
cli: add --use-env-proxy
joyeecheung Jul 26, 2025
39978f5
test: prepare test-crypto-rsa-dsa for newer OpenSSL
richardlau May 1, 2025
dc66422
build,deps,tools: prepare to update to OpenSSL 3.5
richardlau Jul 25, 2025
ba8e95a
test: use mustSucceed in test-fs-read
mete0rfish Jul 27, 2025
1e99086
test: mark web lock held test as flaky
IlyasShabi Jul 27, 2025
927742b
doc: update the instruction on how to verify releases
aduh95 Jul 27, 2025
1f06ca9
deps: upgrade openssl sources to openssl-3.5.1
nodejs-github-bot Jul 27, 2025
998cef1
deps: update archs files for openssl-3.5.1
nodejs-github-bot Jul 27, 2025
931ec10
2025-07-31, Version 24.5.0 (Current)
aduh95 Jul 29, 2025
File filter

Filter by extension

Filter by extension .js  (2) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
Prev Previous commit
Next Next commit
repl: improve REPL disabling completion on proxies and getters
#57909 introduced the disabling
of REPL tab completion on object containing proxies and getters
(since such completion triggers code evaluation which can be
unexpected/disruptive for the user)

the solution in 57909 did not address all possible such cases,
the changes here improve on such solution by using acorn and
AST analysis to cover most if not all possible cases

PR-URL: #58891
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
dario-piotrowicz authored and targos committed Jul 17, 2025
commit 3a3eb6852d220acafd556cbcd62d4acac40c6d54
178 changes: 159 additions & 19 deletions lib/repl.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1523,8 +1523,24 @@ function complete(line, callback) {
return;
}

// If the target ends with a dot (e.g. `obj.foo.`) such code won't be valid for AST parsing
// so in order to make it correct we add an identifier to its end (e.g. `obj.foo.x`)
const parsableCompleteTarget = completeTarget.endsWith('.') ? `${completeTarget}x` : completeTarget;

let completeTargetAst;
try {
completeTargetAst = acornParse(
parsableCompleteTarget, { __proto__: null, sourceType: 'module', ecmaVersion: 'latest' },
);
} catch { /* No need to specifically handle parse errors */ }

if (!completeTargetAst) {
return completionGroupsLoaded();
}

return includesProxiesOrGetters(
StringPrototypeSplit(completeTarget, '.'),
completeTargetAst.body[0].expression,
parsableCompleteTarget,
this.eval,
this.context,
(includes) => {
Expand Down Expand Up @@ -1729,32 +1745,156 @@ function findExpressionCompleteTarget(code) {
return code;
}

function includesProxiesOrGetters(exprSegments, evalFn, context, callback, currentExpr = '', idx = 0) {
const currentSegment = exprSegments[idx];
currentExpr += `${currentExpr.length === 0 ? '' : '.'}${currentSegment}`;
evalFn(`try { ${currentExpr} } catch { }`, context, getREPLResourceName(), (_, currentObj) => {
if (typeof currentObj !== 'object' || currentObj === null) {
return callback(false);
}
/**
* Utility used to determine if an expression includes object getters or proxies.
*
* Example: given `obj.foo`, the function lets you know if `foo` has a getter function
* associated to it, or if `obj` is a proxy
* @param {any} expr The expression, in AST format to analyze
* @param {string} exprStr The string representation of the expression
* @param {(str: string, ctx: any, resourceName: string, cb: (error, evaled) => void) => void} evalFn
* Eval function to use
* @param {any} ctx The context to use for any code evaluation
* @param {(includes: boolean) => void} callback Callback that will be called with the result of the operation
* @returns {void}
*/
function includesProxiesOrGetters(expr, exprStr, evalFn, ctx, callback) {
if (expr?.type !== 'MemberExpression') {
// If the expression is not a member one for obvious reasons no getters are involved
return callback(false);
}

if (isProxy(currentObj)) {
return callback(true);
if (expr.object.type === 'MemberExpression') {
// The object itself is a member expression, so we need to recurse (e.g. the expression is `obj.foo.bar`)
return includesProxiesOrGetters(
expr.object,
exprStr.slice(0, expr.object.end),
evalFn,
ctx,
(includes, lastEvaledObj) => {
if (includes) {
// If the recurred call found a getter we can also terminate
return callback(includes);
}

if (isProxy(lastEvaledObj)) {
return callback(true);
}

// If a getter/proxy hasn't been found by the recursion call we need to check if maybe a getter/proxy
// is present here (e.g. in `obj.foo.bar` we found that `obj.foo` doesn't involve any getters so we now
// need to check if `bar` on `obj.foo` (i.e. `lastEvaledObj`) has a getter or if `obj.foo.bar` is a proxy)
return hasGetterOrIsProxy(lastEvaledObj, expr.property, (doesHaveGetterOrIsProxy) => {
return callback(doesHaveGetterOrIsProxy);
});
},
);
}

// This is the base of the recursion we have an identifier for the object and an identifier or literal
// for the property (e.g. we have `obj.foo` or `obj['foo']`, `obj` is the object identifier and `foo`
// is the property identifier/literal)
if (expr.object.type === 'Identifier') {
return evalFn(`try { ${expr.object.name} } catch {}`, ctx, getREPLResourceName(), (err, obj) => {
if (err) {
return callback(false);
}

if (isProxy(obj)) {
return callback(true);
}

return hasGetterOrIsProxy(obj, expr.property, (doesHaveGetterOrIsProxy) => {
if (doesHaveGetterOrIsProxy) {
return callback(true);
}

return evalFn(
`try { ${exprStr} } catch {} `, ctx, getREPLResourceName(), (err, obj) => {
if (err) {
return callback(false);
}
return callback(false, obj);
});
});
});
}

/**
* Utility to see if a property has a getter associated to it or if
* the property itself is a proxy object.
*/
function hasGetterOrIsProxy(obj, astProp, cb) {
if (!obj || !astProp) {
return cb(false);
}

const nextIdx = idx + 1;
if (astProp.type === 'Literal') {
// We have something like `obj['foo'].x` where `x` is the literal

if (nextIdx >= exprSegments.length) {
return callback(false);
if (isProxy(obj[astProp.value])) {
return cb(true);
}

const propDescriptor = ObjectGetOwnPropertyDescriptor(
obj,
`${astProp.value}`,
);
const propHasGetter = typeof propDescriptor?.get === 'function';
return cb(propHasGetter);
}

const nextSegmentProp = ObjectGetOwnPropertyDescriptor(currentObj, exprSegments[nextIdx]);
const nextSegmentPropHasGetter = typeof nextSegmentProp?.get === 'function';
if (nextSegmentPropHasGetter) {
return callback(true);
if (
astProp.type === 'Identifier' &&
exprStr.at(astProp.start - 1) === '.'
) {
// We have something like `obj.foo.x` where `foo` is the identifier

if (isProxy(obj[astProp.name])) {
return cb(true);
}

const propDescriptor = ObjectGetOwnPropertyDescriptor(
obj,
`${astProp.name}`,
);
const propHasGetter = typeof propDescriptor?.get === 'function';
return cb(propHasGetter);
}

return includesProxiesOrGetters(exprSegments, evalFn, context, callback, currentExpr, nextIdx);
});
return evalFn(
// Note: this eval runs the property expression, which might be side-effectful, for example
// the user could be running `obj[getKey()].` where `getKey()` has some side effects.
// Arguably this behavior should not be too surprising, but if it turns out that it is,
// then we can revisit this behavior and add logic to analyze the property expression
// and eval it only if we can confidently say that it can't have any side effects
`try { ${exprStr.slice(astProp.start, astProp.end)} } catch {} `,
ctx,
getREPLResourceName(),
(err, evaledProp) => {
if (err) {
return callback(false);
}

if (typeof evaledProp === 'string') {
if (isProxy(obj[evaledProp])) {
return cb(true);
}

const propDescriptor = ObjectGetOwnPropertyDescriptor(
obj,
evaledProp,
);
const propHasGetter = typeof propDescriptor?.get === 'function';
return cb(propHasGetter);
}

return callback(false);
},
);
}

return callback(false);
}

REPLServer.prototype.completeOnEditorMode = (callback) => (err, results) => {
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -61,48 +61,102 @@ describe('REPL completion in relation of getters', () => {
test(`completions are generated for properties that don't trigger getters`, () => {
runCompletionTests(
`
function getFooKey() {
return "foo";
}

const fooKey = "foo";

const keys = {
"foo key": "foo",
};

const objWithGetters = {
foo: { bar: { baz: {} }, get gBar() { return { baz: {} } } },
foo: { bar: { baz: { buz: {} } }, get gBar() { return { baz: {} } } },
get gFoo() { return { bar: { baz: {} } }; }
};
`, [
['objWithGetters.', ['objWithGetters.foo']],
['objWithGetters.f', ['objWithGetters.foo']],
['objWithGetters.foo', ['objWithGetters.foo']],
['objWithGetters["foo"].b', ['objWithGetters["foo"].bar']],
['objWithGetters.foo.', ['objWithGetters.foo.bar']],
['objWithGetters.foo.bar.b', ['objWithGetters.foo.bar.baz']],
['objWithGetters.gFo', ['objWithGetters.gFoo']],
['objWithGetters.foo.gB', ['objWithGetters.foo.gBar']],
["objWithGetters.foo['bar'].b", ["objWithGetters.foo['bar'].baz"]],
["objWithGetters['foo']['bar'].b", ["objWithGetters['foo']['bar'].baz"]],
["objWithGetters['foo']['bar']['baz'].b", ["objWithGetters['foo']['bar']['baz'].buz"]],
["objWithGetters[keys['foo key']].b", ["objWithGetters[keys['foo key']].bar"]],
['objWithGetters[fooKey].b', ['objWithGetters[fooKey].bar']],
["objWithGetters['f' + 'oo'].b", ["objWithGetters['f' + 'oo'].bar"]],
['objWithGetters[getFooKey()].b', ['objWithGetters[getFooKey()].bar']],
]);
});

test('no completions are generated for properties that trigger getters', () => {
runCompletionTests(
`
function getGFooKey() {
return "g" + "Foo";
}

const gFooKey = "gFoo";

const keys = {
"g-foo key": "gFoo",
};

const objWithGetters = {
foo: { bar: { baz: {} }, get gBar() { return { baz: {} } } },
foo: { bar: { baz: {} }, get gBar() { return { baz: {}, get gBuz() { return 5; } } } },
get gFoo() { return { bar: { baz: {} } }; }
};
`,
[
['objWithGetters.gFoo.', []],
['objWithGetters.gFoo.b', []],
['objWithGetters["gFoo"].b', []],
['objWithGetters.gFoo.bar.b', []],
['objWithGetters.foo.gBar.', []],
['objWithGetters.foo.gBar.b', []],
["objWithGetters.foo['gBar'].b", []],
["objWithGetters['foo']['gBar'].b", []],
["objWithGetters['foo']['gBar']['gBuz'].", []],
["objWithGetters[keys['g-foo key']].b", []],
['objWithGetters[gFooKey].b', []],
["objWithGetters['g' + 'Foo'].b", []],
['objWithGetters[getGFooKey()].b', []],
]);
});
});

describe('completions on proxies', () => {
test('no completions are generated for a proxy object', () => {
runCompletionTests('const proxyObj = new Proxy({ foo: { bar: { baz: {} } } }, {});', [
['proxyObj.', []],
['proxyObj.f', []],
['proxyObj.foo', []],
['proxyObj.foo.', []],
['proxyObj.foo.bar.b', []],
]);
runCompletionTests(
`
function getFooKey() {
return "foo";
}

const fooKey = "foo";

const keys = {
"foo key": "foo",
};

const proxyObj = new Proxy({ foo: { bar: { baz: {} } } }, {});
`, [
['proxyObj.', []],
['proxyObj.f', []],
['proxyObj.foo', []],
['proxyObj.foo.', []],
['proxyObj.["foo"].', []],
['proxyObj.["f" + "oo"].', []],
['proxyObj.[fooKey].', []],
['proxyObj.[getFooKey()].', []],
['proxyObj.[keys["foo key"]].', []],
['proxyObj.foo.bar.b', []],
]);
});

test('no completions are generated for a proxy present in a standard object', () => {
Expand All @@ -113,6 +167,7 @@ describe('REPL completion in relation of getters', () => {
['objWithProxy.foo.', ['objWithProxy.foo.bar']],
['objWithProxy.foo.b', ['objWithProxy.foo.bar']],
['objWithProxy.foo.bar.', []],
['objWithProxy.foo["b" + "ar"].', []],
]);
});
});
Expand Down

Back | FazBrowse Home | New Git URL