Skip to content

Commit 64776a6

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents 3eb3a6e + 47c739e commit 64776a6

22 files changed

Lines changed: 944 additions & 163 deletions

web-report/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@radix-ui/react-scroll-area": "^1.2.3",
2020
"@radix-ui/react-slot": "^1.1.2",
2121
"@radix-ui/react-tabs": "^1.1.3",
22-
"@radix-ui/react-tooltip": "^1.1.8",
22+
"@radix-ui/react-tooltip": "^1.2.7",
2323
"@tailwindcss/vite": "^4.0.14",
2424
"class-variance-authority": "^0.7.1",
2525
"clsx": "^2.1.1",

web-report/src-e2e/App.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import {resolve} from "path";
44
import {readFileSync} from "fs";
55
import {vi} from "vitest";
66
import App from "../src/App";
7-
import {fetchFileContent, getFaultCounts} from "../src/utils";
7+
import {fetchFileContent, getFaultCounts} from "@/lib/utils";
88

99
// Read the report.json file
1010
const reportJsonPath = resolve(__dirname, './static/report.json');
1111
const reportData = JSON.parse(readFileSync(reportJsonPath, 'utf-8'));
1212

1313
// Mock the fetchFileContent function to return the content of the report.json file
14-
vi.mock('@/utils.tsx', async (importOriginal) => {
14+
vi.mock('@/lib/utils.tsx', async (importOriginal) => {
1515
const originalModule = await importOriginal();
1616
return{
1717
...originalModule as typeof importOriginal,

web-report/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {Dashboard} from "@/components/Dashboard.tsx";
33
import {useEffect, useState} from "react";
44
import {WebFuzzingReport} from "@/types/GeneratedTypes.tsx";
55
import {LoadingScreen} from "@/components/LoadingScreen.tsx";
6-
import {fetchFileContent} from "@/utils.tsx";
6+
import {fetchFileContent} from "@/lib/utils";
77
import {ITestFiles} from "@/types/General.tsx";
88

99
function App() {

web-report/src/assets/info.json

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
{
2+
"number_of_endpoints": "Number of endpoints (verb:path) in the API.",
3+
"number_of_http_calls": "Total number of HTTP calls in the generated test suites.",
4+
"code_number_identifiers": "Code number identifiers for detected fault types",
5+
"identifier_name": "Identifier name for the fault type.",
6+
"number_of_faults_per_code": "Number of faults detected for each code.",
7+
"generated_test_files": "Number of generated test files.",
8+
"generated_test_cases": "Number of generated test cases.",
9+
"total_faults": "Total number of faults detected in the API.",
10+
"distinct_fault_types": "Total number of distinct fault types detected in the API.",
11+
"creation_date": "Date when the report was generated.",
12+
"tool_name_version": "Name and version of the tool that generated the report.",
13+
"schema_version": "Version of the schema used for the report.",
14+
"status_2xx": "Coverage of the 2xx status codes",
15+
"status_3xx": "Coverage of the 3xx status codes",
16+
"status_4xx": "Coverage of the 4xx status codes",
17+
"status_5xx": "Coverage of the 5xx status codes",
18+
"fault_codes": [
19+
{
20+
"short_definition": "HTTP_STATUS_500",
21+
"code": 100,
22+
"description": "HTTP Status 500",
23+
"test_case_name": "causes500_internalServerError"
24+
},
25+
{
26+
"short_definition": "HTTP_INVALID_PAYLOAD_SYNTAX",
27+
"code": 101,
28+
"description": "Invalid Payload Syntax",
29+
"test_case_name": "rejectedWithInvalidPayloadSyntax"
30+
},
31+
{
32+
"short_definition": "HTTP_INVALID_LOCATION",
33+
"code": 102,
34+
"description": "Invalid Location HTTP Header",
35+
"test_case_name": "returnsInvalidLocationHeader"
36+
},
37+
{
38+
"short_definition": "HTTP_NONWORKING_DELETE",
39+
"code": 103,
40+
"description": "DELETE Method Does Not Work",
41+
"test_case_name": "deleteDoesNotWork"
42+
},
43+
{
44+
"short_definition": "HTTP_REPEATED_CREATE_PUT",
45+
"code": 104,
46+
"description": "Repeated PUT Creates Resource With 201",
47+
"test_case_name": "repeatedCreatePut"
48+
},
49+
{
50+
"short_definition": "SCHEMA_INVALID_RESPONSE",
51+
"code": 200,
52+
"description": "Received A Response From API That Is Not Valid According To Its Schema",
53+
"test_case_name": "returnsSchemaInvalidResponse"
54+
},
55+
{
56+
"short_definition": "GQL_ERROR_FIELD",
57+
"code": 301,
58+
"description": "Error Field",
59+
"test_case_name": "returnedErrors"
60+
},
61+
{
62+
"short_definition": "RPC_INTERNAL_ERROR",
63+
"code": 400,
64+
"description": "Internal Error",
65+
"test_case_name": "causesInternalError"
66+
},
67+
{
68+
"short_definition": "RPC_SERVICE_ERROR",
69+
"code": 401,
70+
"description": "Service Error",
71+
"test_case_name": "causesServiceError"
72+
},
73+
{
74+
"short_definition": "RPC_DECLARED_EXCEPTION",
75+
"code": 402,
76+
"description": "Declared Exception",
77+
"test_case_name": "throwsExpectedException"
78+
},
79+
{
80+
"short_definition": "RPC_UNEXPECTED_EXCEPTION",
81+
"code": 403,
82+
"description": "Unexpected Exception",
83+
"test_case_name": "throwsUnexpectedException"
84+
},
85+
{
86+
"short_definition": "RPC_HANDLED_ERROR",
87+
"code": 404,
88+
"description": "Business Logic Error",
89+
"test_case_name": "failsToExecuteCall"
90+
},
91+
{
92+
"short_definition": "WEB_BROKEN_LINK",
93+
"code": 500,
94+
"description": "Broken Link",
95+
"test_case_name": "returnsBrokenLink"
96+
},
97+
{
98+
"short_definition": "SECURITY_EXISTENCE_LEAKAGE",
99+
"code": 800,
100+
"description": "Leakage Information Existence of Protected Resource",
101+
"test_case_name": "allowsUnauthorizedAccessToProtectedResource"
102+
},
103+
{
104+
"short_definition": "SECURITY_NOT_RECOGNIZED_AUTHENTICATED",
105+
"code": 801,
106+
"description": "Wrongly Not Recognized as Authenticated",
107+
"test_case_name": "failedToAuthenticateWithValidCredentials"
108+
},
109+
{
110+
"short_definition": "SECURITY_FORBIDDEN_DELETE",
111+
"code": 802,
112+
"description": "Forbidden Delete But Allowed Modifications",
113+
"test_case_name": "forbidsDeleteButAllowsModifications"
114+
},
115+
{
116+
"short_definition": "SECURITY_FORBIDDEN_PUT",
117+
"code": 803,
118+
"description": "Forbidden Replacement But Allowed Modifications",
119+
"test_case_name": "forbidsReplacementButAllowsModifications"
120+
},
121+
{
122+
"short_definition": "SECURITY_FORBIDDEN_PATCH",
123+
"code": 804,
124+
"description": "Forbidden Updates But Allowed Modifications",
125+
"test_case_name": "forbidsUpdatesButAllowsModifications"
126+
},
127+
{
128+
"short_definition": "SECURITY_ALLOW_MODIFICATION_BY_ALL",
129+
"code": 805,
130+
"description": "Resource Created By An User Can Be Modified By All Other Users",
131+
"test_case_name": "createdResourceCanBeModifiedByEveryone"
132+
},
133+
{
134+
"short_definition": "SECURITY_FORGOTTEN_AUTHENTICATION",
135+
"code": 806,
136+
"description": "A Protected Resource Is Accessible Without Providing Any Authentication",
137+
"test_case_name": "forgottenAuthentication"
138+
}
139+
]
140+
}

web-report/src/components/Dashboard.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ export const Dashboard: React.FC<IDashboard> = ({data, test_files}) => {
4545
setActiveTab(updatedTabs[0].value)
4646
}
4747
}
48+
49+
const numberOfTestCaseOfFiles = data.test_file_paths.map((test_file) => {
50+
return {
51+
"file_name": test_file,
52+
"number_of_test_cases": data.test_cases.filter((test_case) => test_case.file_path === test_file).length
53+
}
54+
});
55+
4856
return (
4957
<div className="border border-black p-4 w-[80%] mx-auto bg-white">
5058
<Header date={data.creation_time}
@@ -101,7 +109,7 @@ export const Dashboard: React.FC<IDashboard> = ({data, test_files}) => {
101109
<TabsContent value="overview" className="mt-0">
102110
<Overview rest={data.problem_details.rest}
103111
test_cases={data.test_cases}
104-
test_file_paths={data.test_file_paths}
112+
test_files={numberOfTestCaseOfFiles}
105113
faults={data.faults}/>
106114
</TabsContent>
107115

web-report/src/components/EndpointAccordion.tsx

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {AccordionContent, AccordionItem, AccordionTrigger} from "@/components/ui
22
import {Badge} from "@/components/ui/badge.tsx";
33
import React, {useState} from "react";
44
import {TestCases} from "@/components/TestCases.tsx";
5-
import {getColor} from "@/utils.tsx";
5+
import {getColor} from "@/lib/utils";
66

77
interface IStatusType {
88
code: number | string;
@@ -25,61 +25,100 @@ export const EndpointAccordion: React.FC<IEndpointAccordionProps> = ({
2525
addTestTab
2626
}) => {
2727

28-
const [selectedCode, setSelectedCode] = useState<number | string>(0);
28+
29+
const sortedStatusCodes = status_codes.sort((a, b) =>
30+
{
31+
const codeA = Number(a.code);
32+
const codeB = Number(b.code);
33+
if (isNaN(codeA) || isNaN(codeB)) {
34+
return String(a.code).localeCompare(String(b.code));
35+
}
36+
return codeA - codeB;
37+
}
38+
);
39+
40+
const [selectedCode, setSelectedCode] = useState<number | string>(sortedStatusCodes[0]?.code || 0);
2941
const [isFault, setIsFault] = useState(false);
3042

3143
const selectedTestCases = status_codes.find((code) => code.code === selectedCode)?.test_cases || [];
3244
const selectedFaultTestCases = faults.find((code) => code.code === selectedCode)?.test_cases || [];
3345

34-
const faultColors = ["bg-red-300", "bg-red-500", "bg-red-700"];
46+
const sortedFaults = faults.sort((a, b) => {
47+
const codeA = Number(a.code);
48+
const codeB = Number(b.code);
49+
if (isNaN(codeA) || isNaN(codeB)) {
50+
return String(a.code).localeCompare(String(b.code));
51+
}
52+
return codeA - codeB;
53+
});
54+
const getSelectedStyle = (code: number | string, fault: boolean) => {
55+
56+
const isSelected = selectedCode === code && isFault === fault;
57+
return isSelected ? "ring-2 ring-offset-2 ring-offset-white ring-blue-400 shadow-md" : "";
3558

59+
}
60+
61+
const faultColors = ["bg-red-300", "bg-red-500", "bg-red-700"];
3662
return (
3763
<AccordionItem value={value} className="border-2 border-black mb-4 overflow-hidden" data-testid={endpoint}>
3864
<AccordionTrigger className="bg-blue-100 px-4 py-3 text-lg font-bold hover:no-underline hover:bg-blue-200">
39-
{endpoint}
65+
<div className="flex-1 font-mono">{endpoint}</div>
66+
<div className="flex flex-wrap justify-end gap-2 mr-4">
67+
{sortedStatusCodes.map((code, idx) => (
68+
<Badge key={`_${idx}`} className={`${getColor(code.code, true, false)} font-mono`}>
69+
H{code.code}
70+
</Badge>
71+
))}
72+
{sortedFaults.map((code, idx) => (
73+
<Badge key={`_${idx}`} className={`${getColor(code.code, true, true)} font-mono`}>
74+
F{code.code}
75+
</Badge>
76+
))}
77+
</div>
4078
</AccordionTrigger>
4179
<AccordionContent className="p-4">
4280
<div className="mb-6">
43-
<div className="font-bold text-lg mb-2">HTTP</div>
81+
<div className="font-bold text-lg mb-2">HTTP CODES</div>
4482
<div className="flex flex-wrap gap-2">
4583
{
46-
status_codes.map((code, index) => (
84+
sortedStatusCodes.map((code, index) => (
4785
<Badge key={index} onClick={() => {
4886
setSelectedCode(code.code);
4987
setIsFault(false);
5088
}}
51-
className={`${getColor(code.code, true, false)} hover:bg-green-600 cursor-pointer text-white px-4 py-2 text-base font-bold border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]`}>
89+
className={`${getColor(code.code, true, false)} ${getSelectedStyle(code.code, false)} hover:bg-green-600 cursor-pointer text-white px-4 py-2 text-base font-bold border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]`}>
5290
{code.code}
5391
</Badge>
5492
))
5593
}
5694
{
57-
status_codes.length == 0 &&
95+
sortedStatusCodes.length == 0 &&
5896
<div className="text-gray-500 italic">No status codes recorded for this endpoint.</div>
5997
}
6098
</div>
6199
</div>
62100

63101
<div>
64-
<div className="font-bold text-lg mb-2 text-red-500">FAULTS</div>
102+
<div className="font-bold text-lg mb-2 text-red-500">FAULT CODES</div>
65103
<div className="flex flex-wrap gap-2">
66104
{
67-
faults.map((fault, index) => (
105+
sortedFaults.map((fault, index) => (
68106
<Badge key={index} onClick={() => {
69107
setSelectedCode(fault.code)
70108
setIsFault(true);
71109
}}
72-
className={`${faultColors[index % faultColors.length]} hover:bg-red-400 cursor-pointer text-white px-4 py-2 text-base font-bold border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]`}>
110+
className={`${faultColors[index % faultColors.length]} ${getSelectedStyle(fault.code, true)} hover:bg-red-400 cursor-pointer text-white px-4 py-2 text-base font-bold border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]`}>
73111
{fault.code}
74112
</Badge>
75113
))
76114
}
77115
{
78-
faults.length == 0 &&
116+
sortedFaults.length == 0 &&
79117
<div className="text-gray-500 italic">No faults recorded for this endpoint.</div>
80118
}
81119
</div>
82120
</div>
121+
<div className="text-xs text-gray-500 mt-1">Click to show test cases.</div>
83122

84123
{
85124
(selectedTestCases.length > 0 || selectedFaultTestCases.length > 0) &&

0 commit comments

Comments
 (0)