@@ -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