Skip to content
Open
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
2 changes: 1 addition & 1 deletion dd-java-agent/appsec/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies {
implementation project(':communication')
implementation project(':products:metrics:metrics-api')
implementation project(':telemetry')
implementation group: 'io.sqreen', name: 'libsqreen', version: '17.4.0'
implementation group: 'io.sqreen', name: 'libsqreen', version: '17.5.0'
implementation libs.moshi

compileOnly project(':dd-java-agent:agent-bootstrap')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,47 @@ public static class RuleMatch {
String operator;
String operator_value;
List<Parameter> parameters;

public RuleMatch(String operator, String operator_value, List<Parameter> parameters) {
this.operator = operator;
this.operator_value = operator_value;
this.parameters = parameters;
}
}

public static class Rule {
public String id; // expose for log message
String name;
Map<String, String> tags;

public Rule(String id, String name, Map<String, String> tags) {
this.id = id;
this.name = name;
this.tags = tags;
}
}

public static class Parameter extends MatchInfo {
MatchInfo resource;
MatchInfo params;
MatchInfo db_type;
List<String> highlight;

public Parameter(String address, List<Object> key_path, String value, List<String> highlight) {
super(address, key_path, value);
this.highlight = highlight;
}
}

public static class MatchInfo {
String address;
List<Object> key_path;
String value;

public MatchInfo(String address, List<Object> key_path, String value) {
this.address = address;
this.key_path = key_path;
this.value = value;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
package com.datadog.appsec.report;

import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;

public class AppSecEventWrapper {

private static final JsonAdapter<AppSecEventWrapper> ADAPTER =
new Moshi.Builder().build().adapter(AppSecEventWrapper.class);
new Moshi.Builder()
.add(Double.class, new IntegralDoubleJsonAdapter())
.build()
.adapter(AppSecEventWrapper.class);

// Writes whole-number Doubles (e.g. key_path array indices) without a trailing ".0".

private static final class IntegralDoubleJsonAdapter extends JsonAdapter<Double> {
@Override
public Double fromJson(JsonReader reader) throws IOException {
return reader.nextDouble();
}

@Override
public void toJson(JsonWriter writer, Double value) throws IOException {
if (value == null) {
writer.nullValue();
} else if (!value.isInfinite() && !value.isNaN() && value == Math.rint(value)) {
writer.value(value.longValue());
} else {
writer.value(value);
}
}
}

private final Collection<AppSecEvent> triggers;
private String json;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1918,7 +1918,8 @@ class WAFModuleSpecification extends DDSpecification {
1 * ctx.closeWafContext()
1 * ctx.reportDerivatives(['_dd.appsec.trace.agent':'RulesCompat/v1', '_dd.appsec.trace.integer': 123456789])
1 * ctx.isThrottled(null)
1 * ctx.reportEvents([])
// libddwaf 2.0.1: ResultWithData.events now reflects the real "events" array, so attributes-only matches don't call reportEvents().
0 * ctx.reportEvents(_)
0 * ctx._(*_)
!flow1.blocking

Expand All @@ -1937,7 +1938,8 @@ class WAFModuleSpecification extends DDSpecification {
1 * ctx.reportDerivatives(['_dd.appsec.trace.agent':'RulesCompat/v2', '_dd.appsec.trace.integer': 987654321])
1 * ctx.isThrottled(null)
1 * ctx.setManuallyKept(true)
1 * ctx.reportEvents([])
// See comment on the previous scenario: event:false means no entry in the "events" array.
0 * ctx.reportEvents(_)
0 * ctx._(*_)
!flow2.blocking

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.datadog.appsec.report;

import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.datadog.appsec.ddwaf.WAFResultData.Parameter;
import com.datadog.appsec.ddwaf.WAFResultData.Rule;
import com.datadog.appsec.ddwaf.WAFResultData.RuleMatch;
import java.util.Arrays;
import org.junit.jupiter.api.Test;

class AppSecEventWrapperTest {

@Test
void validateJsonSerializationForAppSecEvent() {
Parameter parameter =
new Parameter(
"parameter_address",
singletonList("parameter_key_path"),
"parameter_value",
singletonList("parameter_highlight"));
RuleMatch ruleMatch =
new RuleMatch("rule_match_operator", "rule_match_operator_value", singletonList(parameter));
AppSecEvent event =
new AppSecEvent.Builder()
.withRule(new Rule("rule_id", "rule_name", singletonMap("tag", "value")))
.withRuleMatches(singletonList(ruleMatch))
.build();

String json = new AppSecEventWrapper(singletonList(event)).toString();

String expectedJson =
"{\"triggers\":[{\"rule\":{\"id\":\"rule_id\",\"name\":\"rule_name\",\"tags\":{\"tag\":\"value\"}},"
+ "\"rule_matches\":[{\"operator\":\"rule_match_operator\",\"operator_value\":\"rule_match_operator_value\","
+ "\"parameters\":[{\"address\":\"parameter_address\",\"highlight\":[\"parameter_highlight\"],"
+ "\"key_path\":[\"parameter_key_path\"],\"value\":\"parameter_value\"}]}]}]}";
assertEquals(expectedJson, json);
}

// libddwaf 2.x emits array indices in key_path as numbers (e.g. key_path: [0], not ["0"]);
// this guards that a whole-number Double round-trips as "0", not "0.0".
@Test
void validateJsonSerializationForNumericKeyPath() {
Parameter parameter =
new Parameter("server.request.body", Arrays.asList("items", 0.0, "name"), "value", null);
RuleMatch ruleMatch = new RuleMatch("operator", "operator_value", singletonList(parameter));
AppSecEvent event =
new AppSecEvent.Builder()
.withRule(new Rule("rule_id", "rule_name", singletonMap("tag", "value")))
.withRuleMatches(singletonList(ruleMatch))
.build();

String json = new AppSecEventWrapper(singletonList(event)).toString();

assertTrue(json.contains("\"key_path\":[\"items\",0,\"name\"]"), "actual json: " + json);
}

// Non-whole-number Doubles must keep Moshi's default formatting - only whole numbers get the
// integer-style rewrite.
@Test
void validateJsonSerializationForNonIntegralKeyPath() {
Parameter parameter =
new Parameter("server.request.body", Arrays.asList("items", 1.5, "name"), "value", null);
RuleMatch ruleMatch = new RuleMatch("operator", "operator_value", singletonList(parameter));
AppSecEvent event =
new AppSecEvent.Builder()
.withRule(new Rule("rule_id", "rule_name", singletonMap("tag", "value")))
.withRuleMatches(singletonList(ruleMatch))
.build();

String json = new AppSecEventWrapper(singletonList(event)).toString();

assertTrue(json.contains("\"key_path\":[\"items\",1.5,\"name\"]"), "actual json: " + json);
}
}