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

if (gwCtx.isRasp) {
reqCtx.setRaspMatched(true);
WafMetricCollector.get().raspRuleMatch(gwCtx.raspRuleType);
}

String securityResponseId = null;
Expand All @@ -375,13 +374,17 @@ public void onDataAvailable(
securityResponseId = (String) actionInfo.parameters.get("security_response_id");
Flow.Action.RequestBlockingAction rba =
createBlockRequestAction(actionInfo, reqCtx, gwCtx.isRasp, securityResponseId);
flow.setAction(rba);
if (rba != null) {
flow.setAction(rba);
}
} else if ("redirect_request".equals(actionInfo.type)) {
// Extract security_response_id from action parameters for use in triggers
securityResponseId = (String) actionInfo.parameters.get("security_response_id");
Flow.Action.RequestBlockingAction rba =
createRedirectRequestAction(actionInfo, reqCtx, gwCtx.isRasp, securityResponseId);
flow.setAction(rba);
if (rba != null) {
flow.setAction(rba);
}
} else if ("generate_stack".equals(actionInfo.type)) {
if (Config.get().isAppSecStackTraceEnabled()) {
String stackId = (String) actionInfo.parameters.get("stack_id");
Expand Down Expand Up @@ -417,6 +420,9 @@ public void onDataAvailable(
}
}
}
if (gwCtx.isRasp) {
WafMetricCollector.get().raspRuleMatch(gwCtx.raspRuleType, flow.isBlocking());
}
Collection<AppSecEvent> events = buildEvents(resultWithData, securityResponseId);
boolean isThrottled = reqCtx.isThrottled(rateLimiter);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private WafMetricCollector() {
private static final AtomicLongArray raspRuleSkippedCounter =
new AtomicLongArray(RuleType.getNumValues());
private static final AtomicLongArray raspRuleMatchCounter =
new AtomicLongArray(RuleType.getNumValues());
new AtomicLongArray(RuleType.getNumValues() * 2);
private static final AtomicLongArray raspTimeoutCounter =
new AtomicLongArray(RuleType.getNumValues());
private static final AtomicLongArray raspErrorCodeCounter =
Expand Down Expand Up @@ -154,8 +154,8 @@ public void raspRuleSkipped(final RuleType ruleType) {
raspRuleSkippedCounter.incrementAndGet(ruleType.ordinal());
}

public void raspRuleMatch(final RuleType ruleType) {
raspRuleMatchCounter.incrementAndGet(ruleType.ordinal());
public void raspRuleMatch(final RuleType ruleType, final boolean blocked) {
raspRuleMatchCounter.incrementAndGet(ruleType.ordinal() * 2 + (blocked ? 1 : 0));
}

public void raspTimeout(final RuleType ruleType) {
Expand Down Expand Up @@ -272,12 +272,20 @@ public void prepareMetrics() {
}
}

// RASP rule match per rule type
// RASP rule match per rule type: two slots per RuleType: ordinal*2 (non-blocked),
// ordinal*2+1 (blocked)
for (RuleType ruleType : RuleType.values()) {
long counter = raspRuleMatchCounter.getAndSet(ruleType.ordinal(), 0);
if (counter > 0) {
long blockedCount = raspRuleMatchCounter.getAndSet(ruleType.ordinal() * 2 + 1, 0);
if (blockedCount > 0) {
if (!rawMetricsQueue.offer(
new RaspRuleMatch(counter, ruleType, WafMetricCollector.wafVersion))) {
new RaspRuleMatch(blockedCount, ruleType, WafMetricCollector.wafVersion, true))) {
return;
}
}
long nonBlockedCount = raspRuleMatchCounter.getAndSet(ruleType.ordinal() * 2, 0);
if (nonBlockedCount > 0) {
if (!rawMetricsQueue.offer(
new RaspRuleMatch(nonBlockedCount, ruleType, WafMetricCollector.wafVersion, false))) {
return;
}
}
Expand Down Expand Up @@ -552,7 +560,11 @@ public AfterRequestRaspRuleSkipped(final long counter, final RuleType ruleType)
}

public static class RaspRuleMatch extends WafMetric {
public RaspRuleMatch(final long counter, final RuleType ruleType, final String wafVersion) {
public RaspRuleMatch(
final long counter,
final RuleType ruleType,
final String wafVersion,
final boolean blocked) {
super(
"rasp.rule.match",
counter,
Expand All @@ -561,9 +573,12 @@ public RaspRuleMatch(final long counter, final RuleType ruleType, final String w
"rule_type:" + ruleType.type,
"rule_variant:" + ruleType.variant,
"waf_version:" + wafVersion,
"event_rules_version:" + rulesVersion
"event_rules_version:" + rulesVersion,
"block:" + blocked
}
: new String[] {"rule_type:" + ruleType.type, "waf_version:" + wafVersion});
: new String[] {
"rule_type:" + ruleType.type, "waf_version:" + wafVersion, "block:" + blocked
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class WafMetricCollectorTest extends DDSpecification {
WafMetricCollector.get().wafUpdates('rules.3', false)
WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION, false)
WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspTimeout(RuleType.SQL_INJECTION)
WafMetricCollector.get().raspErrorCode(RuleType.SHELL_INJECTION, DD_WAF_RUN_INTERNAL_ERROR)
Expand Down Expand Up @@ -85,7 +85,7 @@ class WafMetricCollectorTest extends DDSpecification {
raspRuleMatch.value == 1
raspRuleMatch.namespace == 'appsec'
raspRuleMatch.metricName == 'rasp.rule.match'
raspRuleMatch.tags.toSet() == ['rule_type:sql_injection', 'waf_version:waf_ver1'].toSet()
raspRuleMatch.tags.toSet() == ['rule_type:sql_injection', 'waf_version:waf_ver1', 'block:false'].toSet()

def raspTimeout = (WafMetricCollector.RaspTimeout) metrics[5]
raspTimeout.type == 'count'
Expand Down Expand Up @@ -317,7 +317,7 @@ class WafMetricCollectorTest extends DDSpecification {
WafMetricCollector.get().wafInit('waf_ver1', 'rules.1', true)
WafMetricCollector.get().raspRuleEval(ruleType)
WafMetricCollector.get().raspRuleEval(ruleType)
WafMetricCollector.get().raspRuleMatch(ruleType)
WafMetricCollector.get().raspRuleMatch(ruleType, false)
WafMetricCollector.get().raspRuleEval(ruleType)
WafMetricCollector.get().raspTimeout(ruleType)
WafMetricCollector.get().raspErrorCode(ruleType, DD_WAF_RUN_INTERNAL_ERROR)
Expand Down Expand Up @@ -349,7 +349,8 @@ class WafMetricCollectorTest extends DDSpecification {
'rule_type:command_injection',
'rule_variant:' + ruleType.variant,
'waf_version:waf_ver1',
'event_rules_version:rules.1'
'event_rules_version:rules.1',
'block:false'
].toSet()

def raspTimeout = (WafMetricCollector.RaspTimeout) metrics[3]
Expand Down Expand Up @@ -585,6 +586,70 @@ class WafMetricCollectorTest extends DDSpecification {
type << [MESSAGES, CONTENT]
}

void 'test rasp rule match block tag'() {
Comment thread
jandro996 marked this conversation as resolved.
given:
final collector = WafMetricCollector.get()
collector.wafInit('waf_ver1', 'rules.1', true)

when:
collector.raspRuleMatch(ruleType, blocked)

then:
collector.prepareMetrics()
final metrics = collector.drain()
final matchMetrics = metrics.findAll { it.metricName == 'rasp.rule.match' }

matchMetrics.size() == 1
final metric = matchMetrics[0]
metric.type == 'count'
metric.value == 1
metric.namespace == 'appsec'
final expectedTags = ruleType.variant != null
? [
'rule_type:' + ruleType.type,
'rule_variant:' + ruleType.variant,
'waf_version:waf_ver1',
'event_rules_version:rules.1',
'block:' + blocked
].toSet()
: ['rule_type:' + ruleType.type, 'waf_version:waf_ver1', 'block:' + blocked].toSet()
metric.tags.toSet() == expectedTags

where:
ruleType | blocked
RuleType.SQL_INJECTION | true
RuleType.SQL_INJECTION | false
RuleType.LFI | true
RuleType.LFI | false
RuleType.SSRF_REQUEST | true
RuleType.SSRF_REQUEST | false
RuleType.SSRF_RESPONSE | true
RuleType.SSRF_RESPONSE | false
RuleType.SHELL_INJECTION | true
RuleType.SHELL_INJECTION | false
RuleType.COMMAND_INJECTION | true
RuleType.COMMAND_INJECTION | false
}

void 'test rasp rule match drains blocked and non-blocked as separate metrics'() {
given:
final collector = WafMetricCollector.get()
collector.wafInit('waf_ver1', 'rules.1', true)

when:
collector.raspRuleMatch(RuleType.SQL_INJECTION, true)
collector.raspRuleMatch(RuleType.SQL_INJECTION, false)

then:
collector.prepareMetrics()
final metrics = collector.drain()
final matchMetrics = metrics.findAll { it.metricName == 'rasp.rule.match' }

matchMetrics.size() == 2
matchMetrics.find { it.tags.contains('block:true') }?.value == 1
matchMetrics.find { it.tags.contains('block:false') }?.value == 1
}

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