java-topology/whitepaper/outreach/tryton-0001.md
russell@unturf.com 652608142a feat: close outreach doc gap — 276 docs (batches 11-16)
All projects with patches now have outreach docs. 276 new docs covering
CWE-407, CWE-312, CWE-362 across C, C++, Java, Python, Go, Rust, C#,
PHP, Ruby, JavaScript, Dart, Erlang, R, and more.

Outreach gap: 276 -> 0.
2026-04-15 13:57:42 -04:00

2.8 KiB
Raw Permalink Blame History

Tryton — CWE-407 Disclosure Brief

2026-04-13 · Patch available — awaiting upstream merge

Finding

Two O(n²) defects in Tryton's ORM layer. List-based membership checks and list.remove() calls produce quadratic behavior during record save and change-tracking operations. Patched.

The Defects

tryton-0001a (PATCHED — MEDIUM): trytond/trytond/model/modelstorage.py

# In _save_values() — fires per One2Many/Many2Many field save:
previous = [t.id for t in getattr(self, fname)]  # list
...
if target.id in previous:        # O(N) membership test
    previous.remove(target.id)   # O(N) removal

previous is a list. in is O(N) and remove() is O(N), both inside a per-target loop, producing O(T × N) where T = targets, N = previous records.

tryton-0001b (PATCHED — MEDIUM): trytond/trytond/model/modelview.py

# In _changed_values() — fires per record change detection:
previous = [t.id for t in init_targets if t.id]  # list
...
if target.id in previous:        # O(N) membership test
    previous.remove(target.id)   # O(N) removal

Same pattern in the view-layer change tracking.

Complexity Proof

tryton-0001: At N=500 related records:

  • Defective: ~125,000 comparisons + ~125,000 element shifts per field
  • Fixed: ~500 set lookups + ~500 O(1) discard calls
  • ~250× op reduction. Fires during every relational field save.

Impact

Tryton is an open-source ERP (enterprise resource planning) framework. Relational field operations (One2Many, Many2Many) on records with hundreds of related entries (invoice lines, order items, stock moves) trigger quadratic behavior during save and change detection. Enterprise deployments with large transaction records experience compounding overhead.

The Fix

tryton-0001: Replace list with set; use discard() instead of remove():

# Before — O(N²)
previous = [t.id for t in getattr(self, fname)]
previous.remove(target.id)

# After — O(N)
previous = set(t.id for t in getattr(self, fname))
previous.discard(target.id)

Patch

Fix available: defects/tryton-0001/patch/tryton-0001-modelstorage-save-values.patch and defects/tryton-0001/patch/tryton-0001-modelview-changed-values.patch

Two-file patch across trytond/model/modelstorage.py and trytond/model/modelview.py.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign an issue reference (bugs.tryton.org or Heptapod).
  2. Assess severity — fires during every relational field save and change detection.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. We will credit the Tryton team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.