Skip to content

Commit

Permalink
Merge branch 'phg/numberStringQueryParam' of github.com:EMResearch/Ev…
Browse files Browse the repository at this point in the history
…oMaster into phg/numberStringQueryParam
  • Loading branch information
Pgarrett committed Dec 20, 2024
2 parents 6075f93 + e472317 commit 06cc46e
Show file tree
Hide file tree
Showing 25 changed files with 448 additions and 74 deletions.
31 changes: 17 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ jobs:
path: core/target/evomaster.jar
retention-days: ${{env.retention-days}}
if-no-files-found: error
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: webfuzzing/evomaster
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and push Docker
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

installer-for-windows:
needs: build-base
Expand Down Expand Up @@ -129,7 +146,6 @@ jobs:
runs-on: "ubuntu-latest"

steps:

- name: Download fat jar
uses: actions/download-artifact@v4
with:
Expand All @@ -146,19 +162,6 @@ jobs:
uses: actions/download-artifact@v4
with:
name: evomaster.msi


## Doesn't seem any longer maintained... plus usability issues
# - uses: "marvinpinto/action-automatic-releases@latest"
# with:
# repo_token: "${{ secrets.GITHUB_TOKEN }}"
# prerelease: false
# files: |
# evomaster.jar
# ${{env.installer-windows}}
# ${{env.installer-osx}}
# ${{env.installer-debian}}

- name: Release
uses: softprops/action-gh-release@v1
with:
Expand Down
46 changes: 46 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
FROM amazoncorretto:21-alpine-jdk

COPY core/target/evomaster.jar .

ENTRYPOINT [ \
"java", \
"-Xmx4G", \
"-jar", "evomaster.jar", \
"--runningInDocker", "true" \
]


###################
###### NOTES ######
###################
# Build
# docker build -t webfuzzing/evomaster .
#
# Run
# docker run webfuzzing/evomaster <options>
#
# Publish (latest, otherwise tag with :TAG)
# docker login
# docker push webfuzzing/evomaster
#
# Example remote BB
# docker run -v "/$(pwd)/generated_tests":/generated_tests webfuzzing/evomaster --blackBox true --bbSwaggerUrl https://api.apis.guru/v2/openapi.yaml --outputFormat JAVA_JUNIT_4 --maxTime 10s --ratePerMinute 60
#
# Example local BB
# docker run -v "/$(pwd)/generated_tests":/generated_tests webfuzzing/evomaster --blackBox true --bbSwaggerUrl http://host.docker.internal:8080/v3/api-docs --maxTime 5s
#
# Example WB (NOT IMPLEMENTED YET)
# docker run -v "/$(pwd)/generated_tests":/generated_tests webfuzzing/evomaster --dockerLocalhost true
#
# Setting for existing em.yaml
# -v "/$(pwd)/em.yaml":/em.yaml
#
# Debugging
# docker run -it --entrypoint sh webfuzzing/evomaster
#
#
#
#
#
#
#
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ __Known limitations__:
-->


### Use in Industry

Several enterprises use _EvoMaster_ to fuzz their Web APIs.
We do few academia-industry collaborations ([see more info here](docs/contribute.md)), where we help test engineers to apply _EvoMaster_ on their systems, as long as we can then report on such experience.
Example of Fortune 500 companies using _EvoMaster_ are:

