Skip to content

Commit 34f8c69

Browse files
committed
refactor
1 parent dd85680 commit 34f8c69

1 file changed

Lines changed: 56 additions & 87 deletions

File tree

  • core/src/main/kotlin/org/evomaster/core/problem/rest/service

core/src/main/kotlin/org/evomaster/core/problem/rest/service/SecurityRest.kt

Lines changed: 56 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -922,100 +922,31 @@ class SecurityRest {
922922
var copy = sliced.copy() as RestIndividual
923923
val actionCopy = copy.seeMainExecutableActions().last() as RestCallAction
924924

925-
// Try to inject XSS payload into string fields
926-
var payloadInjected = false
925+
val genes = GeneUtils.getAllStringFields(actionCopy.parameters)
926+
.filter { it.staticCheckIfImpactPhenotype() }
927927

928-
// Attempt to inject payload into parameters
929-
actionCopy.parameters.forEach { param ->
930-
931-
// Skip if PathParam and payload contains "/" (would break URL structure)
932-
// TODO this in theory should be fine if properly escape entries in RestPath
933-
if(param is PathParam && payload.contains("/")){
934-
return@forEach
935-
}
928+
if(genes.isEmpty()){
929+
continue
930+
}
936931

937-
val genes = param.primaryGene().flatView()
938-
939-
genes.forEach { gene ->
940-
if(gene !is StringGene) return@forEach
941-
942-
try {
943-
// Check if constraints (min-max length) allow the payload
944-
if(payload.length >= gene.minLength && payload.length <= gene.maxLength){
945-
// Check if payload contains any invalid chars
946-
val hasInvalidChars = gene.invalidChars.any { payload.contains(it) }
947-
if(!hasInvalidChars){
948-
// Set the XSS payload value
949-
gene.value = payload
950-
payloadInjected = true
951-
}
952-
}
953-
} catch(e: Exception){
954-
// Constraints might not allow the payload
955-
log.warn("Failed to inject XSS payload into ${gene.name}: ${e.message}")
956-
}
957-
}
932+
val anySuccess = genes.any { gene ->
933+
gene.setFromStringValue(payload)
958934
}
959935

960-
if(!payloadInjected){
936+
if(!anySuccess){
961937
continue
962938
}
963939

964-
965940
// Try to add a linked GET operation for stored XSS detection
941+
//TODO to properly handle POST, we need first to finish the work on CallGraphService
966942
if(action.verb == HttpVerb.POST || action.verb == HttpVerb.PUT || action.verb == HttpVerb.PATCH){
967-
968-
val getIndividuals = RestIndividualSelectorUtils.findIndividuals(
969-
individualsInSolution,
970-
HttpVerb.GET,
971-
actionCopy.path,
972-
statusGroup = StatusGroup.G_2xx
973-
)
974-
975-
if(getIndividuals.isNotEmpty()){
976-
val getInd = getIndividuals.first()
977-
val getActionIndex = RestIndividualSelectorUtils.findIndexOfAction(
978-
getInd,
979-
HttpVerb.GET,
980-
actionCopy.path,
981-
statusGroup = StatusGroup.G_2xx
982-
)
983-
984-
if(getActionIndex >= 0){
985-
val second = RestIndividualBuilder.sliceAllCallsInIndividualAfterAction(getInd.individual, getActionIndex)
986-
987-
val getAction = second.seeMainExecutableActions().last() as RestCallAction
988-
getAction.resetLocalIdRecursively()
989-
990-
getAction.parameters.filter { it is BodyParam || it is QueryParam || it is PathParam }.forEach { param ->
991-
val genes = param.primaryGene().flatView()
992-
993-
genes.forEach { gene ->
994-
if(gene !is StringGene) return@forEach
995-
996-
try {
997-
// Check if constraints (min-max length) allow the payload
998-
if(payload.length >= gene.minLength && payload.length <= gene.maxLength){
999-
// Check if payload contains any invalid chars
1000-
val hasInvalidChars = gene.invalidChars.any { payload.contains(it) }
1001-
if(!hasInvalidChars){
1002-
// Set the XSS payload value
1003-
gene.value = payload
1004-
}
1005-
}
1006-
} catch(e: Exception){
1007-
// Constraints might not allow the payload
1008-
log.warn("Failed to inject XSS payload into GET ${gene.name}: ${e.message}")
1009-
}
1010-
}
1011-
}
1012-
copy = RestIndividualBuilder.merge(copy, second)
1013-
1014-
}
1015-
}
943+
copy = tryAttachLinkedGetForStoredXSS(
944+
ind = copy,
945+
path = actionCopy.path,
946+
payload = payload
947+
) ?: copy
1016948
}
1017949

1018-
1019950
copy.modifySampleType(SampleType.SECURITY)
1020951
copy.ensureFlattenedStructure()
1021952

@@ -1029,16 +960,54 @@ class SecurityRest {
1029960
val faultsCategories = DetectedFaultUtils.getDetectedFaultCategories(evaluatedIndividual)
1030961

1031962
if(DefinedFaultCategory.XSS in faultsCategories){
1032-
1033-
val added = archive.addIfNeeded(evaluatedIndividual)
1034-
assert(added)
963+
archive.addIfNeeded(evaluatedIndividual)
1035964
continue@mainloop
1036965
}
1037-
1038966
}
1039967
}
1040968
}
1041969

970+
private fun tryAttachLinkedGetForStoredXSS(
971+
ind: RestIndividual,
972+
path: RestPath,
973+
payload: String
974+
): RestIndividual? {
975+
976+
val getIndividuals = RestIndividualSelectorUtils.findIndividuals(
977+
individualsInSolution,
978+
HttpVerb.GET,
979+
path,
980+
statusGroup = StatusGroup.G_2xx
981+
)
982+
if (getIndividuals.isEmpty()) return null
983+
984+
val getInd = getIndividuals.first()
985+
val getActionIndex = RestIndividualSelectorUtils.findIndexOfAction(
986+
getInd,
987+
HttpVerb.GET,
988+
path,
989+
statusGroup = StatusGroup.G_2xx
990+
)
991+
if (getActionIndex < 0) return null
992+
993+
val second = RestIndividualBuilder.sliceAllCallsInIndividualAfterAction(
994+
getInd.individual,
995+
getActionIndex
996+
)
997+
998+
val getAction = second.seeMainExecutableActions().last() as RestCallAction
999+
getAction.resetLocalIdRecursively()
1000+
1001+
val genes = GeneUtils.getAllStringFields(getAction.parameters)
1002+
.filter { it.staticCheckIfImpactPhenotype() }
1003+
if (genes.isEmpty()) return null
1004+
1005+
val anySuccess = genes.any { gene -> gene.setFromStringValue(payload) }
1006+
if (!anySuccess) return null
1007+
1008+
return RestIndividualBuilder.merge(ind, second)
1009+
}
1010+
10421011
/**
10431012
* @return a test where last action is for given path and verb returning 403.
10441013
* null if failed.

0 commit comments

Comments
 (0)