From f5a02a99d3bd4237b93a9c5075eb88bf3d1f2209 Mon Sep 17 00:00:00 2001 From: Omur Date: Fri, 30 May 2025 22:05:22 +0200 Subject: [PATCH 1/2] minor fixes --- web-report/src/App.tsx | 6 +++--- web-report/src/components/Dashboard.tsx | 4 ++-- web-report/src/pages/Endpoints.tsx | 6 +++--- web-report/src/types/GeneratedTypes.tsx | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/web-report/src/App.tsx b/web-report/src/App.tsx index a8ff079..97364b2 100644 --- a/web-report/src/App.tsx +++ b/web-report/src/App.tsx @@ -1,14 +1,14 @@ import './App.css' import {Dashboard} from "@/components/Dashboard.tsx"; import {useEffect, useState} from "react"; -import {WebFuzzingReport} from "@/types/GeneratedTypes.tsx"; +import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; import {LoadingScreen} from "@/components/LoadingScreen.tsx"; import {fetchFileContent} from "@/lib/utils"; import {ITestFiles} from "@/types/General.tsx"; function App() { - const [data, setData] = useState(null); + const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [testFiles, setTestFiles] = useState([]); @@ -16,7 +16,7 @@ function App() { useEffect(() => { const fetchData = async () => { try { - const jsonData = await fetchFileContent('./report.json') as WebFuzzingReport; + const jsonData = await fetchFileContent('./report.json') as WebFuzzingCommonsReport; setData(jsonData); } catch (error: Error | unknown) { if (error instanceof Error) { diff --git a/web-report/src/components/Dashboard.tsx b/web-report/src/components/Dashboard.tsx index 06aa80b..0bcafbc 100644 --- a/web-report/src/components/Dashboard.tsx +++ b/web-report/src/components/Dashboard.tsx @@ -8,7 +8,7 @@ import {Endpoints} from "@/pages/Endpoints.tsx"; import {TestResults} from "@/pages/TestResults.tsx"; import {ScrollArea, ScrollBar} from "@/components/ui/scroll-area.tsx"; -import {WebFuzzingReport} from "@/types/GeneratedTypes.tsx"; +import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; import {ITestFiles} from "@/types/General.tsx"; @@ -17,7 +17,7 @@ export interface ITestTabs { } export interface IDashboard { - data: WebFuzzingReport; + data: WebFuzzingCommonsReport; test_files: Array; } diff --git a/web-report/src/pages/Endpoints.tsx b/web-report/src/pages/Endpoints.tsx index 5f2e540..570a7b6 100644 --- a/web-report/src/pages/Endpoints.tsx +++ b/web-report/src/pages/Endpoints.tsx @@ -1,17 +1,17 @@ import React, {useState} from "react"; import {Accordion} from "@/components/ui/accordion.tsx"; import {EndpointAccordion} from "@/components/EndpointAccordion.tsx"; -import {WebFuzzingReport} from "@/types/GeneratedTypes.tsx"; +import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; import {StatusCodeFilters} from "@/components/StatusCodeFilters.tsx"; interface IProps { addTestTab: (value: string, event: React.MouseEvent) => void; - data: WebFuzzingReport + data: WebFuzzingCommonsReport } export const Endpoints: React.FC = ({addTestTab, data}) => { - const transformJson = (original: WebFuzzingReport) => { + const transformJson = (original: WebFuzzingCommonsReport) => { const endpointMap = new Map Date: Sun, 1 Jun 2025 23:50:06 +0200 Subject: [PATCH 2/2] global state and fixing fault counts --- web-report/src-e2e/App.test.tsx | 2 +- web-report/src/App.tsx | 81 +--------- web-report/src/AppContent.tsx | 34 ++++ web-report/src/AppProvider.tsx | 153 ++++++++++++++++++ web-report/src/assets/info.json | 8 +- web-report/src/components/Dashboard.tsx | 26 +-- web-report/src/components/FaultsComponent.tsx | 71 +++++--- web-report/src/components/GeneratedTests.tsx | 8 +- web-report/src/components/RestReports.tsx | 26 ++- .../src/components/StatusCodeFilters.tsx | 15 +- web-report/src/lib/utils.tsx | 137 +++++++++++++++- web-report/src/pages/Endpoints.tsx | 150 +---------------- web-report/src/pages/TestResults.tsx | 49 ++---- 13 files changed, 443 insertions(+), 317 deletions(-) create mode 100644 web-report/src/AppContent.tsx create mode 100644 web-report/src/AppProvider.tsx diff --git a/web-report/src-e2e/App.test.tsx b/web-report/src-e2e/App.test.tsx index b285d39..7e396a7 100644 --- a/web-report/src-e2e/App.test.tsx +++ b/web-report/src-e2e/App.test.tsx @@ -140,7 +140,7 @@ describe('App test', () => { await waitFor(() => { expect(screen.getByTestId('faults-component-total-faults')).toContainHTML(`${total_faults}`); - expect(screen.getByTestId('faults-component-fault-counts')).toContainHTML(faultCounts.size.toString()); + expect(screen.getByTestId('faults-component-fault-counts')).toContainHTML(faultCounts.length.toString()); }); }); }); \ No newline at end of file diff --git a/web-report/src/App.tsx b/web-report/src/App.tsx index 97364b2..8c51965 100644 --- a/web-report/src/App.tsx +++ b/web-report/src/App.tsx @@ -1,81 +1,12 @@ import './App.css' -import {Dashboard} from "@/components/Dashboard.tsx"; -import {useEffect, useState} from "react"; -import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; -import {LoadingScreen} from "@/components/LoadingScreen.tsx"; -import {fetchFileContent} from "@/lib/utils"; -import {ITestFiles} from "@/types/General.tsx"; +import {AppProvider} from "@/AppProvider.tsx"; +import {AppContent} from "@/AppContent.tsx"; function App() { - - const [data, setData] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [testFiles, setTestFiles] = useState([]); - - useEffect(() => { - const fetchData = async () => { - try { - const jsonData = await fetchFileContent('./report.json') as WebFuzzingCommonsReport; - setData(jsonData); - } catch (error: Error | unknown) { - if (error instanceof Error) { - setError("Could not load the report file. Please check if the file exists and is accessible in /public folder."); - } else { - console.error(error); - } - } finally { - setLoading(false); - } - }; - - fetchData(); - }, []); - - useEffect(() => { - if(data?.test_file_paths){ - data.test_file_paths.map(file => { - fetchFileContent(file).then((content) => { - if (typeof content === "string") { - setTestFiles(prev => [...prev, { - name: file, - code: content - }]); - } else { - setError("Could not load the test file. Please check if the file exists and is accessible."); - } - }).catch((error) => { - console.error(error); - setError("Could not load the test file. Please check if the file exists and is accessible."); - }) - }) - } - }, [data]); - - if (error){ - return ( -
-
-
-

Error

-

{error}

-
-
-
- ) - } - - if (loading) { - return ( -
- -
- ) - } - return ( -
- {data && } -
+ return( + + + ) } diff --git a/web-report/src/AppContent.tsx b/web-report/src/AppContent.tsx new file mode 100644 index 0000000..831cd8e --- /dev/null +++ b/web-report/src/AppContent.tsx @@ -0,0 +1,34 @@ +import {useAppContext} from "@/AppProvider.tsx"; +import {LoadingScreen} from "@/components/LoadingScreen.tsx"; +import {Dashboard} from "@/components/Dashboard.tsx"; + +export const AppContent: React.FC = () => { + const {data, loading, error} = useAppContext(); + + if (error){ + return ( +
+
+
+

Error

+

{error}

+
+
+
+ ) + } + + if (loading) { + return ( +
+ +
+ ) + } + + return ( +
+ {data && } +
+ ) +} \ No newline at end of file diff --git a/web-report/src/AppProvider.tsx b/web-report/src/AppProvider.tsx new file mode 100644 index 0000000..26b8915 --- /dev/null +++ b/web-report/src/AppProvider.tsx @@ -0,0 +1,153 @@ +import {createContext, useContext, useState, ReactNode, useEffect} from 'react'; +import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; +import {ITestFiles} from "@/types/General.tsx"; +import {fetchFileContent, ITransformedReport, transformWebFuzzingReport} from "@/lib/utils.tsx"; + +type AppContextType = { + data: WebFuzzingCommonsReport | null; + loading: boolean; + error: string | null; + testFiles: ITestFiles[]; + transformedReport: ITransformedReport[]; + filterEndpoints: (activeFilters: Record) => ITransformedReport[]; + filteredEndpoints: ITransformedReport[]; +}; + +const AppContext = createContext(undefined); + +type AppProviderProps = { + children: ReactNode; +}; + +export const AppProvider = ({ children }: AppProviderProps) => { + + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [testFiles, setTestFiles] = useState([]); + const transformedReport = transformWebFuzzingReport(data); + + useEffect(() => { + const fetchData = async () => { + try { + const jsonData = await fetchFileContent('./report.json') as WebFuzzingCommonsReport; + setData(jsonData); + } catch (error: Error | unknown) { + if (error instanceof Error) { + setError("Could not load the report file. Please check if the file exists and is accessible in /public folder."); + } else { + console.error(error); + } + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); + + useEffect(() => { + if(data?.test_file_paths){ + data.test_file_paths.map(file => { + fetchFileContent(file).then((content) => { + if (typeof content === "string") { + setTestFiles(prev => [...prev, { + name: file, + code: content + }]); + } else { + setError("Could not load the test file. Please check if the file exists and is accessible."); + } + }).catch((error) => { + console.error(error); + setError("Could not load the test file. Please check if the file exists and is accessible."); + }) + }) + } + }, [data]); + + const [filteredEndpoints, setFilteredEndpoints] = useState(transformedReport); + + useEffect(() => { + // Transform the report data into a format suitable for filtering + if (data) { + const transformed = transformWebFuzzingReport(data); + setFilteredEndpoints(transformed); + } + }, [data]); + + const filterEndpoints = (activeFilters: Record) => { + // Filter the endpoints based on the active filters + const filtered = transformedReport.filter(endpoint => { + // If no filters are active, show all endpoints + if (Object.keys(activeFilters).length === 0) { + return true; + } + + // Check if any status code or fault code is marked as "removed" + const hasRemovedStatusCode = endpoint.http_status_codes.some(code => + activeFilters[code.code] === "removed" + ); + const hasRemovedFaultCode = endpoint.faults.some(code => + activeFilters[-code.code] === "removed" + ); + + // Check if any status code or fault code is marked as "active" + const hasActiveStatusCode = endpoint.http_status_codes.some(code => + activeFilters[code.code] === "active" + ); + const hasActiveFaultCode = endpoint.faults.some(code => + activeFilters[-code.code] === "active" + ); + + const hasActiveFilter = activeFilters && Object.values(activeFilters).some((value) => value === "active"); + const hasRemovedFilter = activeFilters && Object.values(activeFilters).some((value) => value === "removed"); + + if (!hasActiveFilter && !hasRemovedFilter) { + // If no filters are active, show all endpoints + return true; + } + + if (hasActiveFilter) { + if (hasRemovedFilter) { + // If there are both active and removed filters, check if the endpoint matches any of them + if(hasRemovedFaultCode || hasRemovedStatusCode) { + return false; + } + + return !!(hasActiveStatusCode || hasActiveFaultCode); + + } else { + // If there are only active filters, check if the endpoint matches any of them + return !!(hasActiveStatusCode || hasActiveFaultCode); + + } + } else if (hasRemovedFilter) { + // If there are only removed filters, check if the endpoint matches any of them + return !(hasRemovedStatusCode || hasRemovedFaultCode); + + } else { + // If there are no active or removed filters, show all endpoints + return true; + } + }); + setFilteredEndpoints(filtered) + return filtered; + } + + const value: AppContextType = { data, loading, error, testFiles, transformedReport, filterEndpoints, filteredEndpoints }; + + return ( + + {children} + + ); +}; + +export const useAppContext = (): AppContextType => { + const context = useContext(AppContext); + if (!context) { + throw new Error('useAppContext must be used within AppProvider'); + } + return context; +}; \ No newline at end of file diff --git a/web-report/src/assets/info.json b/web-report/src/assets/info.json index ff6af9f..e1d98ed 100644 --- a/web-report/src/assets/info.json +++ b/web-report/src/assets/info.json @@ -3,7 +3,11 @@ "number_of_http_calls": "Total number of HTTP calls in the generated test suites.", "code_number_identifiers": "Code number identifiers for detected fault types", "identifier_name": "Identifier name for the fault type.", + "test_files_located": "{number_of_test_cases} test cases are located in {file_name}", + "http_endpoint_codes": "{number_of_endpoints} endpoints have {code} HTTP code out of {total_endpoints} endpoints.", "number_of_faults_per_code": "Number of faults detected for each code.", + "distribution_of_endpoints_per_code": "Distribution of endpoints per code (Affected/Total Endpoints).", + "distribution_tooltip": "{operation_count} {endpoint_text} {code} error code out of {totalEndpointNumber} endpoints.", "generated_test_files": "Number of generated test files.", "generated_test_cases": "Number of generated test cases.", "total_faults": "Total number of faults detected in the API.", @@ -11,10 +15,6 @@ "creation_date": "Date when the report was generated.", "tool_name_version": "Name and version of the tool that generated the report.", "schema_version": "Version of the schema used for the report.", - "status_2xx": "Coverage of the 2xx status codes", - "status_3xx": "Coverage of the 3xx status codes", - "status_4xx": "Coverage of the 4xx status codes", - "status_5xx": "Coverage of the 5xx status codes", "fault_codes": [ { "short_definition": "HTTP_STATUS_500", diff --git a/web-report/src/components/Dashboard.tsx b/web-report/src/components/Dashboard.tsx index 0bcafbc..7f5c8f7 100644 --- a/web-report/src/components/Dashboard.tsx +++ b/web-report/src/components/Dashboard.tsx @@ -8,20 +8,16 @@ import {Endpoints} from "@/pages/Endpoints.tsx"; import {TestResults} from "@/pages/TestResults.tsx"; import {ScrollArea, ScrollBar} from "@/components/ui/scroll-area.tsx"; -import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; -import {ITestFiles} from "@/types/General.tsx"; +import {useAppContext} from "@/AppProvider.tsx"; export interface ITestTabs { value: string; } -export interface IDashboard { - data: WebFuzzingCommonsReport; - test_files: Array; -} +export const Dashboard: React.FC = () => { + const {data} = useAppContext(); -export const Dashboard: React.FC = ({data, test_files}) => { const [activeTab, setActiveTab] = useState("overview") const [testTabs, setTestTabs] = useState>([]); @@ -46,6 +42,14 @@ export const Dashboard: React.FC = ({data, test_files}) => { } } + if(!data) { + return ( +
+

Loading...

+
+ ); + } + const numberOfTestCaseOfFiles = data.test_file_paths.map((test_file) => { return { "file_name": test_file, @@ -114,17 +118,13 @@ export const Dashboard: React.FC = ({data, test_files}) => { - + { testTabs.map((test, index) => ( - + )) } diff --git a/web-report/src/components/FaultsComponent.tsx b/web-report/src/components/FaultsComponent.tsx index 2a26a4d..0380fcc 100644 --- a/web-report/src/components/FaultsComponent.tsx +++ b/web-report/src/components/FaultsComponent.tsx @@ -2,12 +2,16 @@ import {Card} from "@/components/ui/card.tsx"; import {ShieldAlert} from "lucide-react"; import React, {useState} from "react"; import {Faults} from "@/types/GeneratedTypes.tsx"; -import {getFaultCounts} from "@/lib/utils"; +import {getFaultCounts, getText} from "@/lib/utils"; import info from "@/assets/info.json"; import {StatusCodeModal} from "@/components/StatusCodeModal.tsx"; import {ReportTooltip} from "@/components/ui/report-tooltip.tsx"; +import {useAppContext} from "@/AppProvider.tsx"; export const FaultsComponent: React.FC = ({total_number, found_faults}) => { + const {data} = useAppContext(); + const totalEndpointNumber = data?.problem_details.rest?.endpoint_ids.length; + const faultCounts = getFaultCounts(found_faults); const [isModalOpen, setIsModalOpen] = useState(false) const [currentStatus, setCurrentStatus] = useState(-1); @@ -38,30 +42,59 @@ export const FaultsComponent: React.FC = ({total_number, found_faults}) Distinct Fault Types: - {faultCounts.size} + {faultCounts.length} -
-
- - Codes - - - Name - - - # - + {/*
*/} + {/*
*/} + {/* */} + {/* Codes*/} + {/* */} + {/* */} + {/* Name*/} + {/* */} + {/* */} + {/* Distribution*/} + {/* */} + {/* */} + {/* #*/} + {/* */} + {/*
*/} +
+
+
+ +
Codes
+
+ +
Name
+
+ +
Distribution
+
+ +
#
+
+
-
+
{ - Array.from(faultCounts).map(([code, count]) => ( -
- handleOpenModal(code)}>{code} - handleOpenModal(code)}>{getShortNameOfCode(code)} - {count} + faultCounts.map((fault) => ( +
+
handleOpenModal(fault.code)}>{fault.code}
+
handleOpenModal(fault.code)}>{getShortNameOfCode(fault.code)}
+ 1 ? "endpoints have" : "endpoint has", + code: fault.code, + totalEndpointNumber:totalEndpointNumber ? totalEndpointNumber : 0 + })}> +
{fault.operation_count}/{totalEndpointNumber}
+
+
{fault.count}
)) } diff --git a/web-report/src/components/GeneratedTests.tsx b/web-report/src/components/GeneratedTests.tsx index 2347cfe..b88a5be 100644 --- a/web-report/src/components/GeneratedTests.tsx +++ b/web-report/src/components/GeneratedTests.tsx @@ -1,7 +1,7 @@ import {Card} from "@/components/ui/card.tsx"; import {Target} from "lucide-react"; import type React from "react"; -import {getFileColor} from "@/lib/utils"; +import {getFileColor, getText} from "@/lib/utils"; import info from "@/assets/info.json"; import {ReportTooltip} from "@/components/ui/report-tooltip.tsx"; @@ -46,7 +46,11 @@ export const GeneratedTests: React.FC = ({total_tests, test_fil test_files.map((file, index) => (
- + {file.file_name} (# {file.number_of_test_cases})
diff --git a/web-report/src/components/RestReports.tsx b/web-report/src/components/RestReports.tsx index c9aeaac..cad93e5 100644 --- a/web-report/src/components/RestReports.tsx +++ b/web-report/src/components/RestReports.tsx @@ -2,7 +2,7 @@ import {Card} from "@/components/ui/card.tsx"; import type React from "react"; import {CoveragePieChart} from "@/components/CoveragePieChart.tsx"; import {RESTReport} from "@/types/GeneratedTypes.tsx"; -import {calculateAllStatusCounts} from "@/lib/utils"; +import {calculateAllStatusCounts, getText} from "@/lib/utils"; import info from "@/assets/info.json"; import {ReportTooltip} from "@/components/ui/report-tooltip.tsx"; @@ -18,16 +18,32 @@ export const RestReports: React.FC = ({covered_http_status, endpoint
- + - + - + - +
diff --git a/web-report/src/components/StatusCodeFilters.tsx b/web-report/src/components/StatusCodeFilters.tsx index a66d894..e80d31d 100644 --- a/web-report/src/components/StatusCodeFilters.tsx +++ b/web-report/src/components/StatusCodeFilters.tsx @@ -1,22 +1,11 @@ import {useState} from "react" import {StatusCodeFilterButton} from "./StatusCodeFilterButton" +import {ITransformedReport} from "@/lib/utils.tsx"; type FilterState = "inactive" | "active" | "removed" -interface DataProps { - endpoint: string, - faults: { - code: number, - test_cases: string[] - }[], - http_status_codes: { - code: number - test_cases: string[] - }[] -} - interface StatusCodeFiltersProps { - data: DataProps[] + data: ITransformedReport[] onFiltersChange: (activeFilters: Record) => void } diff --git a/web-report/src/lib/utils.tsx b/web-report/src/lib/utils.tsx index 4c857d6..65c4066 100644 --- a/web-report/src/lib/utils.tsx +++ b/web-report/src/lib/utils.tsx @@ -1,4 +1,4 @@ -import {CoveredEndpoint, FoundFault} from "@/types/GeneratedTypes.tsx"; +import {CoveredEndpoint, FoundFault, WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; import {ClassValue, clsx} from "clsx"; import {twMerge} from "tailwind-merge"; @@ -123,12 +123,34 @@ export const calculateAllStatusCounts = (covered_http_status: CoveredEndpoint[], export const getFaultCounts = (found_faults: FoundFault[]) => { const faultCounts = new Map(); + // A fault defines a unique operation_id, code, and context. To define a unique fault, we can use a combination of these three properties. + found_faults.forEach(fault => { fault.fault_categories.forEach(category => { - faultCounts.set(category.code, (faultCounts.get(category.code) || 0) + 1); + faultCounts.set(`${fault.operation_id}|${category.code}|${category.context}`, (faultCounts.get(category.code) || 0) + 1); }); }); - return faultCounts; + + const uniqueFaults = Array.from(faultCounts.keys()).map(key => { + const [operationId, code, context] = key.split('|'); + return { + operation_id: operationId, + code: parseInt(code, 10), + context: context || '', + count: faultCounts.get(key) + }; + }); + const uniqueCodes = new Set(uniqueFaults.map(fault => fault.code)); + + return Array.from(uniqueCodes).map(code => { + const faultsWithCode = uniqueFaults.filter(fault => fault.code === code); + const uniqueOperationCounts = new Set(faultsWithCode.map(fault => fault.operation_id)).size; + return { + code: code, + count: faultsWithCode.length, + operation_count: uniqueOperationCounts, + } + }) } export const getFileColor = (index: number, file: string) => { @@ -152,3 +174,112 @@ export const getFileColor = (index: number, file: string) => { return colorList[index % colorList.length]; } + +export const getLanguage = (file_name: string) => { + + switch (file_name.split('.').pop()) { + case 'java': + return 'java'; + case 'js': + return 'javascript'; + case 'py': + return 'python'; + case 'ts': + return 'typescript'; + default: + return 'plaintext'; + } +} + +export interface ITransformedReport { + endpoint: string; + faults: { + code: number; + test_cases: string[]; + }[]; + http_status_codes: { + code: number; + test_cases: string[]; + }[]; +} + +export const transformWebFuzzingReport = (original: WebFuzzingCommonsReport | null): Array => { + + if (!original || !original.problem_details || !original.problem_details.rest) { + return []; + } + + const endpointMap = new Map(); + + original.problem_details.rest?.endpoint_ids.forEach(endpoint => { + endpointMap.set(endpoint, { + endpoint, + http_status_codes: [], + faults: [] + }); + }); + + original.faults.found_faults.forEach(fault => { + if (!fault.operation_id) { + return; + } + + if (!endpointMap.has(fault.operation_id)) { + console.log(`Endpoint ${fault.operation_id} not found in endpoint_ids`); + } + + const endpointData = endpointMap.get(fault.operation_id); + + if (!endpointData) { + return; + } + + fault.fault_categories.forEach(faultCat => { + let existingFault = endpointData.faults.find((f: { code: number; }) => f.code === faultCat.code); + if (!existingFault) { + existingFault = {code: faultCat.code, test_cases: []}; + endpointData.faults.push(existingFault); + } + if (!existingFault.test_cases.includes(fault.test_case_id)) { + existingFault.test_cases.push(fault.test_case_id); + } + }); + }); + + if (original.problem_details.rest == null) { + return Array.from([]); + } + + original.problem_details.rest.covered_http_status.forEach(status => { + if (!endpointMap.has(status.endpoint_id)) { + console.log(`Endpoint ${status.endpoint_id} not found in endpoint_ids`); + } + + const endpointData = endpointMap.get(status.endpoint_id); + + status.http_status.forEach(code => { + if (!endpointData) { + return; + } + let existingStatus = endpointData.http_status_codes.find((s: { code: number; }) => s.code === code); + if (!existingStatus) { + existingStatus = {code, test_cases: []}; + endpointData.http_status_codes.push(existingStatus); + } + if (!existingStatus.test_cases.includes(status.test_case_id)) { + existingStatus.test_cases.push(status.test_case_id); + } + }); + }); + + return Array.from(endpointMap.values()); +} + +export const getText = ( + text: string, + params?: Record): string => { + + return Object.entries(params || {}).reduce((result, [param, value]) => { + return result.replace(`{${param}}`, String(value)); + }, text); +} \ No newline at end of file diff --git a/web-report/src/pages/Endpoints.tsx b/web-report/src/pages/Endpoints.tsx index 570a7b6..c86c1ff 100644 --- a/web-report/src/pages/Endpoints.tsx +++ b/web-report/src/pages/Endpoints.tsx @@ -1,162 +1,24 @@ -import React, {useState} from "react"; +import React from "react"; import {Accordion} from "@/components/ui/accordion.tsx"; import {EndpointAccordion} from "@/components/EndpointAccordion.tsx"; -import {WebFuzzingCommonsReport} from "@/types/GeneratedTypes.tsx"; import {StatusCodeFilters} from "@/components/StatusCodeFilters.tsx"; +import {useAppContext} from "@/AppProvider.tsx"; interface IProps { addTestTab: (value: string, event: React.MouseEvent) => void; - data: WebFuzzingCommonsReport } -export const Endpoints: React.FC = ({addTestTab, data}) => { +export const Endpoints: React.FC = ({addTestTab}) => { - const transformJson = (original: WebFuzzingCommonsReport) => { - - const endpointMap = new Map(); - - original.problem_details.rest?.endpoint_ids.forEach(endpoint => { - endpointMap.set(endpoint, { - endpoint, - http_status_codes: [], - faults: [] - }); - }); - - original.faults.found_faults.forEach(fault => { - if (!fault.operation_id) { - return; - } - - if (!endpointMap.has(fault.operation_id)) { - console.log(`Endpoint ${fault.operation_id} not found in endpoint_ids`); - } - - const endpointData = endpointMap.get(fault.operation_id); - - if (!endpointData) { - return; - } - - fault.fault_categories.forEach(faultCat => { - let existingFault = endpointData.faults.find((f: { code: number; }) => f.code === faultCat.code); - if (!existingFault) { - existingFault = {code: faultCat.code, test_cases: []}; - endpointData.faults.push(existingFault); - } - if (!existingFault.test_cases.includes(fault.test_case_id)) { - existingFault.test_cases.push(fault.test_case_id); - } - }); - }); - - if (original.problem_details.rest == null) { - return Array.from([]); - } - - original.problem_details.rest.covered_http_status.forEach(status => { - if (!endpointMap.has(status.endpoint_id)) { - console.log(`Endpoint ${status.endpoint_id} not found in endpoint_ids`); - } - - const endpointData = endpointMap.get(status.endpoint_id); - - status.http_status.forEach(code => { - if (!endpointData) { - return; - } - let existingStatus = endpointData.http_status_codes.find((s: { code: number; }) => s.code === code); - if (!existingStatus) { - existingStatus = {code, test_cases: []}; - endpointData.http_status_codes.push(existingStatus); - } - if (!existingStatus.test_cases.includes(status.test_case_id)) { - existingStatus.test_cases.push(status.test_case_id); - } - }); - }); - - return Array.from(endpointMap.values()); - } - - const transformed = transformJson(data); - const [filteredEndpoints, setFilteredEndpoints] = useState(transformed); - - const onFiltersChange = (activeFilters: Record) => { - // Filter the endpoints based on the active filters - const filtered = transformed.filter(endpoint => { - // If no filters are active, show all endpoints - if (Object.keys(activeFilters).length === 0) { - return true; - } - - // Check if any status code or fault code is marked as "removed" - const hasRemovedStatusCode = endpoint.http_status_codes.some(code => - activeFilters[code.code] === "removed" - ); - const hasRemovedFaultCode = endpoint.faults.some(code => - activeFilters[-code.code] === "removed" - ); - - // Check if any status code or fault code is marked as "active" - const hasActiveStatusCode = endpoint.http_status_codes.some(code => - activeFilters[code.code] === "active" - ); - const hasActiveFaultCode = endpoint.faults.some(code => - activeFilters[-code.code] === "active" - ); - - const hasActiveFilter = activeFilters && Object.values(activeFilters).some((value) => value === "active"); - const hasRemovedFilter = activeFilters && Object.values(activeFilters).some((value) => value === "removed"); - - if (!hasActiveFilter && !hasRemovedFilter) { - // If no filters are active, show all endpoints - return true; - } - - if (hasActiveFilter) { - if (hasRemovedFilter) { - // If there are both active and removed filters, check if the endpoint matches any of them - if(hasRemovedFaultCode || hasRemovedStatusCode) { - return false; - } - - return !!(hasActiveStatusCode || hasActiveFaultCode); - - } else { - // If there are only active filters, check if the endpoint matches any of them - return !!(hasActiveStatusCode || hasActiveFaultCode); - - } - } else if (hasRemovedFilter) { - // If there are only removed filters, check if the endpoint matches any of them - return !(hasRemovedStatusCode || hasRemovedFaultCode); - - } else { - // If there are no active or removed filters, show all endpoints - return true; - } - }); - setFilteredEndpoints(filtered); - } + const {transformedReport, filteredEndpoints, filterEndpoints} = useAppContext(); return (
- +

# Endpoints:

-

{filteredEndpoints.length}

/

{transformed.length}

+

{filteredEndpoints.length}

/

{transformedReport.length}

diff --git a/web-report/src/pages/TestResults.tsx b/web-report/src/pages/TestResults.tsx index 9f7e34c..e429080 100644 --- a/web-report/src/pages/TestResults.tsx +++ b/web-report/src/pages/TestResults.tsx @@ -2,31 +2,22 @@ import {Card} from "@/components/ui/card.tsx"; import type React from "react"; import {Badge} from "@/components/ui/badge.tsx"; import {CodeBlock} from "@/components/CodeBlock.tsx"; -import {FoundFault, RESTReport, TestCase} from "@/types/GeneratedTypes.tsx"; -import {extractCodeLines, getColor} from "@/lib/utils"; -import {ITestFiles} from "@/types/General.tsx"; +import {extractCodeLines, getColor, getLanguage} from "@/lib/utils"; +import {useAppContext} from "@/AppProvider.tsx"; interface IProps { test_case_name: string; - test_cases: Array; - found_faults: Array; - problem_details: { - rest?: RESTReport; - [k: string]: unknown; - }; - test_files: Array; } -export const TestResults: React.FC = ({ - test_case_name, - test_cases, - found_faults, - problem_details, - test_files - }) => { +export const TestResults: React.FC = ({test_case_name}) => { - if(!problem_details.rest){ + const {data, testFiles} = useAppContext(); + + const test_cases = data?.test_cases || []; + const found_faults = data?.faults.found_faults || []; + const problem_details = data?.problem_details || {}; + if (!problem_details.rest) { return
We are only supporting REST results now. You need to provide rest results.
} @@ -41,28 +32,11 @@ export const TestResults: React.FC = ({ const all_status_codes = related_http_status.map((status) => status.http_status.map((s) => s)).flat(); const unique_status_codes = [...new Set(all_status_codes)].sort((a, b) => a - b); - const current_file = test_files.find((file) => file.name === test_case?.file_path); + const current_file = testFiles.find((file) => file.name === test_case?.file_path); const extractedCode = current_file && test_case ? extractCodeLines(current_file.code, test_case?.start_line, test_case?.end_line) : ""; - const fileExtension = current_file?.name.split('.').pop(); - - const getLanguage = (extension: string | undefined) => { - switch (extension) { - case 'java': - return 'java'; - case 'js': - return 'javascript'; - case 'py': - return 'python'; - case 'ts': - return 'typescript'; - default: - return 'plaintext'; - } - } - return (
@@ -115,10 +89,9 @@ export const TestResults: React.FC = ({ {test_case?.id}
{ - test_case && current_file && (
-                        
+                        
                     
) }