Skip to content

Commit 773c9bd

Browse files
authored
Add get_root_span helper (#4206)
1 parent e7cdcd5 commit 773c9bd

10 files changed

Lines changed: 118 additions & 161 deletions

File tree

tests/appsec/api_security/test_schemas.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,9 @@
1515

1616
def get_schema(request, address):
1717
"""Get api security schema from spans"""
18-
for _, span in interfaces.library.get_root_spans(request):
19-
meta = span.get("meta", {})
20-
payload = meta.get("_dd.appsec.s." + address)
21-
if payload is not None:
22-
return payload
23-
return None
18+
span = interfaces.library.get_root_span(request)
19+
meta = span.get("meta", {})
20+
return meta.get("_dd.appsec.s." + address)
2421

2522

2623
# can be used to match any value in a schema

tests/appsec/iast/utils.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ def _get_expectation(d):
1717

1818

1919
def _get_span_meta(request):
20-
spans = [span for _, span in interfaces.library.get_root_spans(request=request)]
21-
assert spans, "No root span found"
22-
span = spans[0]
20+
span = interfaces.library.get_root_span(request)
2321
meta = span.get("meta", {})
2422
meta_struct = span.get("meta_struct", {})
2523
return meta, meta_struct
@@ -199,9 +197,7 @@ def assert_no_iast_event(request, tested_vulnerability_type=None) -> None:
199197

200198

201199
def validate_stack_traces(request):
202-
spans = [span for _, span in interfaces.library.get_root_spans(request=request)]
203-
assert spans, "No root span found"
204-
span = spans[0]
200+
span = interfaces.library.get_root_span(request)
205201
meta = span.get("meta", {})
206202
meta_struct = span.get("meta_struct", {})
207203
iast = meta.get("_dd.iast.json") or meta_struct.get("iast")
@@ -290,10 +286,7 @@ def validate_stack_traces(request):
290286

291287

292288
def validate_extended_location_data(request, vulnerability_type, is_expected_location_required=True):
293-
spans = [span for _, span in interfaces.library.get_root_spans(request=request)]
294-
assert spans, "No root span found"
295-
span = spans[0]
296-
289+
span = interfaces.library.get_root_span(request)
297290
iast = span.get("meta", {}).get("_dd.iast.json")
298291
assert iast, "Expected at least one vulnerability"
299292
assert iast["vulnerabilities"], "Expected at least one vulnerability"

tests/appsec/rasp/utils.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,14 @@
99

1010
def validate_span_tags(request, expected_meta=(), expected_metrics=()):
1111
"""Validate RASP span tags are added when an event is generated"""
12-
spans = [s for _, s in interfaces.library.get_root_spans(request=request)]
13-
assert spans, "No spans to validate"
14-
15-
for span in spans:
16-
meta = span["meta"]
17-
for m in expected_meta:
18-
assert m in meta, f"missing span meta tag `{m}` in {meta}"
19-
20-
metrics = span["metrics"]
21-
for m in expected_metrics:
22-
assert m in metrics, f"missing span metric tag `{m}` in {metrics}"
12+
span = interfaces.library.get_root_span(request)
13+
meta = span["meta"]
14+
for m in expected_meta:
15+
assert m in meta, f"missing span meta tag `{m}` in {meta}"
16+
17+
metrics = span["metrics"]
18+
for m in expected_metrics:
19+
assert m in metrics, f"missing span metric tag `{m}` in {metrics}"
2320

2421

2522
def validate_stack_traces(request):

tests/appsec/test_blocking_addresses.py

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -695,26 +695,25 @@ def setup_request_block_attack(self):
695695
@bug(context.library < "ruby@2.10.0-dev", reason="APPSEC-56464")
696696
def test_request_block_attack(self):
697697
assert self.r_attack.status_code == 403
698-
699-
for _, span in interfaces.library.get_root_spans(request=self.r_attack):
700-
meta = span.get("meta", {})
701-
meta_struct = span.get("meta_struct", {})
702-
assert meta["appsec.event"] == "true"
703-
assert ("_dd.appsec.json" in meta) ^ ("appsec" in meta_struct)
704-
appsec = meta.get("_dd.appsec.json", {}) or meta_struct.get("appsec", {})
705-
rule_triggered = appsec["triggers"][0]
706-
parameters = rule_triggered["rule_matches"][0]["parameters"][0]
707-
assert (
708-
parameters["address"] == "graphql.server.all_resolvers"
709-
or parameters["address"] == "graphql.server.resolver"
710-
)
711-
assert rule_triggered["rule"]["id"] == "block-resolvers"
712-
assert parameters["key_path"] == (
713-
["userByName", "name"]
714-
if parameters["address"] == "graphql.server.resolver"
715-
else ["userByName", "0", "name"]
716-
)
717-
assert parameters["value"] == "testblockresolver"
698+
span = interfaces.library.get_root_span(request=self.r_attack)
699+
meta = span.get("meta", {})
700+
meta_struct = span.get("meta_struct", {})
701+
assert meta["appsec.event"] == "true"
702+
assert ("_dd.appsec.json" in meta) ^ ("appsec" in meta_struct)
703+
appsec = meta.get("_dd.appsec.json", {}) or meta_struct.get("appsec", {})
704+
rule_triggered = appsec["triggers"][0]
705+
parameters = rule_triggered["rule_matches"][0]["parameters"][0]
706+
assert (
707+
parameters["address"] == "graphql.server.all_resolvers"
708+
or parameters["address"] == "graphql.server.resolver"
709+
)
710+
assert rule_triggered["rule"]["id"] == "block-resolvers"
711+
assert parameters["key_path"] == (
712+
["userByName", "name"]
713+
if parameters["address"] == "graphql.server.resolver"
714+
else ["userByName", "0", "name"]
715+
)
716+
assert parameters["value"] == "testblockresolver"
718717

719718
def setup_request_block_attack_directive(self):
720719
"""Currently only monitoring is implemented"""
@@ -734,23 +733,22 @@ def setup_request_block_attack_directive(self):
734733
@bug(context.library < "ruby@2.10.0-dev", reason="APPSEC-56464")
735734
def test_request_block_attack_directive(self):
736735
assert self.r_attack.status_code == 403
737-
738-
for _, span in interfaces.library.get_root_spans(request=self.r_attack):
739-
meta = span.get("meta", {})
740-
meta_struct = span.get("meta_struct", {})
741-
assert meta["appsec.event"] == "true"
742-
assert ("_dd.appsec.json" in meta) ^ ("appsec" in meta_struct)
743-
appsec = meta.get("_dd.appsec.json", {}) or meta_struct.get("appsec", {})
744-
rule_triggered = appsec["triggers"][0]
745-
assert rule_triggered["rule"]["id"] == "block-resolvers"
746-
parameters = rule_triggered["rule_matches"][0]["parameters"][0]
747-
assert (
748-
parameters["address"] == "graphql.server.all_resolvers"
749-
or parameters["address"] == "graphql.server.resolver"
750-
)
751-
assert (
752-
parameters["key_path"] == ["userByName", "case", "format"]
753-
if parameters["address"] == "graphql.server.resolver"
754-
else ["userByName", "0", "case", "format"]
755-
)
756-
assert parameters["value"] == "testblockresolver"
736+
span = interfaces.library.get_root_span(request=self.r_attack)
737+
meta = span.get("meta", {})
738+
meta_struct = span.get("meta_struct", {})
739+
assert meta["appsec.event"] == "true"
740+
assert ("_dd.appsec.json" in meta) ^ ("appsec" in meta_struct)
741+
appsec = meta.get("_dd.appsec.json", {}) or meta_struct.get("appsec", {})
742+
rule_triggered = appsec["triggers"][0]
743+
assert rule_triggered["rule"]["id"] == "block-resolvers"
744+
parameters = rule_triggered["rule_matches"][0]["parameters"][0]
745+
assert (
746+
parameters["address"] == "graphql.server.all_resolvers"
747+
or parameters["address"] == "graphql.server.resolver"
748+
)
749+
assert (
750+
parameters["key_path"] == ["userByName", "case", "format"]
751+
if parameters["address"] == "graphql.server.resolver"
752+
else ["userByName", "0", "case", "format"]
753+
)
754+
assert parameters["value"] == "testblockresolver"

tests/appsec/test_metastruct.py

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,18 @@ def setup_appsec_event_use_metastruct(self):
1414
self.r = weblog.get("/", headers={"User-Agent": "Arachni/v1"})
1515

1616
def test_appsec_event_use_metastruct(self):
17-
spans = [s for _, s in interfaces.library.get_root_spans(request=self.r)]
18-
assert spans
17+
span = interfaces.library.get_root_span(request=self.r)
18+
meta = span.get("meta", {})
19+
meta_struct = span.get("meta_struct", {})
20+
assert meta["appsec.event"] == "true"
21+
assert "_dd.appsec.json" not in meta
22+
assert "appsec" in meta_struct
1923

20-
for span in spans:
21-
meta = span.get("meta", {})
22-
meta_struct = span.get("meta_struct", {})
23-
assert meta["appsec.event"] == "true"
24-
assert "_dd.appsec.json" not in meta
25-
assert "appsec" in meta_struct
24+
# The event is not null
25+
assert meta_struct.get("appsec", {}) not in [None, {}]
2626

27-
# The event is not null
28-
assert meta_struct.get("appsec", {}) not in [None, {}]
29-
30-
# There is at least one rule triggered
31-
assert len(meta_struct["appsec"].get("triggers", [])) > 0
27+
# There is at least one rule triggered
28+
assert len(meta_struct["appsec"].get("triggers", [])) > 0
3229

3330

3431
@features.security_events_metastruct
@@ -40,21 +37,18 @@ def setup_iast_event_use_metastruct(self):
4037
self.r = weblog.get("/set_cookie", params={"name": "metastruct-yes", "value": "yes"})
4138

4239
def test_iast_event_use_metastruct(self):
43-
spans = [s for _, s in interfaces.library.get_root_spans(request=self.r)]
44-
assert spans
45-
46-
for span in spans:
47-
meta = span.get("meta", {})
48-
meta_struct = span.get("meta_struct", {})
49-
assert meta["_dd.iast.enabled"] == "1"
50-
assert "_dd.iast.json" not in meta
51-
assert "iast" in meta_struct
40+
span = interfaces.library.get_root_span(request=self.r)
41+
meta = span.get("meta", {})
42+
meta_struct = span.get("meta_struct", {})
43+
assert meta["_dd.iast.enabled"] == "1"
44+
assert "_dd.iast.json" not in meta
45+
assert "iast" in meta_struct
5246

53-
# The event is not null
54-
assert meta_struct.get("iast", {}) not in [None, {}]
47+
# The event is not null
48+
assert meta_struct.get("iast", {}) not in [None, {}]
5549

56-
# There is at least one vulnerability detected
57-
assert len(meta_struct["iast"].get("vulnerabilities", [])) > 0
50+
# There is at least one vulnerability detected
51+
assert len(meta_struct["iast"].get("vulnerabilities", [])) > 0
5852

5953

6054
@features.security_events_metastruct
@@ -66,21 +60,18 @@ def setup_appsec_event_fallback_json(self):
6660
self.r = weblog.get("/", headers={"User-Agent": "Arachni/v1"})
6761

6862
def test_appsec_event_fallback_json(self):
69-
spans = [s for _, s in interfaces.library.get_root_spans(request=self.r)]
70-
assert spans
71-
72-
for span in spans:
73-
meta = span.get("meta", {})
74-
meta_struct = span.get("meta_struct", {})
75-
assert meta["appsec.event"] == "true"
76-
assert "_dd.appsec.json" in meta
77-
assert "appsec" not in meta_struct
63+
span = interfaces.library.get_root_span(request=self.r)
64+
meta = span.get("meta", {})
65+
meta_struct = span.get("meta_struct", {})
66+
assert meta["appsec.event"] == "true"
67+
assert "_dd.appsec.json" in meta
68+
assert "appsec" not in meta_struct
7869

79-
# The event is not null
80-
assert meta.get("_dd.appsec.json", {}) not in [None, {}]
70+
# The event is not null
71+
assert meta.get("_dd.appsec.json", {}) not in [None, {}]
8172

82-
# There is at least one rule triggered
83-
assert len(meta["_dd.appsec.json"].get("triggers", [])) > 0
73+
# There is at least one rule triggered
74+
assert len(meta["_dd.appsec.json"].get("triggers", [])) > 0
8475

8576

8677
@features.security_events_metastruct
@@ -93,18 +84,15 @@ def setup_iast_event_fallback_json(self):
9384
self.r = weblog.get("/set_cookie", params={"name": "metastruct-no", "value": "no"})
9485

9586
def test_iast_event_fallback_json(self):
96-
spans = [s for _, s in interfaces.library.get_root_spans(request=self.r)]
97-
assert spans
98-
99-
for span in spans:
100-
meta = span.get("meta", {})
101-
meta_struct = span.get("meta_struct", {})
102-
assert meta["_dd.iast.enabled"] == "1"
103-
assert "_dd.iast.json" in meta
104-
assert "iast" not in meta_struct
105-
106-
# The event is not null
107-
assert meta.get("_dd.iast.json", {}) not in [None, {}]
108-
109-
# There is at least one vulnerability detected
110-
assert len(meta["_dd.iast.json"].get("vulnerabilities", [])) > 0
87+
span = interfaces.library.get_root_span(request=self.r)
88+
meta = span.get("meta", {})
89+
meta_struct = span.get("meta_struct", {})
90+
assert meta["_dd.iast.enabled"] == "1"
91+
assert "_dd.iast.json" in meta
92+
assert "iast" not in meta_struct
93+
94+
# The event is not null
95+
assert meta.get("_dd.iast.json", {}) not in [None, {}]
96+
97+
# There is at least one vulnerability detected
98+
assert len(meta["_dd.iast.json"].get("vulnerabilities", [])) > 0

tests/appsec/test_user_blocking_full_denylist.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ def test_blocking_test(self):
4343
for r in self.r_blocked_requests:
4444
assert r.status_code == 403
4545
interfaces.library.assert_waf_attack(r, rule="blk-001-002", address="usr.id")
46-
spans = [s for _, s in interfaces.library.get_root_spans(r)]
47-
assert len(spans) == 1
48-
span = spans[0]
46+
span = interfaces.library.get_root_span(r)
4947
assert span["meta"]["appsec.event"] == "true"
5048
assert span["meta"]["appsec.blocked"] == "true"
5149
assert span["meta"]["http.status_code"] == "403"

tests/external_processing/test_apm.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ def setup_correct_span_structure(self):
99

1010
def test_correct_span_structure(self):
1111
assert self.r.status_code == 200
12-
1312
interfaces.library.assert_trace_exists(self.r)
14-
for _, span in interfaces.library.get_root_spans(request=self.r):
15-
assert span["type"] == "web"
16-
assert span["meta"]["span.kind"] == "server"
17-
assert span["meta"]["http.url"] == "http://localhost:7777/"
18-
assert span["meta"]["http.host"] == "localhost:7777"
13+
span = interfaces.library.get_root_span(self.r)
14+
assert span["type"] == "web"
15+
assert span["meta"]["span.kind"] == "server"
16+
assert span["meta"]["http.url"] == "http://localhost:7777/"
17+
assert span["meta"]["http.host"] == "localhost:7777"

tests/test_span_events.py

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@ def setup_v04_v07_default_format(self):
2222
def test_v04_v07_default_format(self):
2323
"""For traces that default to the v0.4 or v0.7 format, send events as a top-level `span_events` field"""
2424
interfaces.library.assert_trace_exists(self.r)
25-
26-
span = self._get_span(self.r)
27-
meta = self._get_root_span_meta(self.r)
28-
25+
span = interfaces.library.get_root_span(self.r)
26+
meta = span.get("meta", {})
2927
assert "span_events" in span
3028
assert "events" not in meta
3129

@@ -38,21 +36,11 @@ def test_v05_default_format(self):
3836
given this format does not support native serialization.
3937
"""
4038
interfaces.library.assert_trace_exists(self.r)
41-
42-
span = self._get_span(self.r)
43-
meta = self._get_root_span_meta(self.r)
44-
39+
span = interfaces.library.get_root_span(self.r)
40+
meta = span.get("meta", {})
4541
assert "span_events" not in span
4642
assert "events" in meta
4743

48-
def _get_root_span_meta(self, request):
49-
return self._get_span(request).get("meta", {})
50-
51-
def _get_span(self, request):
52-
root_spans = [s for _, s in interfaces.library.get_root_spans(request=request)]
53-
assert len(root_spans) == 1
54-
return root_spans[0]
55-
5644

5745
@features.span_events
5846
@scenarios.agent_not_supporting_span_events
@@ -69,17 +57,7 @@ def setup_send_as_a_tag(self):
6957
def test_send_as_a_tag(self):
7058
"""Send span events as the tag `events` when the agent does not support native serialization"""
7159
interfaces.library.assert_trace_exists(self.r)
72-
73-
span = self._get_span(self.r)
74-
meta = self._get_root_span_meta(self.r)
75-
60+
span = interfaces.library.get_root_span(self.r)
61+
meta = span.get("meta", {})
7662
assert "span_events" not in span
7763
assert "events" in meta
78-
79-
def _get_root_span_meta(self, request):
80-
return self._get_span(request).get("meta", {})
81-
82-
def _get_span(self, request):
83-
root_spans = [s for _, s in interfaces.library.get_root_spans(request=request)]
84-
assert len(root_spans) == 1
85-
return root_spans[0]

tests/test_standard_tags.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,5 @@ def test_client_ip_with_appsec_event_and_vendor_headers(self):
338338
assert meta[tag] == value
339339

340340
def _get_root_span_meta(self, request):
341-
root_spans = [s for _, s in interfaces.library.get_root_spans(request=request)]
342-
assert len(root_spans) == 1
343-
span = root_spans[0]
341+
span = interfaces.library.get_root_span(request)
344342
return span.get("meta", {})

0 commit comments

Comments
 (0)