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: 9 additions & 8 deletions src/main/resources/wfc/schemas/report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ properties:
#OPTIONAL
extra:
description: "Extra, optional coverage information, collected by different tools."
type: array
type: [array, "null"]
items:
$ref: "#/$defs/Coverage"

Expand All @@ -75,7 +75,7 @@ $defs:
context:
description: "An optional context for the fault. The same fault type could be manifested in different ways, \
and we use this property to differentiate among them."
type: string
type: [string, "null"]
required: ["code"]
TestFilePath:
description: "A relative path used to unique locate a test suite file."
Expand All @@ -89,16 +89,17 @@ $defs:
testCaseId:
$ref: "#/$defs/TestCaseId"
httpStatus:
description: "As in a test case the same endpoint could be called more than once, here we report all of the
obtained HTTP status codes"
type: array
description: "As in a test case the same endpoint could be called more than once, here we report all of the \
obtained HTTP status codes. If for any reason a call does not return any response (e.g., the TCP connection \
does timeout), then this HTTP status array would be either null or empty."
type: [array,"null"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@omursahin I made this change. but now web-report build fails. can you look into it?

items:
$ref: "#/$defs/HttpStatus"
minItems: 1
minItems: 0
uniqueItems: true
required: ["endpointId","testCaseId","httpStatus"]
HttpStatus:
type: integer
type: [integer,"null"]
minimum: 0
maximum: 599
FoundFault:
Expand Down Expand Up @@ -198,6 +199,6 @@ $defs:
minimum: 0
total:
description: "Optional number of all testing targets for this criterion. For some criteria, this number can be unknown."
type: integer
type: [integer, "null"]
minimum: 0
required: ["name","covered"]
24 changes: 23 additions & 1 deletion web-report/src/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,29 @@ export const AppProvider = ({ children }: AppProviderProps) => {
setInvalidReportErrors(report.error.issues);
return;
}
setData(jsonData);

const result = jsonData?.problemDetails?.rest?.coveredHttpStatus?.map(status => {
const httpStatus = status?.httpStatus ?? [null];
const httpStatusArray = Array.isArray(httpStatus) ? httpStatus : [httpStatus];
const updatedHttpStatus = httpStatusArray.map(code => code === null ? -1 : code);
return {
...status,
httpStatus: updatedHttpStatus
};
}) ?? [];

const updatedJsonData = {
...jsonData,
problemDetails: {
...jsonData.problemDetails,
rest: {
...jsonData.problemDetails.rest,
coveredHttpStatus: result
}
}
} as WebFuzzingCommonsReport;

setData(updatedJsonData);
} 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 main folder.");
Expand Down
4 changes: 2 additions & 2 deletions web-report/src/components/EndpointAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const EndpointAccordion: React.FC<IEndpointAccordionProps> = ({
<div className="flex flex-wrap justify-end gap-2 mr-4">
{sortedStatusCodes.map((code, idx) => (
<Badge key={`_${idx}`} className={`${getColor(code.code, true, false)} font-mono`}>
H{code.code}
{code.code == -1 ? "NO-RESPONSE" : `H${code.code}`}
</Badge>
))}
{sortedFaults.map((code, idx) => (
Expand All @@ -87,7 +87,7 @@ export const EndpointAccordion: React.FC<IEndpointAccordionProps> = ({
setIsFault(false);
}}
className={`${getColor(code.code, true, false)} ${getSelectedStyle(code.code, false)} hover:bg-green-600 cursor-pointer text-white font-mono text-base border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]`}>
{code.code}
{code.code == -1 ? "NO-RESPONSE" : `H${code.code}`}
</Badge>
))
}
Expand Down
4 changes: 2 additions & 2 deletions web-report/src/components/StatusCodeFilterButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function StatusCodeFilterButton({ code, initialState = "inactive", onChan
if(isFault) {
return "bg-red-500"
}

if (code == -1) return "bg-gray-500"
if (code >= 200 && code < 300) return "bg-green-500"
if (code >= 300 && code < 400) return "bg-blue-500"
if (code >= 400 && code < 500) return "bg-orange-500"
Expand Down Expand Up @@ -49,7 +49,7 @@ export function StatusCodeFilterButton({ code, initialState = "inactive", onChan

return (
<Badge className={getStyles()} onClick={toggleState}>
{isFault ? `F${-1 * code}`: `H${code}`}
{isFault ? `F${-1 * code}`: code == -1 ? `NO-RESPONSE`:`H${code}`}
</Badge>
)
}
8 changes: 8 additions & 0 deletions web-report/src/components/StatusCodeFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {useState} from "react"
import {StatusCodeFilterButton} from "./StatusCodeFilterButton"
import {ITransformedReport} from "@/lib/utils.tsx";
import {StatusCodeModal} from "@/components/StatusCodeModal.tsx";

type FilterState = "inactive" | "active" | "removed"

Expand Down Expand Up @@ -38,6 +39,9 @@ export function StatusCodeFilters({data, onFiltersChange}: StatusCodeFiltersProp
onFiltersChange(newFilters)
}

const [isModalOpen, setIsModalOpen] = useState(false)


return (
<div className="mb-6">
<div className="items-center mb-2">
Expand Down Expand Up @@ -68,7 +72,11 @@ export function StatusCodeFilters({data, onFiltersChange}: StatusCodeFiltersProp
</div>
</div>
<div className="text-xs text-gray-500 mt-1">Click to toggle: Default → Active → Removed → Default</div>
<div className="items-center mb-2 flex justify-end">
<div className="text-sm font-mono text-red-500 mt-1 cursor-help hover:text-green-300" onClick={() => setIsModalOpen(true)}>Code Documentation</div>
</div>
<div className="border-t border-black my-2"></div>
<StatusCodeModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} statusCode={-1} />
</div>
)
}
2 changes: 1 addition & 1 deletion web-report/src/components/TestCaseButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function TestCaseButton({ testName, statusCode, onClick, isFault }: TestC
<span className="font-mono text-sm text-left">{testName}</span>
</div>
<div className="flex items-center">
<span className={`font-bold mr-2 ${getColor(statusCode, false, isFault)}`}>{statusCode}</span>
<span className={`font-bold mr-2 ${getColor(statusCode, false, isFault)}`}>{statusCode == -1 ? "NO-RESPONSE" : statusCode}</span>
<ChevronRight
className={`${
isHovered ? "text-blue-600 transform translate-x-1" : "text-gray-400"
Expand Down
23 changes: 19 additions & 4 deletions web-report/src/lib/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

export const getColor = (code: string | number, isBackground: boolean, isFault: boolean) => {
export const getColor = (code: string | number | null | undefined, isBackground: boolean, isFault: boolean) => {
if (isFault) {
return isBackground ? "bg-red-500" : "text-red-500";
}

if(code === null || code === undefined || code === "") {
return isBackground ? "bg-gray-500" : "text-gray-500";
}

if (typeof code === "number") {
return getColorNumber(code, isBackground);
}
Expand Down Expand Up @@ -69,6 +74,7 @@ export const extractCodeLines = (

export const calculateAllStatusCounts = (coveredHttpStatus: CoveredEndpoint[], endpointIds:string[]) => {
const allStatusCounts ={
"NO_RESPONSE": 0,
"2XX": 0,
"3XX": 0,
"4XX": 0,
Expand All @@ -84,6 +90,7 @@ export const calculateAllStatusCounts = (coveredHttpStatus: CoveredEndpoint[], e
const uniqueStatusCodes = [...new Set(allStatusCodes)];

const isContainStatusCode = {
"NO_RESPONSE": false,
"2XX": false,
"3XX": false,
"4XX": false,
Expand All @@ -92,7 +99,10 @@ export const calculateAllStatusCounts = (coveredHttpStatus: CoveredEndpoint[], e

uniqueStatusCodes.map(
(status) => {
if (status >= 200 && status < 300) {
if(status == null) {
isContainStatusCode["NO_RESPONSE"] = true;
}
else if (status >= 200 && status < 300) {
isContainStatusCode["2XX"] = true;
} else if (status >= 300 && status < 400) {
isContainStatusCode["3XX"] = true;
Expand All @@ -103,7 +113,9 @@ export const calculateAllStatusCounts = (coveredHttpStatus: CoveredEndpoint[], e
}
}
)

if(isContainStatusCode["NO_RESPONSE"]){
allStatusCounts["NO_RESPONSE"]++;
}
if (isContainStatusCode["2XX"]) {
allStatusCounts["2XX"]++;
}
Expand Down Expand Up @@ -259,12 +271,15 @@ export const transformWebFuzzingReport = (original: WebFuzzingCommonsReport | nu

const endpointData = endpointMap.get(status.endpointId);

status.httpStatus.forEach(code => {
status.httpStatus?.forEach(code => {
if (!endpointData) {
return;
}
let existingStatus = endpointData.httpStatusCodes.find((s: { code: number; }) => s.code === code);
if (!existingStatus) {
if(code === null || code === undefined) {
code = -1;
}
existingStatus = {code, testCases: []};
endpointData.httpStatusCodes.push(existingStatus);
}
Expand Down
12 changes: 9 additions & 3 deletions web-report/src/pages/TestResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,16 @@ export const TestResults: React.FC<IProps> = ({testCaseName}) => {
const allFaultCodes = relatedFaults.map((fault) =>
fault.faultCategories.map((f) => f.code)).flat();
const uniqueFaultCodes = [...new Set(allFaultCodes)].sort((a, b) => a - b);

const allStatusCodes = relatedHttpStatus.map((status) =>
status.httpStatus.map((s) => s)).flat();
const uniqueStatusCodes = [...new Set(allStatusCodes)].sort((a, b) => a - b);
status.httpStatus?.map((s) => s)).flat();
const uniqueStatusCodes = [...new Set(allStatusCodes)].sort((a, b) => {
const codeA = Number(a);
const codeB = Number(b);
if (isNaN(codeA) || isNaN(codeB)) {
return String(a).localeCompare(String(b));
}
return codeA - codeB;
});
const currentFile = testFiles.find((file) => file.name === testCase?.filePath);


Expand Down
14 changes: 7 additions & 7 deletions web-report/src/types/GeneratedTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type OperationId = string;
* A unique identifier for a test case. It could include its name and file location.
*/
export type TestCaseId = string;
export type HttpStatus = number;
export type HttpStatus = number | null;
/**
* A relative path used to unique locate a test suite file.
*/
Expand Down Expand Up @@ -59,7 +59,7 @@ export interface WebFuzzingCommonsReport {
/**
* Extra, optional coverage information, collected by different tools.
*/
extra?: Coverage[];
extra?: Coverage[] | null;
[k: string]: unknown;
}
export interface Faults {
Expand Down Expand Up @@ -96,7 +96,7 @@ export interface FaultCategoryId {
/**
* An optional context for the fault. The same fault type could be manifested in different ways, and we use this property to differentiate among them.
*/
context?: string;
context?: string | null;
[k: string]: unknown;
}
export interface RESTReport {
Expand All @@ -121,11 +121,11 @@ export interface CoveredEndpoint {
endpointId: OperationId;
testCaseId: TestCaseId;
/**
* As in a test case the same endpoint could be called more than once, here we report all of the obtained HTTP status codes
* As in a test case the same endpoint could be called more than once, here we report all of the obtained HTTP status codes. If for any reason a call does not return any response (e.g., the TCP connection does timeout), then this HTTP status array would be either null or empty.
*
* @minItems 1
* @minItems 0
*/
httpStatus: [HttpStatus, ...HttpStatus[]];
httpStatus: HttpStatus[] | null;
[k: string]: unknown;
}
export interface TestCase {
Expand Down Expand Up @@ -165,6 +165,6 @@ export interface CoverageCriterion {
/**
* Optional number of all testing targets for this criterion. For some criteria, this number can be unknown.
*/
total?: number;
total?: number | null;
[k: string]: unknown;
}
10 changes: 5 additions & 5 deletions web-report/src/types/GeneratedTypesZod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const operationIdSchema = z.string();

export const testCaseIdSchema = z.string();

export const httpStatusSchema = z.number();
export const httpStatusSchema = z.number().nullable();

export const testFilePathSchema = z.string();

Expand All @@ -22,23 +22,23 @@ export const testCaseSchema = z.record(z.unknown()).and(
export const faultCategoryIdSchema = z.record(z.unknown()).and(
z.object({
code: z.number(),
context: z.string().optional(),
context: z.string().optional().nullable(),
}),
);

export const coveredEndpointSchema = z.record(z.unknown()).and(
z.object({
endpointId: operationIdSchema,
testCaseId: testCaseIdSchema,
httpStatus: z.tuple([httpStatusSchema]).rest(httpStatusSchema),
httpStatus: z.array(httpStatusSchema).nullable(),
}),
);

export const coverageCriterionSchema = z.record(z.unknown()).and(
z.object({
name: z.string(),
covered: z.number(),
total: z.number().optional(),
total: z.number().optional().nullable(),
}),
);

Expand Down Expand Up @@ -89,6 +89,6 @@ export const webFuzzingCommonsReportSchema = z.record(z.unknown()).and(
totalTests: z.number(),
testFilePaths: z.array(testFilePathSchema),
testCases: z.array(testCaseSchema),
extra: z.array(coverageSchema).optional(),
extra: z.array(coverageSchema).optional().nullable(),
}),
);