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
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ public void onDataAvailable(

if (stringTooLong > 0 || listMapTooLarge > 0 || objectTooDeep > 0) {
reqCtx.setWafTruncated();
WafMetricCollector.get()
.wafInputTruncated(stringTooLong > 0, listMapTooLarge > 0, objectTooDeep > 0);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

public class WafMetricCollector implements MetricCollector<WafMetricCollector.WafMetric> {

private static final int MASK_STRING_TOO_LONG = 1; // 0b001
private static final int MASK_LIST_MAP_TOO_LARGE = 1 << 1; // 0b010
private static final int MASK_OBJECT_TOO_DEEP = 1 << 2; // 0b100

public static WafMetricCollector INSTANCE = new WafMetricCollector();

public static WafMetricCollector get() {
Expand All @@ -34,6 +38,9 @@ private WafMetricCollector() {
private static final int WAF_REQUEST_COMBINATIONS = 128; // 2^7
private final AtomicLongArray wafRequestCounter = new AtomicLongArray(WAF_REQUEST_COMBINATIONS);

private static final AtomicLongArray wafInputTruncatedCounter =
new AtomicLongArray(1 << 3); // 3 flags → 2^3 = 8 possible bit combinations

private static final AtomicLongArray raspRuleEvalCounter =
new AtomicLongArray(RuleType.getNumValues());
private static final AtomicLongArray raspRuleSkippedCounter =
Expand Down Expand Up @@ -104,6 +111,12 @@ public void wafRequest(
wafRequestCounter.incrementAndGet(index);
}

public void wafInputTruncated(
final boolean stringTooLong, final boolean listMapTooLarge, final boolean objectTooDeep) {
int index = computeWafInputTruncatedIndex(stringTooLong, listMapTooLarge, objectTooDeep);
wafInputTruncatedCounter.incrementAndGet(index);
}

static int computeWafRequestIndex(
boolean ruleTriggered,
boolean requestBlocked,
Expand All @@ -123,6 +136,15 @@ static int computeWafRequestIndex(
return index;
}

static int computeWafInputTruncatedIndex(
boolean stringTooLong, boolean listMapTooLarge, boolean objectTooDeep) {
int index = 0;
if (stringTooLong) index |= MASK_STRING_TOO_LONG;
if (listMapTooLarge) index |= MASK_LIST_MAP_TOO_LARGE;
if (objectTooDeep) index |= MASK_OBJECT_TOO_DEEP;
return index;
}

public void raspRuleEval(final RuleType ruleType) {
raspRuleEvalCounter.incrementAndGet(ruleType.ordinal());
}
Expand Down Expand Up @@ -216,6 +238,16 @@ public void prepareMetrics() {
}
}

// WAF input truncated
for (int i = 0; i < (1 << 3); i++) {
long counter = wafInputTruncatedCounter.getAndSet(i, 0);
if (counter > 0) {
if (!rawMetricsQueue.offer(new WafInputTruncated(counter, i))) {
return;
}
}
}

// RASP rule eval per rule type
for (RuleType ruleType : RuleType.values()) {
long counter = raspRuleEvalCounter.getAndSet(ruleType.ordinal(), 0);
Expand Down Expand Up @@ -516,6 +548,12 @@ public WafError(final long counter, final String wafVersion, final Integer ddwaf
}
}

public static class WafInputTruncated extends WafMetric {
public WafInputTruncated(final long counter, final int bitfield) {
super("waf.input_truncated", counter, "truncation_reason:" + bitfield);
}
}

/**
* Mirror of the {@code WafErrorCode} enum defined in the {@code libddwaf-java} module.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,29 @@ class WafMetricCollectorTest extends DDSpecification {
[triggered, blocked, wafError, wafTimeout, blockFailure, rateLimited, inputTruncated] << allBooleanCombinations(7)
}

void 'test waf input truncated metrics'() {
given:
def collector = WafMetricCollector.get()
def bitField = WafMetricCollector.computeWafInputTruncatedIndex(stringTooLong, listMapTooLarge, objectTooDeep)

when:
collector.wafInputTruncated(stringTooLong, listMapTooLarge, objectTooDeep)

then:
collector.prepareMetrics()
def metrics = collector.drain()
def inputTruncatedMetrics = metrics.findAll { it.metricName == 'waf.input_truncated' }

final metric = inputTruncatedMetrics[0]
metric.type == 'count'
metric.metricName == 'waf.input_truncated'
metric.namespace == 'appsec'
metric.tags == ["truncation_reason:${bitField}"]

where:
[stringTooLong, listMapTooLarge, objectTooDeep] << allBooleanCombinations(3)
}

/**
* Helper method to generate all combinations of n boolean values.
*/
Expand Down
Loading