# Gradle — CWE-407 Disclosure Brief

**Project:** Gradle
**Disclosure date:** 2026-03-27
**Severity:** HIGH
**Speedup:** varies with M×O²
**Status:** PATCHED

---

## Finding

Gradle's CLI option reader rebuilds a list via `CollectionUtils.toList().contains()` for every method-option pair evaluation. The `OptionReader` in `subprojects/cli/` converts option collections to lists on each membership check rather than pre-building the list once, producing O(M×O²) total work where M is the number of methods with options and O is the number of options per method.

## The Defect(s)

| ID | Location | Pattern | Complexity |
|----|----------|---------|------------|
| gradle-0001 | `subprojects/cli/` | `OptionReader` `CollectionUtils.toList().contains()` rebuilt per method-option pair | O(M×O²) |

## Complexity Proof

Let M = number of methods with declared options, O = number of options per method.

For each of the M methods, the `OptionReader` validates options by iterating over all O options and calling `CollectionUtils.toList().contains(option)` for each. `toList()` constructs a new `List` from the collection on every invocation, making each membership check O(O) — both due to the list rebuild and the subsequent linear scan:

```
For method_1:
  For option_1: toList() O(O) + contains() O(O) = O(O)
  For option_2: toList() O(O) + contains() O(O) = O(O)
  ...
  For option_O: O(O)
  Subtotal: O × O = O(O²)

For all M methods: M × O² total work
```

Pre-building the option list or set once per method reduces inner membership checks to O(1), making total work O(M×O). For M = 50 task methods with O = 20 options each, the defective path performs 50 × 400 = 20,000 operations; the fixed path performs 50 × 20 = 1,000.

## Impact

Gradle users invoking tasks with many declared options — custom plugins with extensive option sets, build scripts using `@Option`-annotated task properties extensively — experience superlinear CLI parsing time. Large enterprise Gradle builds with many custom tasks and option declarations are most affected. The defect occurs during task option resolution, which runs on every Gradle invocation before the build graph is even constructed.

## The Fix

Pre-build the option list or `HashSet` once per method before iterating options. Replace `CollectionUtils.toList().contains(option)` with a single pre-built `Set.contains(option)` that is constructed once and reused for all O membership checks.

## Patch

```diff
- for (Method method : methods) {
-     for (String option : options) {
-         if (CollectionUtils.toList(method.getOptions()).contains(option)) {
-             // handle option
-         }
-     }
- }
+ for (Method method : methods) {
+     Set<String> methodOptions = new HashSet<>(CollectionUtils.toList(method.getOptions()));
+     for (String option : options) {
+         if (methodOptions.contains(option)) {
+             // handle option
+         }
+     }
+ }
```

## What We Ask

Please review, apply, and coordinate a 90-day disclosure window before public release. Reply to security@undefect.com.

---

*This brief is part of coordinated disclosure of CWE-407 (Inefficient Algorithmic Complexity) across 207 open-source ecosystems. Full report: https://undefect.com*
