Skip to content

Commit

Permalink
some AppsecFsPlugin tests
Browse files Browse the repository at this point in the history
  • Loading branch information
iunanua committed Aug 27, 2024
1 parent 5539a4a commit dedb483
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 5 deletions.
1 change: 0 additions & 1 deletion packages/dd-trace/src/appsec/fs-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class AppsecFsPlugin extends Plugin {
_onFsOperationFinishOrRenderEnd () {
const store = storage.getStore()
if (store?.fs?.parentStore) {
// TODO: could a fs:operation finish a render store?
storage.enterWith(store.fs.parentStore)
}
}
Expand Down
7 changes: 4 additions & 3 deletions packages/dd-trace/src/appsec/rasp.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,14 @@ function analyzeSsrf (ctx) {
}

function analyzeLfi (ctx) {
const path = ctx?.path
if (!path) return

const store = storage.getStore()
if (!store) return

const { req, fs, res } = store
const path = ctx?.path

if (!req || !path || !fs) return
if (!req || !fs) return

// NOTE 1: only analyze root fs.operations and not excluded (if response is not rendering)
// NOTE 2: only call waf if it is an absolute path or it contains ../ in the path
Expand Down
175 changes: 175 additions & 0 deletions packages/dd-trace/test/appsec/fs-plugin.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
'use strict'

const { assert } = require('chai')
const path = require('path')
const dc = require('dc-polyfill')
const { storage } = require('../../../datadog-core')
const { AppsecFsPlugin } = require('../../src/appsec/fs-plugin')
const agent = require('../plugins/agent')

const opStartCh = dc.channel('apm:fs:operation:start')
const opFinishCh = dc.channel('apm:fs:operation:finish')

describe('AppsecFsPlugin', () => {
let plugin

beforeEach(() => {
plugin = new AppsecFsPlugin()
plugin.enable()
})

afterEach(() => { plugin.disable() })

it('should mark fs root', () => {
const origStore = {}
storage.enterWith(origStore)

plugin._onFsOperationStart()

let store = storage.getStore()
assert.property(store, 'fs')
assert.propertyVal(store.fs, 'parentStore', origStore)
assert.propertyVal(store.fs, 'root', true)

plugin._onFsOperationFinishOrRenderEnd()

store = storage.getStore()
assert.equal(store, origStore)
assert.notProperty(store, 'fs')
})

it('should mark fs children', () => {
const origStore = { orig: true }
storage.enterWith(origStore)

plugin._onFsOperationStart()

const rootStore = storage.getStore()
assert.property(rootStore, 'fs')
assert.propertyVal(rootStore.fs, 'parentStore', origStore)
assert.propertyVal(rootStore.fs, 'root', true)

plugin._onFsOperationStart()

let store = storage.getStore()
assert.property(store, 'fs')
assert.propertyVal(store.fs, 'parentStore', rootStore)
assert.propertyVal(store.fs, 'root', false)
assert.propertyVal(store, 'orig', true)

plugin._onFsOperationFinishOrRenderEnd()

store = storage.getStore()
assert.equal(store, rootStore)

plugin._onFsOperationFinishOrRenderEnd()
store = storage.getStore()
assert.equal(store, origStore)
})

it('should mark fs ops as excluded while response rendering', () => {
plugin.enable()

const origStore = {}
storage.enterWith(origStore)

plugin._onResponseRenderStart()

let store = storage.getStore()
assert.property(store, 'fs')
assert.propertyVal(store.fs, 'parentStore', origStore)
assert.propertyVal(store.fs, 'opExcluded', true)

plugin._onFsOperationFinishOrRenderEnd()

store = storage.getStore()
assert.equal(store, origStore)
assert.notProperty(store, 'fs')
})

describe('integration', () => {
describe('apm:fs:operation', () => {
let fs

afterEach(() => agent.close({ ritmReset: false }))

beforeEach(() => agent.load('fs', undefined, { flushInterval: 1 }).then(() => {
fs = require('fs')
}))

it('should mark root operations', () => {
let count = 0
const onStart = () => {
const store = storage.getStore()
assert.isNotNull(store.fs)

count++
assert.strictEqual(count === 1, store.fs.root)
}

try {
const origStore = {}
storage.enterWith(origStore)

opStartCh.subscribe(onStart)

fs.readFileSync(path.join(__dirname, 'fs-plugin.spec.js'))

assert.strictEqual(count, 4)
} finally {
opStartCh.unsubscribe(onStart)
}
})

it('should mark root even if op is excluded', () => {
let count = 0
const onStart = () => {
const store = storage.getStore()
assert.isNotNull(store.fs)

count++
assert.isUndefined(store.fs.root)
}

try {
const origStore = {
fs: { opExcluded: true }
}
storage.enterWith(origStore)

opStartCh.subscribe(onStart)

fs.readFileSync(path.join(__dirname, 'fs-plugin.spec.js'))

assert.strictEqual(count, 4)
} finally {
opStartCh.unsubscribe(onStart)
}
})

it('should clean up store when finishing op', () => {
let count = 4
const onFinish = () => {
const store = storage.getStore()
count--

if (count === 0) {
assert.isUndefined(store.fs)
}
}
try {
const origStore = {}
storage.enterWith(origStore)

opFinishCh.subscribe(onFinish)

fs.readFileSync(path.join(__dirname, 'fs-plugin.spec.js'))

assert.strictEqual(count, 0)
} finally {
opFinishCh.unsubscribe(onFinish)
}
})
})
})
})
11 changes: 10 additions & 1 deletion packages/dd-trace/test/appsec/rasp.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,17 @@ describe('RASP', () => {
sinon.assert.notCalled(waf.run)
})

it('should NOT analyze lfi for undefined fs (AppsecFsPlugin disabled)', () => {
const fs = undefined
datadogCore.storage.getStore.returns({ req, fs })

fsOperationStart.publish(ctx)

sinon.assert.notCalled(waf.run)
})

it('should NOT analyze lfi for excluded operations', () => {
const fs = { opExcluded: true }
const fs = { opExcluded: true, root: true }
datadogCore.storage.getStore.returns({ req, fs })

fsOperationStart.publish(ctx)
Expand Down

0 comments on commit dedb483

Please sign in to comment.