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 @@ -77,6 +77,9 @@ public class AppSecRequestContext implements DataBundle, Closeable {
public static final Set<String> RESPONSE_HEADERS_ALLOW_LIST =
new TreeSet<>(
Arrays.asList("content-length", "content-type", "content-encoding", "content-language"));
public static final int DD_WAF_RUN_INTERNAL_ERROR = -3;
public static final int DD_WAF_RUN_INVALID_OBJECT_ERROR = -2;
public static final int DD_WAF_RUN_INVALID_ARGUMENT_ERROR = -1;

static {
REQUEST_HEADERS_ALLOW_LIST.addAll(DEFAULT_REQUEST_HEADERS_ALLOW_LIST);
Expand Down Expand Up @@ -124,6 +127,9 @@ public class AppSecRequestContext implements DataBundle, Closeable {
private volatile boolean blocked;
private volatile int wafTimeouts;
private volatile int raspTimeouts;
private volatile int raspInternalErrors;
private volatile int raspInvalidObjectErrors;
private volatile int raspInvalidArgumentErrors;

// keep a reference to the last published usr.id
private volatile String userId;
Expand All @@ -139,6 +145,18 @@ public class AppSecRequestContext implements DataBundle, Closeable {
private static final AtomicIntegerFieldUpdater<AppSecRequestContext> RASP_TIMEOUTS_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AppSecRequestContext.class, "raspTimeouts");

private static final AtomicIntegerFieldUpdater<AppSecRequestContext>
RASP_INTERNAL_ERRORS_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AppSecRequestContext.class, "raspInternalErrors");
private static final AtomicIntegerFieldUpdater<AppSecRequestContext>
RASP_INVALID_OBJECT_ERRORS_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(
AppSecRequestContext.class, "raspInvalidObjectErrors");
private static final AtomicIntegerFieldUpdater<AppSecRequestContext>
RASP_INVALID_ARGUMENT_ERRORS_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(
AppSecRequestContext.class, "raspInvalidArgumentErrors");

// to be called by the Event Dispatcher
public void addAll(DataBundle newData) {
for (Map.Entry<Address<?>, Object> entry : newData) {
Expand Down Expand Up @@ -188,6 +206,22 @@ public void increaseRaspTimeouts() {
RASP_TIMEOUTS_UPDATER.incrementAndGet(this);
}

public void increaseRaspErrorCode(int code) {
switch (code) {
case DD_WAF_RUN_INTERNAL_ERROR:
RASP_INTERNAL_ERRORS_UPDATER.incrementAndGet(this);
break;
case DD_WAF_RUN_INVALID_OBJECT_ERROR:
RASP_INVALID_OBJECT_ERRORS_UPDATER.incrementAndGet(this);
break;
case DD_WAF_RUN_INVALID_ARGUMENT_ERROR:
RASP_INVALID_ARGUMENT_ERRORS_UPDATER.incrementAndGet(this);
break;
default:
break;
}
}

public int getWafTimeouts() {
return wafTimeouts;
}
Expand All @@ -196,6 +230,19 @@ public int getRaspTimeouts() {
return raspTimeouts;
}

public int getRaspError(int code) {
switch (code) {
case DD_WAF_RUN_INTERNAL_ERROR:
return raspInternalErrors;
case DD_WAF_RUN_INVALID_OBJECT_ERROR:
return raspInvalidObjectErrors;
case DD_WAF_RUN_INVALID_ARGUMENT_ERROR:
return raspInvalidArgumentErrors;
default:
return 0;
}
}

public Additive getOrCreateAdditive(PowerwafContext ctx, boolean createMetrics, boolean isRasp) {

if (createMetrics) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import io.sqreen.powerwaf.exception.AbstractPowerwafException;
import io.sqreen.powerwaf.exception.InvalidRuleSetException;
import io.sqreen.powerwaf.exception.TimeoutPowerwafException;
import io.sqreen.powerwaf.exception.UnclassifiedPowerwafException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
Expand Down Expand Up @@ -440,11 +441,21 @@ public void onDataAvailable(
log.debug(LogCollector.EXCLUDE_TELEMETRY, "Timeout calling the WAF", tpe);
}
return;
} catch (AbstractPowerwafException e) {
} catch (UnclassifiedPowerwafException e) {
if (!reqCtx.isAdditiveClosed()) {
log.error("Error calling WAF", e);
}
return;
} catch (AbstractPowerwafException e) {
if (gwCtx.isRasp) {
reqCtx.increaseRaspErrorCode(e.code);
WafMetricCollector.get().raspErrorCode(gwCtx.raspRuleType, e.code);
} else {
// TODO APPSEC-56703
// reqCtx.increaseWafErrorCode(e.code);
// WafMetricCollector.get().wafErrorCode(e.code);
}
return;
} finally {
if (log.isDebugEnabled()) {
long elapsed = System.currentTimeMillis() - start;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,4 +290,17 @@ class AppSecRequestContextSpecification extends DDSpecification {
then:
ctx.getRaspTimeouts() == 2
}

def "test increase and get RaspErrors"() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more readable if you also use the constants in the tests :)

DD_WAF_RUN_INTERNAL_ERROR
DD_WAF_RUN_INVALID_OBJECT_ERROR
DD_WAF_RUN_INVALID_ARGUMENT_ERROR

when:
ctx.increaseRaspErrorCode(AppSecRequestContext.DD_WAF_RUN_INTERNAL_ERROR)
ctx.increaseRaspErrorCode(AppSecRequestContext.DD_WAF_RUN_INTERNAL_ERROR)
ctx.increaseRaspErrorCode(AppSecRequestContext.DD_WAF_RUN_INVALID_OBJECT_ERROR)

then:
ctx.getRaspError(AppSecRequestContext.DD_WAF_RUN_INTERNAL_ERROR) == 2
ctx.getRaspError(AppSecRequestContext.DD_WAF_RUN_INVALID_OBJECT_ERROR) == 1
ctx.getRaspError(AppSecRequestContext.DD_WAF_RUN_INVALID_ARGUMENT_ERROR) == 0
ctx.getRaspError(0) == 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;

public class WafMetricCollector implements MetricCollector<WafMetricCollector.WafMetric> {

public static WafMetricCollector INSTANCE = new WafMetricCollector();
private static final int ABSTRACT_POWERWAF_EXCEPTION_NUMBER =
3; // only 3 error codes are possible for now in AbstractPowerwafException

public static WafMetricCollector get() {
return WafMetricCollector.INSTANCE;
Expand Down Expand Up @@ -40,6 +44,15 @@ private WafMetricCollector() {
new AtomicLongArray(RuleType.getNumValues());
private static final AtomicLongArray raspTimeoutCounter =
new AtomicLongArray(RuleType.getNumValues());
private static final ConcurrentMap<Integer, AtomicLongArray> raspErrorCodeCounter =
new ConcurrentSkipListMap<>();

static {
for (int i = -1 * ABSTRACT_POWERWAF_EXCEPTION_NUMBER; i < 0; i++) {
raspErrorCodeCounter.put(i, new AtomicLongArray(RuleType.getNumValues()));
}
}

private static final AtomicLongArray missingUserLoginQueue =
new AtomicLongArray(LoginFramework.getNumValues() * LoginEvent.getNumValues());
private static final AtomicLongArray missingUserIdQueue =
Expand Down Expand Up @@ -104,6 +117,10 @@ public void raspTimeout(final RuleType ruleType) {
raspTimeoutCounter.incrementAndGet(ruleType.ordinal());
}

public void raspErrorCode(final RuleType ruleType, final int ddwafRunErrorCode) {
raspErrorCodeCounter.get(ddwafRunErrorCode).incrementAndGet(ruleType.ordinal());
}

public void missingUserLogin(final LoginFramework framework, final LoginEvent eventType) {
missingUserLoginQueue.incrementAndGet(
framework.ordinal() * LoginEvent.getNumValues() + eventType.ordinal());
Expand Down Expand Up @@ -216,6 +233,19 @@ public void prepareMetrics() {
}
}

// RASP rule type for each possible error code
for (int i = -1 * ABSTRACT_POWERWAF_EXCEPTION_NUMBER; i < 0; i++) {
for (RuleType ruleType : RuleType.values()) {
long counter = raspErrorCodeCounter.get(i).getAndSet(ruleType.ordinal(), 0);
if (counter > 0) {
if (!rawMetricsQueue.offer(
new RaspError(counter, ruleType, WafMetricCollector.wafVersion, i))) {
return;
}
}
}
}

// Missing user login
for (LoginFramework framework : LoginFramework.values()) {
for (LoginEvent event : LoginEvent.values()) {
Expand Down Expand Up @@ -367,6 +397,31 @@ public RaspTimeout(final long counter, final RuleType ruleType, final String waf
}
}

public static class RaspError extends WafMetric {
public RaspError(
final long counter,
final RuleType ruleType,
final String wafVersion,
final Integer ddwafRunError) {
super(
"rasp.error",
counter,
ruleType.variant != null
? new String[] {
"rule_type:" + ruleType.type,
"rule_variant:" + ruleType.variant,
"waf_version:" + wafVersion,
"event_rules_version:" + rulesVersion,
"waf_error:" + ddwafRunError
}
: new String[] {
"rule_type:" + ruleType.type,
"waf_version:" + wafVersion,
"waf_error:" + ddwafRunError
});
}
}

public static class AtomicRequestCounter {

private final AtomicLong atomicLong = new AtomicLong();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import java.util.concurrent.TimeUnit

class WafMetricCollectorTest extends DDSpecification {

public static final int DD_WAF_RUN_INTERNAL_ERROR = -3
public static final int DD_WAF_RUN_INVALID_OBJECT_ERROR = -2

def "no metrics - drain empty list"() {
when:
WafMetricCollector.get().prepareMetrics()
Expand All @@ -32,6 +35,8 @@ class WafMetricCollectorTest extends DDSpecification {
WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspTimeout(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspErrorCode(RuleType.SHELL_INJECTION, DD_WAF_RUN_INTERNAL_ERROR)
WafMetricCollector.get().raspErrorCode(RuleType.SQL_INJECTION, DD_WAF_RUN_INVALID_OBJECT_ERROR)

WafMetricCollector.get().prepareMetrics()

Expand Down Expand Up @@ -131,6 +136,31 @@ class WafMetricCollectorTest extends DDSpecification {
raspTimeout.namespace == 'appsec'
raspTimeout.metricName == 'rasp.timeout'
raspTimeout.tags.toSet() == ['rule_type:sql_injection', 'waf_version:waf_ver1'].toSet()

def raspInvalidCode = (WafMetricCollector.RaspError)metrics[10]
raspInvalidCode.type == 'count'
raspInvalidCode.value == 1
raspInvalidCode.namespace == 'appsec'
raspInvalidCode.metricName == 'rasp.error'
raspInvalidCode.tags.toSet() == [
'waf_version:waf_ver1',
'rule_type:command_injection',
'rule_variant:shell',
'event_rules_version:rules.3',
'waf_error:' + DD_WAF_RUN_INTERNAL_ERROR
].toSet()

def raspInvalidObjectCode = (WafMetricCollector.RaspError)metrics[11]
raspInvalidObjectCode.type == 'count'
raspInvalidObjectCode.value == 1
raspInvalidObjectCode.namespace == 'appsec'
raspInvalidObjectCode.metricName == 'rasp.error'
raspInvalidObjectCode.tags.toSet() == [
'rule_type:sql_injection',
'waf_version:waf_ver1',
'waf_error:' + DD_WAF_RUN_INVALID_OBJECT_ERROR
]
.toSet()
}

def "overflowing WafMetricCollector does not crash"() {
Expand Down Expand Up @@ -304,6 +334,7 @@ class WafMetricCollectorTest extends DDSpecification {
WafMetricCollector.get().raspRuleMatch(ruleType)
WafMetricCollector.get().raspRuleEval(ruleType)
WafMetricCollector.get().raspTimeout(ruleType)
WafMetricCollector.get().raspErrorCode(ruleType, DD_WAF_RUN_INTERNAL_ERROR)
WafMetricCollector.get().prepareMetrics()

then:
Expand Down Expand Up @@ -345,6 +376,19 @@ class WafMetricCollectorTest extends DDSpecification {
'event_rules_version:rules.1'
].toSet()

def raspInvalidCode = (WafMetricCollector.RaspError)metrics[4]
raspInvalidCode.type == 'count'
raspInvalidCode.value == 1
raspInvalidCode.namespace == 'appsec'
raspInvalidCode.metricName == 'rasp.error'
raspInvalidCode.tags.toSet() == [
'waf_version:waf_ver1',
'rule_type:command_injection',
'rule_variant:' + ruleType.variant,
'event_rules_version:rules.1',
'waf_error:' + DD_WAF_RUN_INTERNAL_ERROR
].toSet()

where:
ruleType << [RuleType.COMMAND_INJECTION, RuleType.SHELL_INJECTION]
}
Expand Down
Loading