@@ -96,6 +96,10 @@ const {
96
96
globalThis,
97
97
} = primordials ;
98
98
99
+ const {
100
+ isProxy,
101
+ } = require ( 'internal/util/types' ) ;
102
+
99
103
const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
100
104
const {
101
105
makeRequireFunction,
@@ -1328,8 +1332,10 @@ function completeFSFunctions(match) {
1328
1332
// -> [['util.print', 'util.debug', 'util.log', 'util.inspect'],
1329
1333
// 'util.' ]
1330
1334
//
1331
- // Warning: This eval's code like "foo.bar.baz", so it will run property
1332
- // getter code.
1335
+ // Warning: This evals code like "foo.bar.baz", so it could run property
1336
+ // getter code. To avoid potential triggering side-effects with getters the completion
1337
+ // logic is skipped when getters or proxies are involved in the expression.
1338
+ // (see: https://github.com/nodejs/node/issues/57829).
1333
1339
function complete ( line , callback ) {
1334
1340
// List of completion lists, one for each inheritance "level"
1335
1341
let completionGroups = [ ] ;
@@ -1525,50 +1531,61 @@ function complete(line, callback) {
1525
1531
return ;
1526
1532
}
1527
1533
1528
- let chaining = '.' ;
1529
- if ( StringPrototypeEndsWith ( expr , '?' ) ) {
1530
- expr = StringPrototypeSlice ( expr , 0 , - 1 ) ;
1531
- chaining = '?.' ;
1532
- }
1533
-
1534
- const memberGroups = [ ] ;
1535
- const evalExpr = `try { ${ expr } } catch {}` ;
1536
- this . eval ( evalExpr , this . context , getREPLResourceName ( ) , ( e , obj ) => {
1537
- try {
1538
- let p ;
1539
- if ( ( typeof obj === 'object' && obj !== null ) ||
1540
- typeof obj === 'function' ) {
1541
- ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( obj ) ) ;
1542
- p = ObjectGetPrototypeOf ( obj ) ;
1543
- } else {
1544
- p = obj . constructor ? obj . constructor . prototype : null ;
1534
+ return includesProxiesOrGetters (
1535
+ StringPrototypeSplit ( match , '.' ) ,
1536
+ this . eval ,
1537
+ this . context ,
1538
+ ( includes ) => {
1539
+ if ( includes ) {
1540
+ // The expression involves proxies or getters, meaning that it
1541
+ // can trigger side-effectful behaviors, so bail out
1542
+ return completionGroupsLoaded ( ) ;
1545
1543
}
1546
- // Circular refs possible? Let's guard against that.
1547
- let sentinel = 5 ;
1548
- while ( p !== null && sentinel -- !== 0 ) {
1549
- ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( p ) ) ;
1550
- p = ObjectGetPrototypeOf ( p ) ;
1544
+
1545
+ let chaining = '.' ;
1546
+ if ( StringPrototypeEndsWith ( expr , '?' ) ) {
1547
+ expr = StringPrototypeSlice ( expr , 0 , - 1 ) ;
1548
+ chaining = '?.' ;
1551
1549
}
1552
- } catch {
1553
- // Maybe a Proxy object without `getOwnPropertyNames` trap.
1554
- // We simply ignore it here, as we don't want to break the
1555
- // autocompletion. Fixes the bug
1556
- // https://github.com/nodejs/node/issues/2119
1557
- }
1558
1550
1559
- if ( memberGroups . length ) {
1560
- expr += chaining ;
1561
- ArrayPrototypeForEach ( memberGroups , ( group ) => {
1562
- ArrayPrototypePush ( completionGroups ,
1563
- ArrayPrototypeMap ( group ,
1564
- ( member ) => `${ expr } ${ member } ` ) ) ;
1565
- } ) ;
1566
- filter &&= `${ expr } ${ filter } ` ;
1567
- }
1551
+ const memberGroups = [ ] ;
1552
+ const evalExpr = `try { ${ expr } } catch {}` ;
1553
+ this . eval ( evalExpr , this . context , getREPLResourceName ( ) , ( e , obj ) => {
1554
+ try {
1555
+ let p ;
1556
+ if ( ( typeof obj === 'object' && obj !== null ) ||
1557
+ typeof obj === 'function' ) {
1558
+ ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( obj ) ) ;
1559
+ p = ObjectGetPrototypeOf ( obj ) ;
1560
+ } else {
1561
+ p = obj . constructor ? obj . constructor . prototype : null ;
1562
+ }
1563
+ // Circular refs possible? Let's guard against that.
1564
+ let sentinel = 5 ;
1565
+ while ( p !== null && sentinel -- !== 0 ) {
1566
+ ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( p ) ) ;
1567
+ p = ObjectGetPrototypeOf ( p ) ;
1568
+ }
1569
+ } catch {
1570
+ // Maybe a Proxy object without `getOwnPropertyNames` trap.
1571
+ // We simply ignore it here, as we don't want to break the
1572
+ // autocompletion. Fixes the bug
1573
+ // https://github.com/nodejs/node/issues/2119
1574
+ }
1568
1575
1569
- completionGroupsLoaded ( ) ;
1570
- } ) ;
1571
- return ;
1576
+ if ( memberGroups . length ) {
1577
+ expr += chaining ;
1578
+ ArrayPrototypeForEach ( memberGroups , ( group ) => {
1579
+ ArrayPrototypePush ( completionGroups ,
1580
+ ArrayPrototypeMap ( group ,
1581
+ ( member ) => `${ expr } ${ member } ` ) ) ;
1582
+ } ) ;
1583
+ filter &&= `${ expr } ${ filter } ` ;
1584
+ }
1585
+
1586
+ completionGroupsLoaded ( ) ;
1587
+ } ) ;
1588
+ } ) ;
1572
1589
}
1573
1590
1574
1591
return completionGroupsLoaded ( ) ;
@@ -1626,6 +1643,34 @@ function complete(line, callback) {
1626
1643
}
1627
1644
}
1628
1645
1646
+ function includesProxiesOrGetters ( exprSegments , evalFn , context , callback , currentExpr = '' , idx = 0 ) {
1647
+ const currentSegment = exprSegments [ idx ] ;
1648
+ currentExpr += `${ currentExpr . length === 0 ? '' : '.' } ${ currentSegment } ` ;
1649
+ evalFn ( `try { ${ currentExpr } } catch { }` , context , getREPLResourceName ( ) , ( _ , currentObj ) => {
1650
+ if ( typeof currentObj !== 'object' || currentObj === null ) {
1651
+ return callback ( false ) ;
1652
+ }
1653
+
1654
+ if ( isProxy ( currentObj ) ) {
1655
+ return callback ( true ) ;
1656
+ }
1657
+
1658
+ const nextIdx = idx + 1 ;
1659
+
1660
+ if ( nextIdx >= exprSegments . length ) {
1661
+ return callback ( false ) ;
1662
+ }
1663
+
1664
+ const nextSegmentProp = ObjectGetOwnPropertyDescriptor ( currentObj , exprSegments [ nextIdx ] ) ;
1665
+ const nextSegmentPropHasGetter = typeof nextSegmentProp ?. get === 'function' ;
1666
+ if ( nextSegmentPropHasGetter ) {
1667
+ return callback ( true ) ;
1668
+ }
1669
+
1670
+ return includesProxiesOrGetters ( exprSegments , evalFn , context , callback , currentExpr , nextIdx ) ;
1671
+ } ) ;
1672
+ }
1673
+
1629
1674
REPLServer . prototype . completeOnEditorMode = ( callback ) => ( err , results ) => {
1630
1675
if ( err ) return callback ( err ) ;
1631
1676
0 commit comments