Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nodejs@igor/iast-security-controls] IAST security controls #3872

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
5 changes: 4 additions & 1 deletion manifests/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ refs:
- &ref_5_33_0 '>=5.33.0'
- &ref_5_34_0 '>=5.34.0'
- &ref_5_35_0 '>=5.35.0'
- &ref_5_36_0 '>=5.36.0'

tests/:
apm_tracing_e2e/:
Expand Down Expand Up @@ -339,7 +340,9 @@ tests/:
test_uri.py:
TestURI: missing_feature
test_security_controls.py:
TestSecurityControls: missing_feature
TestSecurityControls:
'*': *ref_5_36_0
nextjs: missing_feature
rasp/:
test_cmdi.py:
Test_Cmdi_BodyJson:
Expand Down
4 changes: 3 additions & 1 deletion tests/appsec/iast/test_security_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2021 Datadog, Inc.

from utils import features, rfc, weblog, interfaces
from utils import context, features, rfc, weblog, interfaces, irrelevant
from tests.appsec.iast.utils import BaseSinkTest, assert_iast_vulnerability, assert_metric


Expand All @@ -23,6 +23,7 @@ def assert_iast_is_enabled(request):
if meta_struct and meta_struct.get("vulnerability"):
product_enabled = True
break

assert product_enabled, "IAST is not available"

def setup_iast_is_enabled(self):
Expand Down Expand Up @@ -139,6 +140,7 @@ def setup_no_vulnerability_suppression_with_a_sanitizer_configured_for_an_overlo
self.setup_iast_is_enabled()
self.r = weblog.post("iast/sc/s/overloaded/insecure", data={"param": "param"})

@irrelevant(context.library == "nodejs")
def test_no_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloaded_method_with_specific_signature(
self,
):
Expand Down
16 changes: 15 additions & 1 deletion utils/_context/_scenarios/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,21 @@
"INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:"
"overloadedValidation:java.lang.Object,java.lang.String,java.lang.String:1,2"
),
"nodejs": "TODO",
"nodejs": (
"SANITIZER:COMMAND_INJECTION:iast/utils/securityControlUtil.js:sanitize;"
"SANITIZER:*:iast/utils/securityControlUtil.js:sanitizeForAllVulns;"
"SANITIZER:*:iast/utils/securityControlUtil.js:overloadedSanitize:0;"
"INPUT_VALIDATOR:COMMAND_INJECTION:iast/utils/securityControlUtil.js:validate;"
"INPUT_VALIDATOR:*:iast/utils/securityControlUtil.js:validateForAllVulns;"
"INPUT_VALIDATOR:*:iast/utils/securityControlUtil.js:overloadedValidation:1,2;"
# typescript definitions
"SANITIZER:COMMAND_INJECTION:dist/utils/securityControlUtil.js:sanitize;"
"SANITIZER:*:dist/utils/securityControlUtil.js:sanitizeForAllVulns;"
"SANITIZER:*:dist/utils/securityControlUtil.js:overloadedSanitize:0;"
"INPUT_VALIDATOR:COMMAND_INJECTION:dist/utils/securityControlUtil.js:validate;"
"INPUT_VALIDATOR:*:dist/utils/securityControlUtil.js:validateForAllVulns;"
"INPUT_VALIDATOR:*:dist/utils/securityControlUtil.js:overloadedValidation:1,2"
),
"php": "TODO",
"python": "TODO",
"ruby": "TODO",
Expand Down
2 changes: 2 additions & 0 deletions utils/build/docker/nodejs/express/iast/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ function initRoutes (app, tracer) {
})

require('./sources')(app, tracer)

require('./security-controls')(app, tracer)
}

module.exports = { initRoutes, initData, initMiddlewares }
121 changes: 121 additions & 0 deletions utils/build/docker/nodejs/express/iast/security-controls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
'use strict'

const { Client } = require('pg')
const { execSync } = require('child_process')
const SecurityControlUtil = require('./utils/securityControlUtil')

