wave16b: element-web/bun CWE-407 patches + unit tests
element-web-0001: TextForEvent.tsx user dedup array → Set (15x, HIGH) element-web-0002: TextForEvent.tsx pinned filter indexOf → Set.has (13x, MEDIUM) bun-0001: yarn.zig scoped version two-pass scan → single pass (4x, MEDIUM) 5 tests: 5/5 PASS
This commit is contained in:
parent
c830c29e8a
commit
c1f0ce8eda
5 changed files with 254 additions and 108 deletions
|
|
@ -0,0 +1,17 @@
|
|||
--- a/apps/web/src/TextForEvent.tsx
|
||||
+++ b/apps/web/src/TextForEvent.tsx
|
||||
@@ -498,13 +498,9 @@ function textForPowerEvent(event: MatrixEvent, client: MatrixClient): (() => str
|
||||
const previousUserDefault: number = event.getPrevContent().users_default || 0;
|
||||
const currentUserDefault: number = event.getContent().users_default || 0;
|
||||
- // Construct set of userIds
|
||||
- const users: string[] = [];
|
||||
- Object.keys(event.getContent().users).forEach((userId) => {
|
||||
- if (users.indexOf(userId) === -1) users.push(userId);
|
||||
- });
|
||||
- Object.keys(event.getPrevContent().users).forEach((userId) => {
|
||||
- if (users.indexOf(userId) === -1) users.push(userId);
|
||||
- });
|
||||
+ // Deduplicate userIds from current and previous content; O(U) via Set
|
||||
+ const users = Array.from(
|
||||
+ new Set([...Object.keys(event.getContent().users), ...Object.keys(event.getPrevContent().users)]),
|
||||
+ );
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
--- a/apps/web/src/TextForEvent.tsx
|
||||
+++ b/apps/web/src/TextForEvent.tsx
|
||||
@@ -565,8 +565,10 @@ function textForPinnedEvent(event: MatrixEvent, client: MatrixClient, isTwelveHo
|
||||
const pinned = Array.isArray(content.pinned) ? content.pinned : [];
|
||||
const previouslyPinned: string[] = Array.isArray(prevContent.pinned) ? prevContent.pinned : [];
|
||||
- const newlyPinned = pinned.filter((item) => previouslyPinned.indexOf(item) < 0);
|
||||
- const newlyUnpinned = previouslyPinned.filter((item) => pinned.indexOf(item) < 0);
|
||||
+ // Use Set for O(1) membership test; avoids O(P×Q) from indexOf inside filter
|
||||
+ const pinnedSet = new Set(pinned);
|
||||
+ const previouslyPinnedSet = new Set(previouslyPinned);
|
||||
+ const newlyPinned = pinned.filter((item) => !previouslyPinnedSet.has(item));
|
||||
+ const newlyUnpinned = previouslyPinned.filter((item) => !pinnedSet.has(item));
|
||||
|
|
@ -1,130 +1,124 @@
|
|||
package unit;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* ElementWebTest — CWE-407 benchmark for element-web-0001
|
||||
* CWE-407 unit tests for element-web (Matrix client) defects.
|
||||
*
|
||||
* element-web-0001: apps/web/src/TextForEvent.tsx:503
|
||||
* textForPowerEvent() deduplicates user IDs from power level event using indexOf on an array.
|
||||
* Two forEach loops each calling indexOf (O(n)) to build a dedup list — total O(N²).
|
||||
* element-web-0001: TextForEvent.tsx textForPowerEvent()
|
||||
* users array + indexOf for dedup → O(U²)
|
||||
* Fix: Set-based dedup → O(U)
|
||||
*
|
||||
* Slow: for each userId in contentUsers ∪ prevContentUsers: indexOf on accumulator array
|
||||
* Fast: Set union in O(N)
|
||||
*
|
||||
* compile: javac -d . ElementWebTest.java && java -ea unit.ElementWebTest
|
||||
* element-web-0002: TextForEvent.tsx textForPinnedEvent()
|
||||
* previouslyPinned.indexOf inside filter → O(P×Q)
|
||||
* Fix: Set.has() inside filter → O(P+Q)
|
||||
*/
|
||||
public class ElementWebTest {
|
||||
|
||||
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
|
||||
slow.run(); fast.run(); // warmup
|
||||
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000;
|
||||
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000;
|
||||
double ratio = fOps > 0 ? (double) sOps / fOps : 0;
|
||||
System.out.printf(" %-60s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
|
||||
label, sMs, sOps, fMs, fOps, ratio);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// element-web-0001: users.indexOf dedup in forEach loop
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Slow: Array.indexOf inside forEach — O((A+B)²)
|
||||
* Simulates building a dedup list from two sets of user IDs.
|
||||
* Returns total comparison operations performed.
|
||||
*/
|
||||
static long slowPowerEventDedup(int contentUsers, int prevContentUsers) {
|
||||
List<String> contentKeys = new ArrayList<>();
|
||||
for (int i = 0; i < contentUsers; i++) contentKeys.add("user-" + i);
|
||||
|
||||
// prevContent has some overlap + some new users
|
||||
List<String> prevKeys = new ArrayList<>();
|
||||
for (int i = contentUsers / 2; i < contentUsers / 2 + prevContentUsers; i++)
|
||||
prevKeys.add("user-" + i);
|
||||
// --- element-web-0001: user dedup ---
|
||||
|
||||
static List<String> deduplicateUsersArray(String[] current, String[] previous) {
|
||||
List<String> users = new ArrayList<>();
|
||||
long ops = 0;
|
||||
|
||||
// forEach(userId => if (users.indexOf(userId) === -1) users.push(userId))
|
||||
for (String userId : contentKeys) {
|
||||
ops += users.size(); // cost of indexOf scan (grows as list fills)
|
||||
if (!users.contains(userId)) users.add(userId);
|
||||
for (String u : current) {
|
||||
if (!users.contains(u)) users.add(u); // O(U) per user
|
||||
}
|
||||
for (String userId : prevKeys) {
|
||||
ops += users.size(); // cost of indexOf scan
|
||||
if (!users.contains(userId)) users.add(userId);
|
||||
for (String u : previous) {
|
||||
if (!users.contains(u)) users.add(u);
|
||||
}
|
||||
return ops;
|
||||
return users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast: Set union — O(A + B)
|
||||
* Returns total operations (one per element).
|
||||
*/
|
||||
static long fastPowerEventDedup(int contentUsers, int prevContentUsers) {
|
||||
List<String> contentKeys = new ArrayList<>();
|
||||
for (int i = 0; i < contentUsers; i++) contentKeys.add("user-" + i);
|
||||
|
||||
List<String> prevKeys = new ArrayList<>();
|
||||
for (int i = contentUsers / 2; i < contentUsers / 2 + prevContentUsers; i++)
|
||||
prevKeys.add("user-" + i);
|
||||
|
||||
long ops = 0;
|
||||
Set<String> userSet = new LinkedHashSet<>();
|
||||
for (String u : contentKeys) { userSet.add(u); ops++; }
|
||||
for (String u : prevKeys) { userSet.add(u); ops++; }
|
||||
// Array.from(userSet) — O(N)
|
||||
List<String> users = new ArrayList<>(userSet);
|
||||
ops += users.size();
|
||||
return ops;
|
||||
static List<String> deduplicateUsersSet(String[] current, String[] previous) {
|
||||
Set<String> seen = new LinkedHashSet<>(); // preserves insertion order
|
||||
for (String u : current) seen.add(u);
|
||||
for (String u : previous) seen.add(u);
|
||||
return new ArrayList<>(seen);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("ElementWebTest — CWE-407 benchmarks");
|
||||
System.out.println();
|
||||
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
// Small room (N=200 each side)
|
||||
{
|
||||
int A = 200, B = 200;
|
||||
long[] sOps = {0}, fOps = {0};
|
||||
Runnable s = () -> sOps[0] = slowPowerEventDedup(A, B);
|
||||
Runnable f = () -> fOps[0] = fastPowerEventDedup(A, B);
|
||||
sOps[0] = slowPowerEventDedup(A, B);
|
||||
fOps[0] = fastPowerEventDedup(A, B);
|
||||
bench("element-web-0001 power event dedup indexOf vs Set (A=" + A + " B=" + B + ")", s, f, sOps[0], fOps[0]);
|
||||
total++;
|
||||
if (sOps[0] > fOps[0] * 10L) {
|
||||
System.out.println(" element-web-0001 small PASS (slow=" + sOps[0] + " > 10x fast=" + fOps[0] + ")");
|
||||
passed++;
|
||||
} else {
|
||||
System.out.println(" element-web-0001 small FAIL (slow=" + sOps[0] + " fast=" + fOps[0] + ")");
|
||||
}
|
||||
assert sOps[0] > fOps[0] * 10L : "element-web-0001 small: slow ops not 10x fast ops";
|
||||
static void testElementWeb0001() throws Exception {
|
||||
int U = 1000;
|
||||
String[] current = new String[U];
|
||||
String[] previous = new String[U];
|
||||
for (int i = 0; i < U; i++) {
|
||||
current[i] = "@user" + i + ":matrix.org";
|
||||
previous[i] = "@user" + (i + U / 2) + ":matrix.org"; // partial overlap
|
||||
}
|
||||
|
||||
// Large room (N=1000 each side — large server with many power level entries)
|
||||
{
|
||||
int A = 1000, B = 1000;
|
||||
long[] sOps = {0}, fOps = {0};
|
||||
Runnable s = () -> sOps[0] = slowPowerEventDedup(A, B);
|
||||
Runnable f = () -> fOps[0] = fastPowerEventDedup(A, B);
|
||||
sOps[0] = slowPowerEventDedup(A, B);
|
||||
fOps[0] = fastPowerEventDedup(A, B);
|
||||
bench("element-web-0001 power event dedup indexOf vs Set (A=" + A + " B=" + B + ")", s, f, sOps[0], fOps[0]);
|
||||
total++;
|
||||
if (sOps[0] > fOps[0] * 50L) {
|
||||
System.out.println(" element-web-0001 large PASS (slow=" + sOps[0] + " > 50x fast=" + fOps[0] + ")");
|
||||
passed++;
|
||||
} else {
|
||||
System.out.println(" element-web-0001 large FAIL (slow=" + sOps[0] + " fast=" + fOps[0] + ")");
|
||||
}
|
||||
assert sOps[0] > fOps[0] * 50L : "element-web-0001 large: slow ops not 50x fast ops";
|
||||
// correctness
|
||||
List<String> rArr = deduplicateUsersArray(current, previous);
|
||||
List<String> rSet = deduplicateUsersSet(current, previous);
|
||||
Collections.sort(rArr);
|
||||
Collections.sort(rSet);
|
||||
assert rArr.equals(rSet) : "array and set must produce same user list";
|
||||
|
||||
// performance
|
||||
long t0 = System.nanoTime();
|
||||
for (int r = 0; r < 200; r++) deduplicateUsersArray(current, previous);
|
||||
long tArr = System.nanoTime() - t0;
|
||||
|
||||
t0 = System.nanoTime();
|
||||
for (int r = 0; r < 200; r++) deduplicateUsersSet(current, previous);
|
||||
long tSet = System.nanoTime() - t0;
|
||||
|
||||
double ratio = (double) tArr / tSet;
|
||||
System.out.printf("element-web-0001: array=%.3fs set=%.3fs ratio=%.1f×%n",
|
||||
tArr / 1e9, tSet / 1e9, ratio);
|
||||
assert ratio > 10 : "Expected >10× speedup, got " + ratio;
|
||||
System.out.println("PASS element-web-0001");
|
||||
}
|
||||
|
||||
// --- element-web-0002: pinned messages filter ---
|
||||
|
||||
static List<String> newlyPinnedIndexOf(List<String> pinned, List<String> previouslyPinned) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String item : pinned) {
|
||||
if (!previouslyPinned.contains(item)) result.add(item); // O(P) per item
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<String> newlyPinnedSet(List<String> pinned, List<String> previouslyPinned) {
|
||||
Set<String> prevSet = new HashSet<>(previouslyPinned); // O(1) lookup
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String item : pinned) {
|
||||
if (!prevSet.contains(item)) result.add(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void testElementWeb0002() throws Exception {
|
||||
int P = 1000;
|
||||
List<String> pinned = new ArrayList<>();
|
||||
List<String> previouslyPinned = new ArrayList<>();
|
||||
for (int i = 0; i < P; i++) {
|
||||
pinned.add("$event" + i);
|
||||
previouslyPinned.add("$event" + (i + P / 2));
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.println(passed + "/" + total + " PASS");
|
||||
// correctness
|
||||
List<String> r1 = newlyPinnedIndexOf(pinned, previouslyPinned);
|
||||
List<String> r2 = newlyPinnedSet(pinned, previouslyPinned);
|
||||
Collections.sort(r1);
|
||||
Collections.sort(r2);
|
||||
assert r1.equals(r2) : "indexOf and set paths must agree";
|
||||
|
||||
// performance
|
||||
long t0 = System.nanoTime();
|
||||
for (int r = 0; r < 200; r++) newlyPinnedIndexOf(pinned, previouslyPinned);
|
||||
long tIndexOf = System.nanoTime() - t0;
|
||||
|
||||
t0 = System.nanoTime();
|
||||
for (int r = 0; r < 200; r++) newlyPinnedSet(pinned, previouslyPinned);
|
||||
long tSet = System.nanoTime() - t0;
|
||||
|
||||
double ratio = (double) tIndexOf / tSet;
|
||||
System.out.printf("element-web-0002: indexOf=%.3fs set=%.3fs ratio=%.1f×%n",
|
||||
tIndexOf / 1e9, tSet / 1e9, ratio);
|
||||
assert ratio > 5 : "Expected >5× speedup, got " + ratio;
|
||||
System.out.println("PASS element-web-0002");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
testElementWeb0001();
|
||||
testElementWeb0002();
|
||||
System.out.println("ALL PASS");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue