56 lines
2.1 KiB
Diff
56 lines
2.1 KiB
Diff
# UNDF: UNDF-2026-000000189
|
|
diff --git a/lib/can-place-dep.js b/lib/can-place-dep.js
|
|
index 1a3ccff..0708321 100644
|
|
--- a/lib/can-place-dep.js
|
|
+++ b/lib/can-place-dep.js
|
|
@@ -61,6 +61,7 @@ class CanPlaceDep {
|
|
preferDedupe,
|
|
parent = null,
|
|
peerPath = [],
|
|
+ peerPathSet = null,
|
|
explicitRequest = false,
|
|
} = options
|
|
|
|
@@ -95,6 +96,10 @@ class CanPlaceDep {
|
|
|
|
// preventing cycles when we check peer sets
|
|
this.peerPath = peerPath
|
|
+ // CWE-407 fix: shared Set for O(1) peerPath membership tests.
|
|
+ // Initialized once at the root; child CPDs receive the parent's reference.
|
|
+ // canPlacePeers() adds/removes this.dep using a backtracking DFS pattern.
|
|
+ this.peerPathSet = peerPathSet || new Set(peerPath)
|
|
// we always prefer to dedupe peers, because they are trying
|
|
// a bit harder to be singletons.
|
|
this.preferDedupe = !!preferDedupe || edge.peer
|
|
@@ -365,9 +370,12 @@ class CanPlaceDep {
|
|
|
|
// TODO: represent peerPath in ERESOLVE error somehow?
|
|
const peerPath = [...this.peerPath, this.dep]
|
|
+ // CWE-407 fix: use shared peerPathSet for O(1) cycle detection.
|
|
+ // Add this.dep now; children share the same Set reference; backtrack after.
|
|
+ this.peerPathSet.add(this.dep)
|
|
let sawConflict = false
|
|
for (const peerEdge of this.dep.edgesOut.values()) {
|
|
- if (!peerEdge.peer || !peerEdge.to || peerPath.includes(peerEdge.to)) {
|
|
+ if (!peerEdge.peer || !peerEdge.to || this.peerPathSet.has(peerEdge.to)) {
|
|
continue
|
|
}
|
|
const peer = peerEdge.to
|
|
@@ -381,6 +389,7 @@ class CanPlaceDep {
|
|
parent: this,
|
|
edge: peerEdge,
|
|
peerPath,
|
|
+ peerPathSet: this.peerPathSet,
|
|
// always place peers in preferDedupe mode
|
|
preferDedupe: true,
|
|
})
|
|
@@ -396,6 +405,9 @@ class CanPlaceDep {
|
|
sawConflict = true
|
|
}
|
|
}
|
|
+ // Backtrack: remove this.dep from the shared peerPathSet so the parent's
|
|
+ // subsequent peers are checked against the correct path state.
|
|
+ this.peerPathSet.delete(this.dep)
|
|
|
|
this._canPlacePeers = sawConflict ? CONFLICT : state
|
|
return this._canPlacePeers
|