From d592e39ced3c308a15c2363abae197f5c3fbf6f5 Mon Sep 17 00:00:00 2001 From: Ugaitz Urien Date: Tue, 11 Jun 2024 13:27:19 +0200 Subject: [PATCH 1/2] Exploit prevention metrics (#4370) --------- Co-authored-by: Carles Capell <107924659+CarlesDD@users.noreply.github.com> Co-authored-by: simon-id --- packages/dd-trace/src/appsec/rasp.js | 6 +- packages/dd-trace/src/appsec/reporter.js | 22 ++- packages/dd-trace/src/appsec/telemetry.js | 34 +++- packages/dd-trace/src/appsec/waf/index.js | 4 +- .../src/appsec/waf/waf_context_wrapper.js | 4 +- .../test/appsec/rasp.express.plugin.spec.js | 6 + packages/dd-trace/test/appsec/rasp.spec.js | 2 +- .../dd-trace/test/appsec/reporter.spec.js | 30 ++++ .../dd-trace/test/appsec/telemetry.spec.js | 158 ++++++++++++++++++ .../dd-trace/test/appsec/waf/index.spec.js | 64 +++++++ 10 files changed, 320 insertions(+), 10 deletions(-) diff --git a/packages/dd-trace/src/appsec/rasp.js b/packages/dd-trace/src/appsec/rasp.js index 1a4873718b9..268a0a5bd82 100644 --- a/packages/dd-trace/src/appsec/rasp.js +++ b/packages/dd-trace/src/appsec/rasp.js @@ -5,6 +5,10 @@ const addresses = require('./addresses') const { httpClientRequestStart } = require('./channels') const waf = require('./waf') +const RULE_TYPES = { + SSRF: 'ssrf' +} + function enable () { httpClientRequestStart.subscribe(analyzeSsrf) } @@ -26,7 +30,7 @@ function analyzeSsrf (ctx) { // TODO: Currently this is only monitoring, we should // block the request if SSRF attempt and // generate stack traces - waf.run({ persistent }, req) + waf.run({ persistent }, req, RULE_TYPES.SSRF) } module.exports = { diff --git a/packages/dd-trace/src/appsec/reporter.js b/packages/dd-trace/src/appsec/reporter.js index dc85d70256a..db2a564c8b6 100644 --- a/packages/dd-trace/src/appsec/reporter.js +++ b/packages/dd-trace/src/appsec/reporter.js @@ -7,6 +7,7 @@ const { ipHeaderList } = require('../plugins/util/ip_extractor') const { incrementWafInitMetric, updateWafRequestsMetricTags, + updateRaspRequestsMetricTags, incrementWafUpdatesMetric, incrementWafRequestsMetric, getRequestMetrics @@ -92,7 +93,7 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) { incrementWafInitMetric(wafVersion, rulesVersion) } -function reportMetrics (metrics) { +function reportMetrics (metrics, raspRuleType) { const store = storage.getStore() const rootSpan = store?.req && web.root(store.req) if (!rootSpan) return @@ -100,8 +101,11 @@ function reportMetrics (metrics) { if (metrics.rulesVersion) { rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion) } - - updateWafRequestsMetricTags(metrics, store.req) + if (raspRuleType) { + updateRaspRequestsMetricTags(metrics, store.req, raspRuleType) + } else { + updateWafRequestsMetricTags(metrics, store.req) + } } function reportAttack (attackData) { @@ -180,6 +184,18 @@ function finishRequest (req, res) { rootSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt) } + if (metrics?.raspDuration) { + rootSpan.setTag('_dd.appsec.rasp.duration', metrics.raspDuration) + } + + if (metrics?.raspDurationExt) { + rootSpan.setTag('_dd.appsec.rasp.duration_ext', metrics.raspDurationExt) + } + + if (metrics?.raspEvalCount) { + rootSpan.setTag('_dd.appsec.rasp.rule.eval', metrics.raspEvalCount) + } + incrementWafRequestsMetric(req) // collect some headers even when no attack is detected diff --git a/packages/dd-trace/src/appsec/telemetry.js b/packages/dd-trace/src/appsec/telemetry.js index 1b7dee55ef9..6e7fd6ae47d 100644 --- a/packages/dd-trace/src/appsec/telemetry.js +++ b/packages/dd-trace/src/appsec/telemetry.js @@ -31,7 +31,10 @@ function newStore () { return { [DD_TELEMETRY_REQUEST_METRICS]: { duration: 0, - durationExt: 0 + durationExt: 0, + raspDuration: 0, + raspDurationExt: 0, + raspEvalCount: 0 } } } @@ -76,6 +79,28 @@ function getOrCreateMetricTags (store, versionsTags) { return metricTags } +function updateRaspRequestsMetricTags (metrics, req, raspRuleType) { + if (!req) return + + const store = getStore(req) + + // it does not depend on whether telemetry is enabled or not + addRaspRequestMetrics(store, metrics) + + if (!enabled) return + + const tags = { rule_type: raspRuleType, waf_version: metrics.wafVersion } + appsecMetrics.count('appsec.rasp.rule.eval', tags).inc(1) + + if (metrics.wafTimeout) { + appsecMetrics.count('appsec.rasp.timeout', tags).inc(1) + } + + if (metrics.ruleTriggered) { + appsecMetrics.count('appsec.rasp.rule.match', tags).inc(1) + } +} + function updateWafRequestsMetricTags (metrics, req) { if (!req) return @@ -141,6 +166,12 @@ function addRequestMetrics (store, { duration, durationExt }) { store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0 } +function addRaspRequestMetrics (store, { duration, durationExt }) { + store[DD_TELEMETRY_REQUEST_METRICS].raspDuration += duration || 0 + store[DD_TELEMETRY_REQUEST_METRICS].raspDurationExt += durationExt || 0 + store[DD_TELEMETRY_REQUEST_METRICS].raspEvalCount++ +} + function getRequestMetrics (req) { if (req) { const store = getStore(req) @@ -153,6 +184,7 @@ module.exports = { disable, updateWafRequestsMetricTags, + updateRaspRequestsMetricTags, incrementWafInitMetric, incrementWafUpdatesMetric, incrementWafRequestsMetric, diff --git a/packages/dd-trace/src/appsec/waf/index.js b/packages/dd-trace/src/appsec/waf/index.js index 13190e9c7be..8aa30fabbb4 100644 --- a/packages/dd-trace/src/appsec/waf/index.js +++ b/packages/dd-trace/src/appsec/waf/index.js @@ -46,7 +46,7 @@ function update (newRules) { } } -function run (data, req) { +function run (data, req, raspRuleType) { if (!req) { const store = storage.getStore() if (!store || !store.req) { @@ -59,7 +59,7 @@ function run (data, req) { const wafContext = waf.wafManager.getWAFContext(req) - return wafContext.run(data) + return wafContext.run(data, raspRuleType) } function disposeContext (req) { diff --git a/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js b/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js index 53da0d5d5df..7b437388b67 100644 --- a/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +++ b/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js @@ -19,7 +19,7 @@ class WAFContextWrapper { this.addressesToSkip = new Set() } - run ({ persistent, ephemeral }) { + run ({ persistent, ephemeral }, raspRuleType) { const payload = {} let payloadHasData = false const inputs = {} @@ -72,7 +72,7 @@ class WAFContextWrapper { blockTriggered, wafVersion: this.wafVersion, wafTimeout: result.timeout - }) + }, raspRuleType) if (ruleTriggered) { Reporter.reportAttack(JSON.stringify(result.events)) diff --git a/packages/dd-trace/test/appsec/rasp.express.plugin.spec.js b/packages/dd-trace/test/appsec/rasp.express.plugin.spec.js index 249af2ae727..23960db1aa3 100644 --- a/packages/dd-trace/test/appsec/rasp.express.plugin.spec.js +++ b/packages/dd-trace/test/appsec/rasp.express.plugin.spec.js @@ -89,6 +89,9 @@ withVersions('express', 'express', expressVersion => { const span = getWebSpan(traces) assert.property(span.meta, '_dd.appsec.json') assert(span.meta['_dd.appsec.json'].includes('rasp-ssrf-rule-id-1')) + assert.equal(span.metrics['_dd.appsec.rasp.rule.eval'], 1) + assert(span.metrics['_dd.appsec.rasp.duration'] > 0) + assert(span.metrics['_dd.appsec.rasp.duration_ext'] > 0) }) }) @@ -107,6 +110,9 @@ withVersions('express', 'express', expressVersion => { const span = getWebSpan(traces) assert.property(span.meta, '_dd.appsec.json') assert(span.meta['_dd.appsec.json'].includes('rasp-ssrf-rule-id-1')) + assert.equal(span.metrics['_dd.appsec.rasp.rule.eval'], 1) + assert(span.metrics['_dd.appsec.rasp.duration'] > 0) + assert(span.metrics['_dd.appsec.rasp.duration_ext'] > 0) }) }) }) diff --git a/packages/dd-trace/test/appsec/rasp.spec.js b/packages/dd-trace/test/appsec/rasp.spec.js index 7f7d6dc4c50..8e3cc0b82be 100644 --- a/packages/dd-trace/test/appsec/rasp.spec.js +++ b/packages/dd-trace/test/appsec/rasp.spec.js @@ -41,7 +41,7 @@ describe('RASP', () => { httpClientRequestStart.publish(ctx) const persistent = { [addresses.HTTP_OUTGOING_URL]: 'http://example.com' } - sinon.assert.calledOnceWithExactly(waf.run, { persistent }, req) + sinon.assert.calledOnceWithExactly(waf.run, { persistent }, req, 'ssrf') }) it('should not analyze ssrf if rasp is disabled', () => { diff --git a/packages/dd-trace/test/appsec/reporter.spec.js b/packages/dd-trace/test/appsec/reporter.spec.js index 8f189e8a6b5..7388d151137 100644 --- a/packages/dd-trace/test/appsec/reporter.spec.js +++ b/packages/dd-trace/test/appsec/reporter.spec.js @@ -26,6 +26,7 @@ describe('reporter', () => { telemetry = { incrementWafInitMetric: sinon.stub(), updateWafRequestsMetricTags: sinon.stub(), + updateRaspRequestsMetricTags: sinon.stub(), incrementWafUpdatesMetric: sinon.stub(), incrementWafRequestsMetric: sinon.stub(), getRequestMetrics: sinon.stub() @@ -147,6 +148,7 @@ describe('reporter', () => { expect(web.root).to.have.been.calledOnceWithExactly(req) expect(telemetry.updateWafRequestsMetricTags).to.have.been.calledOnceWithExactly(metrics, req) + expect(telemetry.updateRaspRequestsMetricTags).to.not.have.been.called }) it('should set ext duration metrics if set', () => { @@ -155,6 +157,7 @@ describe('reporter', () => { expect(web.root).to.have.been.calledOnceWithExactly(req) expect(telemetry.updateWafRequestsMetricTags).to.have.been.calledOnceWithExactly(metrics, req) + expect(telemetry.updateRaspRequestsMetricTags).to.not.have.been.called }) it('should set rulesVersion if set', () => { @@ -162,6 +165,7 @@ describe('reporter', () => { expect(web.root).to.have.been.calledOnceWithExactly(req) expect(span.setTag).to.have.been.calledOnceWithExactly('_dd.appsec.event_rules.version', '1.2.3') + expect(telemetry.updateRaspRequestsMetricTags).to.not.have.been.called }) it('should call updateWafRequestsMetricTags', () => { @@ -171,6 +175,17 @@ describe('reporter', () => { Reporter.reportMetrics(metrics) expect(telemetry.updateWafRequestsMetricTags).to.have.been.calledOnceWithExactly(metrics, store.req) + expect(telemetry.updateRaspRequestsMetricTags).to.not.have.been.called + }) + + it('should call updateRaspRequestsMetricTags when ruleType if provided', () => { + const metrics = { rulesVersion: '1.2.3' } + const store = storage.getStore() + + Reporter.reportMetrics(metrics, 'rule_type') + + expect(telemetry.updateRaspRequestsMetricTags).to.have.been.calledOnceWithExactly(metrics, store.req, 'rule_type') + expect(telemetry.updateWafRequestsMetricTags).to.not.have.been.called }) }) @@ -457,6 +472,21 @@ describe('reporter', () => { expect(span.setTag).to.have.been.calledWithExactly('_dd.appsec.waf.duration', 1337) expect(span.setTag).to.have.been.calledWithExactly('_dd.appsec.waf.duration_ext', 42) + expect(span.setTag).to.not.have.been.calledWith('_dd.appsec.rasp.duration') + expect(span.setTag).to.not.have.been.calledWith('_dd.appsec.rasp.duration_ext') + expect(span.setTag).to.not.have.been.calledWith('_dd.appsec.rasp.rule.eval') + }) + + it('should set rasp.duration tags if there are metrics stored', () => { + telemetry.getRequestMetrics.returns({ raspDuration: 123, raspDurationExt: 321, raspEvalCount: 3 }) + + Reporter.finishRequest({}, {}) + + expect(span.setTag).to.not.have.been.calledWith('_dd.appsec.waf.duration') + expect(span.setTag).to.not.have.been.calledWith('_dd.appsec.waf.duration_ext') + expect(span.setTag).to.have.been.calledWithExactly('_dd.appsec.rasp.duration', 123) + expect(span.setTag).to.have.been.calledWithExactly('_dd.appsec.rasp.duration_ext', 321) + expect(span.setTag).to.have.been.calledWithExactly('_dd.appsec.rasp.rule.eval', 3) }) }) }) diff --git a/packages/dd-trace/test/appsec/telemetry.spec.js b/packages/dd-trace/test/appsec/telemetry.spec.js index c181630a3e8..0e234ad962d 100644 --- a/packages/dd-trace/test/appsec/telemetry.spec.js +++ b/packages/dd-trace/test/appsec/telemetry.spec.js @@ -161,6 +161,72 @@ describe('Appsec Telemetry metrics', () => { }) }) + describe('updateRaspRequestsMetricTags', () => { + it('should increment appsec.rasp.rule.eval metric', () => { + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 42, + durationExt: 52 + }, req, 'rule-type') + + expect(count).to.have.been.calledWith('appsec.rasp.rule.eval') + expect(count).to.not.have.been.calledWith('appsec.rasp.timeout') + expect(count).to.not.have.been.calledWith('appsec.rasp.rule.match') + expect(inc).to.have.been.calledOnceWith(1) + }) + + it('should increment appsec.rasp.timeout metric if timeout', () => { + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 42, + durationExt: 52, + wafTimeout: true + }, req, 'rule-type') + + expect(count).to.have.been.calledWith('appsec.rasp.rule.eval') + expect(count).to.have.been.calledWith('appsec.rasp.timeout') + expect(count).to.not.have.been.calledWith('appsec.rasp.rule.match') + expect(inc).to.have.been.calledTwice + }) + + it('should increment appsec.rasp.rule.match metric if ruleTriggered', () => { + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 42, + durationExt: 52, + ruleTriggered: true + }, req, 'rule-type') + + expect(count).to.have.been.calledWith('appsec.rasp.rule.match') + expect(count).to.have.been.calledWith('appsec.rasp.rule.eval') + expect(count).to.not.have.been.calledWith('appsec.rasp.timeout') + expect(inc).to.have.been.calledTwice + }) + + it('should sum rasp.duration and eval metrics', () => { + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 42, + durationExt: 52 + }, req, 'rule-type') + + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 24, + durationExt: 25 + }, req, 'rule-type') + + const { + duration, + durationExt, + raspDuration, + raspDurationExt, + raspEvalCount + } = appsecTelemetry.getRequestMetrics(req) + + expect(duration).to.be.eq(0) + expect(durationExt).to.be.eq(0) + expect(raspDuration).to.be.eq(66) + expect(raspDurationExt).to.be.eq(77) + expect(raspEvalCount).to.be.eq(2) + }) + }) + describe('incWafInitMetric', () => { it('should increment waf.init metric', () => { appsecTelemetry.incrementWafInitMetric(wafVersion, rulesVersion) @@ -238,6 +304,35 @@ describe('Appsec Telemetry metrics', () => { }) }) + it('should not modify waf.requests metric tags when rasp rule type is provided', () => { + appsecTelemetry.updateWafRequestsMetricTags({ + blockTriggered: false, + ruleTriggered: false, + wafTimeout: false, + wafVersion, + rulesVersion + }, req) + + appsecTelemetry.updateRaspRequestsMetricTags({ + blockTriggered: true, + ruleTriggered: true, + wafTimeout: true, + wafVersion, + rulesVersion + }, req, 'rule_type') + + expect(count).to.have.not.been.calledWith('waf.requests') + appsecTelemetry.incrementWafRequestsMetric(req) + + expect(count).to.have.been.calledWithExactly('waf.requests', { + request_blocked: false, + rule_triggered: false, + waf_timeout: false, + waf_version: wafVersion, + event_rules_version: rulesVersion + }) + }) + it('should not fail if req has no previous tag', () => { appsecTelemetry.incrementWafRequestsMetric(req) @@ -316,5 +411,68 @@ describe('Appsec Telemetry metrics', () => { expect(durationExt).to.be.eq(77) }) }) + + describe('updateRaspRequestsMetricTags', () => { + it('should sum rasp.duration and rasp.durationExt request metrics', () => { + appsecTelemetry.enable({ + enabled: false, + metrics: true + }) + + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 42, + durationExt: 52 + }, req, 'rasp_rule') + + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 24, + durationExt: 25 + }, req, 'rasp_rule') + + const { raspDuration, raspDurationExt, raspEvalCount } = appsecTelemetry.getRequestMetrics(req) + + expect(raspDuration).to.be.eq(66) + expect(raspDurationExt).to.be.eq(77) + expect(raspEvalCount).to.be.eq(2) + }) + + it('should sum rasp.duration and rasp.durationExt with telemetry enabled and metrics disabled', () => { + appsecTelemetry.enable({ + enabled: true, + metrics: false + }) + + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 42, + durationExt: 52 + }, req, 'rule_type') + + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 24, + durationExt: 25 + }, req, 'rule_type') + + const { raspDuration, raspDurationExt, raspEvalCount } = appsecTelemetry.getRequestMetrics(req) + + expect(raspDuration).to.be.eq(66) + expect(raspDurationExt).to.be.eq(77) + expect(raspEvalCount).to.be.eq(2) + }) + + it('should not increment any metric if telemetry metrics are disabled', () => { + appsecTelemetry.enable({ + enabled: true, + metrics: false + }) + + appsecTelemetry.updateRaspRequestsMetricTags({ + duration: 24, + durationExt: 25 + }, req, 'rule_type') + + expect(count).to.not.have.been.called + expect(inc).to.not.have.been.called + }) + }) }) }) diff --git a/packages/dd-trace/test/appsec/waf/index.spec.js b/packages/dd-trace/test/appsec/waf/index.spec.js index 56676f1ccdd..1b313b0eaa6 100644 --- a/packages/dd-trace/test/appsec/waf/index.spec.js +++ b/packages/dd-trace/test/appsec/waf/index.spec.js @@ -94,6 +94,32 @@ describe('WAF Manager', () => { }) }) + describe('run', () => { + it('should call wafManager.run with raspRuleType', () => { + const run = sinon.stub() + WAFManager.prototype.getWAFContext = sinon.stub().returns({ run }) + waf.init(rules, config.appsec) + + const payload = { persistent: { 'server.io.net.url': 'http://example.com' } } + const req = {} + waf.run(payload, req, 'ssrf') + + expect(run).to.be.calledOnceWithExactly(payload, 'ssrf') + }) + + it('should call wafManager.run without raspRuleType', () => { + const run = sinon.stub() + WAFManager.prototype.getWAFContext = sinon.stub().returns({ run }) + waf.init(rules, config.appsec) + + const payload = { persistent: { 'server.io.net.url': 'http://example.com' } } + const req = {} + waf.run(payload, req) + + expect(run).to.be.calledOnceWithExactly(payload, undefined) + }) + }) + describe('wafManager.createDDWAFContext', () => { beforeEach(() => { DDWAF.prototype.constructor.version.returns('4.5.6') @@ -271,6 +297,44 @@ describe('WAF Manager', () => { expect(reportMetricsArg.ruleTriggered).to.be.true }) + it('should report raspRuleType', () => { + const result = { + totalRuntime: 1, + durationExt: 1 + } + + ddwafContext.run.returns(result) + const params = { + persistent: { + 'server.request.headers.no_cookies': { header: 'value' } + } + } + + wafContextWrapper.run(params, 'rule_type') + + expect(Reporter.reportMetrics).to.be.calledOnce + expect(Reporter.reportMetrics.firstCall.args[1]).to.be.equal('rule_type') + }) + + it('should not report raspRuleType when it is not provided', () => { + const result = { + totalRuntime: 1, + durationExt: 1 + } + + ddwafContext.run.returns(result) + const params = { + persistent: { + 'server.request.headers.no_cookies': { header: 'value' } + } + } + + wafContextWrapper.run(params) + + expect(Reporter.reportMetrics).to.be.calledOnce + expect(Reporter.reportMetrics.firstCall.args[1]).to.be.equal(undefined) + }) + it('should not report attack when ddwafContext does not return events', () => { ddwafContext.run.returns({ totalRuntime: 1, durationExt: 1 }) const params = { From 5278b1cc13ce67fc3e7b3cdc9b5df50c6f6a2df6 Mon Sep 17 00:00:00 2001 From: simon-id Date: Tue, 11 Jun 2024 16:18:57 +0200 Subject: [PATCH 2/2] update appsec rules to 1.12.0 (#4398) --- packages/dd-trace/src/appsec/recommended.json | 211 +++++++++++++++++- 1 file changed, 208 insertions(+), 3 deletions(-) diff --git a/packages/dd-trace/src/appsec/recommended.json b/packages/dd-trace/src/appsec/recommended.json index 7912743b40a..0b25be934c8 100644 --- a/packages/dd-trace/src/appsec/recommended.json +++ b/packages/dd-trace/src/appsec/recommended.json @@ -1,7 +1,7 @@ { "version": "2.2", "metadata": { - "rules_version": "1.11.0" + "rules_version": "1.12.0" }, "rules": [ { @@ -1921,7 +1921,6 @@ "$ifs", "$oldpwd", "$ostype", - "$path", "$pwd", "dev/fd/", "dev/null", @@ -5849,7 +5848,8 @@ "/website.php", "/stats.php", "/assets/plugins/mp3_id/mp3_id.php", - "/siteminderagent/forms/smpwservices.fcc" + "/siteminderagent/forms/smpwservices.fcc", + "/eval-stdin.php" ] } } @@ -6236,6 +6236,155 @@ ], "transformers": [] }, + { + "id": "rasp-930-100", + "name": "Local file inclusion exploit", + "enabled": false, + "tags": { + "type": "lfi", + "category": "vulnerability_trigger", + "cwe": "22", + "capec": "1000/255/153/126", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.io.fs.file" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ] + }, + "operator": "lfi_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, + { + "id": "rasp-934-100", + "name": "Server-side request forgery exploit", + "enabled": false, + "tags": { + "type": "ssrf", + "category": "vulnerability_trigger", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.io.net.url" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ] + }, + "operator": "ssrf_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, + { + "id": "rasp-942-100", + "name": "SQL injection exploit", + "enabled": false, + "tags": { + "type": "sql_injection", + "category": "vulnerability_trigger", + "cwe": "89", + "capec": "1000/152/248/66", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.db.statement" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "db_type": [ + { + "address": "server.db.system" + } + ] + }, + "operator": "sqli_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, { "id": "sqr-000-001", "name": "SSRF: Try to access the credential manager of the main cloud services", @@ -8391,6 +8540,34 @@ } ], "scanners": [ + { + "id": "406f8606-52c4-4663-8db9-df70f9e8766c", + "name": "ZIP Code", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:zip|postal)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "^[0-9]{5}(?:-[0-9]{4})?$", + "options": { + "case_sensitive": true, + "min_length": 5 + } + } + }, + "tags": { + "type": "zipcode", + "category": "address" + } + }, { "id": "JU1sRk3mSzqSUJn6GrVn7g", "name": "American Express Card Scanner (4+4+4+3 digits)", @@ -9157,6 +9334,34 @@ "category": "payment" } }, + { + "id": "18b608bd7a764bff5b2344c0", + "name": "Phone number", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\bphone|number|mobile\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "^(?:\\(\\+\\d{1,3}\\)|\\+\\d{1,3}|00\\d{1,3})?[-\\s\\.]?(?:\\(\\d{3}\\)[-\\s\\.]?)?(?:\\d[-\\s\\.]?){6,10}$", + "options": { + "case_sensitive": false, + "min_length": 6 + } + } + }, + "tags": { + "type": "phone", + "category": "pii" + } + }, { "id": "de0899e0cbaaa812bb624cf04c912071012f616d-mod", "name": "UK National Insurance Number Scanner",