Skip to content

Refactor fault definition #1256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 2, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@ import com.webfuzzing.commons.faults.FaultCategory
*/
class DetectedFault(
val category: FaultCategory,
_context: String
/**
* For a REST API, this would be the endpoint VERB:PATH
*/
val operationId: String,
/**
* For same operation and save fault type, we could detect more than 1 fault.
* We distinguish them based on the context, ie, a discriminating string.
* This does not apply to all kinds of faults, so it is an optional field.
* For example, in WB for HTTP 500, the discriminating context could be based on last executed
* line in the SUT.
*/
_context: String?
) {

//otherwise issues when printing in comments
val context = _context.replace('\n',' ')
val context = _context?.replace('\n',' ')

private val _toString = "Detected ${category.label}. $context}"
private val _toString = "Detected ${category.label} in ${operationId}. Context: $context}"

override fun toString(): String {
return _toString
Expand All @@ -26,14 +37,16 @@ class DetectedFault(
other as DetectedFault

if (category != other.category) return false
if (operationId != other.operationId) return false
if (context != other.context) return false

return true
}

override fun hashCode(): Int {
var result = category.hashCode()
result = 31 * result + context.hashCode()
result = 31 * result + operationId.hashCode()
result = 31 * result + (context?.hashCode() ?: 0)
return result
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
val bugId = idMapper.handleLocalTarget(descriptiveId)
fv.updateTarget(bugId, 1.0, indexOfAction)

result.addFault(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, postfix))
result.addFault(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, name,location5xx))
}
}

Expand Down Expand Up @@ -724,7 +724,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
idMapper.getFaultDescriptiveId(DefinedFaultCategory.SCHEMA_INVALID_RESPONSE, discriminant)
)
fv.updateTarget(scenarioId, 1.0, a.positionAmongMainActions())
rcr.addFault(DetectedFault(DefinedFaultCategory.SCHEMA_INVALID_RESPONSE, discriminant))
rcr.addFault(DetectedFault(DefinedFaultCategory.SCHEMA_INVALID_RESPONSE, a.getName(), it.message))
}
}

Expand Down Expand Up @@ -1088,7 +1088,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {

val ar = actionResults.find { it.sourceLocalId == put.getLocalId() } as RestCallResult?
?: return
ar.addFault(DetectedFault(category, put.getName()))
ar.addFault(DetectedFault(category, put.getName(), null))
}

private fun handleDeleteShouldDelete(
Expand All @@ -1114,7 +1114,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
val delete = individual.seeMainExecutableActions()[res.index]
val ar = actionResults.find { it.sourceLocalId == delete.getLocalId() } as RestCallResult?
?: return
ar.addFault(DetectedFault(category, res.name))
ar.addFault(DetectedFault(category, res.name, null))
}
}

Expand Down Expand Up @@ -1165,7 +1165,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
)
fv.updateTarget(scenarioId, 1.0, it.positionAmongMainActions())
val r = actionResults.find { r -> r.sourceLocalId == it.getLocalId() } as RestCallResult
r.addFault(DetectedFault(ExperimentalFaultCategory.SECURITY_NOT_RECOGNIZED_AUTHENTICATED, it.getName()))
r.addFault(DetectedFault(ExperimentalFaultCategory.SECURITY_NOT_RECOGNIZED_AUTHENTICATED, it.getName(), null))
}
}

Expand Down Expand Up @@ -1193,7 +1193,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
idMapper.getFaultDescriptiveId(ExperimentalFaultCategory.SECURITY_EXISTENCE_LEAKAGE, a.getName())
)
fv.updateTarget(scenarioId, 1.0, index)
r.addFault(DetectedFault(ExperimentalFaultCategory.SECURITY_EXISTENCE_LEAKAGE, a.getName()))
r.addFault(DetectedFault(ExperimentalFaultCategory.SECURITY_EXISTENCE_LEAKAGE, a.getName(), null))
}
}
}
Expand All @@ -1217,7 +1217,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
idMapper.getFaultDescriptiveId(faultCategory, action.getName())
)
fv.updateTarget(scenarioId, 1.0, actionIndex)
result.addFault(DetectedFault(faultCategory, action.getName()))
result.addFault(DetectedFault(faultCategory, action.getName(), null))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class GraphQLActionNamingStrategyTest {

@Test
fun testQueryOnAddCausesInternalServerError() {
val eIndividual = getEvaluatedIndividualWithFaults(GQMethodType.QUERY, singletonList(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items")))
val eIndividual = getEvaluatedIndividualWithFaults(GQMethodType.QUERY, singletonList(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items", null)))
val solution = Solution(singletonList(eIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList())

val namingStrategy = GraphQLActionTestCaseNamingStrategy(solution, pythonFormatter, MAX_NAME_LENGTH)
Expand All @@ -68,7 +68,7 @@ class GraphQLActionNamingStrategyTest {

@Test
fun testActionAndMethodNameAreAddedTogether() {
val eIndividual = getEvaluatedIndividualWithFaults(GQMethodType.QUERY, singletonList(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items")))
val eIndividual = getEvaluatedIndividualWithFaults(GQMethodType.QUERY, singletonList(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items", null)))
val solution = Solution(singletonList(eIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList())

val withActionAndMethodName = GraphQLActionTestCaseNamingStrategy(solution, pythonFormatter, 20).getTestCases()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ open class RestActionNamingStrategyTest {
@Test
fun test500ResponseNamedWithInternalServerError() {
val restAction = getRestCallAction()
val eIndividual = getEvaluatedIndividualWithFaults(restAction, singletonList(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items")), 500)
val eIndividual = getEvaluatedIndividualWithFaults(restAction, singletonList(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items", null)), 500)
val solution = Solution(singletonList(eIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList())

val namingStrategy = RestActionTestCaseNamingStrategy(solution, pythonFormatter, NO_QUERY_PARAMS_IN_NAME, MAX_NAME_LENGTH)
Expand All @@ -207,9 +207,9 @@ open class RestActionNamingStrategyTest {
@Test
fun testResponseNamedWithMultipleFaults() {
val faults = listOf(
DetectedFault(ExperimentalFaultCategory.GQL_ERROR_FIELD, "items"),
DetectedFault(ExperimentalFaultCategory.HTTP_INVALID_LOCATION, "items"),
DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items"))
DetectedFault(ExperimentalFaultCategory.GQL_ERROR_FIELD, "items", null),
DetectedFault(ExperimentalFaultCategory.HTTP_INVALID_LOCATION, "items", null),
DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, "items", null))
val restAction = getRestCallAction()
val eIndividual = getEvaluatedIndividualWithFaults(restAction, faults, 500)
val solution = Solution(singletonList(eIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList())
Expand Down