Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ jobs:
name: Checkout
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
clean: true
- name: Cache Gradle artifacts
uses: actions/cache@v2
Expand Down Expand Up @@ -49,7 +48,6 @@ jobs:
- name: Submit coverage
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: helper
verbose: true
files: build/coverage.xml,build/reports/jacoco/test/jacocoTestReport.xml
Expand All @@ -68,7 +66,6 @@ jobs:
name: Checkout
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
clean: true
- name: Create Build Directory for libddwaf
run: cmake -E make_directory "${{ env.tempdir }}/buildPW"
Expand Down Expand Up @@ -162,7 +159,6 @@ jobs:
name: Checkout
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
clean: true
- uses: ilammy/msvc-dev-cmd@v1
name: Setup amd64 build
Expand Down Expand Up @@ -232,7 +228,6 @@ jobs:
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
clean: true
- uses: docker/setup-buildx-action@v1
id: buildx
Expand Down Expand Up @@ -262,7 +257,6 @@ jobs:
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
clean: true
- name: Create artifacts directory
run: mkdir -p ${{ env.artifactsDirectory }}
Expand Down Expand Up @@ -315,7 +309,6 @@ jobs:
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
clean: true
- name: Create artifacts directory
run: mkdir -p ${{ env.artifactsDirectory }}
Expand Down Expand Up @@ -361,7 +354,6 @@ jobs:
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- name: Install GCC 9 and clang 8
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
Expand Down Expand Up @@ -431,7 +423,6 @@ jobs:
name: Checkout
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
clean: true
- run: chmod +x gradlew
name: Make gradlew executable
Expand Down
2 changes: 1 addition & 1 deletion libddwaf
Submodule libddwaf updated 50 files
+6 −0 CHANGELOG.md
+1 −2 CMakeLists.txt
+1 −1 examples/CMakeLists.txt
+66 −0 examples/validate_schema.cpp
+2 −1 examples/verify_ruleset.cpp
+113 −0 schema/appsec-event-1.0.0.json
+2 −2 src/PWAdditive.cpp
+2 −1 src/PWManifest.cpp
+46 −40 src/PWProcessor.cpp
+4 −4 src/PWProcessor.hpp
+53 −92 src/PWRet.cpp
+5 −4 src/PWRet.hpp
+1 −1 src/PWRetriever.cpp
+0 −2 src/PWRetriever.hpp
+0 −71 src/PWRule.hpp
+0 −37 src/PWRuleManager.cpp
+0 −33 src/PWRuleManager.hpp
+7 −8 src/PowerWAF.cpp
+6 −10 src/PowerWAF.hpp
+7 −7 src/parser/parser.cpp
+3 −3 src/parser/parser.hpp
+36 −31 src/parser/parser_v1.cpp
+38 −33 src/parser/parser_v2.cpp
+19 −14 src/rule.cpp
+83 −0 src/rule.hpp
+5 −6 tests/TestAdditive.cpp
+6 −6 tests/TestInterface.cpp
+3 −3 tests/TestParser.cpp
+16 −20 tests/TestProcessor.cpp
+7 −8 tests/TestRetriever.cpp
+6 −4 tests/TestRule.cpp
+184 −0 tests/TestSchema.cpp
+1 −127 tests/TestTransform.cpp
+2 −2 tests/rule_processor/TestLibInjectionSQL.cpp
+2 −2 tests/rule_processor/TestLibInjectionXSS.cpp
+6 −0 tests/yaml/interface.yaml
+2 −0 tests/yaml/interface2.yaml
+2 −0 tests/yaml/powerwaf.yaml
+2 −0 tests/yaml/processor.yaml
+2 −0 tests/yaml/processor2.yaml
+4 −0 tests/yaml/processor3.yaml
+4 −0 tests/yaml/processor4.yaml
+4 −0 tests/yaml/processor5.yaml
+2 −0 tests/yaml/regressions.yaml
+4 −0 tests/yaml/retriever.yaml
+46 −0 tests/yaml/schema.yaml
+1 −1 tests/yaml/slow.yaml
+2 −0 tests/yaml/transform.yaml
+6 −2 third_party/CMakeLists.txt
+1 −1 version
5 changes: 3 additions & 2 deletions src/main/c/powerwaf_jni.c
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,9 @@ static ddwaf_object _convert_checked(JNIEnv *env, jobject obj,
"Error calling toString() on map key");
JAVA_CALL(value_obj, entry_value, entry);

