Skip to content

Commit 6e4d018

Browse files
committed
Merge branch 'master' into ssrf-path-param
2 parents b23c991 + f1c71e6 commit 6e4d018

File tree

19 files changed

+754
-19
lines changed

19 files changed

+754
-19
lines changed

core/src/main/kotlin/org/evomaster/core/EMConfig.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.evomaster.core
22

3+
import com.webfuzzing.commons.faults.DefinedFaultCategory
4+
import com.webfuzzing.commons.faults.FaultCategory
35
import joptsimple.*
46
import org.evomaster.client.java.controller.api.ControllerConstants
57
import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto
@@ -13,6 +15,7 @@ import org.evomaster.core.logging.LoggingUtil
1315
import org.evomaster.core.output.OutputFormat
1416
import org.evomaster.core.output.naming.NamingStrategy
1517
import org.evomaster.core.output.sorting.SortingStrategy
18+
import org.evomaster.core.problem.enterprise.ExperimentalFaultCategory
1619
import org.evomaster.core.search.impact.impactinfocollection.GeneMutationSelectionMethod
1720
import org.evomaster.core.search.service.IdMapper
1821
import org.slf4j.LoggerFactory
@@ -48,7 +51,7 @@ class EMConfig {
4851
private const val timeRegex = "(\\s*)((?=(\\S+))(\\d+h)?(\\d+m)?(\\d+s)?)(\\s*)"
4952

5053
private const val headerRegex = "(.+:.+)|(^$)"
51-
54+
private const val faultCodeRegex = "(\\s*\\d{3}\\s*(,\\s*\\d{3}\\s*)*)?"
5255
private const val targetSeparator = ";"
5356
private const val targetNone = "\\b(None|NONE|none)\\b"
5457
private const val targetPrefix = "\\b(Class|CLASS|class|Line|LINE|line|Branch|BRANCH|branch|MethodReplacement|METHODREPLACEMENT|method[r|R]eplacement|Success_Call|SUCCESS_CALL|success_[c|C]all|Local|LOCAL|local|PotentialFault|POTENTIALFAULT|potential[f|F]ault)\\b"
@@ -2521,6 +2524,12 @@ class EMConfig {
25212524
@Cfg("To apply SSRF detection as part of security testing.")
25222525
var ssrf = false
25232526

2527+
@Regex(faultCodeRegex)
2528+
@Cfg("Disable oracles. Provide a comma-separated list of codes to disable. " +
2529+
"By default, all oracles are enabled."
2530+
)
2531+
var disabledOracleCodes = ""
2532+
25242533
enum class VulnerableInputClassificationStrategy {
25252534
/**
25262535
* Uses the manual methods to select the vulnerable inputs.
@@ -2797,4 +2806,30 @@ class EMConfig {
27972806
fun getTagFilters() = endpointTagFilter?.split(",")?.map { it.trim() } ?: listOf()
27982807

27992808
fun isEnabledAIModelForResponseClassification() = aiModelForResponseClassification != AIResponseClassifierModel.NONE
2809+
2810+
private var disabledOracleCodesList: List<FaultCategory>? = null
2811+
2812+
fun getDisabledOracleCodesList(): List<FaultCategory> {
2813+
if (disabledOracleCodesList == null) {
2814+
disabledOracleCodesList = disabledOracleCodes
2815+
.split(",")
2816+
.mapNotNull { it.trim().takeIf { s -> s.isNotEmpty() } }
2817+
.map { str ->
2818+
val code = str.toIntOrNull()
2819+
?: throw ConfigProblemException("Invalid number: $str")
2820+
2821+
val allCategories = DefinedFaultCategory.values().asList() +
2822+
ExperimentalFaultCategory.values()
2823+
2824+
allCategories.firstOrNull { it.code == code }
2825+
?: throw ConfigProblemException(
2826+
"Invalid fault code: $code" +
2827+
" All available codes are: \n" +
2828+
allCategories.joinToString("\n") { "${it.code} (${it.name})" }
2829+
)
2830+
}
2831+
}
2832+
return disabledOracleCodesList!!
2833+
}
2834+
28002835
}

core/src/main/kotlin/org/evomaster/core/Main.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.google.inject.Injector
44
import com.google.inject.Key
55
import com.google.inject.TypeLiteral
66
import com.netflix.governator.guice.LifecycleInjector
7+
import com.webfuzzing.commons.faults.DefinedFaultCategory
78
import org.evomaster.client.java.controller.api.dto.ControllerInfoDto
89
import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming
910
import org.evomaster.core.AnsiColor.Companion.inBlue
@@ -419,13 +420,18 @@ class Main {
419420
val securityRest = injector.getInstance(SecurityRest::class.java)
420421
val solution = securityRest.applySecurityPhase()
421422

422-
if (config.ssrf) {
423+
if (config.ssrf && DefinedFaultCategory.SSRF !in config.getDisabledOracleCodesList()) {
423424
LoggingUtil.getInfoLogger().info("Starting to apply SSRF detection.")
424425

425426
val ssrfAnalyser = injector.getInstance(SSRFAnalyser::class.java)
426427
ssrfAnalyser.apply()
427428
} else {
428-
solution
429+
if(DefinedFaultCategory.SSRF in config.getDisabledOracleCodesList())
430+
{
431+
LoggingUtil.uniqueUserInfo("Skipping security test for SSRF detection as disabled in configuration")
432+
}
433+
434+
return solution
429435
}
430436
}
431437

core/src/main/kotlin/org/evomaster/core/problem/enterprise/DetectedFaultUtils.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.evomaster.core.problem.enterprise
33
import com.webfuzzing.commons.faults.FaultCategory
44
import org.evomaster.core.search.EvaluatedIndividual
55
import org.evomaster.core.search.Solution
6+
import org.evomaster.core.search.action.ActionResult
67

78
object DetectedFaultUtils {
89

@@ -37,4 +38,15 @@ object DetectedFaultUtils {
3738
.toSet()
3839
}
3940

41+
fun verifyExcludedCategories(ei: EvaluatedIndividual<*>, excludedCategories: List<FaultCategory>) : Boolean {
42+
43+
// if not an enterprise individual, then no need to check
44+
if(ei.individual !is EnterpriseIndividual){
45+
return true
46+
}
47+
48+
val detected = getDetectedFaultCategories(ei)
49+
return excludedCategories.intersect(detected).isEmpty()
50+
}
51+
4052
}

core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.evomaster.core.problem.graphql.service
22

33
import com.webfuzzing.commons.faults.DefinedFaultCategory
4-
import com.webfuzzing.commons.faults.FaultCategory
54
import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto
65
import org.evomaster.core.Lazy
76
import org.evomaster.core.sql.SqlAction
@@ -223,7 +222,7 @@ open class GraphQLFitness : HttpWsFitness<GraphQLIndividual>() {
223222
fv.updateTarget(faultId, 1.0, indexOfAction)
224223
}
225224

226-
if (status == 500) {
225+
if (status == 500 && DefinedFaultCategory.HTTP_STATUS_500 !in config.getDisabledOracleCodesList()) {
227226
Lazy.assert {
228227
location5xx != null || config.blackBox
229228
}
@@ -238,6 +237,8 @@ open class GraphQLFitness : HttpWsFitness<GraphQLIndividual>() {
238237
val bugId = idMapper.handleLocalTarget(descriptiveId)
239238
fv.updateTarget(bugId, 1.0, indexOfAction)
240239

240+
} else if (DefinedFaultCategory.HTTP_STATUS_500 !in config.getDisabledOracleCodesList()) {
241+
LoggingUtil.uniqueUserInfo("Found endpoints with status code 500. But those are not marked as fault, as HTTP 500 fault detection has been disabled.")
241242
}
242243
}
243244

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

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest.service
22

33
import com.google.inject.Inject
44
import com.webfuzzing.commons.faults.DefinedFaultCategory
5-
import com.webfuzzing.commons.faults.FaultCategory
5+
import org.evomaster.core.EMConfig
66
import javax.annotation.PostConstruct
77

88
import org.evomaster.core.logging.LoggingUtil
@@ -64,6 +64,9 @@ class SecurityRest {
6464
@Inject
6565
private lateinit var builder: RestIndividualBuilder
6666

67+
@Inject
68+
protected lateinit var config: EMConfig
69+
6770
/**
6871
* All actions that can be defined from the OpenAPI schema
6972
*/
@@ -250,18 +253,35 @@ class SecurityRest {
250253

251254
private fun accessControlBasedOnRESTGuidelines() {
252255

253-
// quite a few rules here that can be defined
254-
handleForbiddenOperationButOKOthers(HttpVerb.DELETE)
255-
handleForbiddenOperationButOKOthers(HttpVerb.PUT)
256-
handleForbiddenOperationButOKOthers(HttpVerb.PATCH)
256+
if(config.getDisabledOracleCodesList().contains(DefinedFaultCategory.SECURITY_WRONG_AUTHORIZATION)){
257+
LoggingUtil.uniqueUserInfo("Skipping security test for forbidden but ok others as disabled in configuration")
258+
} else {
259+
// quite a few rules here that can be defined
260+
handleForbiddenOperationButOKOthers(HttpVerb.DELETE)
261+
handleForbiddenOperationButOKOthers(HttpVerb.PUT)
262+
handleForbiddenOperationButOKOthers(HttpVerb.PATCH)
263+
}
257264

258-
// getting 404 instead of 403
259-
handleExistenceLeakage()
265+
if(config.getDisabledOracleCodesList().contains(DefinedFaultCategory.SECURITY_EXISTENCE_LEAKAGE)){
266+
LoggingUtil.uniqueUserInfo("Skipping security test for existence leakage as disabled in configuration")
267+
} else {
268+
// getting 404 instead of 403
269+
handleExistenceLeakage()
270+
}
260271

261-
//authenticated, but wrongly getting 401 (eg instead of 403)
262-
handleNotRecognizedAuthenticated()
272+
if(config.getDisabledOracleCodesList().contains(DefinedFaultCategory.SECURITY_NOT_RECOGNIZED_AUTHENTICATED)){
273+
LoggingUtil.uniqueUserInfo("Skipping security test for not recognized authenticated as disabled in configuration")
274+
} else {
275+
//authenticated, but wrongly getting 401 (eg instead of 403)
276+
handleNotRecognizedAuthenticated()
277+
}
278+
279+
if(config.getDisabledOracleCodesList().contains(ExperimentalFaultCategory.SECURITY_FORGOTTEN_AUTHENTICATION)) {
280+
LoggingUtil.uniqueUserInfo("Skipping experimental security test for forgotten authentication as disabled in configuration")
281+
} else {
282+
handleForgottenAuthentication()
283+
}
263284

264-
handleForgottenAuthentication()
265285
//TODO other rules. See FaultCategory
266286
//etc.
267287
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
543543
fv.updateTarget(faultId, 1.0, indexOfAction)
544544
}
545545

546-
if (status == 500) {
546+
if (status == 500 && DefinedFaultCategory.HTTP_STATUS_500 !in config.getDisabledOracleCodesList()) {
547547
/*
548548
500 codes "might" be bugs. To distinguish between different bugs
549549
that crash the same endpoint, we need to know what was the last
@@ -560,6 +560,8 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
560560
fv.updateTarget(bugId, 1.0, indexOfAction)
561561

562562
result.addFault(DetectedFault(DefinedFaultCategory.HTTP_STATUS_500, name,location5xx))
563+
} else if (DefinedFaultCategory.HTTP_STATUS_500 !in config.getDisabledOracleCodesList()) {
564+
LoggingUtil.uniqueUserInfo("Found endpoints with status code 500. But those are not marked as fault, as HTTP 500 fault detection has been disabled.")
563565
}
564566
}
565567

@@ -729,15 +731,19 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
729731
}
730732
}
731733

732-
handleSchemaOracles(a, rcr, fv)
734+
if(DefinedFaultCategory.SCHEMA_INVALID_RESPONSE !in config.getDisabledOracleCodesList()){
735+
handleSchemaOracles(a, rcr, fv)
736+
} else {
737+
LoggingUtil.uniqueUserInfo("Schema oracles disabled via configuration")
738+
}
733739

734740
val handledSavedLocation = handleSaveLocation(a, rcr, chainState)
735741

736742
if(config.isEnabledAIModelForResponseClassification()) {
737743
responseClassifier.updateModel(a, rcr)
738744
}
739745

740-
if (config.security && config.ssrf) {
746+
if (config.security && config.ssrf && DefinedFaultCategory.SSRF !in config.getDisabledOracleCodesList()) {
741747
if (ssrfAnalyser.anyCallsMadeToHTTPVerifier(a)) {
742748
rcr.setVulnerableForSSRF(true)
743749
}
@@ -1116,7 +1122,7 @@ abstract class AbstractRestFitness : HttpWsFitness<RestIndividual>() {
11161122
analyzeSecurityProperties(individual,actionResults,fv)
11171123
}
11181124

1119-
if (config.ssrf) {
1125+
if (config.ssrf && DefinedFaultCategory.SSRF !in config.getDisabledOracleCodesList()) {
11201126
handleSsrfFaults(individual, actionResults, fv)
11211127
}
11221128

core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package org.evomaster.core.search.service
22

33
import com.google.inject.Inject
4+
import com.webfuzzing.commons.faults.FaultCategory
45
import org.evomaster.core.EMConfig
6+
import org.evomaster.core.Lazy
7+
import org.evomaster.core.problem.enterprise.DetectedFaultUtils
58
import org.evomaster.core.search.EvaluatedIndividual
69
import org.evomaster.core.search.Individual
710
import org.evomaster.core.search.service.monitor.SearchProcessMonitor
@@ -97,6 +100,13 @@ abstract class FitnessFunction<T> where T : Individual {
97100
// ei?.updateInitImpactAfterDoCalculateCoverage(calculatedBefore, null, config)
98101
// }
99102

103+
// check that excluded fault categories are not present
104+
Lazy.assert{
105+
DetectedFaultUtils.verifyExcludedCategories(ei as EvaluatedIndividual<Individual>,
106+
config.getDisabledOracleCodesList() as List<FaultCategory>
107+
)
108+
}
109+
100110
return ei
101111
}
102112

docs/options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ There are 3 types of options:
8585
|`customNaming`| __Boolean__. Enable custom naming and sorting criteria. *Default value*: `true`.|
8686
|`d`| __Double__. When weight-based mutation rate is enabled, specify a percentage of calculating mutation rate based on a number of candidate genes to mutate. For instance, d = 1.0 means that the mutation rate fully depends on a number of candidate genes to mutate, and d = 0.0 means that the mutation rate fully depends on weights of candidates genes to mutate. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.8`.|
8787
|`dependencyFile`| __String__. Specify a file that saves derived dependencies. *DEBUG option*. *Default value*: `dependencies.csv`.|
88+
|`disabledOracleCodes`| __String__. Disable oracles. Provide a comma-separated list of codes to disable. By default, all oracles are enabled. *Constraints*: `regex (\s*\d{3}\s*(,\s*\d{3}\s*)*)?`. *Default value*: `""`.|
8889
|`doCollectImpact`| __Boolean__. Specify whether to collect impact info that provides an option to enable of collecting impact info when archive-based gene selection is disable. *DEBUG option*. *Default value*: `false`.|
8990
|`doesApplyNameMatching`| __Boolean__. Whether to apply text/name analysis to derive relationships between name entities, e.g., a resource identifier with a name of table. *Default value*: `true`.|
9091
|`e_u1f984`| __Boolean__. QWN0aXZhdGUgdGhlIFVuaWNvcm4gTW9kZQ==. *Default value*: `false`.|

0 commit comments

Comments
 (0)