function execQuery (user, password) {
const sql = 'SELECT * FROM IAST_USER WHERE USERNAME = \'' + user +
'\' AND PASSWORD = \'' + password + '\''

const client = new Client()
return client.connect().then(() => {
return client.query(sql)
})
}

function init (app, tracer) {
app.post('/iast/sc/s/configured', (req, res) => {
const sanitized = SecurityControlUtil.sanitize(req.body.param)
try {
execSync(sanitized)
} catch (e) {
// ignore
}
res.send('OK')
})

app.post('/iast/sc/s/not-configured', (req, res) => {
const sanitized = SecurityControlUtil.sanitize(req.body.param)
execQuery(sanitized, 'password').then((queryResult) => {
res.send('OK')
}).catch((err) => {
res.send('Error: ' + err)
})
})

app.post('/iast/sc/s/all', (req, res) => {
const sanitized = SecurityControlUtil.sanitizeForAllVulns(req.body.param)
execQuery(sanitized, 'password').then((queryResult) => {
res.send('OK')
}).catch((err) => {
res.send('Error: ' + err)
})
})

app.post('/iast/sc/s/overloaded/secure', (req, res) => {
const sanitized = SecurityControlUtil.overloadedSanitize(req.body.param)
try {
execSync(sanitized)
} catch (e) {
// ignore
}
res.send('OK')
})

app.post('/iast/sc/s/overloaded/insecure', (req, res) => {
const sanitized = SecurityControlUtil.overloadedSanitize(null, req.body.param)
try {
execSync(sanitized)
} catch (e) {
// ignore
}
res.send('OK')
})

app.post('/iast/sc/iv/configured', (req, res) => {
// TODO: problem in express5?
if (SecurityControlUtil.validate(req.body.param)) {
try {
execSync(req.body.param)
} catch (e) {
// ignore
}
}
res.send('OK')
})

app.post('/iast/sc/iv/not-configured', (req, res) => {
if (SecurityControlUtil.validate(req.body.param)) {
execQuery(req.body.param, 'password').then((queryResult) => {
res.send('OK')
}).catch((err) => {
res.send('Error: ' + err)
})
}
})

app.post('/iast/sc/iv/all', (req, res) => {
if (SecurityControlUtil.validateForAllVulns(req.body.param)) {
execQuery(req.body.param, 'password').then((queryResult) => {
res.send('OK')
}).catch((err) => {
res.send('Error: ' + err)
})
}
})

app.post('/iast/sc/iv/overloaded/secure', (req, res) => {
const { user, password } = req.body
if (SecurityControlUtil.overloadedValidation(null, user, password)) {
execQuery(user, password).then((queryResult) => {
res.send('OK')
}).catch((err) => {
res.send('Error: ' + err)
})
}
})

app.post('/iast/sc/iv/overloaded/insecure', (req, res) => {
const { user, password } = req.body
if (SecurityControlUtil.overloadedValidation(user, password, null)) {
execQuery(user, password).then((queryResult) => {
res.send('OK')
}).catch((err) => {
res.send('Error: ' + err)
})
}
})
}

module.exports = init
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict'

function sanitize (input) {
return input // `Sanitized ${input}`
}

function sanitizeForAllVulns (input) {
return `Sanitized for all vulns ${input}`
}

function overloadedSanitize (input, obj) {
return `Sanitized ${input}`
}

function validate (input) {
return true // dummy implementation
}

function validateForAllVulns (input) {
return true // dummy implementation
}

function overloadedValidation (obj, input, input2) {
return true // dummy implementation
}

module.exports = {
sanitize,
sanitizeForAllVulns,
overloadedSanitize,
validate,
validateForAllVulns,
overloadedValidation
}
2 changes: 2 additions & 0 deletions utils/build/docker/nodejs/express4-typescript/iast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const pug = require('pug')
const { unserialize } = require('node-serialize')

const ldap = require('./integrations/ldap')
const initSecurityControls = require('./security-controls')

