164 lines
6.5 KiB
Java
164 lines
6.5 KiB
Java
package unit;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* CWE-407 unit test: keystone-0001
|
|
* Implied-role deduplication: list comprehension vs set.
|
|
*
|
|
* Slow: `if imp_role_obj['id'] not in [x['id'] for x in roles]`
|
|
* rebuilds list every check — O(n) per implied role.
|
|
* Fast: maintain a seen_role_ids set — O(1) per check.
|
|
*/
|
|
public class KeystoneImpliedRoleAlgorithm {
|
|
|
|
// Simulates the implied-role graph: role_id -> list of implied role_ids
|
|
static Map<String, List<String>> buildImpliedGraph(int R, int I) {
|
|
Map<String, List<String>> graph = new HashMap<>();
|
|
for (int i = 0; i < R; i++) {
|
|
List<String> implied = new ArrayList<>();
|
|
for (int j = 0; j < I; j++) {
|
|
implied.add("implied-" + i + "-" + j);
|
|
}
|
|
graph.put("role-" + i, implied);
|
|
// implied roles themselves have no further implications
|
|
for (int j = 0; j < I; j++) {
|
|
graph.put("implied-" + i + "-" + j, new ArrayList<>());
|
|
}
|
|
}
|
|
return graph;
|
|
}
|
|
|
|
// Defective: list comprehension for deduplication
|
|
static long expandRolesSlow(List<Map<String, String>> roles,
|
|
Map<String, List<String>> impliedGraph) {
|
|
long ops = 0;
|
|
// NOTE: iterating over a mutable list that grows — simulating the defect
|
|
for (int idx = 0; idx < roles.size(); idx++) {
|
|
Map<String, String> role = roles.get(idx);
|
|
List<String> implied = impliedGraph.getOrDefault(role.get("id"),
|
|
new ArrayList<>());
|
|
for (String impId : implied) {
|
|
ops++;
|
|
// O(n) list scan — rebuild the id list each time
|
|
boolean found = false;
|
|
for (Map<String, String> r : roles) { // O(current size)
|
|
ops++;
|
|
if (r.get("id").equals(impId)) { found = true; break; }
|
|
}
|
|
if (!found) {
|
|
Map<String, String> newRole = new HashMap<>();
|
|
newRole.put("id", impId);
|
|
roles.add(newRole);
|
|
}
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
// Fixed: set for deduplication
|
|
static long expandRolesFast(List<Map<String, String>> roles,
|
|
Map<String, List<String>> impliedGraph) {
|
|
long ops = 0;
|
|
Set<String> seenIds = new HashSet<>();
|
|
for (Map<String, String> r : roles) seenIds.add(r.get("id"));
|
|
|
|
List<Map<String, String>> snapshot = new ArrayList<>(roles);
|
|
for (Map<String, String> role : snapshot) {
|
|
List<String> implied = impliedGraph.getOrDefault(role.get("id"),
|
|
new ArrayList<>());
|
|
for (String impId : implied) {
|
|
ops++;
|
|
if (!seenIds.contains(impId)) { // O(1)
|
|
seenIds.add(impId);
|
|
Map<String, String> newRole = new HashMap<>();
|
|
newRole.put("id", impId);
|
|
roles.add(newRole);
|
|
}
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
static List<Map<String, String>> makeRoles(int count) {
|
|
List<Map<String, String>> roles = new ArrayList<>();
|
|
for (int i = 0; i < count; i++) {
|
|
Map<String, String> r = new HashMap<>();
|
|
r.put("id", "role-" + i);
|
|
roles.add(r);
|
|
}
|
|
return roles;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
int R = 50; // initial roles
|
|
int I = 10; // implied roles per role
|
|
|
|
int passed = 0;
|
|
int total = 0;
|
|
|
|
Map<String, List<String>> graph = buildImpliedGraph(R, I);
|
|
|
|
// Test 1: op count slow vs fast
|
|
List<Map<String, String>> rolesSlow = makeRoles(R);
|
|
List<Map<String, String>> rolesFast = makeRoles(R);
|
|
long slowOps = expandRolesSlow(rolesSlow, graph);
|
|
long fastOps = expandRolesFast(rolesFast, graph);
|
|
total++;
|
|
assert slowOps > fastOps * 5 :
|
|
"slow=" + slowOps + " fast=" + fastOps + " speedup insufficient";
|
|
System.out.println("Test 1 PASS: implied-role expand slow=" + slowOps +
|
|
" ops, fast=" + fastOps + " ops, speedup=" + (slowOps / Math.max(1, fastOps)) + "x");
|
|
passed++;
|
|
|
|
// Test 2: correctness — same final role set
|
|
Set<String> slowIds = new HashSet<>();
|
|
for (Map<String, String> r : rolesSlow) slowIds.add(r.get("id"));
|
|
Set<String> fastIds = new HashSet<>();
|
|
for (Map<String, String> r : rolesFast) fastIds.add(r.get("id"));
|
|
total++;
|
|
assert slowIds.equals(fastIds) :
|
|
"role sets differ: slow=" + slowIds.size() + " fast=" + fastIds.size();
|
|
System.out.println("Test 2 PASS: role sets agree (" + slowIds.size() + " roles)");
|
|
passed++;
|
|
|
|
// Test 3: no duplicates in fast result
|
|
total++;
|
|
assert rolesFast.size() == fastIds.size() :
|
|
"fast result contains duplicates: list=" + rolesFast.size() +
|
|
" set=" + fastIds.size();
|
|
System.out.println("Test 3 PASS: no duplicates in fast result");
|
|
passed++;
|
|
|
|
// Test 4: token_roles deduplication — list vs set
|
|
// Simulate `token_roles = [r['id'] for r in token.roles]` + loop check
|
|
List<String> tokenRolesListBuild = new ArrayList<>();
|
|
for (int i = 0; i < R; i++) tokenRolesListBuild.add("role-" + i);
|
|
Set<String> tokenRolesSet = new HashSet<>(tokenRolesListBuild);
|
|
|
|
long listCheckOps = 0;
|
|
long setCheckOps = 0;
|
|
List<Map<String, String>> allRoles = makeRoles(R * 2); // some not in token
|
|
for (Map<String, String> role : allRoles) {
|
|
listCheckOps += tokenRolesListBuild.size(); // O(T) list scan
|
|
setCheckOps++; // O(1) set lookup
|
|
@SuppressWarnings("unused") boolean listContains =
|
|
tokenRolesListBuild.contains(role.get("id"));
|
|
@SuppressWarnings("unused") boolean setContains =
|
|
tokenRolesSet.contains(role.get("id"));
|
|
}
|
|
total++;
|
|
assert listCheckOps > setCheckOps * 5 :
|
|
"token_roles: list=" + listCheckOps + " set=" + setCheckOps;
|
|
System.out.println("Test 4 PASS: token_roles list=" + listCheckOps +
|
|
" ops, set=" + setCheckOps + " ops");
|
|
passed++;
|
|
|
|
System.out.println(passed + "/" + total + " PASS");
|
|
}
|
|
}
|