Skip to content

Commit 23d13e6

Browse files
authored
Add api10 redirect test (#5768)
1 parent 6653396 commit 23d13e6

File tree

10 files changed

+80
-2
lines changed

10 files changed

+80
-2
lines changed

docs/weblog/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,31 @@ if the request to `internal_server` is a failure, it must return a json body wit
134134
- `status` the status code of the `internal_server` response if available or a null value
135135
- `error` a string describing the error, for debug purposes
136136

137+
### GET /external_request/redirect
138+
139+
This endpoint tests HTTP redirect chains with downstream requests, using the fastapi application in `/utils/build/docker/internal_server/app.py`
140+
141+
Query parameters:
142+
- `totalRedirects`: number of redirects (default 0)
143+
144+
The endpoint calls `/redirect?totalRedirects={totalRedirects}` and follows all 302 redirects until receiving a 200 response.
145+
146+
How it works:
147+
- `/redirect` decrements `totalRedirects` and redirects to itself until `totalRedirects=0`
148+
- When `totalRedirects=0`, redirects to `/mirror/200` which returns 200 OK
149+
150+
Example with `totalRedirects=2`:
151+
1. `/redirect?totalRedirects=2` → 302
152+
2. `/redirect?totalRedirects=1` → 302
153+
3. `/redirect?totalRedirects=0` → 302
154+
4. `/mirror/200` → 200 OK
155+
156+
Total: 4 downstream requests (totalRedirects + 2).
157+
158+
All query parameters are sent as headers to `internal_server` requests.
159+
160+
Returns 200 status code.
161+
137162
### GET /spans
138163

139164
The endpoint may accept two query string parameters:

manifests/dotnet.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ tests/:
205205
Test_API10_all: missing_feature
206206
Test_API10_downstream_request_tag: missing_feature
207207
Test_API10_downstream_ssrf_telemetry: missing_feature
208+
Test_API10_redirect: missing_feature
208209
Test_API10_request_body: missing_feature
209210
Test_API10_request_headers: missing_feature
210211
Test_API10_request_method: missing_feature

manifests/golang.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ tests/:
216216
Test_API10_all: v2.4.0
217217
Test_API10_downstream_request_tag: v2.5.0-dev
218218
Test_API10_downstream_ssrf_telemetry: v2.4.0
219+
Test_API10_redirect: missing_feature
219220
Test_API10_request_body: v2.4.0
220221
Test_API10_request_headers: v2.4.0
221222
Test_API10_request_method: v2.4.0

manifests/java.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ tests/:
783783
'*': missing_feature
784784
vertx3: v1.54.0
785785
Test_API10_downstream_ssrf_telemetry: missing_feature
786+
Test_API10_redirect: missing_feature
786787
Test_API10_request_body:
787788
'*': missing_feature
788789
vertx3: v1.54.0

manifests/nodejs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ tests/:
523523
Test_API10_all: missing_feature
524524
Test_API10_downstream_request_tag: missing_feature
525525
Test_API10_downstream_ssrf_telemetry: missing_feature
526+
Test_API10_redirect: missing_feature
526527
Test_API10_request_body: missing_feature
527528
Test_API10_request_headers: missing_feature
528529
Test_API10_request_method: missing_feature

manifests/php.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ tests/:
215215
Test_API10_all: missing_feature
216216
Test_API10_downstream_request_tag: missing_feature
217217
Test_API10_downstream_ssrf_telemetry: missing_feature
218+
Test_API10_redirect: missing_feature
218219
Test_API10_request_body: missing_feature
219220
Test_API10_request_headers: missing_feature
220221
Test_API10_request_method: missing_feature

manifests/python.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ tests/:
305305
'fastapi': v3.15.0.dev (with requests support)
306306
Test_API10_downstream_request_tag: v3.18.0
307307
Test_API10_downstream_ssrf_telemetry: v3.18.0
308+
Test_API10_redirect: missing_feature
308309
Test_API10_request_body:
309310
'*': v3.14.0.rc
310311
'fastapi': v3.15.0.dev (with requests support)

manifests/ruby.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ tests/:
225225
Test_API10_all: missing_feature
226226
Test_API10_downstream_request_tag: missing_feature
227227
Test_API10_downstream_ssrf_telemetry: missing_feature
228+
Test_API10_redirect: missing_feature
228229
Test_API10_request_body: missing_feature
229230
Test_API10_request_headers: missing_feature
230231
Test_API10_request_method: missing_feature

tests/appsec/rasp/test_api10.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
class API10:
3333
TAGS_EXPECTED: list[tuple[str, str]] = []
34+
TAGS_EXPECTED_METRIC: list[tuple[str, str]] = []
3435

3536
def validate(self, span: dict):
3637
if span.get("parent_id") not in (0, None):
@@ -49,7 +50,7 @@ def validate(self, span: dict):
4950
return True
5051

5152
def validate_metric(self, span: dict):
52-
for tag, expected in self.TAGS_EXPECTED:
53+
for tag, expected in self.TAGS_EXPECTED_METRIC:
5354
# check also in meta to be safe
5455
assert tag in span["metrics"] or tag in span["meta"], f"Missing {tag} from span's meta/metrics"
5556
values = span["metrics"] if tag in span["metrics"] else span["meta"]
@@ -241,7 +242,7 @@ def test_api10(self):
241242
class Test_API10_downstream_request_tag(API10):
242243
"""API 10 span tag validation"""
243244

244-
TAGS_EXPECTED = [
245+
TAGS_EXPECTED_METRIC = [
245246
("_dd.appsec.downstream_request", "1"),
246247
]
247248

@@ -348,3 +349,29 @@ def test_api10_res_body(self):
348349
assert "error" not in body
349350
assert int(body["status"]) == 200
350351
interfaces.library.validate_one_span(self.r, validator=self.validate_absence)
352+
353+
354+
@rfc("https://docs.google.com/document/d/1gCXU3LvTH9en3Bww0AC2coSJWz1m7HcavZjvMLuDCWg/edit#heading=h.giijrtyn1fdx")
355+
@features.api10
356+
@scenarios.appsec_rasp
357+
@scenarios.appsec_standalone_rasp
358+
class Test_API10_redirect(API10):
359+
"""API 10 for multiple redirect responses"""
360+
361+
TAGS_EXPECTED = [
362+
("_dd.appsec.trace.req_headers", "TAG_API10_REQ_HEADERS"),
363+
]
364+
365+
TAGS_EXPECTED_METRIC = [
366+
("_dd.appsec.downstream_request", "5"),
367+
]
368+
369+
PARAMS = {"Witness": "pwq3ojtropiw3hjtowir", "totalRedirects": "3"}
370+
371+
def setup_api10_redirect(self):
372+
self.r = weblog.get("/external_request/redirect", params=self.PARAMS)
373+
374+
def test_api10_redirect(self):
375+
assert self.r.status_code == 200
376+
interfaces.library.validate_one_span(self.r, validator=self.validate)
377+
interfaces.library.validate_one_span(self.r, validator=self.validate_metric)

utils/build/docker/internal_server/app.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ async def mirror(status: int, request: fastapi.Request):
3131
return fastapi.responses.JSONResponse({"status": "OK", "payload": body}, status_code=status, headers=query)
3232

3333

34+
@app.get("/redirect", response_class=fastapi.responses.RedirectResponse)
35+
async def redirect(request: fastapi.Request):
36+
"""Redirect endpoint for testing API 10 with redirects
37+
38+
Query parameter:
39+
- totalRedirects: number of redirects remaining (default 0)
40+
"""
41+
query = request.query_params
42+
total_redirects = int(query.get("totalRedirects", "0"))
43+
44+
if total_redirects > 0:
45+
# Redirect to itself with totalRedirects-1
46+
location = f"/redirect?totalRedirects={total_redirects - 1}"
47+
else:
48+
location = "/mirror/200"
49+
50+
return fastapi.responses.RedirectResponse(url=location, status_code=302)
51+
52+
3453
@app.get("/shutdown")
3554
async def shutdown():
3655
os.kill(os.getpid(), signal.SIGTERM)

0 commit comments

Comments
 (0)