UNDF-2026-000000468: three.js src/nodes/core/Node.js:351 traverse() recurses without a visited set; on shared-node (diamond) TSL shader graphs yields 2^D callback invocations. D=10 gives 4093× overhead. Fix: add optional visited Set parameter, default new Set() at root call. Unit test: 10/10 PASS (ThreeJSNodeTraverseTest.java) CLEAN markers written for: webpack (visitedModules WeakSet throughout), valhalla (Dijkstra+BFS, no recursive DAG traversal), traefik (traverse() has proper visited map), wasmer (petgraph+BTreeMap), wasmtime (SCC algorithms).
30 lines
904 B
Diff
30 lines
904 B
Diff
# UNDF: UNDF-2026-000000468
|
|
--- a/src/nodes/core/Node.js
|
|
+++ b/src/nodes/core/Node.js
|
|
@@ -346,18 +346,22 @@ class Node extends EventDispatcher {
|
|
* Can be used to traverse through the node's hierarchy.
|
|
*
|
|
* @param {traverseCallback} callback - A callback that is executed per node.
|
|
+ * @param {Set<Node>} [visited] - Internal visited set; prevents exponential
|
|
+ * re-visits on diamond-shaped DAGs (nodes shared by multiple parents).
|
|
*/
|
|
- traverse( callback ) {
|
|
+ traverse( callback, visited = new Set() ) {
|
|
|
|
- callback( this );
|
|
+ // CWE-407 fix: skip nodes already visited; was O(2^D) on diamond DAGs.
|
|
+ if ( visited.has( this ) ) return;
|
|
+ visited.add( this );
|
|
|
|
- for ( const childNode of this.getChildren() ) {
|
|
+ callback( this );
|
|
|
|
- childNode.traverse( callback );
|
|
+ for ( const childNode of this.getChildren() ) {
|
|
|
|
- }
|
|
+ childNode.traverse( callback, visited );
|
|
+
|
|
+ }
|
|
|
|
}
|