JNI(DeleteLocalRef, key_obj);
JNI(DeleteLocalRef, entry);

ddwaf_object value =
_convert_checked(env, value_obj, lims, rec_level + 1);
if (JNI(ExceptionCheck)) {
Expand All @@ -1250,8 +1253,6 @@ static ddwaf_object _convert_checked(JNIEnv *env, jobject obj,
* on a loop and we don't want to run out of local refs */
JNI(DeleteLocalRef, value_obj);
JNI(DeleteLocalRef, key_jstr);
JNI(DeleteLocalRef, key_obj);
JNI(DeleteLocalRef, entry);
if (!success) {
JNI(ThrowNew, jcls_rte, "ddwaf_object_map_add failed (OOM?)");
goto error;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/sqreen/powerwaf/Powerwaf.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import java.util.Map;

public final class Powerwaf {
public static final String LIB_VERSION = "1.0.13";
public static final String LIB_VERSION = "1.0.14";

private static final Logger LOGGER = LoggerFactory.getLogger(Powerwaf.class);
final static boolean ENABLE_BYTE_BUFFERS;
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/io/sqreen/powerwaf/PowerwafContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,15 @@ public class PowerwafContext {

this.uniqueName = uniqueName;

if (!definition.containsKey("version") ||
!definition.containsKey("events")) {
if (!definition.containsKey("version")) {
throw new IllegalStateException(
"Invalid definition. Expected keys 'version' and 'events' to exist");
"Invalid definition. Expected key 'version' to exist");
}

if (!definition.containsKey("events") &&
!definition.containsKey("rules")) {
throw new IllegalStateException(
"Invalid definition. Expected keys 'events' or 'rules' to exist");
}
this.handle = Powerwaf.addRules(definition);

Expand Down
4 changes: 2 additions & 2 deletions src/test/groovy/io/sqreen/powerwaf/AdditiveTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,14 @@ class AdditiveTest implements ReactiveTrait {

@Test(expected = IllegalArgumentException)
void 'Should throw IllegalArgumentException if Limits is null while run'() {
ctx = new PowerwafContext('test', ARACHNI_ATOM)
ctx = new PowerwafContext('test', ARACHNI_ATOM_V2_1)
additive = ctx.openAdditive()
additive.runAdditive([:], null)
}

@Test
void 'should defer context destruction if the context is closed'() {
ctx = new PowerwafContext('test', ARACHNI_ATOM)
ctx = new PowerwafContext('test', ARACHNI_ATOM_V2_1)
additive = ctx.openAdditive()
assert ctx.refcount.get() == 2
ctx.delReference()
Expand Down
2 changes: 1 addition & 1 deletion src/test/groovy/io/sqreen/powerwaf/BadRuleTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class BadRuleTests implements PowerwafTrait {

@Test(expected = IllegalArgumentException)
void 'version is not a string'() {
def rules = [:] + ARACHNI_ATOM
def rules = [:] + ARACHNI_ATOM_V1_0
rules['version'] = 99
ctx = Powerwaf.createContext('test', rules)
}
Expand Down
50 changes: 40 additions & 10 deletions src/test/groovy/io/sqreen/powerwaf/BasicTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class BasicTests implements PowerwafTrait {
}

@Test
void 'test running basic rule'() {
def ruleSet = ARACHNI_ATOM
void 'test running basic rule v1_0'() {
def ruleSet = ARACHNI_ATOM_V1_0

ctx = Powerwaf.createContext('test', ruleSet)

Expand All @@ -32,14 +32,44 @@ class BasicTests implements PowerwafTrait {
assertThat awd.action, is(Powerwaf.Action.MONITOR)

def json = slurper.parseText(awd.data)
assert json.ret_code == [1]
assert json.flow == ['arachni_detection']
assert json.rule == ['arachni_rule']

assert json[0].rule.id == 'arachni_rule'
assert json[0].rule.name == 'Arachni'
assert json[0].rule.tags == [category: '', type: 'arachni_detection']
assert json[0].rule_matches[0]['operator'] == 'match_regex'
assert json[0].rule_matches[0]['operator_value'] == 'Arachni'
assert json[0].rule_matches[0]['parameters'][0].address == 'server.request.headers.no_cookies'
assert json[0].rule_matches[0]['parameters'][0].key_path == ['user-agent']
assert json[0].rule_matches[0]['parameters'][0].value == 'Arachni'
assert json[0].rule_matches[0]['parameters'][0].highlight == ['Arachni']
}

@Test
void 'test running basic rule v2_1'() {
def ruleSet = ARACHNI_ATOM_V2_1

ctx = Powerwaf.createContext('test', ruleSet)

ActionWithData awd = ctx.runRules(
['server.request.headers.no_cookies': ['user-agent': 'Arachni/v1']], limits)
assertThat awd.action, is(Powerwaf.Action.MONITOR)

def json = slurper.parseText(awd.data)

assert json[0].rule.id == 'arachni_rule'
assert json[0].rule.name == 'Arachni'
assert json[0].rule.tags == [category: 'attack_attempt', type: 'security_scanner']
assert json[0].rule_matches[0]['operator'] == 'match_regex'
assert json[0].rule_matches[0]['operator_value'] == '^Arachni\\/v'
assert json[0].rule_matches[0]['parameters'][0].address == 'server.request.headers.no_cookies'
assert json[0].rule_matches[0]['parameters'][0].key_path == ['user-agent']
assert json[0].rule_matches[0]['parameters'][0].value == 'Arachni/v1'
assert json[0].rule_matches[0]['parameters'][0].highlight == ['Arachni/v']
}

@Test
void 'test with array of string lists'() {
def ruleSet = ARACHNI_ATOM
def ruleSet = ARACHNI_ATOM_V1_0

ctx = Powerwaf.createContext('test', ruleSet)

Expand All @@ -54,7 +84,7 @@ class BasicTests implements PowerwafTrait {

@Test
void 'test with array'() {
def ruleSet = ARACHNI_ATOM
def ruleSet = ARACHNI_ATOM_V1_0

ctx = Powerwaf.createContext('test', ruleSet)

Expand All @@ -66,7 +96,7 @@ class BasicTests implements PowerwafTrait {

@Test
void 'test null argument'() {
def ruleSet = ARACHNI_ATOM
def ruleSet = ARACHNI_ATOM_V1_0

ctx = Powerwaf.createContext('test', ruleSet)

Expand All @@ -78,7 +108,7 @@ class BasicTests implements PowerwafTrait {

@Test
void 'test boolean arguments'() {
def ruleSet = ARACHNI_ATOM
def ruleSet = ARACHNI_ATOM_V1_0

ctx = Powerwaf.createContext('test', ruleSet)

Expand All @@ -93,7 +123,7 @@ class BasicTests implements PowerwafTrait {

@Test
void 'test unencodable arguments'() {
def ruleSet = ARACHNI_ATOM
def ruleSet = ARACHNI_ATOM_V1_0

ctx = Powerwaf.createContext('test', ruleSet)

Expand Down
8 changes: 4 additions & 4 deletions src/test/groovy/io/sqreen/powerwaf/EncodingTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,31 @@ class EncodingTests implements PowerwafTrait {

@Before
void assignContext() {
ctx = Powerwaf.createContext('test', ARACHNI_ATOM)
ctx = Powerwaf.createContext('test', ARACHNI_ATOM_V1_0)
}

@Test
void 'user input has an unpaired leading surrogate'() {
Powerwaf.ActionWithData awd = runRules('Arachni\uD800')

def json = slurper.parseText(awd.data)
assert json.filter.first().first().resolved_value == 'Arachni\uFFFD'
assert json[0].rule_matches[0].parameters[0].value == 'Arachni\uFFFD'
}

@Test
void 'user input has unpaired leading surrogate'() {
Powerwaf.ActionWithData awd = runRules 'Arachni\uD800Ā'

def json = slurper.parseText(awd.data)
assert json.filter.first().first().resolved_value == 'Arachni\uFFFDĀ'
assert json[0].rule_matches[0].parameters[0].value == 'Arachni\uFFFDĀ'
}

@Test
void 'user input has unpaired trailing surrogate'() {
Powerwaf.ActionWithData awd = runRules 'Arachni\uDC00x'

def json = slurper.parseText(awd.data)
assert json.filter.first().first().resolved_value == 'Arachni\uFFFDx'
assert json[0].rule_matches[0].parameters[0].value == 'Arachni\uFFFDx'
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class InvalidInvocationTests implements ReactiveTrait {

@Test
void 'runRule with conversion throwing exception'() {
ctx = Powerwaf.createContext('test', ARACHNI_ATOM)
ctx = Powerwaf.createContext('test', ARACHNI_ATOM_V2_1)
def exc = shouldFail(UnclassifiedPowerwafException) {
ctx.runRules(new BadMap(delegate: [:]), limits)
}
Expand All @@ -55,7 +55,7 @@ class InvalidInvocationTests implements ReactiveTrait {

@Test
void 'runRule with conversion throwing exception additive variant'() {
ctx = Powerwaf.createContext('test', ARACHNI_ATOM)
ctx = Powerwaf.createContext('test', ARACHNI_ATOM_V2_1)
additive = ctx.openAdditive()
def exc = shouldFail(UnclassifiedPowerwafException) {
additive.run(new BadMap(delegate: [:]), limits)
Expand All @@ -67,7 +67,7 @@ class InvalidInvocationTests implements ReactiveTrait {

@Test
void 'rule is run on closed context'() {
ctx = Powerwaf.createContext('test', ARACHNI_ATOM)
ctx = Powerwaf.createContext('test', ARACHNI_ATOM_V2_1)
ctx.delReference()
def exc = shouldFail(UnclassifiedPowerwafException) {
ctx.runRules([:], limits)
Expand All @@ -80,7 +80,7 @@ class InvalidInvocationTests implements ReactiveTrait {
void 'bytebuffer passed does not represent a map'() {
Assume.assumeTrue Powerwaf.ENABLE_BYTE_BUFFERS

ctx = Powerwaf.createContext('test', ARACHNI_ATOM)
ctx = Powerwaf.createContext('test', ARACHNI_ATOM_V2_1)
additive = ctx.openAdditive()

ByteBufferSerializer serializer = new ByteBufferSerializer(limits)
Expand All @@ -98,7 +98,7 @@ class InvalidInvocationTests implements ReactiveTrait {
void 'bytebuffer passed is not direct buffer'() {
Assume.assumeTrue Powerwaf.ENABLE_BYTE_BUFFERS

ctx = Powerwaf.createContext('test', ARACHNI_ATOM)
ctx = Powerwaf.createContext('test', ARACHNI_ATOM_V2_1)
additive = ctx.openAdditive()

shouldFail(IllegalArgumentException) {
Expand Down
4 changes: 3 additions & 1 deletion src/test/groovy/io/sqreen/powerwaf/LimitsTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package io.sqreen.powerwaf

import groovy.json.JsonSlurper
import io.sqreen.powerwaf.exception.TimeoutPowerwafException
import org.junit.Ignore
import org.junit.Test

import static groovy.test.GroovyAssert.shouldFail
Expand All @@ -22,7 +23,7 @@ class LimitsTests implements PowerwafTrait {

@Lazy
PowerwafContext ctxWithArachniAtom =
Powerwaf.createContext('test', ARACHNI_ATOM)
Powerwaf.createContext('test', ARACHNI_ATOM_V1_0)

@Test
void 'maxDepth is respected'() {
Expand Down Expand Up @@ -137,6 +138,7 @@ class LimitsTests implements PowerwafTrait {
}

@Test
@Ignore
void 'runBudgetInUs is observed'() {
def atom = new JsonSlurper().parseText('''
{
Expand Down
34 changes: 33 additions & 1 deletion src/test/groovy/io/sqreen/powerwaf/PowerwafTrait.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import org.junit.AfterClass
@CompileStatic
trait PowerwafTrait extends JNITrait {

static final Map ARACHNI_ATOM = (Map) new JsonSlurper().parseText('''
static final Map ARACHNI_ATOM_V1_0 = (Map) new JsonSlurper().parseText('''
{
"version": "1.0",
"events": [
Expand All @@ -41,6 +41,38 @@ trait PowerwafTrait extends JNITrait {
]
}''')

static final Map ARACHNI_ATOM_V2_1 = (Map) new JsonSlurper().parseText('''
{
"version": "2.1",
"rules": [
{
"id": "arachni_rule",
"name": "Arachni",
"tags": {
"type": "security_scanner",
"category": "attack_attempt"
},
"conditions": [
{
"parameters": {
"inputs": [
{
"address": "server.request.headers.no_cookies",
"key_path": [
"user-agent"
]
}
],
"regex": "^Arachni\\\\/v"
},
"operator": "match_regex"
}
],
"transformers": []
}
]
}''')

int maxDepth = 5
int maxElements = 20
int maxStringSize = 100
Expand Down