diff --git a/docs/test.ts b/docs/test.ts index 4618befb6e7..380bfd6bba9 100644 --- a/docs/test.ts +++ b/docs/test.ts @@ -139,6 +139,11 @@ tracer.init({ redactionEnabled: true, redactionNamePattern: 'password', redactionValuePattern: 'bearer' + }, + appsec: { + standalone: { + enabled: true + } } } }) diff --git a/index.d.ts b/index.d.ts index 28c8f1ad1b6..d0b634f5dd8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -556,6 +556,19 @@ declare namespace tracer { */ redactionValuePattern?: string } + + appsec?: { + /** + * Configuration of Standalone ASM mode + */ + standalone?: { + /** + * Whether to enable Standalone ASM. + * @default false + */ + enabled?: boolean + } + } }; /** diff --git a/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js b/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js index cd6bf5f1180..cc25d51b1e9 100644 --- a/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +++ b/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js @@ -4,6 +4,7 @@ const { MANUAL_KEEP } = require('../../../../../ext/tags') const LRU = require('lru-cache') const vulnerabilitiesFormatter = require('./vulnerabilities-formatter') const { IAST_ENABLED_TAG_KEY, IAST_JSON_TAG_KEY } = require('./tags') +const standalone = require('../standalone') const VULNERABILITIES_KEY = 'vulnerabilities' const VULNERABILITY_HASHES_MAX_SIZE = 1000 @@ -57,6 +58,9 @@ function sendVulnerabilities (vulnerabilities, rootSpan) { tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend) tags[MANUAL_KEEP] = 'true' span.addTags(tags) + + standalone.sample(span) + if (!rootSpan) span.finish() } } diff --git a/packages/dd-trace/src/appsec/reporter.js b/packages/dd-trace/src/appsec/reporter.js index db2a564c8b6..a75e1c92984 100644 --- a/packages/dd-trace/src/appsec/reporter.js +++ b/packages/dd-trace/src/appsec/reporter.js @@ -13,6 +13,8 @@ const { getRequestMetrics } = require('./telemetry') const zlib = require('zlib') +const { MANUAL_KEEP } = require('../../../../ext/tags') +const standalone = require('./standalone') // default limiter, configurable with setRateLimit() let limiter = new Limiter(100) @@ -88,7 +90,7 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) { metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors)) } - metricsQueue.set('manual.keep', 'true') + metricsQueue.set(MANUAL_KEEP, 'true') incrementWafInitMetric(wafVersion, rulesVersion) } @@ -121,7 +123,9 @@ function reportAttack (attackData) { newTags['appsec.event'] = 'true' if (limiter.isAllowed()) { - newTags['manual.keep'] = 'true' // TODO: figure out how to keep appsec traces with sampling revamp + newTags[MANUAL_KEEP] = 'true' + + standalone.sample(rootSpan) } // TODO: maybe add this to format.js later (to take decision as late as possible) @@ -172,6 +176,8 @@ function finishRequest (req, res) { if (metricsQueue.size) { rootSpan.addTags(Object.fromEntries(metricsQueue)) + standalone.sample(rootSpan) + metricsQueue.clear() } diff --git a/packages/dd-trace/src/appsec/sdk/track_event.js b/packages/dd-trace/src/appsec/sdk/track_event.js index 8debb932090..61500e2cfbe 100644 --- a/packages/dd-trace/src/appsec/sdk/track_event.js +++ b/packages/dd-trace/src/appsec/sdk/track_event.js @@ -4,6 +4,7 @@ const log = require('../../log') const { getRootSpan } = require('./utils') const { MANUAL_KEEP } = require('../../../../../ext/tags') const { setUserTags } = require('./set_user') +const standalone = require('../standalone') function trackUserLoginSuccessEvent (tracer, user, metadata) { // TODO: better user check here and in _setUser() ? @@ -73,6 +74,8 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) { } rootSpan.addTags(tags) + + standalone.sample(rootSpan) } module.exports = { diff --git a/packages/dd-trace/src/appsec/standalone.js b/packages/dd-trace/src/appsec/standalone.js new file mode 100644 index 00000000000..d0402d8ab55 --- /dev/null +++ b/packages/dd-trace/src/appsec/standalone.js @@ -0,0 +1,43 @@ +'use strict' + +const { channel } = require('dc-polyfill') +const { APM_TRACING_ENABLED_KEY, APPSEC_PROPAGATION_KEY } = require('../constants') + +const startCh = channel('dd-trace:span:start') + +let enabled + +function onSpanStart ({ span, fields }) { + const tags = span.context?.()?._tags + if (!tags) return + + const { parent } = fields + if (!parent || parent._isRemote) { + tags[APM_TRACING_ENABLED_KEY] = 0 + } +} + +function configure (config) { + const configChanged = enabled !== config.appsec?.standalone?.enabled + if (!configChanged) return + + enabled = config.appsec?.standalone?.enabled + + if (enabled) { + startCh.subscribe(onSpanStart) + } else { + startCh.unsubscribe(onSpanStart) + } +} + +function sample (span) { + const context = span.context?.() + if (enabled && context._trace?.tags) { + context._trace.tags[APPSEC_PROPAGATION_KEY] = '1' + } +} + +module.exports = { + configure, + sample +} diff --git a/packages/dd-trace/src/config.js b/packages/dd-trace/src/config.js index a2df6ff9043..5c9c1372160 100644 --- a/packages/dd-trace/src/config.js +++ b/packages/dd-trace/src/config.js @@ -440,6 +440,7 @@ class Config { this._setValue(defaults, 'appsec.rateLimit', 100) this._setValue(defaults, 'appsec.rules', undefined) this._setValue(defaults, 'appsec.sca.enabled', null) + this._setValue(defaults, 'appsec.standalone.enabled', undefined) this._setValue(defaults, 'appsec.stackTrace.enabled', true) this._setValue(defaults, 'appsec.stackTrace.maxDepth', 32) this._setValue(defaults, 'appsec.stackTrace.maxStackTraces', 2) @@ -542,6 +543,7 @@ class Config { DD_DOGSTATSD_HOSTNAME, DD_DOGSTATSD_PORT, DD_ENV, + DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED, DD_EXPERIMENTAL_PROFILING_ENABLED, JEST_WORKER_ID, DD_IAST_DEDUPLICATION_ENABLED, @@ -633,6 +635,7 @@ class Config { this._setString(env, 'appsec.rules', DD_APPSEC_RULES) // DD_APPSEC_SCA_ENABLED is never used locally, but only sent to the backend this._setBoolean(env, 'appsec.sca.enabled', DD_APPSEC_SCA_ENABLED) + this._setBoolean(env, 'appsec.standalone.enabled', DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED) this._setBoolean(env, 'appsec.stackTrace.enabled', DD_APPSEC_STACK_TRACE_ENABLED) this._setValue(env, 'appsec.stackTrace.maxDepth', maybeInt(DD_APPSEC_MAX_STACK_TRACE_DEPTH)) this._envUnprocessed['appsec.stackTrace.maxDepth'] = DD_APPSEC_MAX_STACK_TRACE_DEPTH @@ -778,6 +781,7 @@ class Config { this._setValue(opts, 'appsec.rateLimit', maybeInt(options.appsec.rateLimit)) this._optsUnprocessed['appsec.rateLimit'] = options.appsec.rateLimit this._setString(opts, 'appsec.rules', options.appsec.rules) + this._setBoolean(opts, 'appsec.standalone.enabled', options.experimental?.appsec?.standalone?.enabled) this._setBoolean(opts, 'appsec.stackTrace.enabled', options.appsec.stackTrace?.enabled) this._setValue(opts, 'appsec.stackTrace.maxDepth', maybeInt(options.appsec.stackTrace?.maxDepth)) this._optsUnprocessed['appsec.stackTrace.maxDepth'] = options.appsec.stackTrace?.maxDepth diff --git a/packages/dd-trace/src/constants.js b/packages/dd-trace/src/constants.js index 47fd67e438d..0d9bcc495dd 100644 --- a/packages/dd-trace/src/constants.js +++ b/packages/dd-trace/src/constants.js @@ -32,5 +32,7 @@ module.exports = { PEER_SERVICE_SOURCE_KEY: '_dd.peer.service.source', PEER_SERVICE_REMAP_KEY: '_dd.peer.service.remapped_from', SCI_REPOSITORY_URL: '_dd.git.repository_url', - SCI_COMMIT_SHA: '_dd.git.commit.sha' + SCI_COMMIT_SHA: '_dd.git.commit.sha', + APM_TRACING_ENABLED_KEY: '_dd.apm.enabled', + APPSEC_PROPAGATION_KEY: '_dd.p.appsec' } diff --git a/packages/dd-trace/src/opentracing/span.js b/packages/dd-trace/src/opentracing/span.js index f71cf329c02..723597ff043 100644 --- a/packages/dd-trace/src/opentracing/span.js +++ b/packages/dd-trace/src/opentracing/span.js @@ -99,7 +99,10 @@ class DatadogSpan { unfinishedRegistry.register(this, operationName, this) } spanleak.addSpan(this, operationName) - startCh.publish(this) + + if (startCh.hasSubscribers) { + startCh.publish({ span: this, fields }) + } } toString () { diff --git a/packages/dd-trace/src/proxy.js b/packages/dd-trace/src/proxy.js index f7b1c9e26eb..863c2fdcce4 100644 --- a/packages/dd-trace/src/proxy.js +++ b/packages/dd-trace/src/proxy.js @@ -15,6 +15,7 @@ const NoopDogStatsDClient = require('./noop/dogstatsd') const spanleak = require('./spanleak') const { SSIHeuristics } = require('./profiling/ssi-heuristics') const telemetryLog = require('dc-polyfill').channel('datadog:telemetry:log') +const appsecStandalone = require('./appsec/standalone') class LazyModule { constructor (provider) { @@ -180,6 +181,7 @@ class Tracer extends NoopProxy { if (!this._tracingInitialized) { this._tracer = new DatadogTracer(config) this.appsec = new AppsecSdk(this._tracer, config) + appsecStandalone.configure(config) this._tracingInitialized = true } if (config.iast.enabled) { diff --git a/packages/dd-trace/test/appsec/iast/vulnerability-reporter.spec.js b/packages/dd-trace/test/appsec/iast/vulnerability-reporter.spec.js index bab1f9aca53..c96171eddbd 100644 --- a/packages/dd-trace/test/appsec/iast/vulnerability-reporter.spec.js +++ b/packages/dd-trace/test/appsec/iast/vulnerability-reporter.spec.js @@ -1,6 +1,9 @@ const { addVulnerability, sendVulnerabilities, clearCache, start, stop } = require('../../../src/appsec/iast/vulnerability-reporter') const VulnerabilityAnalyzer = require('../../../../dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer') +const appsecStandalone = require('../../../src/appsec/standalone') +const { APPSEC_PROPAGATION_KEY } = require('../../../src/constants') + describe('vulnerability-reporter', () => { let vulnerabilityAnalyzer @@ -9,6 +12,8 @@ describe('vulnerability-reporter', () => { vulnerabilityAnalyzer = new VulnerabilityAnalyzer('ANALYZER_TYPE') }) + afterEach(sinon.restore) + describe('addVulnerability', () => { it('should not break with invalid input', () => { addVulnerability() @@ -130,10 +135,13 @@ describe('vulnerability-reporter', () => { describe('sendVulnerabilities', () => { let span + let context beforeEach(() => { + context = { _trace: { tags: {} } } span = { - addTags: sinon.stub() + addTags: sinon.stub(), + context: sinon.stub().returns(context) } start({ iast: { @@ -378,6 +386,40 @@ describe('vulnerability-reporter', () => { '{"spanId":888,"path":"filename.js","line":88}}]}' }) }) + + it('should add _dd.p.appsec trace tag with standalone enabled', () => { + appsecStandalone.configure({ appsec: { standalone: { enabled: true } } }) + const iastContext = { rootSpan: span } + addVulnerability(iastContext, + vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 999)) + + sendVulnerabilities(iastContext.vulnerabilities, span) + + expect(span.addTags).to.have.been.calledOnceWithExactly({ + 'manual.keep': 'true', + '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3254801297,' + + '"evidence":{"value":"sha1"},"location":{"spanId":999}}]}' + }) + + expect(span.context()._trace.tags).to.have.property(APPSEC_PROPAGATION_KEY) + }) + + it('should not add _dd.p.appsec trace tag with standalone disabled', () => { + appsecStandalone.configure({ appsec: {} }) + const iastContext = { rootSpan: span } + addVulnerability(iastContext, + vulnerabilityAnalyzer._createVulnerability('INSECURE_HASHING', { value: 'sha1' }, 999)) + + sendVulnerabilities(iastContext.vulnerabilities, span) + + expect(span.addTags).to.have.been.calledOnceWithExactly({ + 'manual.keep': 'true', + '_dd.iast.json': '{"sources":[],"vulnerabilities":[{"type":"INSECURE_HASHING","hash":3254801297,' + + '"evidence":{"value":"sha1"},"location":{"spanId":999}}]}' + }) + + expect(span.context()._trace.tags).to.not.have.property(APPSEC_PROPAGATION_KEY) + }) }) describe('clearCache', () => { diff --git a/packages/dd-trace/test/appsec/reporter.spec.js b/packages/dd-trace/test/appsec/reporter.spec.js index 7388d151137..721534f6d5b 100644 --- a/packages/dd-trace/test/appsec/reporter.spec.js +++ b/packages/dd-trace/test/appsec/reporter.spec.js @@ -9,6 +9,7 @@ describe('reporter', () => { let span let web let telemetry + let sample beforeEach(() => { span = { @@ -32,9 +33,14 @@ describe('reporter', () => { getRequestMetrics: sinon.stub() } + sample = sinon.stub() + Reporter = proxyquire('../../src/appsec/reporter', { '../plugins/util/web': web, - './telemetry': telemetry + './telemetry': telemetry, + './standalone': { + sample + } }) }) @@ -289,6 +295,27 @@ describe('reporter', () => { 'network.client.ip': '8.8.8.8' }) }) + + it('should call standalone sample', () => { + span.context()._tags = { '_dd.appsec.json': '{"triggers":[{"rule":{},"rule_matches":[{}]}]}' } + + const result = Reporter.reportAttack('[{"rule":{}},{"rule":{},"rule_matches":[{}]}]') + expect(result).to.not.be.false + expect(web.root).to.have.been.calledOnceWith(req) + + expect(span.addTags).to.have.been.calledOnceWithExactly({ + 'http.request.headers.host': 'localhost', + 'http.request.headers.user-agent': 'arachni', + 'appsec.event': 'true', + 'manual.keep': 'true', + '_dd.origin': 'appsec', + '_dd.appsec.json': '{"triggers":[{"rule":{},"rule_matches":[{}]},{"rule":{}},{"rule":{},"rule_matches":[{}]}]}', + 'http.useragent': 'arachni', + 'network.client.ip': '8.8.8.8' + }) + + expect(sample).to.have.been.calledOnceWithExactly(span) + }) }) describe('reportWafUpdate', () => { diff --git a/packages/dd-trace/test/appsec/sdk/track_event.spec.js b/packages/dd-trace/test/appsec/sdk/track_event.spec.js index 93d8959783e..106a145f3b4 100644 --- a/packages/dd-trace/test/appsec/sdk/track_event.spec.js +++ b/packages/dd-trace/test/appsec/sdk/track_event.spec.js @@ -14,6 +14,7 @@ describe('track_event', () => { let getRootSpan let setUserTags let trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackCustomEvent, trackEvent + let sample beforeEach(() => { log = { @@ -28,6 +29,8 @@ describe('track_event', () => { setUserTags = sinon.stub() + sample = sinon.stub() + const trackEvents = proxyquire('../../../src/appsec/sdk/track_event', { '../../log': log, './utils': { @@ -35,6 +38,9 @@ describe('track_event', () => { }, './set_user': { setUserTags + }, + '../standalone': { + sample } }) @@ -249,6 +255,16 @@ describe('track_event', () => { 'appsec.events.event.metakey2': 'metaValue2' }) }) + + it('should call standalone sample', () => { + trackEvent('event', undefined, 'trackEvent', rootSpan, undefined) + + expect(rootSpan.addTags).to.have.been.calledOnceWithExactly({ + 'appsec.events.event.track': 'true', + 'manual.keep': 'true' + }) + expect(sample).to.have.been.calledOnceWithExactly(rootSpan) + }) }) }) diff --git a/packages/dd-trace/test/appsec/standalone.spec.js b/packages/dd-trace/test/appsec/standalone.spec.js new file mode 100644 index 00000000000..f41ea70f1b8 --- /dev/null +++ b/packages/dd-trace/test/appsec/standalone.spec.js @@ -0,0 +1,109 @@ +'use strict' + +const { channel } = require('dc-polyfill') +const { assert } = require('chai') +const standalone = require('../../src/appsec/standalone') +const DatadogSpan = require('../../src/opentracing/span') +const { APM_TRACING_ENABLED_KEY } = require('../../src/constants') + +const startCh = channel('dd-trace:span:start') + +describe('Appsec Standalone', () => { + let config + let tracer, processor, prioritySampler + + beforeEach(() => { + config = { appsec: { standalone: { enabled: true } } } + + tracer = {} + processor = {} + prioritySampler = {} + }) + + afterEach(() => { sinon.restore() }) + + describe('configure', () => { + let startChSubscribe + let startChUnsubscribe + + beforeEach(() => { + startChSubscribe = sinon.stub(startCh, 'subscribe') + startChUnsubscribe = sinon.stub(startCh, 'unsubscribe') + }) + + it('should subscribe to start span if standalone enabled', () => { + standalone.configure(config) + + sinon.assert.calledOnce(startChSubscribe) + }) + + it('should not subscribe to start span if standalone disabled', () => { + delete config.appsec.standalone + + standalone.configure(config) + + sinon.assert.calledOnce(startChUnsubscribe) + }) + + it('should subscribe only once', () => { + standalone.configure(config) + standalone.configure(config) + standalone.configure(config) + + sinon.assert.calledOnce(startChSubscribe) + }) + }) + + describe('onStartSpan', () => { + it('should not add _dd.apm.enabled tag when standalone is disabled', () => { + delete config.appsec.standalone + standalone.configure(config) + + const span = new DatadogSpan(tracer, processor, prioritySampler, { + operationName: 'operation' + }) + + assert.notProperty(span.context()._tags, APM_TRACING_ENABLED_KEY) + }) + + it('should add _dd.apm.enabled tag when standalone is enabled', () => { + standalone.configure(config) + + const span = new DatadogSpan(tracer, processor, prioritySampler, { + operationName: 'operation' + }) + + assert.property(span.context()._tags, APM_TRACING_ENABLED_KEY) + }) + + it('should not add _dd.apm.enabled tag in child spans with local parent', () => { + const parent = new DatadogSpan(tracer, processor, prioritySampler, { + operationName: 'operation' + }) + + assert.propertyVal(parent.context()._tags, APM_TRACING_ENABLED_KEY, 0) + + const child = new DatadogSpan(tracer, processor, prioritySampler, { + operationName: 'operation', + parent + }) + + assert.notProperty(child.context()._tags, APM_TRACING_ENABLED_KEY) + }) + + it('should add _dd.apm.enabled tag in child spans with remote parent', () => { + const parent = new DatadogSpan(tracer, processor, prioritySampler, { + operationName: 'operation' + }) + + parent._isRemote = true + + const child = new DatadogSpan(tracer, processor, prioritySampler, { + operationName: 'operation', + parent + }) + + assert.propertyVal(child.context()._tags, APM_TRACING_ENABLED_KEY, 0) + }) + }) +}) diff --git a/packages/dd-trace/test/config.spec.js b/packages/dd-trace/test/config.spec.js index 133bf5da40e..63cd17af002 100644 --- a/packages/dd-trace/test/config.spec.js +++ b/packages/dd-trace/test/config.spec.js @@ -227,6 +227,7 @@ describe('Config', () => { expect(config).to.have.nested.property('appsec.apiSecurity.enabled', true) expect(config).to.have.nested.property('appsec.apiSecurity.requestSampling', 0.1) expect(config).to.have.nested.property('appsec.sca.enabled', null) + expect(config).to.have.nested.property('appsec.standalone.enabled', undefined) expect(config).to.have.nested.property('remoteConfig.enabled', true) expect(config).to.have.nested.property('remoteConfig.pollInterval', 5) expect(config).to.have.nested.property('iast.enabled', false) @@ -260,6 +261,7 @@ describe('Config', () => { { name: 'appsec.rateLimit', value: 100, origin: 'default' }, { name: 'appsec.rules', value: undefined, origin: 'default' }, { name: 'appsec.sca.enabled', value: null, origin: 'default' }, + { name: 'appsec.standalone.enabled', value: undefined, origin: 'default' }, { name: 'appsec.stackTrace.enabled', value: true, origin: 'default' }, { name: 'appsec.stackTrace.maxDepth', value: 32, origin: 'default' }, { name: 'appsec.stackTrace.maxStackTraces', value: 2, origin: 'default' }, @@ -439,6 +441,7 @@ describe('Config', () => { process.env.DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON = BLOCKED_TEMPLATE_GRAPHQL_PATH process.env.DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING = 'extended' process.env.DD_APPSEC_SCA_ENABLED = true + process.env.DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED = 'true' process.env.DD_REMOTE_CONFIGURATION_ENABLED = 'false' process.env.DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS = '42' process.env.DD_IAST_ENABLED = 'true' @@ -533,6 +536,7 @@ describe('Config', () => { expect(config).to.have.nested.property('appsec.apiSecurity.enabled', true) expect(config).to.have.nested.property('appsec.apiSecurity.requestSampling', 1) expect(config).to.have.nested.property('appsec.sca.enabled', true) + expect(config).to.have.nested.property('appsec.standalone.enabled', true) expect(config).to.have.nested.property('remoteConfig.enabled', false) expect(config).to.have.nested.property('remoteConfig.pollInterval', 42) expect(config).to.have.nested.property('iast.enabled', true) @@ -565,6 +569,7 @@ describe('Config', () => { { name: 'appsec.stackTrace.maxDepth', value: '42', origin: 'env_var' }, { name: 'appsec.stackTrace.maxStackTraces', value: '5', origin: 'env_var' }, { name: 'appsec.sca.enabled', value: true, origin: 'env_var' }, + { name: 'appsec.standalone.enabled', value: true, origin: 'env_var' }, { name: 'appsec.wafTimeout', value: '42', origin: 'env_var' }, { name: 'clientIpEnabled', value: true, origin: 'env_var' }, { name: 'clientIpHeader', value: 'x-true-client-ip', origin: 'env_var' }, @@ -747,6 +752,11 @@ describe('Config', () => { redactionNamePattern: 'REDACTION_NAME_PATTERN', redactionValuePattern: 'REDACTION_VALUE_PATTERN', telemetryVerbosity: 'DEBUG' + }, + appsec: { + standalone: { + enabled: true + } } }, appsec: false, @@ -795,6 +805,7 @@ describe('Config', () => { expect(config).to.have.nested.property('experimental.exporter', 'log') expect(config).to.have.nested.property('experimental.enableGetRumData', true) expect(config).to.have.nested.property('appsec.enabled', false) + expect(config).to.have.nested.property('appsec.standalone.enabled', true) expect(config).to.have.nested.property('remoteConfig.pollInterval', 42) expect(config).to.have.nested.property('iast.enabled', true) expect(config).to.have.nested.property('iast.requestSampling', 50) @@ -830,6 +841,7 @@ describe('Config', () => { expect(updateConfig.getCall(0).args[0]).to.deep.include.members([ { name: 'appsec.enabled', value: false, origin: 'code' }, + { name: 'appsec.standalone.enabled', value: true, origin: 'code' }, { name: 'clientIpEnabled', value: true, origin: 'code' }, { name: 'clientIpHeader', value: 'x-true-client-ip', origin: 'code' }, { name: 'dogstatsd.hostname', value: 'agent-dsd', origin: 'code' }, @@ -1244,6 +1256,9 @@ describe('Config', () => { rasp: { enabled: false }, + standalone: { + enabled: undefined + }, stackTrace: { enabled: true, maxStackTraces: 2, diff --git a/packages/dd-trace/test/opentracing/span.spec.js b/packages/dd-trace/test/opentracing/span.spec.js index 326796cae26..dbb248eb920 100644 --- a/packages/dd-trace/test/opentracing/span.spec.js +++ b/packages/dd-trace/test/opentracing/span.spec.js @@ -5,6 +5,9 @@ require('../setup/tap') const Config = require('../../src/config') const TextMapPropagator = require('../../src/opentracing/propagation/text_map') +const { channel } = require('dc-polyfill') +const startCh = channel('dd-trace:span:start') + describe('Span', () => { let Span let span @@ -163,6 +166,24 @@ describe('Span', () => { expect(span.context()._trace.tags['_dd.p.tid']).to.match(/^[a-f0-9]{8}0{8}$/) }) + it('should be published via dd-trace:span:start channel', () => { + const onSpan = sinon.stub() + startCh.subscribe(onSpan) + + const fields = { + operationName: 'operation' + } + + try { + span = new Span(tracer, processor, prioritySampler, fields) + + expect(onSpan).to.have.been.calledOnce + expect(onSpan.firstCall.args[0]).to.deep.equal({ span, fields }) + } finally { + startCh.unsubscribe(onSpan) + } + }) + describe('tracer', () => { it('should return its parent tracer', () => { span = new Span(tracer, processor, prioritySampler, { operationName: 'operation' }) diff --git a/packages/dd-trace/test/proxy.spec.js b/packages/dd-trace/test/proxy.spec.js index 07fcd41eca6..6b694ea805f 100644 --- a/packages/dd-trace/test/proxy.spec.js +++ b/packages/dd-trace/test/proxy.spec.js @@ -523,6 +523,30 @@ describe('TracerProxy', () => { expect(telemetry.start).to.have.been.called }) + + it('should configure appsec standalone', () => { + const standalone = { + configure: sinon.stub() + } + + const options = {} + const DatadogProxy = proxyquire('../src/proxy', { + './tracer': DatadogTracer, + './config': Config, + './appsec': appsec, + './appsec/iast': iast, + './appsec/remote_config': remoteConfig, + './appsec/sdk': AppsecSdk, + './appsec/standalone': standalone, + './telemetry': telemetry + }) + + const proxy = new DatadogProxy() + proxy.init(options) + + const config = AppsecSdk.firstCall.args[1] + expect(standalone.configure).to.have.been.calledOnceWithExactly(config) + }) }) describe('trace', () => {