import java.util.*; /** * CWE-407 unit tests for GitLab CE (gitlab-foss) defects. * * Simulates the Ruby Array#include? vs Set#include? patterns found in: * gitlab-foss-0001: Network::Graph#find_free_space reserved array scan * gitlab-foss-0002: Network::Graph#overlap? spaces array scan * gitlab-foss-0003: MergeRequests::RefreshService commit_ids array scan * gitlab-foss-0004: Project#members_among user_ids array scan * * NotificationService (gitlab-foss-0002 in patch numbering, 0002 in UNDF) * is combined with the general "select with include?" pattern in test 4. */ public class GitlabFossTest { // --------------------------------------------------------------- // gitlab-foss-0001: Network::Graph#find_free_space // Array#include? in while loop to find unreserved space number // --------------------------------------------------------------- static long findFreeSpaceDefect(List reserved, int spaceDefault, int spaceBase, int spaceStep) { long ops = 0; int space = spaceDefault; while (reserved.contains(space)) { // O(R) per iteration ops += reserved.size(); space += spaceStep; if (space < spaceBase) { spaceStep *= -1; space = spaceBase + spaceStep; } } return ops; } static long findFreeSpaceFixed(List reserved, int spaceDefault, int spaceBase, int spaceStep) { long ops = 0; Set reservedSet = new HashSet<>(reserved); // O(R) one-time int space = spaceDefault; while (reservedSet.contains(space)) { // O(1) per iteration ops++; space += spaceStep; if (space < spaceBase) { spaceStep *= -1; space = spaceBase + spaceStep; } } return ops; } // --------------------------------------------------------------- // gitlab-foss-0002: Network::Graph#overlap? // spaces.include?(overlap_space) inside range loop // --------------------------------------------------------------- static long overlapDefect(List> commitSpaces, int rangeStart, int rangeEnd, int overlapSpace) { long ops = 0; for (int i = rangeStart; i <= rangeEnd; i++) { if (i != rangeStart && i != rangeEnd) { // Array#include? on spaces list List spaces = commitSpaces.get(i); for (int s : spaces) { ops++; if (s == overlapSpace) break; } } } return ops; } static long overlapFixed(List> commitSpaces, int rangeStart, int rangeEnd, int overlapSpace) { long ops = 0; for (int i = rangeStart; i <= rangeEnd; i++) { if (i != rangeStart && i != rangeEnd) { Set spacesSet = new HashSet<>(commitSpaces.get(i)); ops++; // O(1) lookup spacesSet.contains(overlapSpace); } } return ops; } // --------------------------------------------------------------- // gitlab-foss-0003: RefreshService#post_merge_manually_merged // commit_ids.include?(mr.diff_head_sha) inside .select // --------------------------------------------------------------- static long commitIdsScanDefect(List commitIds, List mrHeadShas) { long ops = 0; for (String sha : mrHeadShas) { for (String cid : commitIds) { ops++; if (cid.equals(sha)) break; } } return ops; } static long commitIdsScanFixed(List commitIds, List mrHeadShas) { long ops = 0; Set commitSet = new HashSet<>(commitIds); for (String sha : mrHeadShas) { ops++; commitSet.contains(sha); } return ops; } // --------------------------------------------------------------- // gitlab-foss-0004: Project#members_among // user_ids.include?(user.id) inside .select // Also covers NotificationService new_mentioned_users.include?(r.user) // --------------------------------------------------------------- static long membersAmongDefect(List userIds, List inputUsers) { long ops = 0; for (int uid : inputUsers) { for (int aid : userIds) { ops++; if (aid == uid) break; } } return ops; } static long membersAmongFixed(List userIds, List inputUsers) { long ops = 0; Set idSet = new HashSet<>(userIds); for (int uid : inputUsers) { ops++; idSet.contains(uid); } return ops; } // --------------------------------------------------------------- // Test runner // --------------------------------------------------------------- public static void main(String[] args) { int pass = 0, fail = 0; // Test 1: find_free_space — reserved array with 500 entries, searching for free space { List reserved = new ArrayList<>(); for (int i = 1; i <= 500; i++) reserved.add(i); // spaces 1..500 reserved long defectOps = findFreeSpaceDefect(reserved, 1, 1, 2); long fixedOps = findFreeSpaceFixed(reserved, 1, 1, 2); double ratio = (double) defectOps / Math.max(fixedOps, 1); boolean ok = ratio > 5.0; System.out.printf("TEST 1 find_free_space: defect=%d fixed=%d ratio=%.1fx %s%n", defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL"); if (ok) pass++; else fail++; } // Test 2: overlap? — 300 commits, each with 100 spaces { List> commitSpaces = new ArrayList<>(); for (int i = 0; i < 300; i++) { List spaces = new ArrayList<>(); for (int s = 0; s < 100; s++) spaces.add(s); commitSpaces.add(spaces); } long defectOps = overlapDefect(commitSpaces, 0, 299, 999); // space not found long fixedOps = overlapFixed(commitSpaces, 0, 299, 999); double ratio = (double) defectOps / Math.max(fixedOps, 1); boolean ok = ratio > 5.0; System.out.printf("TEST 2 overlap?: defect=%d fixed=%d ratio=%.1fx %s%n", defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL"); if (ok) pass++; else fail++; } // Test 3: post_merge_manually_merged — 500 commits, 50 MRs { List commitIds = new ArrayList<>(); for (int i = 0; i < 500; i++) commitIds.add("sha_" + i); List mrHeadShas = new ArrayList<>(); for (int i = 0; i < 50; i++) mrHeadShas.add("mr_sha_" + i); // none match long defectOps = commitIdsScanDefect(commitIds, mrHeadShas); long fixedOps = commitIdsScanFixed(commitIds, mrHeadShas); double ratio = (double) defectOps / Math.max(fixedOps, 1); boolean ok = ratio > 5.0; System.out.printf("TEST 3 commit_ids scan: defect=%d fixed=%d ratio=%.1fx %s%n", defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL"); if (ok) pass++; else fail++; } // Test 4: members_among / notification mentioned_users — 500 IDs, 200 input users { List userIds = new ArrayList<>(); for (int i = 0; i < 500; i++) userIds.add(i); List inputUsers = new ArrayList<>(); for (int i = 1000; i < 1200; i++) inputUsers.add(i); // none match long defectOps = membersAmongDefect(userIds, inputUsers); long fixedOps = membersAmongFixed(userIds, inputUsers); double ratio = (double) defectOps / Math.max(fixedOps, 1); boolean ok = ratio > 5.0; System.out.printf("TEST 4 members_among: defect=%d fixed=%d ratio=%.1fx %s%n", defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL"); if (ok) pass++; else fail++; } System.out.printf("%n%d/%d PASS%n", pass, pass + fail); if (fail > 0) System.exit(1); } }