Fuzzing setup + documentation

This commit is contained in:
Arne Keller 2021-03-08 10:53:59 +01:00
parent bd8a7a26cf
commit 3f07a6d234
11 changed files with 173 additions and 11 deletions

2
.gitignore vendored
View File

@ -18,3 +18,5 @@ drivers/
# Error screenshots generated by TestBench for failed integration tests # Error screenshots generated by TestBench for failed integration tests
error-screenshots/ error-screenshots/
webpack.generated.js webpack.generated.js
/fuzz-results

View File

@ -1,11 +1,11 @@
# Typicalc # Typicalc
The project is a standard Maven project, so you can import it to your IDE of choice. [Read more how to set up a development environment](https://vaadin.com/docs/v14/flow/installing/installing-overview.html) for Vaadin projects (Windows, Linux, macOS). The project is a standard Maven project, so you can import it to your IDE of choice.
[Read more how to set up a development environment](https://vaadin.com/docs/v18/flow/installing/installing-overview.html) for Vaadin projects (Windows, Linux, macOS).
[To Vaadin documentation](https://vaadin.com/docs-beta/latest/flow/overview/) [To Vaadin documentation](https://vaadin.com/docs-beta/latest/flow/overview/)
## Running and debugging the application
## Running and debugging the applcation
### Running the application from the command line. ### Running the application from the command line.
To run from the command line, use `mvn` and open http://localhost:8080 in your browser. To run from the command line, use `mvn` and open http://localhost:8080 in your browser.
@ -15,7 +15,7 @@ To run from the command line, use `mvn` and open http://localhost:8080 in your b
- Right click on the Application class - Right click on the Application class
- Select "Debug 'Application.main()'" from the list - Select "Debug 'Application.main()'" from the list
After the application has started, you can view your it at http://localhost:8080/ in your browser. After the application has started, you can view it at http://localhost:8080/ in your browser.
You can now also attach break points in code for debugging purposes, by clicking next to a line number in any source file. You can now also attach break points in code for debugging purposes, by clicking next to a line number in any source file.
### Running and debugging the application in Eclipse ### Running and debugging the application in Eclipse
@ -27,13 +27,29 @@ Do not worry if the debugger breaks at a `SilentExitException`. This is a Spring
After the application has started, you can view your it at http://localhost:8080/ in your browser. After the application has started, you can view your it at http://localhost:8080/ in your browser.
You can now also attach break points in code for debugging purposes, by clicking next to a line number in any source file. You can now also attach break points in code for debugging purposes, by clicking next to a line number in any source file.
## What next? ## Fuzzing with [JQF](https://github.com/rohanpadhye/JQF)
[vaadin.com](https://vaadin.com) has lots of material to help you get you started: ### [Zest](https://github.com/rohanpadhye/JQF/wiki/Fuzzing-with-Zest)
- Follow the tutorials in [vaadin.com/tutorials](https://vaadin.com/tutorials). Especially [vaadin.com/tutorials/getting-started-with-flow](https://vaadin.com/tutorials/getting-started-with-flow) is good for getting a grasp of the basic Vaadin concepts. Run:
- Read the documentation in [vaadin.com/docs](https://vaadin.com/docs). ```
- For a bigger Vaadin application example, check out the Full Stack App starter from [vaadin.com/start](https://vaadin.com/start). mvn test-compile jqf:fuzz -Dclass=edu.kit.typicalc.model.parser.LambdaParserFuzzTest -Dmethod=testInference
```
This will use the `LambdaTermGenerator` to create random lambda terms that are then passed to the `ModelImpl`.
### [AFL](https://lcamtuf.coredump.cx/afl/)
First install the necessary JQF tools: https://github.com/rohanpadhye/jqf/wiki/Fuzzing-with-AFL
Remove the `@Ignore` annotation in `LambdaParserFuzzTest` and run:
```
mvn test-compile
jqf-afl-fuzz -c target/test-classes:target/classes -i src/test/resources/terms/ edu.kit.typicalc.model.parser.LambdaParserFuzzTest testLambdaParserAFL
```
Generated inputs are stored in `fuzz-results/queue/`.
More samples can be added to `src/test/resources/terms/` to speed up the process.
## Deploying using Docker ## Deploying using Docker
@ -48,3 +64,14 @@ Once the Docker image is correctly built, you can test it locally using
``` ```
docker run -p 8080:8080 myapp:latest docker run -p 8080:8080 myapp:latest
``` ```
## Deploying using a JAR
First build the project:
```
mvn package -Pproduction
```
Then run the server:
```
java -jar target/typicalc-1.0-SNAPSHOT.jar
```

11
pom.xml
View File

@ -92,6 +92,12 @@
<artifactId>spring-boot-devtools</artifactId> <artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>edu.berkeley.cs.jqf</groupId>
<artifactId>jqf-fuzz</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-testbench</artifactId> <artifactId>vaadin-testbench</artifactId>
@ -159,6 +165,11 @@
<build> <build>
<defaultGoal>spring-boot:run</defaultGoal> <defaultGoal>spring-boot:run</defaultGoal>
<plugins> <plugins>
<plugin>
<groupId>edu.berkeley.cs.jqf</groupId>
<artifactId>jqf-maven-plugin</artifactId>
<version>1.6</version>
</plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>

View File

@ -1,6 +1,5 @@
server.port=${PORT:8080} server.port=${PORT:8080}
server.servlet.session.persistent=false
logging.level.org.atmosphere = warn logging.level.org.atmosphere = warn
spring.mustache.check-template-location = false spring.mustache.check-template-location = false
spring.devtools.add-properties = false spring.devtools.add-properties = false
# vaadin.whitelisted-packages= org/vaadin/example

View File

@ -0,0 +1,65 @@
package edu.kit.typicalc.model.parser;
import edu.berkeley.cs.jqf.fuzz.Fuzz;
import edu.berkeley.cs.jqf.fuzz.JQF;
import edu.kit.typicalc.model.Model;
import edu.kit.typicalc.model.ModelImpl;
import edu.kit.typicalc.model.TypeInfererInterface;
import edu.kit.typicalc.model.step.InferenceStep;
import edu.kit.typicalc.model.term.LambdaTerm;
import edu.kit.typicalc.util.Result;
import org.junit.Ignore;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import com.pholser.junit.quickcheck.*;
@RunWith(JQF.class)
public class LambdaParserFuzzTest {
/**
* Runs the type inference algorithm and gets the first inference step.
* Only validates that the algorithm produced something.
*
* @param term lambda term
*/
@Fuzz
public void testInference(@From(LambdaTermGenerator.class) String term) {
Model model = new ModelImpl();
Result<TypeInfererInterface, ParseError> typer = model.getTypeInferer(term, new HashMap<>());
InferenceStep first = typer.unwrap().getFirstInferenceStep();
}
// this is how to rerun a specific fuzz result
/*
@Fuzz(repro="target/fuzz-results/edu.kit.typicalc.model.parser.LambdaParserFuzzTest/testInference/corpus/id_000066")
public void testWithGenerator(@From(LambdaTermGenerator.class) String code) {
System.out.println(code);
}
*/
@Fuzz
@Ignore // remove if you intend to use AFL
public void testLambdaParserAFL(InputStream input) {
String term;
try {
term = new String(input.readAllBytes());
} catch (Throwable t) {
return;
}
LambdaParser parser = new LambdaParser(term);
Result<LambdaTerm, ParseError> result = parser.parse();
if (result.isOk()) {
LambdaTerm term1 = result.unwrap();
String sameTerm = term1.toString();
LambdaParser parser2 = new LambdaParser(sameTerm);
Result<LambdaTerm, ParseError> result2 = parser2.parse();
LambdaTerm term2 = result2.unwrap();
if (!term2.equals(term1)) {
throw new IllegalStateException("results differ, when parsing: " + term);
}
}
}
}

View File

@ -0,0 +1,53 @@
package edu.kit.typicalc.model.parser;
import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;
import edu.kit.typicalc.model.term.AbsTerm;
import edu.kit.typicalc.model.term.AppTerm;
import edu.kit.typicalc.model.term.IntegerTerm;
import edu.kit.typicalc.model.term.LambdaTerm;
import edu.kit.typicalc.model.term.LetTerm;
import edu.kit.typicalc.model.term.VarTerm;
public class LambdaTermGenerator extends Generator<String> {
public LambdaTermGenerator() {
super(String.class); // Register the type of objects that we can create
}
public static final String[] VARIABLES = new String[]{"x", "y", "z"};
/**
* Generate a random lambda term.
*
* @param random source of randomness
* @param status not used
* @return a random lambda term
*/
@Override
public String generate(SourceOfRandomness random, GenerationStatus status) {
return generateReal(random).toString();
}
private LambdaTerm generateReal(SourceOfRandomness random) {
if (random.nextInt(1, 7) < 3) {
LambdaTerm one = generateReal(random);
LambdaTerm two = generateReal(random);
if (random.nextInt(1, 10) < 8) {
return new AppTerm(one, two);
} else {
return new LetTerm(new VarTerm(random.choose(VARIABLES)), one, two);
}
} else {
if (random.nextBoolean()) {
LambdaTerm one = generateReal(random);
return new AbsTerm(new VarTerm(random.choose(VARIABLES)), one);
} else if (random.nextBoolean()) {
return new VarTerm(random.choose(VARIABLES));
} else {
return new IntegerTerm(5);
}
}
}
}

View File

@ -0,0 +1 @@
λx. x

View File

@ -0,0 +1 @@
x y

View File

@ -0,0 +1 @@
(λy. y 5)(λx.x)

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
let f = λx.x in f 5