java-topology/defects/scribus/unit/ScribusTest.java
russell@unturf.com 13db08af50 darktable+scribus: CWE-407 scan — 6 defects (3 darktable, 3 scribus)
darktable-0001: map_locations image diff g_list_find O(N*M) MEDIUM 375x
darktable-0002: tags _tag_add_tags_to_list g_list_find O(T*L) MEDIUM 375x
darktable-0003: map view clustering g_list_find(sel_imgs) O(I²×S) HIGH 160x
scribus-0001: getSortedStyleList retList.contains O(N²) MEDIUM 167x (×4 copies)
scribus-0002: getUsedPatterns results.contains O(I×R) MEDIUM 98x
scribus-0003: Selection::addItems m_SelList.contains O(N×M) MEDIUM 2100x
2026-03-30 11:28:25 -04:00

195 lines
7.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import java.util.*;
/**
* CWE-407 simulation tests for Scribus defects.
*
* scribus-0001: getSortedStyleList retList.contains O(N²) dedup
* scribus-0002: getUsedPatterns results.contains O(I×R) pattern accumulation
* scribus-0003: Selection::addItems m_SelList.contains O(N×M) selection dedup
*/
public class ScribusTest {
// ---------------------------------------------------------------
// scribus-0001: getSortedStyleList — QList<int>::contains inside loop
// Simulates walking parent chains and deduplicating into retList
// ---------------------------------------------------------------
/** Defective: List.contains O(N) inside O(N) loop */
static int sortedStyleListDefective(int styleCount, int[] parentIdx) {
int ops = 0;
List<Integer> retList = new ArrayList<>();
for (int i = 0; i < styleCount; i++) {
// Check if already in retList
for (int x : retList) { ops++; if (x == i) break; }
if (!retList.contains(i)) {
// Walk parent chain
List<Integer> chain = new ArrayList<>();
chain.add(i);
int p = parentIdx[i];
while (p >= 0) {
boolean found = false;
for (int x : chain) { ops++; if (x == p) { found = true; break; } }
if (!found) chain.add(0, p);
p = parentIdx[p];
}
// Merge chain into retList with contains check
for (int idx : chain) {
boolean found = false;
for (int x : retList) { ops++; if (x == idx) { found = true; break; } }
if (!found) retList.add(idx);
}
}
}
return ops;
}
/** Fixed: QSet<int> companion */
static int sortedStyleListFixed(int styleCount, int[] parentIdx) {
int ops = 0;
List<Integer> retList = new ArrayList<>();
Set<Integer> retSet = new HashSet<>();
for (int i = 0; i < styleCount; i++) {
ops++;
if (!retSet.contains(i)) {
List<Integer> chain = new ArrayList<>();
Set<Integer> chainSet = new HashSet<>();
chain.add(i); chainSet.add(i); ops++;
int p = parentIdx[i];
while (p >= 0) {
ops++;
if (!chainSet.contains(p)) { chain.add(0, p); chainSet.add(p); }
p = parentIdx[p];
}
for (int idx : chain) {
ops++;
if (!retSet.contains(idx)) { retList.add(idx); retSet.add(idx); }
}
}
}
return ops;
}
static void testSortedStyleList() {
int N = 500;
int[] parentIdx = new int[N];
// Linear parent chain: 0←1←2←...←N-1 (worst case)
parentIdx[0] = -1;
for (int i = 1; i < N; i++) parentIdx[i] = i - 1;
int defectOps = sortedStyleListDefective(N, parentIdx);
int fixedOps = sortedStyleListFixed(N, parentIdx);
double ratio = (double) defectOps / fixedOps;
System.out.printf("scribus-0001 sorted style list: defect=%d fixed=%d ratio=%.1fx%n",
defectOps, fixedOps, ratio);
assert ratio > 10 : "Expected >10x ratio, got " + ratio;
System.out.println(" PASS");
}
// ---------------------------------------------------------------
// scribus-0002: getUsedPatterns — QStringList::contains inside item loop
// ---------------------------------------------------------------
/** Defective: linear scan for each pattern check */
static int getUsedPatternsDefective(String[][] itemPatterns) {
int ops = 0;
List<String> results = new ArrayList<>();
for (String[] pats : itemPatterns) {
for (String pat : pats) {
if (pat == null || pat.isEmpty()) continue;
boolean found = false;
for (String r : results) { ops++; if (r.equals(pat)) { found = true; break; } }
if (!found) results.add(pat);
}
}
return ops;
}
/** Fixed: HashSet companion */
static int getUsedPatternsFixed(String[][] itemPatterns) {
int ops = 0;
Set<String> resultSet = new HashSet<>();
for (String[] pats : itemPatterns) {
for (String pat : pats) {
if (pat == null || pat.isEmpty()) continue;
ops++;
if (!resultSet.contains(pat)) resultSet.add(pat);
}
}
return ops;
}
static void testGetUsedPatterns() {
int items = 2000;
int patsPerItem = 3; // fill, stroke, mask
int uniquePatterns = 200;
Random rng = new Random(42);
String[][] itemPatterns = new String[items][patsPerItem];
for (int i = 0; i < items; i++)
for (int j = 0; j < patsPerItem; j++)
itemPatterns[i][j] = "pattern_" + rng.nextInt(uniquePatterns);
int defectOps = getUsedPatternsDefective(itemPatterns);
int fixedOps = getUsedPatternsFixed(itemPatterns);
double ratio = (double) defectOps / fixedOps;
System.out.printf("scribus-0002 used patterns: defect=%d fixed=%d ratio=%.1fx%n",
defectOps, fixedOps, ratio);
assert ratio > 10 : "Expected >10x ratio, got " + ratio;
System.out.println(" PASS");
}
// ---------------------------------------------------------------
// scribus-0003: Selection::addItems — m_SelList.contains inside loop
// ---------------------------------------------------------------
/** Defective: QList::contains O(M) for each of N items */
static int selectionAddItemsDefective(int existing, int toAdd) {
int ops = 0;
List<Integer> selList = new ArrayList<>();
for (int i = 0; i < existing; i++) selList.add(i);
for (int i = existing; i < existing + toAdd; i++) {
// linear scan
for (int s : selList) { ops++; if (s == i) break; }
selList.add(i);
}
return ops;
}
/** Fixed: QSet companion for O(1) check */
static int selectionAddItemsFixed(int existing, int toAdd) {
int ops = 0;
Set<Integer> selSet = new HashSet<>();
for (int i = 0; i < existing; i++) { selSet.add(i); ops++; }
for (int i = existing; i < existing + toAdd; i++) {
ops++;
if (!selSet.contains(i)) selSet.add(i);
}
return ops;
}
static void testSelectionAddItems() {
int existing = 2000;
int toAdd = 3000;
int defectOps = selectionAddItemsDefective(existing, toAdd);
int fixedOps = selectionAddItemsFixed(existing, toAdd);
double ratio = (double) defectOps / fixedOps;
System.out.printf("scribus-0003 selection addItems: defect=%d fixed=%d ratio=%.1fx%n",
defectOps, fixedOps, ratio);
assert ratio > 10 : "Expected >10x ratio, got " + ratio;
System.out.println(" PASS");
}
// ---------------------------------------------------------------
public static void main(String[] args) {
testSortedStyleList();
testGetUsedPatterns();
testSelectionAddItems();
System.out.println("\nAll 3 Scribus CWE-407 tests PASSED.");
}
}