Skip to content

Commit 39cb3aa

Browse files
fix(iast): report cookies vuln [backport 2.0] (#7499)
Backport 58a5968 from #7497 to 2.0. Fix Cookie vulnerability errors: When IAST finds ONE of the Cookie vulnerabilities, it stops and returns the report. IAST should check all possible Cookie vulnerabilities and report if any one, two, or all three types of Cookie vulnerabilities are found. ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [x] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) - [x] If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. - [x] This PR doesn't touch any of that. Co-authored-by: Alberto Vara <[email protected]>
1 parent 7cd9392 commit 39cb3aa

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

ddtrace/appsec/_iast/taint_sinks/insecure_cookie.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,10 @@ def asm_check_cookies(cookies): # type: (Optional[Dict[str, str]]) -> None
4949
if ";secure" not in lvalue:
5050
_set_metric_iast_executed_sink(InsecureCookie.vulnerability_type)
5151
InsecureCookie.report(evidence_value=evidence)
52-
return
5352

5453
if ";httponly" not in lvalue:
5554
_set_metric_iast_executed_sink(NoHttpOnlyCookie.vulnerability_type)
5655
NoHttpOnlyCookie.report(evidence_value=evidence)
57-
return
5856

5957
if ";samesite=" in lvalue:
6058
ss_tokens = lvalue.split(";samesite=")

tests/appsec/iast/taint_sinks/test_insecure_cookie.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,39 @@ def test_insecure_cookies(iast_span_defaults):
1414
cookies = {"foo": "bar"}
1515
asm_check_cookies(cookies)
1616
span_report = core.get_item(IAST.CONTEXT_KEY, span=iast_span_defaults)
17-
assert list(span_report.vulnerabilities)[0].type == VULN_INSECURE_COOKIE
18-
assert list(span_report.vulnerabilities)[0].evidence.value == "foo=bar"
17+
vulnerabilities = list(span_report.vulnerabilities)
18+
vulnerabilities_types = [vuln.type for vuln in vulnerabilities]
19+
assert len(vulnerabilities) == 3
20+
assert VULN_NO_HTTPONLY_COOKIE in vulnerabilities_types
21+
assert VULN_INSECURE_COOKIE in vulnerabilities_types
22+
assert VULN_NO_SAMESITE_COOKIE in vulnerabilities_types
23+
24+
assert vulnerabilities[0].evidence.value == "foo=bar"
25+
assert vulnerabilities[1].evidence.value == "foo=bar"
26+
assert vulnerabilities[2].evidence.value == "foo=bar"
27+
28+
assert vulnerabilities[0].location.line is None
29+
assert vulnerabilities[0].location.path is None
1930

2031

2132
@pytest.mark.skipif(not python_supported_by_iast(), reason="Python version not supported by IAST")
2233
def test_nohttponly_cookies(iast_span_defaults):
2334
cookies = {"foo": "bar;secure"}
2435
asm_check_cookies(cookies)
2536
span_report = core.get_item(IAST.CONTEXT_KEY, span=iast_span_defaults)
26-
assert list(span_report.vulnerabilities)[0].type == VULN_NO_HTTPONLY_COOKIE
27-
assert list(span_report.vulnerabilities)[0].evidence.value == "foo=bar;secure"
28-
assert list(span_report.vulnerabilities)[0].location.line is None
29-
assert list(span_report.vulnerabilities)[0].location.path is None
37+
38+
vulnerabilities = list(span_report.vulnerabilities)
39+
vulnerabilities_types = [vuln.type for vuln in vulnerabilities]
40+
assert len(vulnerabilities) == 2
41+
assert VULN_NO_HTTPONLY_COOKIE in vulnerabilities_types
42+
assert VULN_NO_SAMESITE_COOKIE in vulnerabilities_types
43+
44+
assert vulnerabilities[0].evidence.value == "foo=bar;secure"
45+
assert vulnerabilities[1].evidence.value == "foo=bar;secure"
46+
47+
assert vulnerabilities[0].location.line is None
48+
assert vulnerabilities[0].location.path is None
49+
3050
str_report = _iast_report_to_str(span_report)
3151
# Double check to verify we're not sending an empty key
3252
assert '"line"' not in str_report
@@ -36,26 +56,40 @@ def test_nohttponly_cookies(iast_span_defaults):
3656
def test_nosamesite_cookies_missing(iast_span_defaults):
3757
cookies = {"foo": "bar;secure;httponly"}
3858
asm_check_cookies(cookies)
59+
3960
span_report = core.get_item(IAST.CONTEXT_KEY, span=iast_span_defaults)
40-
assert list(span_report.vulnerabilities)[0].type == VULN_NO_SAMESITE_COOKIE
41-
assert list(span_report.vulnerabilities)[0].evidence.value == "foo=bar;secure;httponly"
61+
62+
vulnerabilities = list(span_report.vulnerabilities)
63+
64+
assert len(vulnerabilities) == 1
65+
assert vulnerabilities[0].type == VULN_NO_SAMESITE_COOKIE
66+
assert vulnerabilities[0].evidence.value == "foo=bar;secure;httponly"
4267

4368

4469
def test_nosamesite_cookies_none(iast_span_defaults):
4570
cookies = {"foo": "bar;secure;httponly;samesite=none"}
4671
asm_check_cookies(cookies)
4772
span_report = core.get_item(IAST.CONTEXT_KEY, span=iast_span_defaults)
48-
assert list(span_report.vulnerabilities)[0].type == VULN_NO_SAMESITE_COOKIE
4973

50-
assert list(span_report.vulnerabilities)[0].evidence.value == "foo=bar;secure;httponly;samesite=none"
74+
vulnerabilities = list(span_report.vulnerabilities)
75+
76+
assert len(vulnerabilities) == 1
77+
78+
assert vulnerabilities[0].type == VULN_NO_SAMESITE_COOKIE
79+
assert vulnerabilities[0].evidence.value == "foo=bar;secure;httponly;samesite=none"
5180

5281

5382
def test_nosamesite_cookies_other(iast_span_defaults):
5483
cookies = {"foo": "bar;secure;httponly;samesite=none"}
5584
asm_check_cookies(cookies)
5685
span_report = core.get_item(IAST.CONTEXT_KEY, span=iast_span_defaults)
57-
assert list(span_report.vulnerabilities)[0].type == VULN_NO_SAMESITE_COOKIE
58-
assert list(span_report.vulnerabilities)[0].evidence.value == "foo=bar;secure;httponly;samesite=none"
86+
87+
vulnerabilities = list(span_report.vulnerabilities)
88+
89+
assert len(vulnerabilities) == 1
90+
91+
assert vulnerabilities[0].type == VULN_NO_SAMESITE_COOKIE
92+
assert vulnerabilities[0].evidence.value == "foo=bar;secure;httponly;samesite=none"
5993

6094

6195
def test_nosamesite_cookies_lax_no_error(iast_span_defaults):

tests/contrib/flask/test_flask_appsec_iast.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
from ddtrace.appsec._constants import IAST
77
from ddtrace.appsec._iast import oce
88
from ddtrace.appsec._iast._utils import _is_python_version_supported as python_supported_by_iast
9+
from ddtrace.appsec._iast.constants import VULN_INSECURE_COOKIE
10+
from ddtrace.appsec._iast.constants import VULN_NO_HTTPONLY_COOKIE
11+
from ddtrace.appsec._iast.constants import VULN_NO_SAMESITE_COOKIE
912
from ddtrace.appsec._iast.constants import VULN_SQL_INJECTION
1013
from ddtrace.contrib.sqlite3.patch import patch
1114
from tests.appsec.iast.iast_utils import get_line_and_hash
@@ -430,7 +433,12 @@ def test_sqli():
430433
assert vulnerability["location"]["path"] == TEST_FILE_PATH
431434
assert vulnerability["hash"] == hash_value
432435

433-
assert {VULN_SQL_INJECTION, "INSECURE_COOKIE"} == vulnerabilities
436+
assert {
437+
VULN_SQL_INJECTION,
438+
VULN_INSECURE_COOKIE,
439+
VULN_NO_HTTPONLY_COOKIE,
440+
VULN_NO_SAMESITE_COOKIE,
441+
} == vulnerabilities
434442

435443
@pytest.mark.skipif(not python_supported_by_iast(), reason="Python version not supported by IAST")
436444
def test_flask_full_sqli_iast_http_request_parameter(self):

0 commit comments

Comments
 (0)