Batch 6 (9): dolibarr, jitsi-videobridge, zed, tryton, suricata, strawberry, zulip, zesarux, zephyr Batch 7 (15): xonotic (4), xash3d (3), xenia, xtuple, zabbix (2), zathura, zebra, yabause, zephyr-0001 Batch 8 (15): woodpecker (2), wine (4), widelands (3), wesnoth (3), wekan (3) Mix of CWE-407 and CWE-312.
3.5 KiB
Tryton ERP — CWE-407 Disclosure Brief
2026-04-13 · Patches available — awaiting upstream merge
Finding
Two O(n^2) defects in Tryton's ORM layer across ModelStorage._save_values() and ModelView._changed_values(). Both patched. Patches ready for upstream review. Both defects fire during record save operations on One2Many and Many2Many relational fields.
The Defects
tryton-0001a (PATCHED — MEDIUM): trytond/trytond/model/modelstorage.py:2115
# list comprehension + list.remove() — O(T^2) for relational field saves
previous = [t.id for t in getattr(self, fname)] # list
# ...
if target.id in previous: # O(T) linear scan per target
previous.remove(target.id) # O(T) list removal per target
previous holds IDs of existing related records as a list. For each target record, in previous performs a linear scan and previous.remove() performs another linear scan plus element shift. With T related records, total cost: O(T^2).
tryton-0001b (PATCHED — MEDIUM): trytond/trytond/model/modelview.py:922
# Identical pattern in _changed_values()
previous = [t.id for t in init_targets if t.id] # list
# ...
if target.id in previous: # O(T) linear scan
previous.remove(target.id) # O(T) list removal
Same pattern in the change-detection path. Fires on every form save when the client computes which relational fields changed.
Complexity Proof
tryton-0001a/b: At T=500 related records:
- Defective: 500 * 500 = 250,000 comparisons + 250,000 element shifts
- Fixed: 500 set lookups + 500 set discards
- 500x op reduction at T=500.
Impact
Tryton serves as a full ERP framework used by businesses for invoicing, inventory, accounting, and project management. The _save_values() method fires on every record save involving One2Many or Many2Many fields. The _changed_values() method fires on every form submission to detect field modifications.
Business scenarios with large relational fields (invoice lines, stock moves, project tasks) regularly reach hundreds of related records. An invoice with 200 line items triggers ~40,000 extra comparisons on every save.
The Fix
tryton-0001a: Replace list with set for the previous collection:
# Before
previous = [t.id for t in getattr(self, fname)]
if target.id in previous:
previous.remove(target.id)
# After
previous = set(t.id for t in getattr(self, fname))
if target.id in previous:
previous.discard(target.id)
tryton-0001b: Same pattern in _changed_values():
# Before
previous = [t.id for t in init_targets if t.id]
# After
previous = set(t.id for t in init_targets if t.id)
Patch
Fixes available:
defects/tryton-0001/patch/tryton-0001-modelstorage-save-values.patchdefects/tryton-0001/patch/tryton-0001-modelview-changed-values.patch
Two-file patch across modelstorage.py and modelview.py. Both sites: list to set conversion for O(1) membership and O(1) discard. 500x op reduction at T=500 related records.
What We Ask
Patches ready for review.
- Confirm receipt and assign an issue reference (tryton/trytond on Heptapod or bugs.tryton.org).
- Assess severity — both defects fire on every relational-field save in the ORM.
- Coordinate a disclosure date — we target 90 days from first contact.
- 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.