Skip to content

Commit 5ec931b

Browse files
authored
Improve SSRF ignoring tainted values after # (#6023)
1 parent a1dbec0 commit 5ec931b

File tree

4 files changed

+61
-4
lines changed

4 files changed

+61
-4
lines changed

packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class InjectionAnalyzer extends Analyzer {
77
_isVulnerable (value, iastContext) {
88
let ranges = value && getRanges(iastContext, value)
99
if (ranges?.length > 0) {
10-
ranges = this._filterSecureRanges(ranges)
10+
ranges = this._filterSecureRanges(ranges, value)
1111
if (!ranges?.length) {
1212
this._incrementSuppressedMetric(iastContext)
1313
}
@@ -27,11 +27,13 @@ class InjectionAnalyzer extends Analyzer {
2727
return ranges?.some(range => range.iinfo.type !== SQL_ROW_VALUE)
2828
}
2929

30-
_filterSecureRanges (ranges) {
31-
return ranges?.filter(range => !this._isRangeSecure(range))
30+
_filterSecureRanges (ranges, value) {
31+
return ranges?.filter(range => !this._isRangeSecure(range, value))
3232
}
3333

34-
_isRangeSecure (range) {
34+
_isRangeSecure (range, _value) {
35+
// _value is not necessary in this method, but could be used in overridden methods
36+
// added here for visibility
3537
const { secureMarks } = range
3638
return (secureMarks & this._secureMark) === this._secureMark
3739
}

packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ class SSRFAnalyzer extends InjectionAnalyzer {
2323
}
2424
})
2525
}
26+
27+
_isRangeSecure (range, value) {
28+
const fragmentIndex = value.indexOf('#')
29+
if (fragmentIndex !== -1 && range.start >= fragmentIndex) {
30+
return true
31+
}
32+
33+
return super._isRangeSecure(range, value)
34+
}
2635
}
2736

2837
module.exports = new SSRFAnalyzer()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict'
2+
// Move this file to temp dir before requiring to force a rewriting
3+
// because the files into the tracer are not rewritten
4+
5+
module.exports = {
6+
add (param1, param2) {
7+
return param1 + param2
8+
}
9+
}

packages/dd-trace/test/appsec/iast/analyzers/ssrf-analyzer.spec.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
'use strict'
22

3+
const fs = require('fs')
4+
const os = require('os')
5+
const path = require('path')
6+
const axios = require('axios')
37
const { prepareTestServerForIast } = require('../utils')
48
const { storage } = require('../../../../../datadog-core')
59
const iastContextFunctions = require('../../../../src/appsec/iast/iast-context')
@@ -78,6 +82,22 @@ describe('ssrf analyzer', () => {
7882
].forEach(requestMethodData => {
7983
describe(requestMethodData.httpMethodName, () => {
8084
describe('with url', () => {
85+
const taintTrackingUtilsFilename = 'taint-tracking-utils.js'
86+
let taintTrackingUtilsFilePath
87+
88+
before(() => {
89+
taintTrackingUtilsFilePath = path.join(os.tmpdir(), taintTrackingUtilsFilename)
90+
91+
fs.copyFileSync(
92+
path.join(__dirname, 'resources', taintTrackingUtilsFilename),
93+
taintTrackingUtilsFilePath
94+
)
95+
})
96+
97+
after(() => {
98+
fs.unlinkSync(taintTrackingUtilsFilePath)
99+
})
100+
81101
testThatRequestHasVulnerability(() => {
82102
const store = storage('legacy').getStore()
83103
const iastContext = iastContextFunctions.getIastContext(store)
@@ -93,6 +113,23 @@ describe('ssrf analyzer', () => {
93113
const https = require(pluginName)
94114
return requestMethodData.methodToExecute(https, url)
95115
}, 'SSRF')
116+
117+
testThatRequestHasNoVulnerability((req, res) => {
118+
const utils = require(taintTrackingUtilsFilePath)
119+
120+
const hash = req.headers.hash
121+
let url = pluginName + '://www.google.com/#'
122+
url = utils.add(url, hash)
123+
const https = require(pluginName)
124+
requestMethodData.methodToExecute(https, url)
125+
res.end()
126+
}, 'SSRF', (done, config) => {
127+
axios.get(`http://localhost:${config.port}`, {
128+
headers: {
129+
hash: 'taintedHash'
130+
}
131+
})
132+
}, 'should not have SSRF vulnerability when the tainted value is after #')
96133
})
97134

98135
describe('with options', () => {

0 commit comments

Comments
 (0)