Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into base64
Browse files Browse the repository at this point in the history
  • Loading branch information
jgaleotti committed Oct 20, 2023
2 parents 586c6a0 + b005199 commit c02d5f6
Show file tree
Hide file tree
Showing 22 changed files with 1,781 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.regex.Pattern;

/**
* Heuristics calculation for Java Beans, when dealing with javax.validation constraints
Expand All @@ -32,6 +31,7 @@ always get higher than this value (this can be achieved by using it as a base).
private static final String PREFIX_HIBERNATE = "org.hibernate.validator.constraints.";
private static final String PREFIX_JIRUKTA = "cz.jirutka.validator.collection.constraints.";

static final String EMAIL_REGEX_PATTERN = "^[A-Za-z\\+\\._-]{2,}@[A-Za-z]+(\\.[A-Za-z]{2,}+)+$";
/**
*
* @param validator A Object reference to a javax.validation.Validator instance.
Expand Down Expand Up @@ -122,7 +122,6 @@ private static double computeHeuristicToSolveFailedConstraint(Object violation)
DecimalMax
DecimalMin
Digits
Email
Future
FutureOrPresent
Past
Expand Down Expand Up @@ -213,21 +212,31 @@ private static double computeHeuristicToSolveFailedConstraint(Object violation)
return defaultFailed;
}

if (annotationType.endsWith(".Pattern")) {
if (annotationType.endsWith(".Pattern")
|| annotationType.endsWith(".Email")) {
/*
Quite expensive to handle, see RegexDistanceUtils.
so, for now, we just ensure we handle taint analysis for this
*/
assert invalidValue != null; // otherwise would had been valid
String value = invalidValue.toString();
if (ExecutionTracer.isTaintInput(value)) {
final String pattern;
if (annotationType.endsWith(".Pattern")) {
pattern = attributes.get("regexp").toString();
} else {
assert(annotationType.endsWith(".Email"));
pattern = EMAIL_REGEX_PATTERN;
}

ExecutionTracer.addStringSpecialization(value,
new StringSpecializationInfo(StringSpecialization.REGEX_WHOLE,
attributes.get("regexp").toString()));
pattern));
}

return defaultFailed;
}

}

