Skip to content

Commit 7c28d22

Browse files
authored
Merge pull request #11 from WebFuzzing/runtime-validation-file-copy
Runtime validation file copy
2 parents ec1324e + 4f762db commit 7c28d22

10 files changed

Lines changed: 727 additions & 28 deletions

File tree

web-report/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<meta charset="UTF-8" />
55
<link rel="icon" type="image/svg+xml" href="/src/assets/icon.svg" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<meta name="description" content="Web Fuzzing Commons" />
78
<title>WFC Reports</title>
89
</head>
910
<body>

web-report/package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
8-
"generate": "json2ts ../src/main/resources/wfc/schemas/report.yaml src/types/GeneratedTypes.tsx",
8+
"generate": "json2ts ../src/main/resources/wfc/schemas/report.yaml src/types/GeneratedTypes.tsx && ts-to-zod src/types/GeneratedTypes.tsx src/types/GeneratedTypesZod.ts",
99
"build": "tsc -b && vite build",
10-
"installAndBuild": "yarn install && yarn generate && vitest --no-watch && tsc -b && vite build",
10+
"installAndBuild": "yarn install && yarn generate && vitest --no-watch && tsc -b && vite build && yarn copyRunFiles",
11+
"copyRunFiles": "cpx webreport.bat ../target/classes/webreport && cpx webreport.command ../target/classes/webreport && cpx webreport.py ../target/classes/webreport && cpx src-e2e/static/robots.txt ../target/classes/webreport",
1112
"lint": "eslint .",
1213
"preview": "vite preview",
13-
"debug": "vite build && cpx \"src-e2e/static/*\" ../target/classes/webreport && vite preview",
14+
"debug": "vite build && cpx src-e2e/static/* ../target/classes/webreport && vite preview",
1415
"test": "vitest"
1516
},
1617
"dependencies": {
@@ -31,7 +32,8 @@
3132
"recharts": "^2.15.1",
3233
"tailwind-merge": "^3.0.2",
3334
"tailwindcss": "^4.0.14",
34-
"tailwindcss-animate": "^1.0.7"
35+
"tailwindcss-animate": "^1.0.7",
36+
"zod": "^3.25.67"
3537
},
3638
"devDependencies": {
3739
"@eslint/js": "^9.21.0",
@@ -51,6 +53,7 @@
5153
"eslint-plugin-react-refresh": "^0.4.19",
5254
"globals": "^15.15.0",
5355
"happy-dom": "^17.4.7",
56+
"ts-to-zod": "^3.15.0",
5457
"typescript": "^5.8.3",
5558
"typescript-eslint": "^8.24.1",
5659
"vite": "^6.2.0",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ 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 "@/lib/utils";
7+
import {fetchFileContent, getFaultCounts} from "../src/lib/utils";
88

99
// Read the report.json file
1010
const reportJsonPath = resolve(__dirname, './static/report.json');
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
User-agent: *
2+
Disallow:

web-report/src/App.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#root {
22
min-width: 100%;
3+
min-height: 100vh;
34
margin: 0 auto;
45
padding: 2rem;
56
text-align: center;

web-report/src/AppProvider.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {createContext, useContext, useState, ReactNode, useEffect} from 'react';
22
import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx";
33
import {ITestFiles} from "@/types/General.tsx";
44
import {fetchFileContent, ITransformedReport, transformWebFuzzingReport} from "@/lib/utils.tsx";
5+
import {webFuzzingCommonsReportSchema} from "@/types/GeneratedTypesZod.ts";
56

67
type AppContextType = {
78
data: WebFuzzingCommonsReport | null;
@@ -31,10 +32,17 @@ export const AppProvider = ({ children }: AppProviderProps) => {
3132
const fetchData = async () => {
3233
try {
3334
const jsonData = await fetchFileContent('./report.json') as WebFuzzingCommonsReport;
35+
36+
// Validate the JSON data against the schema
37+
const report = webFuzzingCommonsReportSchema.safeParse(jsonData);
38+
if (!report.success) {
39+
setError("Invalid report format. Please ensure the report is generated correctly.");
40+
return;
41+
}
3442
setData(jsonData);
3543
} catch (error: Error | unknown) {
3644
if (error instanceof Error) {
37-
setError("Could not load the report file. Please check if the file exists and is accessible in /public folder.");
45+
setError("Could not load the report file. Please check if the file exists and is accessible in main folder.");
3846
} else {
3947
console.error(error);
4048
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Generated by ts-to-zod
2+
import { z } from "zod";
3+
4+
export const operationIdSchema = z.string();
5+
6+
export const testCaseIdSchema = z.string();
7+
8+
export const httpStatusSchema = z.number();
9+
10+
export const testFilePathSchema = z.string();
11+
12+
export const testCaseSchema = z.record(z.unknown()).and(
13+
z.object({
14+
id: testCaseIdSchema.optional(),
15+
filePath: testFilePathSchema.optional(),
16+
name: z.string().optional(),
17+
startLine: z.number().optional(),
18+
endLine: z.number().optional(),
19+
}),
20+
);
21+
22+
export const faultCategoryIdSchema = z.record(z.unknown()).and(
23+
z.object({
24+
code: z.number(),
25+
context: z.string().optional(),
26+
}),
27+
);
28+
29+
export const coveredEndpointSchema = z.record(z.unknown()).and(
30+
z.object({
31+
endpointId: operationIdSchema,
32+
testCaseId: testCaseIdSchema,
33+
httpStatus: z.tuple([httpStatusSchema]).rest(httpStatusSchema),
34+
}),
35+
);
36+
37+
export const coverageCriterionSchema = z.record(z.unknown()).and(
38+
z.object({
39+
name: z.string(),
40+
covered: z.number(),
41+
total: z.number().optional(),
42+
}),
43+
);
44+
45+
export const rESTReportSchema = z.record(z.unknown()).and(
46+
z.object({
47+
totalHttpCalls: z.number(),
48+
endpointIds: z.array(operationIdSchema),
49+
coveredHttpStatus: z.array(coveredEndpointSchema),
50+
}),
51+
);
52+
53+
export const coverageSchema = z.record(z.unknown()).and(
54+
z.object({
55+
toolName: z.string(),
56+
criteria: z.array(coverageCriterionSchema),
57+
}),
58+
);
59+
60+
export const foundFaultSchema = z.record(z.unknown()).and(
61+
z.object({
62+
operationId: operationIdSchema.optional(),
63+
testCaseId: testCaseIdSchema,
64+
faultCategories: z
65+
.tuple([faultCategoryIdSchema])
66+
.rest(faultCategoryIdSchema),
67+
}),
68+
);
69+
70+
export const faultsSchema = z.record(z.unknown()).and(
71+
z.object({
72+
totalNumber: z.number(),
73+
foundFaults: z.array(foundFaultSchema),
74+
}),
75+
);
76+
77+
export const webFuzzingCommonsReportSchema = z.record(z.unknown()).and(
78+
z.object({
79+
schemaVersion: z.string(),
80+
toolName: z.string(),
81+
toolVersion: z.string(),
82+
creationTime: z.string(),
83+
faults: faultsSchema,
84+
problemDetails: z.record(z.unknown()).and(
85+
z.object({
86+
rest: rESTReportSchema.optional(),
87+
}),
88+
),
89+
totalTests: z.number(),
90+
testFilePaths: z.array(testFilePathSchema),
91+
testCases: z.array(testCaseSchema),
92+
extra: z.array(coverageSchema).optional(),
93+
}),
94+
);

web-report/vite.config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ export default defineConfig({
1919
},
2020
},
2121
build: {
22-
outDir: '../target/classes/webreport',
23-
rollupOptions: {
22+
outDir: '../target/classes/webreport',
23+
rollupOptions: {
2424
output: {
2525
entryFileNames: `assets/report.js`,
2626
chunkFileNames: `assets/[name].js`,
@@ -33,7 +33,7 @@ export default defineConfig({
3333
return `assets/[name].svg`;
3434
}
3535
return `assets/[name].[ext]`;
36-
},
36+
}
3737
}
3838
}
3939
},

web-report/webreport.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ class Handler(http.server.SimpleHTTPRequestHandler):
1111
def __init__(self, *args, **kwargs):
1212
super().__init__(*args, directory=os.getcwd(), **kwargs)
1313

14+
def end_headers(self):
15+
self.send_header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
16+
self.send_header("Pragma", "no-cache")
17+
self.send_header("Expires", "0")
18+
super().end_headers()
19+
1420
def start_server():
1521
with socketserver.TCPServer((HOST, PORT), Handler) as httpd:
1622
print(f"Serving at http://{HOST}:{PORT}")
1723
webbrowser.open_new_tab(f"http://{HOST}:{PORT}")
1824
httpd.serve_forever()
1925

2026
if __name__ == "__main__":
21-
start_server()
27+
start_server()

0 commit comments

Comments
 (0)