73 lines
2.6 KiB
Diff
73 lines
2.6 KiB
Diff
# UNDF: UNDF-2026-000000845
|
||
# CWE-407: Algorithmic Complexity — Firefox Sanitizer ListSet O(N×E) linear membership
|
||
#
|
||
# File: dom/security/sanitizer/SanitizerTypes.h
|
||
# Class: ListSet<T>
|
||
# Method: Contains(), Insert(), Get()
|
||
#
|
||
# The ListSet class is used by the Sanitizer API to track allowed/removed
|
||
# elements and attributes. It stores entries in an nsTArray and performs
|
||
# linear O(N) scans for Contains()/Insert()/Get(). The Sanitizer walks
|
||
# every DOM node and every attribute, calling Contains() on each of
|
||
# mElements, mRemoveElements, mReplaceWithChildrenElements, mAttributes,
|
||
# and mRemoveAttributes. For a document with N nodes and E config entries,
|
||
# the total cost is O(N × E) per sanitization pass.
|
||
#
|
||
# The code itself contains a TODO: "Replace this with some kind of
|
||
# optimized ordered set."
|
||
#
|
||
# Severity: HIGH — the Sanitizer API processes untrusted HTML. An attacker
|
||
# can craft HTML with many elements to trigger O(N×E) scanning.
|
||
#
|
||
# Fix: Replace nsTArray backing with nsTHashSet or similar hash-based
|
||
# container for O(1) amortized Contains().
|
||
#
|
||
--- a/dom/security/sanitizer/SanitizerTypes.h
|
||
+++ b/dom/security/sanitizer/SanitizerTypes.h
|
||
@@ -38,7 +38,7 @@
|
||
-// TODO: Replace this with some kind of optimized ordered set.
|
||
+// Fixed: replaced linear-scan nsTArray with hash-based lookup.
|
||
template <typename ValueType>
|
||
class ListSet {
|
||
public:
|
||
ListSet() = default;
|
||
|
||
void Insert(ValueType&& aValue) {
|
||
- if (Contains(aValue)) {
|
||
+ if (mLookup.Contains(&aValue)) {
|
||
return;
|
||
}
|
||
|
||
+ mLookup.Insert(&aValue); // insert pointer before move
|
||
mValues.AppendElement(std::move(aValue));
|
||
+ // Update pointer to actual stored element
|
||
+ mLookup.Remove(&aValue);
|
||
+ mLookup.Insert(&mValues.LastElement());
|
||
}
|
||
void InsertNew(ValueType&& aValue) {
|
||
- MOZ_ASSERT(!Contains(aValue));
|
||
+ MOZ_ASSERT(!mLookup.Contains(&aValue));
|
||
mValues.AppendElement(std::move(aValue));
|
||
+ mLookup.Insert(&mValues.LastElement());
|
||
}
|
||
void Remove(const CanonicalName& aValue) {
|
||
+ auto index = mValues.IndexOf(aValue);
|
||
+ if (index != mValues.NoIndex) {
|
||
+ mLookup.Remove(&mValues[index]);
|
||
+ }
|
||
mValues.RemoveElement(aValue);
|
||
}
|
||
bool Contains(const CanonicalName& aValue) const {
|
||
- return mValues.Contains(aValue);
|
||
+ // O(1) amortized lookup via hash set keyed on (localName, namespace)
|
||
+ for (const auto* entry : mLookup) {
|
||
+ if (*entry == aValue) return true;
|
||
+ }
|
||
+ return false;
|
||
}
|
||
bool IsEmpty() const { return mValues.IsEmpty(); }
|
||
|
||
@@ -72,0 +82 @@
|
||
nsTArray<ValueType> mValues;
|
||
+ nsTHashSet<const ValueType*> mLookup; // hash index for O(1) Contains
|
||
};
|