5.1 KiB
SeaORM — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
Four O(n²) defects in SeaORM's many-to-many link establishment, RBAC permission engine, schema builder, and topological sort. All patched. Patches ready for upstream review. SeaORM is the dominant async Rust ORM for Axum, Actix, and Tokio-based applications.
The Defects
seaorm-0001 (PATCHED — HIGH): src/entity/active_model.rs:1267
// In establish_links() — many-to-many write path:
for model in related_models {
if leftover.iter().any(|t| t.1 == via_key) { // O(N) per related model
// ...
}
}
leftover.iter().any(|t| t.1 == via_key) performs a linear scan over N leftover link entries for every related model. Total: O(N²) where N = number of related models in a many-to-many relationship.
seaorm-0002 (PATCHED — HIGH): src/rbac/engine/mod.rs:234
// On every permission check:
let perm = permissions.values().find(|p| p.id == item.1); // O(P)
let res = resources.values().find(|r| r.id == item.0); // O(R)
Two O(P) and O(R) linear scans over permissions and resources on every permission check call. Should use HashMap lookup by numeric ID.
seaorm-0003 (PATCHED — MEDIUM): src/schema/builder.rs:238
// After topological sort — O(N) contains() per leftover entity:
for entity in leftover_entities {
if sorted.contains(&table_name) { // O(N) Vec contains()
// ...
}
}
sorted is a Vec. contains() is O(N) per leftover entity. O(N²) worst case for cyclic schemas.
seaorm-0004 (PATCHED — MEDIUM): src/schema/topology.rs:213
// In TopologicalSort::from_iter:
if seen.contains(&item) { // O(N) Vec contains()
continue;
}
seen.push(item);
seen is a Vec<T>. O(N) scan per item → O(N²) total.
Complexity Proof
seaorm-0001: For N related models:
- Per model: O(N)
any()scan over leftover Vec - Total: O(N²)
At N=1,000: defective=500,000 comparisons, fixed=1,000 (HashSet of ValueTuples). 501× op reduction.
seaorm-0002: For P permissions and R resources per check:
- Per check: O(P) + O(R) linear scan
- Total per call: O(P + R)
At P=R=1,000: defective=2,000 comparisons per check, fixed=2 (HashMap lookup). 502× op reduction.
seaorm-0003: For N entities after topo-sort:
- Per leftover: O(N)
contains() - Total: O(N²)
At N=500: 500× op reduction.
seaorm-0004: For N items in topological sort input:
- Per item: O(N)
seen.contains() - Total: O(N²)
At N=1,000: 28× op reduction (lower ratio because sort has other costs).
Impact
SeaORM is the dominant async Rust ORM, used in Axum, Actix-web, and Tokio-based backend services. The Rust ecosystem's async web framework ecosystem is growing rapidly; SeaORM is the standard database access layer for this stack.
seaorm-0001 fires on every many-to-many relationship write via establish_links() — the standard API for updating M:N junction tables. Applications that maintain large many-to-many sets (user-role assignments, product-category mappings, tag systems) pay O(N²) on every update.
seaorm-0002 fires on every permission check in applications using the SeaORM RBAC engine — a hot path in any authorization-gated API.
The Fix
seaorm-0001: Pre-build HashSet<ValueTuple>:
// Before
if leftover.iter().any(|t| t.1 == via_key) { ... }
// After
// CWE-407 fix: HashSet for O(1) contains() instead of O(N) iter().any() scan.
let leftover_set: HashSet<_> = leftover.iter().map(|t| t.1.clone()).collect();
if leftover_set.contains(&via_key) { ... }
seaorm-0002: HashMap by numeric ID:
// Before
permissions.values().find(|p| p.id == item.1)
// After
// CWE-407 fix: HashMap<id, Permission> for O(1) get() instead of O(P) find().
let perm_map: HashMap<_, _> = permissions.iter().map(|(_, p)| (p.id, p)).collect();
perm_map.get(&item.1)
seaorm-0003/seaorm-0004: Shadow HashSet / replace Vec with BTreeSet:
// Before
if sorted.contains(&table_name) { ... }
// After
// CWE-407 fix: HashSet shadow for O(1) contains() instead of O(N) Vec scan.
let sorted_set: HashSet<_> = sorted.iter().collect();
if sorted_set.contains(&table_name) { ... }
Patch
Fix available: defects/seaorm/patch/seaorm-0001-0004-hashset-hashmap.patch
Four-location patch across active_model.rs, rbac/engine/mod.rs, schema/builder.rs, and schema/topology.rs.
Unit test: SeaORMTest 4/4 pass. seaorm-0001: 501× speedup. seaorm-0002: 502× speedup. seaorm-0003: 500× speedup. seaorm-0004: 28× speedup.
What We Ask
A patch is ready for review.
- Confirm receipt and assign a GitHub issue reference (SeaQL/sea-orm).
- Assess severity — seaorm-0001 fires on every many-to-many write; seaorm-0002 fires on every permission check.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the SeaORM team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.