-
Notifications
You must be signed in to change notification settings - Fork 317
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[asm] IAST security controls (#5117)
* Security controls parser and secure marks for vulnerabilities * Use new NOSQL_MONGODB_INJECTION_MARK in nosql-injection-mongodb-analyzer * Config * first hooks * wrap object properties and more tests * Use dd-trace:moduleLoad(Start|End) channels * iterate object strings and more tests * fix parameter index, include createNewTainted flag and do not use PluginManager in the tests * Fix parameter index and include a test with incorrect index * Avoid to hook multiple times the same module and config tests * sql_injection_mark test * vulnerable ranges tests * fix windows paths * Upgrade taint-tracking to 3.3.0 * Fix * secure mark * add createNewTainted flag to addSecureMark * Use existing _isRangeSecure * supressed vulnerabilities metric * increment supressed vulnerability metric * typo * handle esm default export and filenames starting with file:// * esm integration tests * clean up * secure-marks tests * fix secure-marks generator test * fix config test * empty * check for repeated marks * Update packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js Co-authored-by: Ugaitz Urien <[email protected]> * Update packages/dd-trace/src/appsec/iast/security-controls/index.js Co-authored-by: Ugaitz Urien <[email protected]> * Update packages/dd-trace/src/appsec/iast/taint-tracking/secure-marks.js Co-authored-by: Ugaitz Urien <[email protected]> * some suggestions * move _isRangeSecure to InjectionAnalyzer * Add programatically config option * index.d.ts * StoredInjectionAnalyzer * Update packages/dd-trace/test/appsec/iast/analyzers/command-injection-analyzer.spec.js Co-authored-by: ishabi <[email protected]> * store control keys to avoid recreating the array * check visited before iterating * test suggestions * Update packages/dd-trace/src/appsec/iast/security-controls/parser.js Co-authored-by: Ilyas Shabi <[email protected]> * lint * ritm test * clean up * Reject security control with non numeric parameters * fix parameter 0 * Update integration-tests/appsec/iast.esm-security-controls.spec.js Co-authored-by: Ugaitz Urien <[email protected]> * suggestions * use legacy store * fix test * fix test * fix test --------- Co-authored-by: Ugaitz Urien <[email protected]> Co-authored-by: ishabi <[email protected]>
- Loading branch information
1 parent
784b6f3
commit fd1dd7e
Showing
40 changed files
with
1,589 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
'use strict' | ||
|
||
import childProcess from 'node:child_process' | ||
import express from 'express' | ||
import { sanitize } from './sanitizer.mjs' | ||
import sanitizeDefault from './sanitizer-default.mjs' | ||
import { validate, validateNotConfigured } from './validator.mjs' | ||
|
||
const app = express() | ||
const port = process.env.APP_PORT || 3000 | ||
|
||
app.get('/cmdi-s-secure', (req, res) => { | ||
const command = sanitize(req.query.command) | ||
try { | ||
childProcess.execSync(command) | ||
} catch (e) { | ||
// ignore | ||
} | ||
|
||
res.end() | ||
}) | ||
|
||
app.get('/cmdi-s-secure-comparison', (req, res) => { | ||
const command = sanitize(req.query.command) | ||
try { | ||
childProcess.execSync(command) | ||
} catch (e) { | ||
// ignore | ||
} | ||
|
||
try { | ||
childProcess.execSync(req.query.command) | ||
} catch (e) { | ||
// ignore | ||
} | ||
|
||
res.end() | ||
}) | ||
|
||
app.get('/cmdi-s-secure-default', (req, res) => { | ||
const command = sanitizeDefault(req.query.command) | ||
try { | ||
childProcess.execSync(command) | ||
} catch (e) { | ||
// ignore | ||
} | ||
|
||
res.end() | ||
}) | ||
|
||
app.get('/cmdi-iv-insecure', (req, res) => { | ||
if (validateNotConfigured(req.query.command)) { | ||
childProcess.execSync(req.query.command) | ||
} | ||
|
||
res.end() | ||
}) | ||
|
||
app.get('/cmdi-iv-secure', (req, res) => { | ||
if (validate(req.query.command)) { | ||
childProcess.execSync(req.query.command) | ||
} | ||
|
||
res.end() | ||
}) | ||
|
||
app.listen(port, () => { | ||
process.send({ port }) | ||
}) |
7 changes: 7 additions & 0 deletions
7
integration-tests/appsec/esm-security-controls/sanitizer-default.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
'use strict' | ||
|
||
function sanitizeDefault (input) { | ||
return input | ||
} | ||
|
||
export default sanitizeDefault |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use strict' | ||
|
||
export function sanitize (input) { | ||
return input | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
'use strict' | ||
|
||
export function validate (input) { | ||
return true | ||
} | ||
|
||
export function validateNotConfigured (input) { | ||
return true | ||
} |
126 changes: 126 additions & 0 deletions
126
integration-tests/appsec/iast.esm-security-controls.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
'use strict' | ||
|
||
const { createSandbox, spawnProc, FakeAgent } = require('../helpers') | ||
const path = require('path') | ||
const getPort = require('get-port') | ||
const Axios = require('axios') | ||
const { assert } = require('chai') | ||
|
||
describe('ESM Security controls', () => { | ||
let axios, sandbox, cwd, appPort, appFile, agent, proc | ||
|
||
before(async function () { | ||
this.timeout(process.platform === 'win32' ? 90000 : 30000) | ||
sandbox = await createSandbox(['express']) | ||
appPort = await getPort() | ||
cwd = sandbox.folder | ||
appFile = path.join(cwd, 'appsec', 'esm-security-controls', 'index.mjs') | ||
|
||
axios = Axios.create({ | ||
baseURL: `http://localhost:${appPort}` | ||
}) | ||
}) | ||
|
||
after(async function () { | ||
await sandbox.remove() | ||
}) | ||
|
||
const nodeOptions = '--import dd-trace/initialize.mjs' | ||
|
||
beforeEach(async () => { | ||
agent = await new FakeAgent().start() | ||
|
||
proc = await spawnProc(appFile, { | ||
cwd, | ||
env: { | ||
DD_TRACE_AGENT_PORT: agent.port, | ||
APP_PORT: appPort, | ||
DD_IAST_ENABLED: 'true', | ||
DD_IAST_REQUEST_SAMPLING: '100', | ||
// eslint-disable-next-line no-multi-str | ||
DD_IAST_SECURITY_CONTROLS_CONFIGURATION: '\ | ||
SANITIZER:COMMAND_INJECTION:appsec/esm-security-controls/sanitizer.mjs:sanitize;\ | ||
SANITIZER:COMMAND_INJECTION:appsec/esm-security-controls/sanitizer-default.mjs;\ | ||
INPUT_VALIDATOR:*:appsec/esm-security-controls/validator.mjs:validate', | ||
NODE_OPTIONS: nodeOptions | ||
} | ||
}) | ||
}) | ||
|
||
afterEach(async () => { | ||
proc.kill() | ||
await agent.stop() | ||
}) | ||
|
||
it('test endpoint with iv not configured does have COMMAND_INJECTION vulnerability', async function () { | ||
await axios.get('/cmdi-iv-insecure?command=ls -la') | ||
|
||
await agent.assertMessageReceived(({ payload }) => { | ||
const spans = payload.flatMap(p => p.filter(span => span.name === 'express.request')) | ||
spans.forEach(span => { | ||
assert.property(span.meta, '_dd.iast.json') | ||
assert.include(span.meta['_dd.iast.json'], '"COMMAND_INJECTION"') | ||
}) | ||
}, null, 1, true) | ||
}) | ||
|
||
it('test endpoint sanitizer does not have COMMAND_INJECTION vulnerability', async () => { | ||
await axios.get('/cmdi-s-secure?command=ls -la') | ||
|
||
await agent.assertMessageReceived(({ payload }) => { | ||
const spans = payload.flatMap(p => p.filter(span => span.name === 'express.request')) | ||
spans.forEach(span => { | ||
assert.notProperty(span.meta, '_dd.iast.json') | ||
assert.property(span.metrics, '_dd.iast.telemetry.suppressed.vulnerabilities.command_injection') | ||
}) | ||
}, null, 1, true) | ||
}) | ||
|
||
it('test endpoint with default sanitizer does not have COMMAND_INJECTION vulnerability', async () => { | ||
await axios.get('/cmdi-s-secure-default?command=ls -la') | ||
|
||
await agent.assertMessageReceived(({ payload }) => { | ||
const spans = payload.flatMap(p => p.filter(span => span.name === 'express.request')) | ||
spans.forEach(span => { | ||
assert.notProperty(span.meta, '_dd.iast.json') | ||
assert.property(span.metrics, '_dd.iast.telemetry.suppressed.vulnerabilities.command_injection') | ||
}) | ||
}, null, 1, true) | ||
}) | ||
|
||
it('test endpoint with default sanitizer does have COMMAND_INJECTION with original tainted', async () => { | ||
await axios.get('/cmdi-s-secure-comparison?command=ls -la') | ||
|
||
await agent.assertMessageReceived(({ payload }) => { | ||
const spans = payload.flatMap(p => p.filter(span => span.name === 'express.request')) | ||
spans.forEach(span => { | ||
assert.property(span.meta, '_dd.iast.json') | ||
assert.include(span.meta['_dd.iast.json'], '"COMMAND_INJECTION"') | ||
}) | ||
}, null, 1, true) | ||
}) | ||
|
||
it('test endpoint with default sanitizer does have COMMAND_INJECTION vulnerability', async () => { | ||
await axios.get('/cmdi-s-secure-default?command=ls -la') | ||
|
||
await agent.assertMessageReceived(({ payload }) => { | ||
const spans = payload.flatMap(p => p.filter(span => span.name === 'express.request')) | ||
spans.forEach(span => { | ||
assert.notProperty(span.meta, '_dd.iast.json') | ||
assert.property(span.metrics, '_dd.iast.telemetry.suppressed.vulnerabilities.command_injection') | ||
}) | ||
}, null, 1, true) | ||
}) | ||
|
||
it('test endpoint with iv does not have COMMAND_INJECTION vulnerability', async () => { | ||
await axios.get('/cmdi-iv-secure?command=ls -la') | ||
|
||
await agent.assertMessageReceived(({ payload }) => { | ||
const spans = payload.flatMap(p => p.filter(span => span.name === 'express.request')) | ||
spans.forEach(span => { | ||
assert.notProperty(span.meta, '_dd.iast.json') | ||
assert.property(span.metrics, '_dd.iast.telemetry.suppressed.vulnerabilities.command_injection') | ||
}) | ||
}, null, 1, true) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.