async function initData () {
const query = readFileSync(join(__dirname, '..', 'resources', 'iast-data.sql')).toString()
Expand Down Expand Up @@ -696,6 +697,7 @@ function initSourceRoutes (app: Express): void {
function initRoutes (app: Express): void {
initSinkRoutes(app)
initSourceRoutes(app)
initSecurityControls(app)
}


Expand Down
122 changes: 122 additions & 0 deletions utils/build/docker/nodejs/express4-typescript/security-controls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use strict'

import type { Express, NextFunction, Request, Response } from 'express'
const { Client } = require('pg')
const { execSync } = require('child_process')
const SecurityControlUtil = require('./utils/securityControlUtil')

function execQuery (user: string, password: string) {
const sql = 'SELECT * FROM IAST_USER WHERE USERNAME = \'' + user +
'\' AND PASSWORD = \'' + password + '\''

const client = new Client()
return client.connect().then(() => {
return client.query(sql)
})
}

function initSecurityControls (app: Express) {
app.post('/iast/sc/s/configured', (req: Request, res: Response): void => {
const sanitized = SecurityControlUtil.sanitize(req.body.param)
try {
execSync(sanitized)
} catch (e) {
// ignore
}
res.send('OK')
})

app.post('/iast/sc/s/not-configured', (req: Request, res: Response): void => {
const sanitized = SecurityControlUtil.sanitize(req.body.param)
execQuery(sanitized, 'password').then((queryResult: any) => {
res.send('OK')
}).catch((err: Error) => {
res.send('Error: ' + err)
})
})

app.post('/iast/sc/s/all', (req: Request, res: Response): void => {
const sanitized = SecurityControlUtil.sanitizeForAllVulns(req.body.param)
execQuery(sanitized, 'password').then((queryResult: any) => {
res.send('OK')
}).catch((err: Error) => {
res.send('Error: ' + err)
})
})

app.post('/iast/sc/s/overloaded/secure', (req: Request, res: Response): void => {
const sanitized = SecurityControlUtil.overloadedSanitize(req.body.param)
try {
execSync(sanitized)
} catch (e) {
// ignore
}
res.send('OK')
})

app.post('/iast/sc/s/overloaded/insecure', (req: Request, res: Response): void => {
const sanitized = SecurityControlUtil.overloadedSanitize(null, req.body.param)
try {
execSync(sanitized)
} catch (e) {
// ignore
}
res.send('OK')
})

app.post('/iast/sc/iv/configured', (req: Request, res: Response): void => {
// TODO: problem in express5?
if (SecurityControlUtil.validate(req.body.param)) {
try {
execSync(req.body.param)
} catch (e) {
// ignore
}
}
res.send('OK')
})

app.post('/iast/sc/iv/not-configured', (req: Request, res: Response): void => {
if (SecurityControlUtil.validate(req.body.param)) {
execQuery(req.body.param, 'password').then((queryResult: any) => {
res.send('OK')
}).catch((err: Error) => {
res.send('Error: ' + err)
})
}
})

app.post('/iast/sc/iv/all', (req: Request, res: Response): void => {
if (SecurityControlUtil.validateForAllVulns(req.body.param)) {
execQuery(req.body.param, 'password').then((queryResult: any) => {
res.send('OK')
}).catch((err: Error) => {
res.send('Error: ' + err)
})
}
})

app.post('/iast/sc/iv/overloaded/secure', (req: Request, res: Response): void => {
const { user, password } = req.body
if (SecurityControlUtil.overloadedValidation(null, user, password)) {
execQuery(user, password).then((queryResult: any) => {
res.send('OK')
}).catch((err: Error) => {
res.send('Error: ' + err)
})
}
})

app.post('/iast/sc/iv/overloaded/insecure', (req: Request, res: Response): void => {
const { user, password } = req.body
if (SecurityControlUtil.overloadedValidation(user, password, null)) {
execQuery(user, password).then((queryResult: any) => {
res.send('OK')
}).catch((err: Error) => {
res.send('Error: ' + err)
})
}
})
}

module.exports = initSecurityControls
Loading
Loading