Skip to content

Commit b76eee5

Browse files
uuriencursoragent
andauthored
Add API10 system tests for downstream response body limits (#7046)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9338df5 commit b76eee5

18 files changed

Lines changed: 327 additions & 74 deletions

File tree

docs/understand/weblogs/end-to-end_weblog.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,20 @@ if the request to `internal_server` is a failure, it must return a json body wit
225225
- `status` the status code of the `internal_server` response if available or a null value
226226
- `error` a string describing the error, for debug purposes
227227

228+
### GET /external_request/body_limit/{failure_reason}
229+
### POST /external_request/body_limit/{failure_reason}
230+
### TRACE /external_request/body_limit/{failure_reason}
231+
### PUT /external_request/body_limit/{failure_reason}
232+
233+
Same behavior as `/external_request`, but the downstream call targets `http://internal_server:8089/downstream_response/{failure_reason}`.
234+
235+
Supported `{failure_reason}` values (defined in `internal_server`):
236+
- `invalid_content_type`
237+
- `content_length_missing`
238+
- `content_length_too_big`
239+
240+
Unknown values must return HTTP 404 with a json error body.
241+
228242
### GET /external_request/redirect
229243

230244
This endpoint tests HTTP redirect chains with downstream requests, using the fastapi application in `/utils/build/docker/internal_server/app.py`

manifests/dotnet.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ manifest:
238238
tests/appsec/rasp/test_api10.py::Test_API10_request_headers: missing_feature
239239
tests/appsec/rasp/test_api10.py::Test_API10_request_method: missing_feature
240240
tests/appsec/rasp/test_api10.py::Test_API10_response_body: missing_feature
241+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_missing: missing_feature
242+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_too_big: missing_feature
243+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_type: missing_feature
241244
tests/appsec/rasp/test_api10.py::Test_API10_response_headers: missing_feature
242245
tests/appsec/rasp/test_api10.py::Test_API10_response_status: missing_feature
243246
tests/appsec/rasp/test_api10.py::Test_API10_without_downstream_body_analysis_using_max: missing_feature

manifests/golang.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ manifest:
214214
tests/appsec/rasp/test_api10.py::Test_API10_request_headers: v2.7.0-dev
215215
tests/appsec/rasp/test_api10.py::Test_API10_request_method: v2.4.0
216216
tests/appsec/rasp/test_api10.py::Test_API10_response_body: v2.7.0-dev
217+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_missing: missing_feature
218+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_too_big: missing_feature
219+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_type: missing_feature
217220
tests/appsec/rasp/test_api10.py::Test_API10_response_headers: v2.4.0
218221
tests/appsec/rasp/test_api10.py::Test_API10_response_status: v2.4.0
219222
tests/appsec/rasp/test_api10.py::Test_API10_without_downstream_body_analysis_using_max: v2.4.0

manifests/java.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,9 @@ manifest:
12051205
- weblog_declaration:
12061206
"*": missing_feature
12071207
vertx3: v1.60.1 # TODO: a lower version might be supported
1208+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_missing: missing_feature
1209+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_too_big: missing_feature
1210+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_type: missing_feature
12081211
tests/appsec/rasp/test_api10.py::Test_API10_response_headers:
12091212
- weblog_declaration:
12101213
"*": missing_feature

manifests/nodejs.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ refs:
9797
- &ref_5_104_0 '>=5.104.0'
9898
- &ref_5_106_0 '>=5.106.0'
9999
- &ref_5_107_0 '>=5.107.0'
100+
- &ref_5_109_0 '>=5.109.0'
100101
- &ref_5_110_0 '>=5.110.0'
101102
- &ref_6_0_0 '>=6.0.0-pre'
102103
manifest:
@@ -706,6 +707,9 @@ manifest:
706707
tests/appsec/rasp/test_api10.py::Test_API10_request_headers: *ref_5_87_0
707708
tests/appsec/rasp/test_api10.py::Test_API10_request_method: *ref_5_87_0
708709
tests/appsec/rasp/test_api10.py::Test_API10_response_body: *ref_5_87_0
710+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_missing: *ref_5_109_0
711+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_too_big: *ref_5_109_0
712+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_type: *ref_5_109_0
709713
tests/appsec/rasp/test_api10.py::Test_API10_response_headers: *ref_5_87_0
710714
tests/appsec/rasp/test_api10.py::Test_API10_response_status: *ref_5_87_0
711715
tests/appsec/rasp/test_api10.py::Test_API10_without_downstream_body_analysis_using_max: *ref_5_87_0

manifests/php.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ manifest:
227227
tests/appsec/rasp/test_api10.py::Test_API10_request_headers: missing_feature
228228
tests/appsec/rasp/test_api10.py::Test_API10_request_method: missing_feature
229229
tests/appsec/rasp/test_api10.py::Test_API10_response_body: missing_feature
230+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_missing: missing_feature
231+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_too_big: missing_feature
232+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_type: missing_feature
230233
tests/appsec/rasp/test_api10.py::Test_API10_response_headers: missing_feature
231234
tests/appsec/rasp/test_api10.py::Test_API10_response_status: missing_feature
232235
tests/appsec/rasp/test_api10.py::Test_API10_without_downstream_body_analysis_using_max: missing_feature

manifests/python.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,9 @@ manifest:
586586
fastapi: v3.15.0.dev (with requests support)
587587
*django: v4.3.0-dev0 (with httpx support)
588588
tests/appsec/rasp/test_api10.py::Test_API10_response_body: v4.5.0-rc1
589+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_missing: missing_feature
590+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_too_big: missing_feature
591+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_type: missing_feature
589592
tests/appsec/rasp/test_api10.py::Test_API10_response_headers:
590593
- weblog_declaration:
591594
"*": v3.14.0.rc

manifests/ruby.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ manifest:
393393
- weblog_declaration:
394394
"*": v2.29.0
395395
"graphql23": irrelevant
396+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_missing: missing_feature
397+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_length_too_big: missing_feature
398+
tests/appsec/rasp/test_api10.py::Test_API10_response_body_ignored_content_type: missing_feature
396399
tests/appsec/rasp/test_api10.py::Test_API10_response_headers:
397400
- weblog_declaration:
398401
"*": v2.27.0-dev

tests/appsec/rasp/test_api10.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,78 @@ def test_api10_res_body(self):
339339
interfaces.library.validate_one_span(self.r, validator=self.validate_absence)
340340

341341

342+
class API10ResponseBodyIgnored(API10):
343+
"""Base for downstream response body guard tests."""
344+
345+
TAGS_NOT_EXPECTED = ["_dd.appsec.trace.res_body"]
346+
347+
def validate_ignored(self, span: DataDogLibrarySpan):
348+
if span.get("parent_id") not in (0, None):
349+
return None
350+
351+
if not self.validate_metric(span):
352+
return None
353+
354+
for tag in self.TAGS_NOT_EXPECTED:
355+
assert tag not in span.get("meta", {}), f"Tag {tag} should NOT be present in span's meta"
356+
357+
return True
358+
359+
360+
@rfc("https://docs.google.com/document/d/1gCXU3LvTH9en3Bww0AC2coSJWz1m7HcavZjvMLuDCWg/edit#heading=h.giijrtyn1fdx")
361+
@features.api10
362+
@scenarios.appsec_rasp
363+
class Test_API10_response_body_ignored_content_type(API10ResponseBodyIgnored):
364+
"""Downstream response body ignored when content-type is unsupported."""
365+
366+
TAGS_EXPECTED_METRIC = [
367+
("_dd.appsec.downstream_request.response_body_ignored.content_type_invalid", "1"),
368+
]
369+
370+
def setup_api10_response_body_ignored_content_type(self):
371+
self.r = weblog.get("/external_request/body_limit/invalid_content_type")
372+
373+
def test_api10_response_body_ignored_content_type(self):
374+
assert self.r.status_code == 200
375+
interfaces.library.validate_one_span(self.r, validator=self.validate_ignored)
376+
377+
378+
@rfc("https://docs.google.com/document/d/1gCXU3LvTH9en3Bww0AC2coSJWz1m7HcavZjvMLuDCWg/edit#heading=h.giijrtyn1fdx")
379+
@features.api10
380+
@scenarios.appsec_rasp
381+
class Test_API10_response_body_ignored_content_length_missing(API10ResponseBodyIgnored):
382+
"""Downstream response body ignored when content-length is missing."""
383+
384+
TAGS_EXPECTED_METRIC = [
385+
("_dd.appsec.downstream_request.response_body_ignored.content_length_missing", "1"),
386+
]
387+
388+
def setup_api10_response_body_ignored_content_length_missing(self):
389+
self.r = weblog.get("/external_request/body_limit/content_length_missing")
390+
391+
def test_api10_response_body_ignored_content_length_missing(self):
392+
assert self.r.status_code == 200
393+
interfaces.library.validate_one_span(self.r, validator=self.validate_ignored)
394+
395+
396+
@rfc("https://docs.google.com/document/d/1gCXU3LvTH9en3Bww0AC2coSJWz1m7HcavZjvMLuDCWg/edit#heading=h.giijrtyn1fdx")
397+
@features.api10
398+
@scenarios.appsec_rasp
399+
class Test_API10_response_body_ignored_content_length_too_big(API10ResponseBodyIgnored):
400+
"""Downstream response body ignored when content-length exceeds maxBytes."""
401+
402+
TAGS_EXPECTED_METRIC = [
403+
("_dd.appsec.downstream_request.response_body_ignored.content_length_too_big", "1"),
404+
]
405+
406+
def setup_api10_response_body_ignored_content_length_too_big(self):
407+
self.r = weblog.get("/external_request/body_limit/content_length_too_big")
408+
409+
def test_api10_response_body_ignored_content_length_too_big(self):
410+
assert self.r.status_code == 200
411+
interfaces.library.validate_one_span(self.r, validator=self.validate_ignored)
412+
413+
342414
@rfc("https://docs.google.com/document/d/1gCXU3LvTH9en3Bww0AC2coSJWz1m7HcavZjvMLuDCWg/edit#heading=h.giijrtyn1fdx")
343415
@features.api10
344416
@scenarios.appsec_rasp

utils/_context/_scenarios/appsec_rasp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def __init__(
2424
# added to test Test_ExtendedRequestBodyCollection
2525
"DD_APPSEC_RASP_COLLECT_REQUEST_BODY": "true",
2626
"DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE": "1.0",
27+
"DD_API_SECURITY_MAX_DOWNSTREAM_BODY_BYTES": "128",
2728
"OPENAI_BASE_URL": "http://internal_server:8089",
2829
}
2930
merged_env = default_env | weblog_env

0 commit comments

Comments
 (0)