Skip to content

Commit 9f29f0f

Browse files
committed
Refactored policy handling to use CustomPolicy class and updated related tests
1 parent 394e6fb commit 9f29f0f

File tree

4 files changed

+129
-94
lines changed

4 files changed

+129
-94
lines changed

src/main/java/it/unipr/crosschain/Bridge.java

Lines changed: 15 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import it.unipr.analysis.contract.SmartContract;
66
import it.unipr.cfg.EVMCFG;
77
import it.unipr.cfg.ProgramCounterLocation;
8+
import it.unipr.utils.CustomPolicy;
89
import it.unipr.utils.VulnerabilitiesObject;
910
import it.unive.lisa.program.ClassUnit;
1011
import it.unive.lisa.program.cfg.CodeMemberDescriptor;
@@ -16,7 +17,6 @@
1617
import java.nio.file.Path;
1718
import java.util.*;
1819
import org.apache.commons.io.FilenameUtils;
19-
import org.apache.commons.lang3.tuple.Pair;
2020
import org.apache.logging.log4j.LogManager;
2121
import org.apache.logging.log4j.Logger;
2222
import org.json.JSONArray;
@@ -30,7 +30,7 @@ public class Bridge implements Iterable<SmartContract> {
3030

3131
private EVMCFG xCFG;
3232

33-
private final List<Pair<String, String>> policy;
33+
private CustomPolicy policy;
3434

3535
/** Detected vulnerabilities in the bridge. */
3636
private VulnerabilitiesObject _vulnerabilities;
@@ -85,7 +85,6 @@ public Bridge(Path bytecodeDirectoryPath, Path abiDirectoryPath) {
8585
public Bridge(Path bytecodeDirectoryPath, Path abiDirectoryPath, Path policyPath, String name) {
8686
this.name = name;
8787
this.contracts = new ArrayList<>();
88-
this.policy = new ArrayList<>();
8988

9089
Map<String, Path> abiFiles = mapFilesByName(abiDirectoryPath, ".abi");
9190
Map<String, Path> bytecodeFiles = mapFilesByName(bytecodeDirectoryPath, ".bytecode");
@@ -100,10 +99,17 @@ public Bridge(Path bytecodeDirectoryPath, Path abiDirectoryPath, Path policyPath
10099
log.info("Created bridge {} with {} contracts using bytecodes at {} and ABIs at {}.",
101100
name, contracts.size(), bytecodeDirectoryPath.toString(), abiDirectoryPath.toString());
102101

103-
if (policyPath != null)
104-
loadPolicy(policyPath);
105-
else
102+
if (policyPath != null) {
103+
try {
104+
this.policy = new CustomPolicy(policyPath);
105+
} catch (IOException e) {
106+
log.warn("Error reading policy file: {}. Creating bridge without custom policy. Using default policy",
107+
e.getMessage());
108+
}
109+
} else {
110+
this.policy = new CustomPolicy();
106111
log.info("Created bridge without custom policy. Using default policy");
112+
}
107113

108114
}
109115

@@ -163,36 +169,8 @@ public EVMCFG getXCFG() {
163169
*
164170
* @return a list of event-function pairs
165171
*/
166-
public List<Pair<String, String>> getPolicy() {
167-
return policy;
168-
}
169-
170-
/**
171-
* Checks if an event has an associated function in the policy.
172-
*
173-
* @param event the name of the event to check
174-
*
175-
* @return true if the event has an associated function, false otherwise
176-
*/
177-
public boolean hasEventFunctionMapping(String event) {
178-
for (Pair<String, String> pair : policy)
179-
if (pair.getLeft().equals(event))
180-
return true;
181-
return false;
182-
}
183-
184-
/**
185-
* Gets the function name associated with an event from the policy.
186-
*
187-
* @param event the name of the event
188-
*
189-
* @return the associated function name, or null if not found
190-
*/
191-
public String getFunctionForEvent(String event) {
192-
for (Pair<String, String> pair : policy)
193-
if (pair.getLeft().equals(event))
194-
return pair.getRight();
195-
return null;
172+
public CustomPolicy getPolicy() {
173+
return policy != null ? policy : new CustomPolicy();
196174
}
197175

198176
/**
@@ -266,43 +244,6 @@ public EVMCFG buildPartialXCFG() {
266244
return xCFG;
267245
}
268246

269-
/**
270-
* Loads the cross-chain policy from a JSON file and populates the policy
271-
* list. The JSON file should contain an array named "policy" with
272-
* event-function pairs.
273-
*
274-
* @param policyPath the path to the policy JSON file
275-
*/
276-
private void loadPolicy(Path policyPath) {
277-
try {
278-
log.info("Loading policy from: {}", policyPath);
279-
String content = Files.readString(policyPath);
280-
JSONObject policyJson = new JSONObject(content);
281-
282-
if (policyJson.has("policy")) {
283-
JSONArray eventFunctionPairs = policyJson.getJSONArray("policy");
284-
285-
for (int i = 0; i < eventFunctionPairs.length(); i++) {
286-
JSONObject pair = eventFunctionPairs.getJSONObject(i);
287-
String event = pair.getString("event");
288-
String function = pair.getString("function");
289-
policy.add(Pair.of(event, function));
290-
}
291-
292-
log.info("Loaded {} event-function pairs from policy.", policy.size());
293-
log.debug("Policy: {}", policy);
294-
} else {
295-
log.warn("Policy file does not contain 'policy' array.");
296-
}
297-
} catch (IOException e) {
298-
log.warn("Error reading policy file: {}. Creating bridge without custom policy. Using default policy",
299-
e.getMessage());
300-
} catch (Exception e) {
301-
log.warn("Error parsing policy JSON: {}. Creating bridge without custom policy. Using default policy",
302-
e.getMessage());
303-
}
304-
}
305-
306247
/**
307248
* Scans a directory for files with a given extension and maps them by their
308249
* base name. The base name is derived from the file name without its
@@ -342,14 +283,9 @@ public JSONObject toJson() {
342283
contractsArray.put(contract.toJson());
343284
}
344285

345-
JSONObject policyJson = new JSONObject();
346-
for (Pair<String, String> pair : policy) {
347-
policyJson.put(pair.getLeft(), pair.getRight());
348-
}
349-
350286
json.put("name", name);
351287
json.put("smart_contracts", contractsArray);
352-
json.put("policy", policyJson);
288+
json.put("policy", policy != null ? policy.toJson() : new JSONArray());
353289
json.put("bridge_vulnerabilities", _vulnerabilities != null ? _vulnerabilities.toJson() : new JSONArray());
354290
return json;
355291
}

src/main/java/it/unipr/crosschain/xEVMLiSA.java

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@
1111
import it.unipr.crosschain.taint.*;
1212
import it.unipr.frontend.EVMLiSAFeatures;
1313
import it.unipr.frontend.EVMLiSATypeSystem;
14-
import it.unipr.utils.EVMLiSAExecutor;
15-
import it.unipr.utils.LiSAConfigurationManager;
16-
import it.unipr.utils.MyCache;
17-
import it.unipr.utils.VulnerabilitiesObject;
14+
import it.unipr.utils.*;
1815
import it.unive.lisa.LiSA;
1916
import it.unive.lisa.analysis.SimpleAbstractState;
2017
import it.unive.lisa.analysis.heap.MonolithicHeap;
@@ -27,7 +24,6 @@
2724
import java.nio.file.Path;
2825
import java.util.*;
2926
import java.util.concurrent.Future;
30-
import org.apache.commons.lang3.tuple.Pair;
3127
import org.apache.logging.log4j.LogManager;
3228
import org.apache.logging.log4j.Logger;
3329
import org.json.JSONArray;
@@ -152,17 +148,39 @@ public static Set<Edge> getCrossChainEdgesUsingEventsAndFunctionsEntrypoint(Brid
152148
* @return true if the event should be linked to the function according to
153149
* the policy
154150
*/
155-
public static boolean applyPolicy(List<Pair<String, String>> policy, Signature event, Signature function) {
151+
public static boolean applyPolicy(CustomPolicy policy, Signature event, Signature function) {
156152
if (policy == null || policy.isEmpty())
157153
return event.getName().equalsIgnoreCase(function.getName());
158154

159-
for (Pair<String, String> pair : policy) {
160-
String eventPolicy = pair.getLeft();
161-
String functionPolicy = pair.getRight();
162-
if (eventPolicy.equalsIgnoreCase(event.getName())
163-
&& functionPolicy.equalsIgnoreCase(function.getName()))
155+
String eventName = event.getName();
156+
String functionName = function.getName();
157+
158+
List<CustomPolicy.PolicyEntry> candidates = policy.getEntriesByEvent(eventName);
159+
if (candidates.isEmpty())
160+
candidates = policy.getEntries();
161+
162+
for (CustomPolicy.PolicyEntry entry : candidates)
163+
if (eventMatches(entry.getSourceFunction(), eventName)
164+
&& entry.getDestinationFunction().getName().equalsIgnoreCase(functionName))
165+
return true;
166+
167+
return false;
168+
}
169+
170+
/**
171+
* Verifies whether the provided event name matches any of the events
172+
* associated with the given source function specification.
173+
*
174+
* @param sourceSpec the source function descriptor declared in the policy
175+
* @param eventName the name of the runtime event being considered
176+
*
177+
* @return {@code true} if {@code eventName} matches one of the policy event
178+
* names, {@code false} otherwise
179+
*/
180+
private static boolean eventMatches(CustomPolicy.FunctionSpec sourceSpec, String eventName) {
181+
for (String policyEvent : sourceSpec.getEvents())
182+
if (policyEvent.equalsIgnoreCase(eventName))
164183
return true;
165-
}
166184
return false;
167185
}
168186

src/main/java/it/unipr/utils/CustomPolicy.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ public final class CustomPolicy {
5050
private final Map<String, List<PolicyEntry>> entriesByEvent;
5151
private final Set<String> knownEvents;
5252

53+
/**
54+
* Creates an empty policy with no entries. All lookups return empty
55+
* collections and {@link #toJson()} yields an empty {@code policy} array.
56+
*/
57+
public CustomPolicy() {
58+
this.entries = Collections.emptyList();
59+
this.entriesBySourceName = Collections.emptyMap();
60+
this.entriesByDestinationName = Collections.emptyMap();
61+
this.entriesByEvent = Collections.emptyMap();
62+
this.knownEvents = Collections.emptySet();
63+
}
64+
5365
/**
5466
* Creates a policy view by eagerly loading and caching the data contained
5567
* in the provided JSON file.
@@ -237,8 +249,26 @@ public Set<String> getKnownEvents() {
237249
}
238250

239251
/**
240-
* Serializes the in-memory representation of this policy into a JSON string
241-
* that mirrors the original input format.
252+
* Checks whether the policy contains no entries.
253+
*
254+
* @return {@code true} if the policy is empty, {@code false} otherwise
255+
*/
256+
public boolean isEmpty() {
257+
return entries.isEmpty();
258+
}
259+
260+
/**
261+
* Returns the number of policy entries loaded in memory.
262+
*
263+
* @return the amount of entries contained in this policy
264+
*/
265+
public int size() {
266+
return entries.size();
267+
}
268+
269+
/**
270+
* Serializes the in-memory representation of this policy into a
271+
* {@link JSONObject} that mirrors the original input format.
242272
*
243273
* @return the JSON serialization of the policy
244274
*/

src/test/java/it/unipr/analysis/cron/utils/CustomPolicyTest.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package it.unipr.analysis.cron.utils;
22

3+
import it.unipr.analysis.contract.Signature;
4+
import it.unipr.crosschain.xEVMLiSA;
35
import it.unipr.utils.CustomPolicy;
46
import it.unipr.utils.CustomPolicy.PolicyEntry;
57
import java.io.IOException;
@@ -8,6 +10,7 @@
810
import java.nio.file.Files;
911
import java.nio.file.Path;
1012
import java.nio.file.Paths;
13+
import java.util.Collections;
1114
import java.util.List;
1215
import java.util.Set;
1316
import org.json.JSONObject;
@@ -21,7 +24,8 @@ public void loadsPolicyAndProvidesLookups() throws IOException {
2124
Path policyPath = Paths.get("evm-testcases", "cross-chain-custom-policy", "custom-policy.json");
2225
CustomPolicy policy = new CustomPolicy(policyPath);
2326

24-
assert policy.getEntries().size() == 17;
27+
assert policy.size() == 17;
28+
assert !policy.isEmpty();
2529

2630
List<PolicyEntry> transferOutEntries = policy.getEntriesBySourceFunction("transferOut");
2731
assert transferOutEntries.size() == 1;
@@ -53,4 +57,51 @@ public void toJsonRoundTripMatchesOriginal() throws IOException {
5357

5458
assert original.similar(serialized);
5559
}
60+
61+
@Test
62+
public void emptyPolicyProvidesEmptyCollections() {
63+
CustomPolicy emptyPolicy = new CustomPolicy();
64+
assert emptyPolicy.isEmpty();
65+
assert emptyPolicy.size() == 0;
66+
assert emptyPolicy.getEntries().isEmpty();
67+
assert emptyPolicy.getEntriesByEvent("any").isEmpty();
68+
assert emptyPolicy.getEntriesBySourceFunction("any").isEmpty();
69+
assert emptyPolicy.getKnownEvents().isEmpty();
70+
JSONObject json = emptyPolicy.toJson();
71+
assert json.has("policy");
72+
assert json.getJSONArray("policy").isEmpty();
73+
}
74+
75+
@Test
76+
public void applyPolicyRespectsConfiguration() throws IOException {
77+
Path policyPath = Paths.get("evm-testcases", "cross-chain-custom-policy", "custom-policy.json");
78+
CustomPolicy policy = new CustomPolicy(policyPath);
79+
80+
Signature depositEvent = new Signature("Deposit", "event", Collections.emptyList(), Collections.emptyList(),
81+
"Deposit(address)", "0x00000000");
82+
Signature depositFunction = new Signature("deposit", "function", Collections.emptyList(),
83+
Collections.emptyList(), "deposit(address)", "0x00000000");
84+
assert xEVMLiSA.applyPolicy(policy, depositEvent, depositFunction);
85+
86+
Signature outboundEvent = new Signature("Outbound", "event", Collections.emptyList(), Collections.emptyList(),
87+
"Outbound(address)", "0x00000001");
88+
Signature transferOutFunction = new Signature("transferOut", "function", Collections.emptyList(),
89+
Collections.emptyList(), "transferOut(address)", "0x00000002");
90+
assert xEVMLiSA.applyPolicy(policy, outboundEvent, transferOutFunction);
91+
92+
Signature randomEvent = new Signature("Random", "event", Collections.emptyList(), Collections.emptyList(),
93+
"Random()", "0x00000003");
94+
assert !xEVMLiSA.applyPolicy(policy, randomEvent, depositFunction);
95+
96+
Signature fallbackEvent = new Signature("Foo", "event", Collections.emptyList(), Collections.emptyList(),
97+
"Foo()", "0x00000004");
98+
Signature fallbackFunction = new Signature("foo", "function", Collections.emptyList(), Collections.emptyList(),
99+
"foo()", "0x00000005");
100+
assert xEVMLiSA.applyPolicy(null, fallbackEvent, fallbackFunction);
101+
CustomPolicy emptyPolicy = new CustomPolicy();
102+
assert xEVMLiSA.applyPolicy(emptyPolicy, fallbackEvent, fallbackFunction);
103+
Signature mismatchFunction = new Signature("bar", "function", Collections.emptyList(), Collections.emptyList(),
104+
"bar()", "0x00000006");
105+
assert !xEVMLiSA.applyPolicy(emptyPolicy, fallbackEvent, mismatchFunction);
106+
}
56107
}

0 commit comments

Comments
 (0)