CycloneDX / CycloneDX/specification
[Defect]: traverseSchema lacks cycle detection and produces ambiguous dot paths
- Dominant language
- XSLT
- Stars
- 547
- Forks
- 93
- Avg merge
- 7h 11m
- Merged PRs (30d)
- 37
Description
## Describe the defect
`traverseSchema` currently has two reliability/diagnostic issues:
- **Ambiguous path output:** It builds paths using dot-notation (`${path}.${k}`), which becomes ambiguous when a key contains `.` or other non-identifier characters. Example: a literal key `"a.b"` is reported as `$.a.b` (looks like nested keys).
- **No cycle detection:** If a schema object becomes circular in-memory (e.g., programmatic construction, loader back-references, alias graphs), traversal can recurse indefinitely and crash with `Maximum call stack size exceeded`.
**Scope:** `tools/src/main/js/linter/index.js` only.
## Proposed fix
- Add **stack-based cycle detection** using a `WeakSet` of nodes on the current recursion path (add on entry, remove on unwind) so only true cycles are skipped while shared references (DAGs) remain traversable.
- Introduce **safe path joining**:
- dot notation for simple identifiers
- bracket notation for non-identifier keys (e.g. `$["a.b"]`)
Optional hardening (kept backward-compatible):
- Add an opt-in `maxDepth` guard (default `Infinity`) with an `onDepthLimit` hook so truncation is never silent when enabled.
## Alternatives considered
- Implement cycle detection only (leave paths unchanged) — avoids crashes but keeps ambiguous diagnostics.
- Implement safe paths only (leave cycles unchanged) — improves diagnostics but doesn’t prevent crashes.
- Add depth-only protection without cycle detection — may help with deep recursion but does not address true cycles.
## Additional context
Observed while working on the 2.0 schema tooling
- This affects issue paths emitted by all checks that rely on `traverseSchema`.
- Backwards compatibility: path formatting changes only for keys that are not simple identifiers; default traversal behavior remains unchanged unless a finite `maxDepth` is explicitly set.
Contributor guide
Assessment
This issue has not been assessed yet.