SimpleLogger.warn("Not able to handle constrain type: " + annotationType);
Expand Down Expand Up @@ -370,9 +379,9 @@ private static Integer computeSize(Object invalidValue){
if(invalidValue instanceof CharSequence){
size = ((CharSequence) invalidValue).length();
} else if(invalidValue instanceof Collection){
size = ((Collection) invalidValue).size();
size = ((Collection<?>) invalidValue).size();
} else if(invalidValue instanceof Map){
size = ((Map)invalidValue).size();
size = ((Map<?,?>)invalidValue).size();
} else if(invalidValue.getClass().isArray()){
size = Array.getLength(invalidValue);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.evomaster.client.java.instrumentation.heuristic;

import org.evomaster.client.java.instrumentation.coverage.methodreplacement.RegexDistanceUtils;
import org.evomaster.client.java.instrumentation.heuristic.validator.javax.*;
import org.evomaster.client.java.instrumentation.shared.TaintInputName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -234,6 +235,18 @@ public void testHeuristicForString() {
assertTrue(t17.getOfTrue() > t16.getOfTrue());

assertTrue(t17.isTrue());

bean.m = " "; //@Email
Truthness t18 = ValidatorHeuristics.computeTruthness(validator, bean);
assertFalse(t18.isTrue());

bean.m = "r@g.com"; //@Email
Truthness t19 = ValidatorHeuristics.computeTruthness(validator, bean);
assertEquals(t19.getOfTrue(), t17.getOfTrue(), 0.00001); // null and valid email are the same
assertTrue(t19.getOfTrue() > t18.getOfTrue());

assertTrue(t19.isTrue());

}


Expand Down Expand Up @@ -385,7 +398,7 @@ public void testCollectionBeanMultiViolations() {


@Test
public void testPattern(){
public void testPattern() {

PatternBean bean = new PatternBean();

Expand All @@ -403,4 +416,81 @@ public void testPattern(){
bean.foo = TaintInputName.getTaintName(0);
ValidatorHeuristics.computeTruthness(validator, bean); //should not crash
}

@Test
public void testValidEmailRegexDistance() {
assertEquals(0, RegexDistanceUtils.getStandardDistance("someaddress@somedomain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN), 0.0);

assertEquals(0, RegexDistanceUtils.getStandardDistance("someaddress@subdomain.somedomain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN), 0.0);

assertEquals(0, RegexDistanceUtils.getStandardDistance("firstname.lastname@subdomain.somedomain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN), 0.0);

assertEquals(0, RegexDistanceUtils.getStandardDistance("firstname_lastname@subdomain.somedomain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN), 0.0);

assertEquals(0, RegexDistanceUtils.getStandardDistance("firstname-lastname@subdomain.somedomain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN), 0.0);

assertEquals(0, RegexDistanceUtils.getStandardDistance("firstname+lastname@subdomain.somedomain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN), 0.0);

}

@Test
public void testInvalidEmailRegexDistance() {

// Missing "@" symbol:
assertTrue(RegexDistanceUtils.getStandardDistance("johndoe.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("examplemail",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);

// Missing domain name:
assertTrue(RegexDistanceUtils.getStandardDistance("user@",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user@.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user@.net",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);

// Multiple "@" symbols:
assertTrue(RegexDistanceUtils.getStandardDistance("user@domain@company.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("john@doe@gmail.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);

// Invalid characters
assertTrue(RegexDistanceUtils.getStandardDistance("user$@domain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user#@domain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user!@domain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);

assertTrue(RegexDistanceUtils.getStandardDistance("user@domain$.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user#example.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user&domain.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);

// Spaces
assertTrue(RegexDistanceUtils.getStandardDistance("user name@example.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user@ example.com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user@example .com",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);

// Invalid top-level domain (TLD)
assertTrue(RegexDistanceUtils.getStandardDistance("user@example.c0m",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);
assertTrue(RegexDistanceUtils.getStandardDistance("user@example.c",
ValidatorHeuristics.EMAIL_REGEX_PATTERN) > 0);


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ public class StringBean {

@Size(min = 1, max = 3)
public Map<String,String> l;

@Email
public String m;

}
3 changes: 2 additions & 1 deletion core/src/main/kotlin/org/evomaster/core/EMConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,8 @@ class EMConfig {
@Important(3.2)
@Url
@Cfg("When in black-box mode for REST APIs, specify the URL of where the OpenAPI/Swagger schema can be downloaded from." +
" If the schema is on the local machine, you can use a URL starting with 'file://'")
" If the schema is on the local machine, you can use a URL starting with 'file://'." +
" If the given URL is neither starting with 'file' nor 'http', then it will be treated as a local file path.")
var bbSwaggerUrl: String = ""

@Important(3.5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ abstract class ApiWsIndividual (
*/
children: MutableList<out ActionComponent>,
childTypeVerifier: (Class<*>) -> Boolean,
groups : GroupsOfChildren<StructuralElement> = getEnterpriseTopGroups(children, children.size, 0)
groups : GroupsOfChildren<StructuralElement> = getEnterpriseTopGroups(children, children.size, 0, 0, 0)
): EnterpriseIndividual(sampleType, trackOperator, index, children, childTypeVerifier, groups){


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.evomaster.core.sql.SqlActionUtils
import org.evomaster.core.mongo.MongoDbAction
import org.evomaster.core.problem.api.ApiWsIndividual
import org.evomaster.core.problem.externalservice.ApiExternalServiceAction
import org.evomaster.core.problem.externalservice.DnsAction
import org.evomaster.core.search.*
import org.evomaster.core.search.gene.utils.GeneUtils
import org.evomaster.core.search.service.Randomness
Expand Down Expand Up @@ -46,7 +47,10 @@ abstract class EnterpriseIndividual(
*/
children: MutableList<out ActionComponent>,
childTypeVerifier: (Class<*>) -> Boolean,
groups : GroupsOfChildren<StructuralElement> = getEnterpriseTopGroups(children,children.size,0)
/**
* if no group definition is specified, then it is assumed that all action are for the MAIN group
*/
groups : GroupsOfChildren<StructuralElement> = getEnterpriseTopGroups(children,children.size,0, 0, 0)
) : Individual(
trackOperator,
index,
Expand All @@ -59,52 +63,70 @@ abstract class EnterpriseIndividual(

/**
* Return group definition for the given children.
* The first [sizeDb] are assumed to be database actions, followed by [sizeMain] main actions
* The first [sizeSQL] are assumed to be database actions, followed by [sizeMain] main actions
*/
fun getEnterpriseTopGroups(
children: List<ActionComponent>,
sizeMain: Int,
sizeDb: Int
sizeSQL: Int,
sizeMongo: Int,
sizeDNS: Int
) : GroupsOfChildren<StructuralElement>{

if(children.size != sizeDb + sizeMain){
if(children.size != sizeSQL +sizeMongo + sizeDNS + sizeMain){
throw IllegalArgumentException("Group size mismatch. Expected a total of ${children.size}, but" +
" got main=$sizeMain and db=$sizeDb")
" got main=$sizeMain, sql=$sizeSQL, mongo=$sizeMongo, dns=$sizeDNS")
}
if(sizeSQL < 0){
throw IllegalArgumentException("Negative size for sizeSQL: $sizeSQL")
}
if(sizeMongo < 0){
throw IllegalArgumentException("Negative size for sizeMongo: $sizeMain")
}
if(sizeDb < 0){
throw IllegalArgumentException("Negative size for sizeDb: $sizeDb")
if(sizeDNS < 0){
throw IllegalArgumentException("Negative size for sizeDNS: $sizeMain")
}
if(sizeMain < 0){
throw IllegalArgumentException("Negative size for sizeMain: $sizeMain")
}

//TODO in future ll need to refactor to handle multiple databases and NoSQL ones
//CHANGE: This is momentary. Needs refactor to handle multiple databases
/*
TODO in future ll need to refactor to handle multiple databases, possibly handled with
one action group per database...
but is it really common that a SUT directly access several databases of same kind?
*/

val startIndexSQL = children.indexOfFirst { a -> a is SqlAction }
val endIndexSQL = children.indexOfLast { a -> a is SqlAction }
val startIndexMongo = children.indexOfFirst { a -> a is MongoDbAction }
val endIndexMongo = children.indexOfLast { a -> a is MongoDbAction }

val db = ChildGroup<StructuralElement>(GroupsOfChildren.INITIALIZATION_SQL,{e -> e is ActionComponent && e.flatten().all { a -> a is SqlAction }},
if(sizeDb==0) -1 else startIndexSQL , if(sizeDb==0) -1 else endIndexSQL
if(sizeSQL==0) -1 else startIndexSQL , if(sizeSQL==0) -1 else endIndexSQL
)

val startIndexMongo = children.indexOfFirst { a -> a is MongoDbAction }
val endIndexMongo = children.indexOfLast { a -> a is MongoDbAction }
val mongodb = ChildGroup<StructuralElement>(GroupsOfChildren.INITIALIZATION_MONGO,{e -> e is ActionComponent && e.flatten().all { a -> a is MongoDbAction }},
if(sizeDb==0) -1 else startIndexMongo , if(sizeDb==0) -1 else endIndexMongo
if(sizeMongo==0) -1 else startIndexMongo , if(sizeMongo==0) -1 else endIndexMongo
)

val startIndexDns = children.indexOfFirst { a -> a is DnsAction }
val endIndexDns = children.indexOfLast { a -> a is DnsAction }
val dns = ChildGroup<StructuralElement>(GroupsOfChildren.INITIALIZATION_DNS,{e -> e is ActionComponent && e.flatten().all { a -> a is DnsAction }},
if(sizeDNS==0) -1 else startIndexDns , if(sizeDNS==0) -1 else endIndexDns
)

val main = ChildGroup<StructuralElement>(GroupsOfChildren.MAIN, {e -> e !is SqlAction && e !is ApiExternalServiceAction },
if(sizeMain == 0) -1 else sizeDb, if(sizeMain == 0) -1 else sizeDb + sizeMain - 1)
val initSize = sizeSQL+sizeMongo+sizeDNS

val main = ChildGroup<StructuralElement>(GroupsOfChildren.MAIN, {e -> e !is EnvironmentAction },
if(sizeMain == 0) -1 else initSize, if(sizeMain == 0) -1 else initSize + sizeMain - 1)

return GroupsOfChildren(children, listOf(db, mongodb, main))
return GroupsOfChildren(children, listOf(db, mongodb, dns, main))
}
}

/**
* a list of db actions for its Initialization
*/
private val dbInitialization: List<SqlAction>
private val sqlInitialization: List<SqlAction>
get() {
return groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_SQL)
.flatMap { (it as ActionComponent).flatten() }
Expand All @@ -117,9 +139,10 @@ abstract class EnterpriseIndividual(
ActionFilter.MAIN_EXECUTABLE -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN)
.flatMap { (it as ActionComponent).flatten() }
.filter { it !is SqlAction && it !is ApiExternalServiceAction }
ActionFilter.INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_SQL)
.flatMap { (it as ActionComponent).flatten() } + groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_MONGO)
.flatMap { (it as ActionComponent).flatten() }
ActionFilter.INIT ->
groupsView()!!
.getAllInGroup(GroupsOfChildren.INITIALIZATION_SQL).flatMap { (it as ActionComponent).flatten() } + groupsView()!!
.getAllInGroup(GroupsOfChildren.INITIALIZATION_MONGO).flatMap { (it as ActionComponent).flatten()}
// WARNING: this can still return DbAction, MongoDbAction and External ones...
ActionFilter.NO_INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN).flatMap { (it as ActionComponent).flatten() }
ActionFilter.ONLY_SQL -> seeAllActions().filterIsInstance<SqlAction>()
Expand Down Expand Up @@ -214,15 +237,15 @@ abstract class EnterpriseIndividual(
if (!verifyInitializationActions()) {
if (log.isTraceEnabled)
log.trace("invoke GeneUtils.repairBrokenDbActionsList")
val previous = dbInitialization.toMutableList()
val previous = sqlInitialization.toMutableList()
SqlActionUtils.repairBrokenDbActionsList(previous, randomness)
resetInitializingActions(previous)
Lazy.assert{verifyInitializationActions()}
}
}

override fun hasAnyAction(): Boolean {
return super.hasAnyAction() || dbInitialization.isNotEmpty()
return super.hasAnyAction() || sqlInitialization.isNotEmpty()
}

override fun size() = seeMainExecutableActions().size
Expand Down Expand Up @@ -266,7 +289,7 @@ abstract class EnterpriseIndividual(
}

/**
* remove specified dbactions i.e., [actions] from [dbInitialization]
* remove specified dbactions i.e., [actions] from [sqlInitialization]
*/
fun removeInitDbActions(actions: List<SqlAction>) {
killChildren { it is SqlAction && actions.contains(it)}
Expand All @@ -276,6 +299,6 @@ abstract class EnterpriseIndividual(
* @return a list table names which are used to insert data directly
*/
open fun getInsertTableNames(): List<String>{
return dbInitialization.filterNot { it.representExistingData }.map { it.table.name }
return sqlInitialization.filterNot { it.representExistingData }.map { it.table.name }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.evomaster.core.problem.externalservice

import org.evomaster.core.search.EnvironmentAction
import org.evomaster.core.search.StructuralElement
import org.evomaster.core.search.gene.Gene


//TODO
class DnsAction(children: List<StructuralElement>) : EnvironmentAction(children) {
override fun getName(): String {
TODO("Not yet implemented")
}

override fun seeTopGenes(): List<out Gene> {
TODO("Not yet implemented")
}

override fun copyContent(): StructuralElement {
TODO("Not yet implemented")
}
}
Loading

0 comments on commit c02d5f6

Please sign in to comment.