Skip to content

Commit

Permalink
Activate AVM OSS via environment variable (#4119)
Browse files Browse the repository at this point in the history
* Add SCA enabled to configuration

* Add comment to new config

* Fix test for telemtry after app-extended-heartbeat behaviour change

* Do not send sca enabled in telemetry messages when not informed

* Fix lint

* Rename sca.enabled to appsec.sca.enabled

* Prevent sending some configuration in telemetry message when it has not been set

* Improve loggers naming

* Appsec SCA default to null. Avoid stripping config entry off when no set.

* Fix sca default value assertion

* Fix config sorting
  • Loading branch information
CarlesDD authored and rochdev committed May 14, 2024
1 parent 82ca41d commit 588a926
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 4 deletions.
4 changes: 4 additions & 0 deletions packages/dd-trace/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ class Config {
this._setValue(defaults, 'appsec.obfuscatorValueRegex', defaultWafObfuscatorValueRegex)
this._setValue(defaults, 'appsec.rateLimit', 100)
this._setValue(defaults, 'appsec.rules', undefined)
this._setValue(defaults, 'appsec.sca.enabled', null)
this._setValue(defaults, 'appsec.wafTimeout', 5e3) // µs
this._setValue(defaults, 'clientIpEnabled', false)
this._setValue(defaults, 'clientIpHeader', null)
Expand Down Expand Up @@ -528,6 +529,7 @@ class Config {
DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
DD_APPSEC_RULES,
DD_APPSEC_SCA_ENABLED,
DD_APPSEC_TRACE_RATE_LIMIT,
DD_APPSEC_WAF_TIMEOUT,
DD_DATA_STREAMS_ENABLED,
Expand Down Expand Up @@ -615,6 +617,8 @@ class Config {
this._setString(env, 'appsec.obfuscatorValueRegex', DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP)
this._setValue(env, 'appsec.rateLimit', maybeInt(DD_APPSEC_TRACE_RATE_LIMIT))
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._setValue(env, 'appsec.wafTimeout', maybeInt(DD_APPSEC_WAF_TIMEOUT))
this._setBoolean(env, 'clientIpEnabled', DD_TRACE_CLIENT_IP_ENABLED)
this._setString(env, 'clientIpHeader', DD_TRACE_CLIENT_IP_HEADER)
Expand Down
12 changes: 9 additions & 3 deletions packages/dd-trace/src/telemetry/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const dependencies = require('./dependencies')
const { sendData } = require('./send-data')
const { errors } = require('../startup-log')
const { manager: metricsManager } = require('./metrics')
const logs = require('./logs')
const telemetryLogger = require('./logs')
const logger = require('../log')

const telemetryStartChannel = dc.channel('datadog:telemetry:start')
const telemetryStopChannel = dc.channel('datadog:telemetry:stop')
Expand Down Expand Up @@ -211,7 +212,7 @@ function createPayload (currReqType, currPayload = {}) {
function heartbeat (config, application, host) {
heartbeatTimeout = setTimeout(() => {
metricsManager.send(config, application, host)
logs.send(config, application, host)
telemetryLogger.send(config, application, host)

const { reqType, payload } = createPayload('app-heartbeat')
sendData(config, application, host, reqType, payload, updateRetryData)
Expand All @@ -235,6 +236,10 @@ function extendedHeartbeat (config) {

function start (aConfig, thePluginManager) {
if (!aConfig.telemetry.enabled) {
if (aConfig.sca?.enabled) {
logger.warn('DD_APPSEC_SCA_ENABLED requires enabling telemetry to work.')
}

return
}
config = aConfig
Expand All @@ -245,7 +250,7 @@ function start (aConfig, thePluginManager) {
integrations = getIntegrations()

dependencies.start(config, application, host, getRetryData, updateRetryData)
logs.start(config)
telemetryLogger.start(config)

sendData(config, application, host, 'app-started', appStarted(config))

Expand Down Expand Up @@ -318,6 +323,7 @@ function updateConfig (changes, config) {

for (const change of changes) {
const name = nameMapping[change.name] || change.name

names.push(name)
const { origin, value } = change
const entry = { name, value, origin }
Expand Down
8 changes: 8 additions & 0 deletions packages/dd-trace/test/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ describe('Config', () => {
expect(config).to.have.nested.property('appsec.eventTracking.mode', 'safe')
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('remoteConfig.enabled', true)
expect(config).to.have.nested.property('remoteConfig.pollInterval', 5)
expect(config).to.have.nested.property('iast.enabled', false)
Expand Down Expand Up @@ -294,6 +295,7 @@ describe('Config', () => {
value: '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}',
origin: 'default'
},
{ name: 'appsec.sca.enabled', value: null, origin: 'default' },
{ name: 'remoteConfig.enabled', value: true, origin: 'env_var' },
{ name: 'remoteConfig.pollInterval', value: 5, origin: 'default' },
{ name: 'iast.enabled', value: false, origin: 'default' },
Expand Down Expand Up @@ -417,6 +419,7 @@ describe('Config', () => {
process.env.DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON = BLOCKED_TEMPLATE_JSON_PATH
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_REMOTE_CONFIGURATION_ENABLED = 'false'
process.env.DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS = '42'
process.env.DD_IAST_ENABLED = 'true'
Expand Down Expand Up @@ -503,6 +506,7 @@ describe('Config', () => {
expect(config).to.have.nested.property('appsec.eventTracking.mode', 'extended')
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('remoteConfig.enabled', false)
expect(config).to.have.nested.property('remoteConfig.pollInterval', 42)
expect(config).to.have.nested.property('iast.enabled', true)
Expand Down Expand Up @@ -557,6 +561,7 @@ describe('Config', () => {
{ name: 'appsec.blockedTemplateJson', value: BLOCKED_TEMPLATE_JSON, origin: 'env_var' },
{ name: 'appsec.obfuscatorKeyRegex', value: '.*', origin: 'env_var' },
{ name: 'appsec.obfuscatorValueRegex', value: '.*', origin: 'env_var' },
{ name: 'appsec.sca.enabled', value: true, origin: 'env_var' },
{ name: 'remoteConfig.enabled', value: false, origin: 'env_var' },
{ name: 'remoteConfig.pollInterval', value: 42, origin: 'env_var' },
{ name: 'iast.enabled', value: true, origin: 'env_var' },
Expand Down Expand Up @@ -1176,6 +1181,9 @@ describe('Config', () => {
apiSecurity: {
enabled: true,
requestSampling: 1.0
},
sca: {
enabled: null
}
})
})
Expand Down
133 changes: 132 additions & 1 deletion packages/dd-trace/test/telemetry/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require('../setup/tap')

const tracerVersion = require('../../../../package.json').version
const proxyquire = require('proxyquire')
const proxyquire = require('proxyquire').noPreserveCache()
const http = require('http')
const { once } = require('events')
const { storage } = require('../../../datadog-core')
Expand Down Expand Up @@ -769,6 +769,137 @@ describe('Telemetry retry', () => {
})
})

describe('AVM OSS', () => {
describe('SCA configuration in telemetry messages', () => {
let telemetry
let telemetryConfig
let clock

const HEARTBEAT_INTERVAL = 86410000

const suite = [
{
scaValue: true,
scaValueOrigin: 'env_var',
testDescription: 'should send when env var is true'
},
{
scaValue: false,
scaValueOrigin: 'env_var',
testDescription: 'should send when env var is false'
},
{
scaValue: null,
scaValueOrigin: 'default',
testDescription: 'should send null (default) when no env var is set'
}
]

suite.forEach(({ scaValue, scaValueOrigin, testDescription }) => {
describe(testDescription, () => {
before((done) => {
clock = sinon.useFakeTimers()

storage.run({ noop: true }, () => {
traceAgent = http.createServer(async (req, res) => {
const chunks = []
for await (const chunk of req) {
chunks.push(chunk)
}
req.body = JSON.parse(Buffer.concat(chunks).toString('utf8'))
traceAgent.reqs.push(req)
traceAgent.emit('handled-req')
res.end()
}).listen(0, done)
})

traceAgent.reqs = []

delete require.cache[require.resolve('../../src/telemetry/send-data')]
delete require.cache[require.resolve('../../src/telemetry')]
telemetry = require('../../src/telemetry')

telemetryConfig = {
telemetry: { enabled: true, heartbeatInterval: HEARTBEAT_INTERVAL },
hostname: 'localhost',
port: traceAgent.address().port,
service: 'test service',
version: '1.2.3-beta4',
env: 'preprod',
tags: {
'runtime-id': '1a2b3c'
},
appsec: { enabled: false },
profiling: { enabled: false }
}
})

before(() => {
telemetry.updateConfig(
[{ name: 'appsec.sca.enabled', value: scaValue, origin: scaValueOrigin }],
telemetryConfig
)
telemetry.start(telemetryConfig, { _pluginsByName: {} })
})

after((done) => {
clock.restore()
telemetry.stop()
traceAgent.close(done)
})

it('in app-started message', () => {
return testSeq(1, 'app-started', payload => {
expect(payload).to.have.property('configuration').that.deep.equal([
{ name: 'appsec.sca.enabled', value: scaValue, origin: scaValueOrigin }
])
}, true)
})

it('in app-extended-heartbeat message', () => {
// Skip a full day
clock.tick(86400000)
return testSeq(2, 'app-extended-heartbeat', payload => {
expect(payload).to.have.property('configuration').that.deep.equal([
{ name: 'appsec.sca.enabled', value: scaValue, origin: scaValueOrigin }
])
}, true)
})
})
})
})

describe('Telemetry and SCA misconfiguration', () => {
let telemetry

const logSpy = {
warn: sinon.spy()
}

before(() => {
telemetry = proxyquire('../../src/telemetry', {
'../log': logSpy
})
})

after(() => {
telemetry.stop()
sinon.restore()
})

it('should log a warning when sca is enabled and telemetry no', () => {
telemetry.start(
{
telemetry: { enabled: false },
sca: { enabled: true }
}
)

expect(logSpy.warn).to.have.been.calledOnceWith('DD_APPSEC_SCA_ENABLED requires enabling telemetry to work.')
})
})
})

async function testSeq (seqId, reqType, validatePayload) {
while (traceAgent.reqs.length < seqId) {
await once(traceAgent, 'handled-req')
Expand Down

0 comments on commit 588a926

Please sign in to comment.