201 lines
8.3 KiB
Java
201 lines
8.3 KiB
Java
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<Integer> 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<Integer> reserved, int spaceDefault, int spaceBase, int spaceStep) {
|
|
long ops = 0;
|
|
Set<Integer> 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<List<Integer>> 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<Integer> spaces = commitSpaces.get(i);
|
|
for (int s : spaces) {
|
|
ops++;
|
|
if (s == overlapSpace) break;
|
|
}
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
static long overlapFixed(List<List<Integer>> commitSpaces, int rangeStart, int rangeEnd, int overlapSpace) {
|
|
long ops = 0;
|
|
for (int i = rangeStart; i <= rangeEnd; i++) {
|
|
if (i != rangeStart && i != rangeEnd) {
|
|
Set<Integer> 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<String> commitIds, List<String> mrHeadShas) {
|
|
long ops = 0;
|
|
for (String sha : mrHeadShas) {
|
|
for (String cid : commitIds) {
|
|
ops++;
|
|
if (cid.equals(sha)) break;
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
static long commitIdsScanFixed(List<String> commitIds, List<String> mrHeadShas) {
|
|
long ops = 0;
|
|
Set<String> 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<Integer> userIds, List<Integer> inputUsers) {
|
|
long ops = 0;
|
|
for (int uid : inputUsers) {
|
|
for (int aid : userIds) {
|
|
ops++;
|
|
if (aid == uid) break;
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
static long membersAmongFixed(List<Integer> userIds, List<Integer> inputUsers) {
|
|
long ops = 0;
|
|
Set<Integer> 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<Integer> 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<List<Integer>> commitSpaces = new ArrayList<>();
|
|
for (int i = 0; i < 300; i++) {
|
|
List<Integer> 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<String> commitIds = new ArrayList<>();
|
|
for (int i = 0; i < 500; i++) commitIds.add("sha_" + i);
|
|
List<String> 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<Integer> userIds = new ArrayList<>();
|
|
for (int i = 0; i < 500; i++) userIds.add(i);
|
|
List<Integer> 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);
|
|
}
|
|
}
|