* [Meituan](https://www.meituan.com): see [TOSEM'23](docs/publications/2023_tosem_rpc.pdf), [ASE'24](docs/publications/2024_ase.pdf).

* [Volkswagen](https://www.volkswagen.com): see [AUSE'24](docs/publications/2024_ause_vw.pdf), [ICST'25](docs/publications/2025_icst.pdf).


### Videos

![](docs/img/video-player-flaticon.png)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public VariableDescriptor(String columnName) {
}

public VariableDescriptor(String columnName, String alias, String tableName) {
this.columnName = columnName.trim().toLowerCase();
this.columnName = (columnName==null || columnName.trim().isEmpty() ?
null : columnName.trim().toLowerCase());
this.alias = (alias == null || alias.trim().isEmpty() ?
this.columnName :
alias.trim().toLowerCase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,26 @@ public static SqlDistanceWithMetrics computeDistance(
QueryResult... data
) {

/**
* This is not an ideal solution, but it will remain this way until config.heuristicsForSQLAdvanced remains
* experimental and it does not replace the "Plain" SqlHeuristicsCalculator
*/
if (advancedHeuristics) {
return SqlHeuristicsCalculator.computeDistance(sqlCommand,schema,taintHandler,data);
}

if (data.length == 0 || Arrays.stream(data).allMatch(QueryResult::isEmpty)){
//if no data, we have no info whatsoever
return new SqlDistanceWithMetrics(Double.MAX_VALUE,0, false);
}

Statement stmt = SqlParserUtils.parseSqlCommand(sqlCommand);

Expression where = getWhere(stmt);
if (where == null) {
//no constraint and at least one data point
return new SqlDistanceWithMetrics(0.0,0, false);
}


SqlNameContext context = new SqlNameContext(stmt);
if (schema != null) {
context.setSchema(schema);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.evomaster.client.java.sql.internal;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.FromItem;
import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto;
import org.evomaster.client.java.sql.QueryResult;

import static org.evomaster.client.java.sql.internal.SqlParserUtils.getFrom;
import static org.evomaster.client.java.sql.internal.SqlParserUtils.getWhere;

public class SqlHeuristicsCalculator {

public static SqlDistanceWithMetrics computeDistance(String sqlCommand,
DbInfoDto schema,
TaintHandler taintHandler,
QueryResult... data) {

Statement parsedSqlCommand = SqlParserUtils.parseSqlCommand(sqlCommand);
Expression whereClause= getWhere(parsedSqlCommand);
FromItem fromItem = getFrom(parsedSqlCommand);

if (fromItem == null && whereClause == null) {
return new SqlDistanceWithMetrics(0.0,0,false);
}


return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
Expand Down Expand Up @@ -55,7 +56,6 @@ public static boolean isSelectOne(String sqlCommand) {


public static Expression getWhere(Statement parsedStatement) {

if (parsedStatement instanceof Select) {
Select select = (Select) parsedStatement;
PlainSelect plainSelect = select.getPlainSelect();
Expand All @@ -69,6 +69,32 @@ public static Expression getWhere(Statement parsedStatement) {
}
}

/**
* Extracts the "FROM" clause or the primary table involved in a SQL statement.
* This method supports SELECT, DELETE, and UPDATE SQL statements.
*
* @param parsedStatement The parsed SQL statement as a {@link Statement} object.
* This is typically obtained using JSQLParser's `CCJSqlParserUtil.parse`.
* @return The {@link FromItem} representing the "FROM" clause or the main table for the statement.
* - For a SELECT statement, returns the main {@link FromItem} in the "FROM" clause.
* - For a DELETE statement, returns the table being deleted from.
* - For an UPDATE statement, returns the table being updated.
* @throws IllegalArgumentException If the provided statement type is not SELECT, DELETE, or UPDATE.
*/
public static FromItem getFrom(Statement parsedStatement) {
if (parsedStatement instanceof Select) {
Select select = (Select) parsedStatement;
PlainSelect plainSelect = select.getPlainSelect();
return plainSelect.getFromItem();
} else if(parsedStatement instanceof Delete){
return ((Delete) parsedStatement).getTable();
} else if(parsedStatement instanceof Update){
return ((Update) parsedStatement).getTable();
} else {
throw new IllegalArgumentException("Cannot handle statement: " + parsedStatement.toString());
}
}

/**
* This method assumes that the SQL command can be successfully parsed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,13 @@ public void testWhereWithNoColumns() {
assertEquals(0d, dist);
}

@Test
public void testNoWhereNoFrom() {
String sqlCommand = "SELECT 1 as example_column";
QueryResult data = new QueryResult(Arrays.asList("example_column"), null);
data.addRow(new DataRow("example_column",1, null));
double distance = HeuristicsCalculator.computeDistance(sqlCommand,data);
assertEquals(0, distance);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.evomaster.client.java.sql.internal;

import org.evomaster.client.java.sql.DataRow;
import org.evomaster.client.java.sql.QueryResult;
import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class SqlHeuristicsCalculatorTest {

@Test
public void testNoWhereNoFromClause() {
String sqlCommand = "SELECT 1 AS example_column";
QueryResult data = new QueryResult(Arrays.asList("example_column"), null);
data.addRow(new DataRow("example_column",1, null));
SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, data);
assertEquals(0, distanceWithMetrics.sqlDistance);
}
}
69 changes: 53 additions & 16 deletions core/src/main/kotlin/org/evomaster/core/EMConfig.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package org.evomaster.core

import joptsimple.BuiltinHelpFormatter
import joptsimple.OptionDescriptor
import joptsimple.OptionParser
import joptsimple.OptionSet
import joptsimple.*
import org.evomaster.client.java.controller.api.ControllerConstants
import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto
import org.evomaster.client.java.instrumentation.shared.ExternalServiceSharedUtils
Expand Down Expand Up @@ -585,6 +582,18 @@ class EMConfig {
throw ConfigProblemException("The use of 'prematureStop' is meaningful only if the stopping criterion" +
" 'stoppingCriterion' is based on time")
}

if(blackBox){
if(sutControllerHost != ControllerConstants.DEFAULT_CONTROLLER_HOST){
throw ConfigProblemException("Changing 'sutControllerHost' has no meaning in black-box testing, as no controller is used")
}
if(!overrideOpenAPIUrl.isNullOrBlank()){
throw ConfigProblemException("Changing 'overrideOpenAPIUrl' has no meaning in black-box testing, as no controller is used")
}
}
if(dockerLocalhost && !runningInDocker){
throw ConfigProblemException("Specifying 'dockerLocalhost' only makes sense when running EvoMaster inside Docker.")
}
}

private fun checkPropertyConstraints(m: KMutableProperty<*>) {
Expand Down Expand Up @@ -696,8 +705,11 @@ class EMConfig {
return
}

val opt = options.valueOf(m.name)?.toString()
?: throw ConfigProblemException("Value not found for property ${m.name}")
val opt = try{
options.valueOf(m.name)?.toString()
} catch (e: OptionException){
throw ConfigProblemException("Error in parsing configuration option '${m.name}'. Library message: ${e.message}")
} ?: throw ConfigProblemException("Value not found for property '${m.name}'")

updateValue(opt, m)
}
Expand Down Expand Up @@ -1048,9 +1060,43 @@ class EMConfig {
" If no tag is specified here, then such filter is not applied.")
var endpointTagFilter: String? = null

@Important(6.0)
@Cfg("Host name or IP address of where the SUT EvoMaster Controller Driver is listening on." +
" This option is only needed for white-box testing.")
var sutControllerHost = ControllerConstants.DEFAULT_CONTROLLER_HOST


@Important(6.1)
@Cfg("TCP port of where the SUT EvoMaster Controller Driver is listening on." +
" This option is only needed for white-box testing.")
@Min(0.0)
@Max(maxTcpPort)
var sutControllerPort = ControllerConstants.DEFAULT_CONTROLLER_PORT


@Important(7.0)
@Url
@Cfg("If specified, override the OpenAPI URL location given by the EvoMaster Driver." +
" This option is only needed for white-box testing.")
var overrideOpenAPIUrl = ""

//-------- other options -------------

@Cfg("Inform EvoMaster process that it is running inside Docker." +
" Users should not modify this parameter, as it is set automatically in the Docker image of EvoMaster.")
var runningInDocker = false

/**
* TODO this is currently not implemented.
* Even if did, there would still be major issues with handling WireMock.
* Until we can think of a good solution there, no point in implementing this.
*/
@Experimental
@Cfg("Replace references to 'localhost' to point to the actual host machine." +
" Only needed when running EvoMaster inside Docker.")
var dockerLocalhost = false


@FilePath
@Cfg("When generating tests in JavaScript, there is the need to know where the driver is located in respect to" +
" the generated tests")
Expand Down Expand Up @@ -1137,14 +1183,6 @@ class EMConfig {
"A negative value means the CPU clock time will be rather used as seed")
var seed: Long = -1

@Cfg("TCP port of where the SUT REST controller is listening on")
@Min(0.0)
@Max(maxTcpPort)
var sutControllerPort = ControllerConstants.DEFAULT_CONTROLLER_PORT

@Cfg("Host name or IP address of where the SUT REST controller is listening on")
var sutControllerHost = ControllerConstants.DEFAULT_CONTROLLER_HOST

@Cfg("Limit of number of individuals per target to keep in the archive")
@Min(1.0)
var archiveTargetLimit = 10
Expand Down Expand Up @@ -2398,9 +2436,8 @@ class EMConfig {
" instead of OFF.")
val probabilityOfOnVsOffInAllOptionals = 0.8

@Experimental
@Cfg("Add summary comments on each test")
var addTestComments = false
var addTestComments = true

@Min(1.0)
@Cfg("Max length for test comments. Needed when enumerating some names/values, making comments too long to be" +
Expand Down
Loading

0 comments on commit 06cc46e

Please sign in to comment.