Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions src/medusa/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,14 @@ export function processMedusa(line: string, jobStats: FuzzingResults): void {
jobStats.duration =
captureFuzzingDuration(line.replace("fuzz: elapsed:", "")) ?? ""; // TODO 0XSI - fix this
const coverageMatch = line.match(/coverage: (\d+)/);
const numberOfTestsMatch = line.match( /calls:\s(\d+)/);
const numberOfTestsMatch = line.match(/calls:\s(\d+)/);
if (coverageMatch) {
jobStats.coverage = +coverageMatch[1];
}


if (numberOfTestsMatch) {
jobStats.numberOfTests = parseInt(numberOfTestsMatch[1]);
}

} else if (line.includes("Test summary:")) {
const passedMatch = line.match(/(\d+ test\(s\) passed)/);
const failedMatch = line.match(/(\d+ test\(s\) failed)/);
Expand Down Expand Up @@ -141,7 +139,7 @@ export function getPropertyAndSequenceString(
const bodies = splitted.map((entry) =>
vmData
? getFunctionCallsWithVM(entry, vmData)
: getFunctionCalls(entry).map((body) => body.replace(" (block=", ";"))
: getFunctionCalls(entry)?.map((body) => body.replace(" (block=", ";"))
);
const headers = splitted.map((entry, counter) => getHeaders(entry, counter));
if (bodies.length != headers.length) {
Expand Down Expand Up @@ -172,7 +170,8 @@ export function getFunctionCallsWithVM(
logs: string,
vmData?: VmParsingData
): string[] {
const pattern: RegExp =/(?<=\.)[\w]+\(([^()]*(?:\([^()]*\)[^()]*)*)\)\(?([^()]*)\)?\s+\(block=\d*,\s*time=\d*,\s*gas=\d*,\s*gasprice=\d*,\s*value=\d*,\s*sender=0x[0-9a-fA-F]{1,40}\)/gm
const pattern: RegExp =
/(?<=\.)[\w]+\(([^()]*(?:\([^()]*\)[^()]*)*)\)\(?([^()]*)\)?\s+\(block=\d*,\s*time=\d*,\s*gas=\d*,\s*gasprice=\d*,\s*value=\d*,\s*sender=0x[0-9a-fA-F]{1,40}\)/gm;
const matches: RegExpMatchArray | null = logs.match(pattern);
const functionCalls = matches?.map((entry) => {
let returnData = "";
Expand All @@ -188,16 +187,16 @@ export function getFunctionCallsWithVM(
// Check for uncommon scenarios like: ((hex"address",uint256)[])([])
if (cleanedData.includes("((")) {
// Remove the content inside the first set of parentheses and replace it with an empty string
cleanedData = cleanedData.replace(patternArrayParams, '');
cleanedData = cleanedData.replace(patternArrayParams, "");

// Remove the extra '([])' from the second set of parentheses
cleanedData = cleanedData.replace(emptyArrayPattern, '');
cleanedData = cleanedData.replace(emptyArrayPattern, "");
} else if (cleanedData.includes("()()")) {
// For common cases like: check_liquidation_solvency()();
cleanedData = cleanedData.replace("()()", "()");
} else if (/\([^\(\)]*\)\([^\(\)]*\)/.test(cleanedData)) {
// If there are two sets of parentheses, remove the first set and its contents
cleanedData = cleanedData.replace(/\([^\(\)]*\)(?=\([^\(\)]*\))/, '');
cleanedData = cleanedData.replace(/\([^\(\)]*\)(?=\([^\(\)]*\))/, "");
}

if (vmData) {
Expand Down Expand Up @@ -244,7 +243,7 @@ export function getFunctionCalls(logs: string): string[] {
const pattern: RegExp = /\b(\w+)\(([^)]*)\)\s+\(block=/gm;
const matches: RegExpMatchArray | null = logs.match(pattern);

const functionCalls = matches?.map((entry) => entry.toString()) as string[];
const functionCalls = matches?.map((entry) => entry.toString()) || [];
return functionCalls;
}

Expand Down
103 changes: 103 additions & 0 deletions src/medusa/medusa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { getPropertyAndSequenceString } from "./index";

describe("Medusa Parser", () => {
describe("getPropertyAndSequenceString", () => {
it("should handle logs with no function calls without crashing", () => {
const logsWithNoFunctionCalls = `
[FAILED] Assertion Test: CryticTester.restakingRouter_redeem(address,uint256)
Test for method "CryticTester.restakingRouter_redeem(address,uint256)" resulted in an assertion failure after the following call sequence:
[Call Sequence]
1) CryticTester.restakingBondMM_unpause()() (block=68924, time=621386, gas=12500000, gasprice=1, value=0, sender=0x30000)
2) CryticTester.restakingRouter_redeem(address,uint256)(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, 1000309797381228308400539929892) (block=68924, time=621386, gas=12500000, gasprice=1, value=0, sender=0x30000)
[Execution Trace]
=> [call] CryticTester.restakingRouter_redeem(address,uint256)(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, 1000309797381228308400539929892) (addr=0x2e234DAe75C793f67A35089C9d99245E1C58470b, value=0, sender=0x30000)
=> [panic: assertion failed]
[Logs]
"loanData[_maturity].l", 0
"cashOut", 0
`;

// This should not crash
expect(() => {
const result = getPropertyAndSequenceString(logsWithNoFunctionCalls);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
}).not.toThrow();
});

it("should handle logs with function calls correctly", () => {
const logsWithFunctionCalls = `
[FAILED] Assertion Test: CryticTester.restakingRouter_redeem(address,uint256)
Test for method "CryticTester.restakingRouter_redeem(address,uint256)" resulted in an assertion failure after the following call sequence:
[Call Sequence]
1) CryticTester.restakingBondMM_unpause()() (block=68924, time=621386, gas=12500000, gasprice=1, value=0, sender=0x30000)
2) CryticTester.restakingRouter_redeem(address,uint256)(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, 1000309797381228308400539929892) (block=68924, time=621386, gas=12500000, gasprice=1, value=0, sender=0x30000)
`;

const result = getPropertyAndSequenceString(logsWithFunctionCalls);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBeGreaterThan(0);

if (result.length > 0) {
expect(result[0]).toHaveProperty("brokenProperty");
expect(result[0]).toHaveProperty("sequence");
expect(result[0].brokenProperty).toBe("restakingRouter_redeem");
}
});

it("should handle empty logs without crashing", () => {
const emptyLogs = "";

expect(() => {
const result = getPropertyAndSequenceString(emptyLogs);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(0);
}).not.toThrow();
});

it("should handle logs with no [FAILED] sections", () => {
const logsWithoutFailed = `
Some random log content
without any failed sections
`;

expect(() => {
const result = getPropertyAndSequenceString(logsWithoutFailed);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
// The function may return some results even without [FAILED] sections
// The important thing is that it doesn't crash
}).not.toThrow();
});

it("should handle the actual failed logs from the file", () => {
// This is a simplified version of the actual failed logs that were causing the crash
const actualFailedLogs = `
[FAILED] Assertion Test: CryticTester.restakingRouter_redeem(address,uint256)
Test for method "CryticTester.restakingRouter_redeem(address,uint256)" resulted in an assertion failure after the following call sequence:
[Call Sequence]
1) CryticTester.restakingBondMM_unpause()() (block=68924, time=621386, gas=12500000, gasprice=1, value=0, sender=0x30000)
2) CryticTester.restakingRouter_redeem(address,uint256)(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, 1000309797381228308400539929892) (block=68924, time=621386, gas=12500000, gasprice=1, value=0, sender=0x30000)
[Execution Trace]
=> [call] CryticTester.restakingRouter_redeem(address,uint256)(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, 1000309797381228308400539929892) (addr=0x2e234DAe75C793f67A35089C9d99245E1C58470b, value=0, sender=0x30000)
=> [call] RestakingRouter.getRate(address)(0xcDAA741ad6c9B73a249718651B6ff86565beC634) (addr=0x6985c91901D47804564bB155267991BF3Fd9f476, value=<nil>, sender=0x2e234DAe75C793f67A35089C9d99245E1C58470b)
=> [call] RestakingBondMM.getUintRate()() (addr=0xcDAA741ad6c9B73a249718651B6ff86565beC634, value=<nil>, sender=0x6985c91901D47804564bB155267991BF3Fd9f476)
=> [return (0, 50000000000000000)]
=> [return (0, 50000000000000000)]
=> [panic: assertion failed]
[Logs]
"loanData[_maturity].l", 0
"cashOut", 0
`;

// This should not crash with the fix
expect(() => {
const result = getPropertyAndSequenceString(actualFailedLogs);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
}).not.toThrow();
});
});
});
Loading