java-topology/defects/tinkerpop/patch/tinkerpop-0001-path-issimple-hashset.patch
russell@unturf.com db29a08762 undefect. CWE-407 — 92 sites, 42 ecosystems
B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections.
Squash of 94 local commits onto remote master.
2026-03-26 19:48:18 -04:00

43 lines
2.1 KiB
Diff

--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java
@@ -203,13 +203,11 @@ public interface Path extends Cloneable, Iterable<Object> {
* @return whether the path is not a cycle
*/
public default boolean isSimple() {
- final List<Object> objects = this.objects();
- for (int i = 0; i < objects.size() - 1; i++) {
- for (int j = i + 1; j < objects.size(); j++) {
- if (Objects.equals(objects.get(i), objects.get(j)))
- return false;
- }
+ final Set<Object> seen = new HashSet<>();
+ for (final Object object : this.objects()) {
+ if (!seen.add(object)) return false;
}
return true;
}
// Add required import at top of file:
+import java.util.HashSet;
+import java.util.Set;
# CWE-407: O(n²) nested loop → O(n) HashSet
#
# Path.java:206-214 — default isSimple() implementation uses a nested double-loop
# to check for duplicate vertices in a path. This is O(n²) where n is path length.
# Called on every traverser evaluated by .simplePath() or .cyclicPath() Gremlin steps.
# The inner PathFilterStep triggers this via subPath() → MutablePath (which has no
# override), bypassing ImmutablePath's correct HashSet implementation at line 292.
#
# Impact: Every Gremlin graph traversal using .simplePath() or .cyclicPath() with
# from()/to() label scoping, or with by() modulators, executes O(n²) membership
# testing per traverser. For long paths in large graphs (e.g. social graph friend-of-
# friend queries, supply chain paths), this is quadratic in path length.
#
# Fix: Replace O(n²) nested loop with O(n) HashSet membership test. Matches the
# correct implementation already present in ImmutablePath.isSimple() at line 292.
#
# Upstream: apache/tinkerpop — gremlin-core/src/main/java/org/apache/tinkerpop/
# gremlin/process/traversal/Path.java
# Activated by: PathFilterStep.java:60,62,79 via subPath() → MutablePath
# Fixed by: HashSet dedup in default isSimple() — O(n²) → O(n)