Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create metric: appsec.rasp.rule.skipped #8618

Merged
Merged
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
@@ -417,6 +417,9 @@ public void onDataAvailable(

if (reqCtx.isAdditiveClosed()) {
log.debug("Skipped; the WAF context is closed");
if (gwCtx.isRasp) {
WafMetricCollector.get().raspRuleSkipped(gwCtx.raspRuleType);
}
return;
}

Original file line number Diff line number Diff line change
@@ -1731,6 +1731,30 @@ class PowerWAFModuleSpecification extends DDSpecification {
0 * _
}

void 'raspRuleSkipped if rasp available and WAF context is closed'() {
setup:
ChangeableFlow flow = Mock()
GatewayContext gwCtxMock = new GatewayContext(false, RuleType.SQL_INJECTION)

when:
setupWithStubConfigService('rules_with_data_config.json')
dataListener = pwafModule.dataSubscriptions.first()

def bundle = MapDataBundle.of(
KnownAddresses.USER_ID,
'legit-user'
)
ctx.closeAdditive()
dataListener.onDataAvailable(flow, ctx, bundle, gwCtxMock)

then:
1 * ctx.closeAdditive()
1 * ctx.isAdditiveClosed() >> true
1 * wafMetricCollector.wafInit(Powerwaf.LIB_VERSION, _, true)
1 * wafMetricCollector.raspRuleSkipped(RuleType.SQL_INJECTION)
0 * _
}

private Map<String, Object> getDefaultConfig() {
def service = new StubAppSecConfigService()
service.init()
Original file line number Diff line number Diff line change
@@ -47,6 +47,8 @@ private WafMetricCollector() {
new AtomicLongArray(WafTruncatedType.values().length);
private static final AtomicLongArray raspRuleEvalCounter =
new AtomicLongArray(RuleType.getNumValues());
private static final AtomicLongArray raspRuleSkippedCounter =
new AtomicLongArray(RuleType.getNumValues());
private static final AtomicLongArray raspRuleMatchCounter =
new AtomicLongArray(RuleType.getNumValues());
private static final AtomicLongArray raspTimeoutCounter =
@@ -135,6 +137,10 @@ public void raspRuleEval(final RuleType ruleType) {
raspRuleEvalCounter.incrementAndGet(ruleType.ordinal());
}

public void raspRuleSkipped(final RuleType ruleType) {
raspRuleSkippedCounter.incrementAndGet(ruleType.ordinal());
}

public void raspRuleMatch(final RuleType ruleType) {
raspRuleMatchCounter.incrementAndGet(ruleType.ordinal());
}
@@ -347,6 +353,16 @@ public void prepareMetrics() {
}
}
}

// RASP rule skipped per rule type for after-request reason
for (RuleType ruleType : RuleType.values()) {
long counter = raspRuleSkippedCounter.getAndSet(ruleType.ordinal(), 0);
if (counter > 0) {
if (!rawMetricsQueue.offer(new AfterRequestRaspRuleSkipped(counter, ruleType))) {
return;
}
}
}
}

public abstract static class WafMetric extends MetricCollector.Metric {
@@ -451,6 +467,22 @@ public RaspRuleEval(final long counter, final RuleType ruleType, final String wa
}
}

// Although rasp.rule.skipped reason could be before-request, there is no real case scenario
public static class AfterRequestRaspRuleSkipped extends WafMetric {
public AfterRequestRaspRuleSkipped(final long counter, final RuleType ruleType) {
super(
"rasp.rule.skipped",
counter,
ruleType.variant != null
? new String[] {
"rule_type:" + ruleType.type,
"rule_variant:" + ruleType.variant,
"reason:" + "after-request"
}
: new String[] {"rule_type:" + ruleType.type, "reason:" + "after-request"});
}
}

public static class RaspRuleMatch extends WafMetric {
public RaspRuleMatch(final long counter, final RuleType ruleType, final String wafVersion) {
super(
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ class WafMetricCollectorTest extends DDSpecification {
WafMetricCollector.get().wafErrorCode(RuleType.SHELL_INJECTION, DD_WAF_RUN_INTERNAL_ERROR)
WafMetricCollector.get().raspErrorCode(RuleType.SQL_INJECTION, DD_WAF_RUN_INVALID_OBJECT_ERROR)
WafMetricCollector.get().wafErrorCode(RuleType.SQL_INJECTION, DD_WAF_RUN_INVALID_OBJECT_ERROR)
WafMetricCollector.get().raspRuleSkipped(RuleType.SQL_INJECTION)

WafMetricCollector.get().prepareMetrics()

@@ -224,6 +225,13 @@ class WafMetricCollectorTest extends DDSpecification {
'waf_version:waf_ver1',
'waf_error:'+DD_WAF_RUN_INVALID_OBJECT_ERROR
].toSet()

def raspRuleSkipped = (WafMetricCollector.AfterRequestRaspRuleSkipped)metrics[15]
raspRuleSkipped.type == 'count'
raspRuleSkipped.value == 1
raspRuleSkipped.namespace == 'appsec'
raspRuleSkipped.metricName == 'rasp.rule.skipped'
raspRuleSkipped.tags.toSet() == ['rule_type:sql_injection', 'reason:after-request',].toSet()
}

def "overflowing WafMetricCollector does not crash"() {
@@ -399,6 +407,7 @@ class WafMetricCollectorTest extends DDSpecification {
WafMetricCollector.get().raspTimeout(ruleType)
WafMetricCollector.get().raspErrorCode(ruleType, DD_WAF_RUN_INTERNAL_ERROR)
WafMetricCollector.get().wafErrorCode(ruleType, DD_WAF_RUN_INTERNAL_ERROR)
WafMetricCollector.get().raspRuleSkipped(ruleType)
WafMetricCollector.get().prepareMetrics()

then:
@@ -466,6 +475,17 @@ class WafMetricCollectorTest extends DDSpecification {
'waf_error:' + DD_WAF_RUN_INTERNAL_ERROR
].toSet()

def raspRuleSkipped = (WafMetricCollector.AfterRequestRaspRuleSkipped)metrics[6]
raspRuleSkipped.type == 'count'
raspRuleSkipped.value == 1
raspRuleSkipped.namespace == 'appsec'
raspRuleSkipped.metricName == 'rasp.rule.skipped'
raspRuleSkipped.tags.toSet() == [
'rule_type:command_injection',
'rule_variant:'+ruleType.variant,
'reason:after-request',
].toSet()

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