diff --git a/tests/appsec/iast/sink/test_command_injection.py b/tests/appsec/iast/sink/test_command_injection.py index 5f3a3202f73..e97d091f3a5 100644 --- a/tests/appsec/iast/sink/test_command_injection.py +++ b/tests/appsec/iast/sink/test_command_injection.py @@ -2,10 +2,11 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, missing_feature +from utils import context, coverage, missing_feature, features from .._test_iast_fixtures import BaseSinkTest +@features.iast_sink_command_injection @coverage.basic class TestCommandInjection(BaseSinkTest): """Test command injection detection.""" diff --git a/tests/appsec/iast/sink/test_hardcoded_secrets.py b/tests/appsec/iast/sink/test_hardcoded_secrets.py index bc1ed3a8421..5a3b9004e19 100644 --- a/tests/appsec/iast/sink/test_hardcoded_secrets.py +++ b/tests/appsec/iast/sink/test_hardcoded_secrets.py @@ -2,11 +2,11 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import flaky, context, coverage, missing_feature -from utils.tools import logging +from utils import coverage, missing_feature, features from .._test_iast_fixtures import BaseSinkTest, DetectionStage +@features.iast_sink_hardcoded_secrets @coverage.basic class Test_HardcodedSecrets(BaseSinkTest): """Test Hardcoded secrets detection.""" diff --git a/tests/appsec/iast/sink/test_ldap_injection.py b/tests/appsec/iast/sink/test_ldap_injection.py index c3eefd34679..4216df42bd2 100644 --- a/tests/appsec/iast/sink/test_ldap_injection.py +++ b/tests/appsec/iast/sink/test_ldap_injection.py @@ -2,10 +2,11 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, missing_feature +from utils import context, coverage, missing_feature, features from .._test_iast_fixtures import BaseSinkTest +@features.iast_sink_ldap_injection @coverage.basic class TestLDAPInjection(BaseSinkTest): """Test LDAP injection detection.""" diff --git a/tests/appsec/iast/sink/test_path_traversal.py b/tests/appsec/iast/sink/test_path_traversal.py index 58caa851053..6a1813bfbf7 100644 --- a/tests/appsec/iast/sink/test_path_traversal.py +++ b/tests/appsec/iast/sink/test_path_traversal.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, missing_feature +from utils import context, coverage, missing_feature, features from .._test_iast_fixtures import BaseSinkTest @coverage.basic +@features.iast_sink_path_traversal class TestPathTraversal(BaseSinkTest): """Test path traversal detection.""" diff --git a/tests/appsec/iast/sink/test_sql_injection.py b/tests/appsec/iast/sink/test_sql_injection.py index 603b5b3f24a..f9499f0621b 100644 --- a/tests/appsec/iast/sink/test_sql_injection.py +++ b/tests/appsec/iast/sink/test_sql_injection.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, missing_feature +from utils import context, coverage, missing_feature, features from .._test_iast_fixtures import BaseSinkTest @coverage.basic +@features.iast_sink_sql_injection class TestSqlInjection(BaseSinkTest): """Verify SQL injection detection.""" diff --git a/tests/appsec/iast/sink/test_weak_cipher.py b/tests/appsec/iast/sink/test_weak_cipher.py index 86f6ca5e9e4..3c5f73dacb2 100644 --- a/tests/appsec/iast/sink/test_weak_cipher.py +++ b/tests/appsec/iast/sink/test_weak_cipher.py @@ -1,11 +1,12 @@ # Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0. # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, missing_feature, coverage, flaky +from utils import context, missing_feature, coverage, flaky, features from .._test_iast_fixtures import BaseSinkTest @coverage.basic +@features.weak_cipher_detection class TestWeakCipher(BaseSinkTest): """Verify weak cipher detection.""" diff --git a/tests/appsec/iast/sink/test_weak_hash.py b/tests/appsec/iast/sink/test_weak_hash.py index 2afbdb2e17a..e79993ff844 100644 --- a/tests/appsec/iast/sink/test_weak_hash.py +++ b/tests/appsec/iast/sink/test_weak_hash.py @@ -2,7 +2,7 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import weblog, context, bug, missing_feature, coverage +from utils import weblog, context, bug, missing_feature, coverage, features from .._test_iast_fixtures import BaseSinkTest, assert_iast_vulnerability @@ -32,6 +32,7 @@ def _expected_evidence(): @coverage.basic +@features.weak_hash_vulnerability_detection class TestWeakHash(BaseSinkTest): """Verify weak hash detection.""" diff --git a/tests/appsec/iast/source/test_body.py b/tests/appsec/iast/source/test_body.py index b5dd795ff90..2fc03ef10b1 100644 --- a/tests/appsec/iast/source/test_body.py +++ b/tests/appsec/iast/source/test_body.py @@ -2,10 +2,11 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import coverage, missing_feature, bug +from utils import coverage, missing_feature, bug, features from .._test_iast_fixtures import BaseSourceTest +@features.iast_source_body @coverage.basic class TestRequestBody(BaseSourceTest): """Verify that request json body is tainted""" diff --git a/tests/appsec/iast/source/test_cookie_name.py b/tests/appsec/iast/source/test_cookie_name.py index 8087ff59ba7..023699bd912 100644 --- a/tests/appsec/iast/source/test_cookie_name.py +++ b/tests/appsec/iast/source/test_cookie_name.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import coverage, bug, missing_feature +from utils import coverage, bug, missing_feature, features from .._test_iast_fixtures import BaseSourceTest @coverage.basic +@features.iast_source_cookie_name class TestCookieName(BaseSourceTest): """Verify that request cookies are tainted""" diff --git a/tests/appsec/iast/source/test_cookie_value.py b/tests/appsec/iast/source/test_cookie_value.py index f79ca36d08c..f741bd7a0d6 100644 --- a/tests/appsec/iast/source/test_cookie_value.py +++ b/tests/appsec/iast/source/test_cookie_value.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, bug, missing_feature +from utils import context, coverage, bug, missing_feature, features from .._test_iast_fixtures import BaseSourceTest @coverage.basic +@features.iast_source_cookie_value class TestCookieValue(BaseSourceTest): """Verify that request cookies are tainted""" diff --git a/tests/appsec/iast/source/test_header_name.py b/tests/appsec/iast/source/test_header_name.py index 30e3334a521..8392e5a0105 100644 --- a/tests/appsec/iast/source/test_header_name.py +++ b/tests/appsec/iast/source/test_header_name.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, bug +from utils import context, coverage, bug, features from .._test_iast_fixtures import BaseSourceTest @coverage.basic +@features.iast_source_header_name class TestHeaderName(BaseSourceTest): """Verify that request headers name are tainted""" diff --git a/tests/appsec/iast/source/test_header_value.py b/tests/appsec/iast/source/test_header_value.py index 6b0d231cc02..88831cae736 100644 --- a/tests/appsec/iast/source/test_header_value.py +++ b/tests/appsec/iast/source/test_header_value.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, bug, missing_feature +from utils import context, coverage, bug, missing_feature, features from .._test_iast_fixtures import BaseSourceTest @coverage.basic +@features.iast_source_header_value class TestHeaderValue(BaseSourceTest): """Verify that request headers are tainted""" diff --git a/tests/appsec/iast/source/test_parameter_name.py b/tests/appsec/iast/source/test_parameter_name.py index 344330a9280..c4a5114347c 100644 --- a/tests/appsec/iast/source/test_parameter_name.py +++ b/tests/appsec/iast/source/test_parameter_name.py @@ -2,10 +2,11 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import coverage, missing_feature, bug +from utils import coverage, missing_feature, bug, features from .._test_iast_fixtures import BaseSourceTest +@features.iast_source_request_parameter_name @coverage.basic class TestParameterName(BaseSourceTest): """Verify that request parameters are tainted""" diff --git a/tests/appsec/iast/source/test_parameter_value.py b/tests/appsec/iast/source/test_parameter_value.py index 371aecf8390..819075766f4 100644 --- a/tests/appsec/iast/source/test_parameter_value.py +++ b/tests/appsec/iast/source/test_parameter_value.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, coverage, missing_feature, bug +from utils import context, coverage, missing_feature, bug, features from .._test_iast_fixtures import BaseSourceTest @coverage.basic +@features.iast_source_request_parameter_value class TestParameterValue(BaseSourceTest): """Verify that request parameters are tainted""" diff --git a/tests/appsec/test_alpha.py b/tests/appsec/test_alpha.py index 89e0e7c963f..610721ef4b5 100644 --- a/tests/appsec/test_alpha.py +++ b/tests/appsec/test_alpha.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, weblog, interfaces, missing_feature, bug, coverage +from utils import context, weblog, interfaces, missing_feature, bug, coverage, features @missing_feature(context.library == "ruby" and context.libddwaf_version is None) @coverage.basic +@features.threats_alpha_preview class Test_Basic: """ Detect attacks on raw URI and headers with default rules """ diff --git a/tests/appsec/test_automated_login_events.py b/tests/appsec/test_automated_login_events.py index 3a49e09dac9..29aa1232fdc 100644 --- a/tests/appsec/test_automated_login_events.py +++ b/tests/appsec/test_automated_login_events.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2022 Datadog, Inc. -from utils import weblog, interfaces, context, missing_feature, scenarios, coverage, rfc, bug +from utils import weblog, interfaces, context, missing_feature, scenarios, coverage, rfc, bug, features @rfc("https://docs.google.com/document/d/1-trUpphvyZY7k5ldjhW-MgqWl0xOm7AMEQDJEAZ63_Q/edit#heading=h.8d3o7vtyu1y1") @coverage.good +@features.user_monitoring class Test_Login_Events: "Test login success/failure use cases" # User entries in the internal DB: diff --git a/tests/appsec/test_conf.py b/tests/appsec/test_conf.py index 8c94fab838a..e823c0aadf3 100644 --- a/tests/appsec/test_conf.py +++ b/tests/appsec/test_conf.py @@ -2,18 +2,21 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import weblog, context, coverage, interfaces, missing_feature, irrelevant, rfc, scenarios +from utils import weblog, context, coverage, interfaces, missing_feature, irrelevant, rfc, scenarios, features from utils.tools import nested_lookup from tests.constants import PYTHON_RELEASE_GA_1_1 from .waf.utils import rules @coverage.not_testable +@features.appsec_onboarding +@features.threats_configuration class Test_OneVariableInstallation: """Installation with 1 env variable""" @coverage.basic +@features.threats_configuration class Test_StaticRuleSet: """Appsec loads rules from a static rules file""" @@ -28,6 +31,7 @@ def test_basic_hardcoded_ruleset(self): @coverage.basic +@features.threats_configuration class Test_RuleSet_1_2_4: """ AppSec uses rule set 1.2.4 or higher """ @@ -36,6 +40,7 @@ def test_main(self): @coverage.basic +@features.threats_configuration class Test_RuleSet_1_2_5: """ AppSec uses rule set 1.2.5 or higher """ @@ -44,6 +49,7 @@ def test_main(self): @coverage.good +@features.threats_configuration class Test_RuleSet_1_3_1: """ AppSec uses rule set 1.3.1 or higher """ @@ -70,6 +76,7 @@ def test_nosqli_keys_with_brackets(self): @rfc("https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2355333252/Environment+Variables") @coverage.basic +@features.threats_configuration class Test_ConfigurationVariables: """ Configuration environment variables """ diff --git a/tests/appsec/test_docs.py b/tests/appsec/test_docs.py index 7d42f4186e7..a481d378e92 100644 --- a/tests/appsec/test_docs.py +++ b/tests/appsec/test_docs.py @@ -2,7 +2,7 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import coverage +from utils import coverage, features @coverage.not_testable @@ -11,6 +11,7 @@ class Test_InstallationInstructions: @coverage.not_testable +@features.procedure_to_debug_install class Test_InstallationDebugProcedure: """Procedure to debug install""" diff --git a/tests/appsec/test_event_tracking.py b/tests/appsec/test_event_tracking.py index b03a98bedea..49c9ea85c17 100644 --- a/tests/appsec/test_event_tracking.py +++ b/tests/appsec/test_event_tracking.py @@ -1,10 +1,11 @@ # Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0. # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import weblog, coverage, interfaces +from utils import weblog, coverage, interfaces, features @coverage.basic +@features.user_monitoring class Test_UserLoginSuccessEvent: """Success test for User Login Event SDK for AppSec""" @@ -39,6 +40,7 @@ def validate_user_login_success_tags(span): @coverage.basic +@features.user_monitoring class Test_UserLoginFailureEvent: """Failure test for User Login Event SDK for AppSec""" @@ -74,6 +76,7 @@ def validate_user_login_failure_tags(span): @coverage.basic +@features.custom_business_logic_events class Test_CustomEvent: """Test for Custom Event SDK for AppSec""" diff --git a/tests/appsec/test_identify.py b/tests/appsec/test_identify.py index 13d4bb815bf..a6d9426cb92 100644 --- a/tests/appsec/test_identify.py +++ b/tests/appsec/test_identify.py @@ -2,10 +2,11 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import weblog, coverage, interfaces +from utils import weblog, coverage, interfaces, features @coverage.basic +@features.user_monitoring class Test_Basic: """Basic tests for Identify SDK for AppSec""" diff --git a/tests/appsec/test_reports.py b/tests/appsec/test_reports.py index 1da4c9ff025..b7f74453dd0 100644 --- a/tests/appsec/test_reports.py +++ b/tests/appsec/test_reports.py @@ -5,11 +5,12 @@ import socket -from utils import weblog, context, coverage, interfaces, bug, missing_feature, rfc +from utils import weblog, context, coverage, interfaces, bug, missing_feature, rfc, features @bug(context.library == "python@1.1.0", reason="a PR was not included in the release") @coverage.basic +@features.security_events_metadata class Test_StatusCode: """Appsec reports good status code""" @@ -44,6 +45,7 @@ def check_http_code(span, appsec_data): @missing_feature( True, reason="Bug on system test: with the runner on the host, we do not have the real IP from weblog POV" ) +@features.security_events_metadata class Test_HttpClientIP: """AppSec reports good http client IP""" @@ -77,6 +79,7 @@ def validator(span, appsec_data): @bug(context.library == "python@1.1.0", reason="a PR was not included in the release") @coverage.good +@features.security_events_metadata class Test_Info: """Environment (production, staging) from DD_ENV variable""" @@ -109,6 +112,7 @@ def _check_service(span, appsec_data): @missing_feature(context.library == "ruby" and context.libddwaf_version is None) @bug(context.library == "python@1.1.0", reason="a PR was not included in the release") @coverage.good +@features.security_events_metadata class Test_RequestHeaders: """Request Headers for IP resolution""" @@ -145,6 +149,7 @@ def test_http_request_headers(self): @coverage.basic +@features.security_events_metadata class Test_TagsFromRule: """Tags (Category & event type) from the rule""" @@ -164,6 +169,7 @@ def test_basic(self): @coverage.basic +@features.security_events_metadata class Test_ExtraTagsFromRule: """Extra tags may be added to the rule match since libddwaf 1.10.0""" @@ -179,6 +185,7 @@ def test_basic(self): @coverage.basic +@features.security_events_metadata class Test_AttackTimestamp: """Attack timestamp""" diff --git a/tests/appsec/test_shell_execution.py b/tests/appsec/test_shell_execution.py index 0fc7ce14088..51cbe8f8413 100644 --- a/tests/appsec/test_shell_execution.py +++ b/tests/appsec/test_shell_execution.py @@ -3,24 +3,13 @@ # Copyright 2021 Datadog, Inc. import json -import sys -from utils import ( - bug, - context, - coverage, - interfaces, - irrelevant, - missing_feature, - rfc, - scenarios, - weblog, - flaky, -) +from utils import coverage, interfaces, weblog, features +@features.appsec_shell_execution_tracing @coverage.basic class Test_ShellExecution: - """ Test shell execution tracing """ + """Test shell execution tracing""" def setup_track_cmd_exec(self): self.r_cmd_exec = weblog.post( diff --git a/tests/appsec/test_traces.py b/tests/appsec/test_traces.py index 2cc2b5813be..019dba0af22 100644 --- a/tests/appsec/test_traces.py +++ b/tests/appsec/test_traces.py @@ -3,17 +3,7 @@ # Copyright 2021 Datadog, Inc. from tests.constants import PYTHON_RELEASE_GA_1_1 -from utils import ( - weblog, - bug, - context, - coverage, - interfaces, - irrelevant, - rfc, - missing_feature, - scenarios, -) +from utils import weblog, bug, context, coverage, interfaces, irrelevant, rfc, missing_feature, scenarios, features from utils.tools import nested_lookup @@ -22,6 +12,7 @@ @bug(context.library == "python@1.1.0", reason="a PR was not included in the release") @coverage.good +@features.security_events_metadata class Test_RetainTraces: """Retain trace (manual keep & appsec.event = true)""" @@ -62,6 +53,7 @@ def validate_appsec_event_span_tags(span): @coverage.good +@features.security_events_metadata class Test_AppSecEventSpanTags: """AppSec correctly fill span tags.""" @@ -153,6 +145,8 @@ def validator(span): @rfc("https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2365948382/Sensitive+Data+Obfuscation") @coverage.good +@features.sensitive_data_obfuscation +@features.security_events_metadata class Test_AppSecObfuscator: """AppSec obfuscates sensitive data.""" @@ -314,6 +308,7 @@ def validate_appsec_span_tags(span, appsec_data): # pylint: disable=unused-argu @rfc("https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2186870984/HTTP+header+collection") @coverage.good +@features.security_events_metadata class Test_CollectRespondHeaders: """AppSec should collect some headers for http.response and store them in span tags.""" @@ -334,5 +329,6 @@ def validate_response_headers(span): @coverage.not_implemented +@features.security_events_metadata class Test_DistributedTraceInfo: """Distributed traces info (Services, URL, trace id)""" diff --git a/tests/appsec/waf/test_addresses.py b/tests/appsec/waf/test_addresses.py index 9cb5cd719e3..7f346ac15bd 100644 --- a/tests/appsec/waf/test_addresses.py +++ b/tests/appsec/waf/test_addresses.py @@ -1,20 +1,11 @@ # Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0. # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import ( - weblog, - bug, - context, - coverage, - interfaces, - irrelevant, - missing_feature, - rfc, - scenarios, -) +from utils import weblog, bug, context, coverage, interfaces, irrelevant, missing_feature, rfc, scenarios, features @coverage.basic +@features.http_threats_management class Test_UrlQueryKey: """Appsec supports keys on server.request.query""" @@ -28,6 +19,7 @@ def test_query_key(self): @coverage.good +@features.http_threats_management class Test_UrlQuery: """Appsec supports values on server.request.query""" @@ -59,6 +51,7 @@ def test_query_with_strict_regex(self): @coverage.basic +@features.http_threats_management class Test_UrlRaw: """Appsec supports server.request.uri.raw""" @@ -71,6 +64,7 @@ def test_path(self): @coverage.good +@features.http_threats_management class Test_Headers: """Appsec supports server.request.headers.no_cookies""" @@ -139,6 +133,7 @@ def test_specific_wrong_key(self): @coverage.good +@features.http_threats_management class Test_Cookies: """Appsec supports server.request.cookies""" @@ -237,6 +232,7 @@ def test_cookies_with_special_chars2_custom_rules(self): @coverage.basic +@features.http_threats_management class Test_BodyRaw: """Appsec supports """ @@ -251,6 +247,7 @@ def test_raw_body(self): @coverage.basic @bug(context.library == "nodejs@2.8.0", reason="Capability to read body content is broken") +@features.http_threats_management class Test_BodyUrlEncoded: """Appsec supports """ @@ -278,6 +275,7 @@ def test_body_value(self): @coverage.basic @bug(context.library == "nodejs@2.8.0", reason="Capability to read body content is broken") +@features.http_threats_management class Test_BodyJson: """Appsec supports """ @@ -312,6 +310,7 @@ def test_json_array(self): @bug(context.library == "nodejs@2.8.0", reason="Capability to read body content is broken") +@features.http_threats_management @coverage.basic class Test_BodyXml: """Appsec supports """ @@ -345,16 +344,19 @@ def test_xml_content(self): @coverage.not_implemented +@features.http_threats_management class Test_Method: """Appsec supports server.request.method""" @coverage.not_implemented +@features.http_threats_management class Test_ClientIP: """Appsec supports server.request.client_ip""" @coverage.good +@features.http_threats_management class Test_ResponseStatus: """Appsec supports values on server.response.status""" @@ -372,6 +374,7 @@ def test_basic(self): @coverage.basic +@features.http_threats_management class Test_PathParams: """Appsec supports values on server.request.path_params""" @@ -386,6 +389,8 @@ def test_security_scanner(self): @coverage.basic +@features.http_threats_management +@features.grpc_threats_management class Test_gRPC: """Appsec supports address grpc.server.request.message""" @@ -404,15 +409,20 @@ def test_basic(self): @rfc("https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2278064284/gRPC+Protocol+Support") @coverage.not_implemented +@features.http_threats_management +@features.grpc_threats_management class Test_FullGrpc: """Full gRPC support""" @coverage.not_implemented +@features.http_threats_management +@features.graphql_threats_detection class Test_GraphQL: """GraphQL support""" @coverage.not_implemented +@features.http_threats_management class Test_Lambda: """Lambda support""" diff --git a/tests/appsec/waf/test_miscs.py b/tests/appsec/waf/test_miscs.py index 8310dc507d0..68ef4193b18 100644 --- a/tests/appsec/waf/test_miscs.py +++ b/tests/appsec/waf/test_miscs.py @@ -2,12 +2,13 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, weblog, interfaces, bug, coverage, missing_feature, scenarios +from utils import context, weblog, interfaces, bug, coverage, missing_feature, scenarios, features from .utils import rules @bug(context.library == "python@1.1.0", reason="a PR was not included in the release") @coverage.basic +@features.http_threats_management class Test_404: """Appsec WAF misc tests""" @@ -42,6 +43,7 @@ def test_multiple_hightlight(self): @coverage.good +@features.http_threats_management class Test_MultipleAttacks: """If several attacks are sent threw one requests, all of them are reported""" @@ -86,6 +88,7 @@ def test_main(self): @coverage.basic +@features.threats_configuration class Test_NoWafTimeout: """With an high value of DD_APPSEC_WAF_TIMEOUT, there is no WAF timeout""" diff --git a/tests/appsec/waf/test_reports.py b/tests/appsec/waf/test_reports.py index 94e413f78d4..e24e1a5bc88 100644 --- a/tests/appsec/waf/test_reports.py +++ b/tests/appsec/waf/test_reports.py @@ -4,10 +4,11 @@ import re import json -from utils import weblog, context, interfaces, irrelevant, coverage, scenarios +from utils import weblog, context, interfaces, irrelevant, coverage, scenarios, features @coverage.good +@features.support_in_app_waf_metrics_report class Test_Monitoring: """Support In-App WAF monitoring tags and metrics""" diff --git a/tests/test_identify.py b/tests/test_identify.py index 87b03f65b6c..f480e18e731 100644 --- a/tests/test_identify.py +++ b/tests/test_identify.py @@ -2,7 +2,7 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import weblog, bug, context, coverage, interfaces, rfc +from utils import weblog, bug, context, coverage, interfaces, rfc, features def assertTagInSpanMeta(span, tag, expected): @@ -61,6 +61,7 @@ def test_identify_tags_with_attack(self): @rfc("https://docs.google.com/document/d/1T3qAE5nol18psOaHESQ3r-WRiZWss9nyGmroShug8ao/edit#heading=h.3wmduzc8mwe1") @coverage.basic +@features.propagation_of_user_id_rfc class Test_Propagate_Legacy: """Propagation tests for Identify SDK""" @@ -85,6 +86,7 @@ def test_identify_tags_incoming(self): @rfc("https://docs.google.com/document/d/1T3qAE5nol18psOaHESQ3r-WRiZWss9nyGmroShug8ao/edit#heading=h.3wmduzc8mwe1") @coverage.basic +@features.propagation_of_user_id_rfc class Test_Propagate: """Propagation tests for Identify SDK""" diff --git a/tests/test_library_conf.py b/tests/test_library_conf.py index e8eb2858fb7..cf29812feeb 100644 --- a/tests/test_library_conf.py +++ b/tests/test_library_conf.py @@ -2,11 +2,12 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import coverage, weblog, interfaces, scenarios +from utils import coverage, weblog, interfaces, scenarios, features # basic / legacy tests, just tests user-agent can be received as a tag @coverage.basic +@features.security_events_metadata class Test_HeaderTags: """DD_TRACE_HEADER_TAGS env var support""" diff --git a/tests/test_miscs.py b/tests/test_miscs.py index 6247fef545d..cbcd04d4c35 100644 --- a/tests/test_miscs.py +++ b/tests/test_miscs.py @@ -2,9 +2,10 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2022 Datadog, Inc. -from utils import weblog, interfaces +from utils import weblog, interfaces, features +@features.unix_domain_sockets_support_for_traces class Test_Basic: """ Make sure the spans endpoint is successful """ diff --git a/tests/test_semantic_conventions.py b/tests/test_semantic_conventions.py index b1cac8406d5..a3abaf93305 100644 --- a/tests/test_semantic_conventions.py +++ b/tests/test_semantic_conventions.py @@ -5,7 +5,7 @@ import re from urllib.parse import urlparse -from utils import context, interfaces, bug, missing_feature +from utils import context, interfaces, bug, missing_feature, features RUNTIME_LANGUAGE_MAP = { "nodejs": "javascript", @@ -129,6 +129,8 @@ def get_component_name(weblog_variant, language, span_name): return expected_component +@features.runtime_id_in_span_metadata_for_service_entry_spans +@features.unix_domain_sockets_support_for_traces class Test_Meta: """meta object in spans respect all conventions""" @@ -296,6 +298,7 @@ def validator(span): assert len(list(interfaces.library.get_root_spans())) != 0, "Did not recieve any root spans to validate." +@features.add_metadata_globally_to_all_spans_dd_tags class Test_MetaDatadogTags: """Spans carry meta tags that were set in DD_TAGS tracer environment""" diff --git a/tests/test_smoke.py b/tests/test_smoke.py index ee0cf2e4279..d1fe0739d94 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -2,9 +2,10 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import context, weblog, interfaces +from utils import context, weblog, interfaces, features +@features.unix_domain_sockets_support_for_traces class Test_Backend: """Misc test around agent/backend communication""" @@ -13,6 +14,7 @@ def test_good_backend(self): interfaces.agent.assert_use_domain(context.dd_site) +@features.unix_domain_sockets_support_for_traces class Test_Library: """Misc test around library/agent communication""" diff --git a/tests/test_standard_tags.py b/tests/test_standard_tags.py index ef9becaceb7..2bbddb437dc 100644 --- a/tests/test_standard_tags.py +++ b/tests/test_standard_tags.py @@ -2,10 +2,11 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2022 Datadog, Inc. -from utils import bug, context, coverage, interfaces, irrelevant, missing_feature, rfc, weblog +from utils import bug, context, coverage, interfaces, irrelevant, missing_feature, rfc, weblog, features @coverage.good +@features.security_events_metadata class Test_StandardTagsMethod: """Tests to verify that libraries annotate spans with correct http.method tags""" @@ -32,6 +33,7 @@ def test_method_trace(self): @rfc("https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2490990623/QueryString+-+Sensitive+Data+Obfuscation") @coverage.basic +@features.security_events_metadata class Test_StandardTagsUrl: """Tests to verify that libraries annotate spans with correct http.url tags""" @@ -160,6 +162,7 @@ def test_multiple_matching_substring(self): @coverage.basic +@features.security_events_metadata class Test_StandardTagsUserAgent: """Tests to verify that libraries annotate spans with correct http.useragent tags""" @@ -173,6 +176,7 @@ def test_useragent(self): @coverage.good +@features.security_events_metadata class Test_StandardTagsStatusCode: """Tests to verify that libraries annotate spans with correct http.status_code tags""" @@ -186,6 +190,7 @@ def test_status_code(self): @coverage.basic +@features.security_events_metadata class Test_StandardTagsRoute: """Tests to verify that libraries annotate spans with correct http.route tags""" @@ -217,6 +222,7 @@ def test_route(self): @rfc("https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2118779066/Client+IP+addresses+resolution") @coverage.basic +@features.security_events_metadata class Test_StandardTagsClientIp: """Tests to verify that libraries annotate spans with correct http.client_ip tags""" diff --git a/utils/_features.py b/utils/_features.py index fb76803a77b..34914e45b0f 100644 --- a/utils/_features.py +++ b/utils/_features.py @@ -1,168 +1,1300 @@ +# pylint: disable=too-many-lines + import pytest -import requests -def _feature(feature_id: int): - def decorate(test_object): - pytest.mark.features(feature_id=feature_id)(test_object) +class features: + """ + Data source is https://dd-feature-parity.azurewebsites.net/Import/Features + + run this command to get new features: + + ``` + PYTHONPATH=. python utils/scripts/update_features.py + ``` + """ + + @staticmethod + def add_metadata_globally_to_all_spans_dd_tags(test_object): + """ + Add Metadata globally to all spans (DD_TAGS) + + https://feature-parity.us1.prod.dog/#/?feature=1 + """ + pytest.mark.features(feature_id=1)(test_object) return test_object - return staticmethod(decorate) + @staticmethod + def change_agent_hostname_dd_agent_host(test_object): + """ + Change Agent hostname (DD_AGENT_HOST) + https://feature-parity.us1.prod.dog/#/?feature=2 + """ + pytest.mark.features(feature_id=2)(test_object) + return test_object -class features: - """https://dd-feature-parity.azurewebsites.net/Import/Features""" - - add_metadata_globally_to_all_spans_dd_tags = _feature(1) - change_agent_hostname_dd_agent_host = _feature(2) - add_metadata_to_spans_via_tags_dd_trace_analytics_enabled = _feature(3) - trace_search_automatic_config = _feature(4) - manual_trace_id_injection_into_logs = _feature(5) - unix_domain_sockets_support_for_traces = _feature(6) - unix_domain_sockets_automatic_detection = _feature(7) - twl_customer_controls_ingestion_dd_trace_sampling_rules = _feature(8) - synthetic_apm_http_header_span_tag_x_datadog_origin = _feature(9) - log_tracer_status_at_startup = _feature(10) - fargate_14_tagging_support = _feature(11) - container_tagging = _feature(12) - b3_headers_injection_and_extraction = _feature(13) - unix_domain_sockets_support_for_metrics = _feature(14) - support_ddmeasured = _feature(15) - dd_service_mapping = _feature(16) - datadog_managed_dogstatsd_client = _feature(17) - http_headers_as_tags_dd_trace_header_tags = _feature(18) - dd_profiling_enabled = _feature(19) - dogstatsd_unified_service_tagging = _feature(20) - trace_log_exporting_for_aws_lambda = _feature(21) - runtime_id_in_span_metadata_for_service_entry_spans = _feature(22) - partial_flush = _feature(23) - partial_flush_on_by_default = _feature(24) - automatic_trace_id_injection_into_logs = _feature(25) - mapping_http_status_codes_to_errors = _feature(26) - log_pipelines_updated_for_log_injection = _feature(27) - inject_service_env_version_into_logs = _feature(28) - top_level_detection_in_tracer = _feature(29) - use_sampling_priorities_2_1_in_rules_based_sampler = _feature(30) - trace_annotation = _feature(31) - runtime_metrics = _feature(32) - logs_throttling = _feature(33) - post_processing_traces = _feature(34) - span_baggage_item = _feature(35) - tracer_health_metrics = _feature(36) - kafka_tracing = _feature(37) - numeric_tags_for_trace_search_analytics_step_1 = _feature(38) - grpc_integration_tags = _feature(39) - dd_trace_config_file = _feature(40) - dd_trace_methods = _feature(41) - dd_tags_space_separated_tags = _feature(42) - report_tracer_drop_rate_ddtracer_kr = _feature(43) - obfuscation_of_pii_from_web_span_resource_names = _feature(44) - windows_named_pipe_support_for_traces = _feature(45) - setting_to_rename_service_by_tag_split_by_tag = _feature(46) - collect_application_version_information = _feature(47) - ensure_that_sampling_is_consistent_across_languages = _feature(48) - user_troubleshooting_tool = _feature(49) - option_to_remap_apm_error_response_severity_eg_404_to_error = _feature(50) - obfuscation_of_httpurl_span_tag = _feature(51) - dd_trace_report_hostname = _feature(52) - windows_named_pipe_support_for_metrics = _feature(53) - ensure_consistent_http_client_integration_tags = _feature(54) - aws_sdk_integration_tags = _feature(55) - dont_set_username_tag_because_its_pii = _feature(56) - trace_client_app_tagging = _feature(57) - client_split_by_domain_service_host = _feature(58) - horizontal_propagation_of_x_datadog_tags_between_services = _feature(59) - vertical_propagation_of_x_datadog_tags_onto_each_chunk_root_span = _feature(60) - creation_and_propagation_of_ddpdm = _feature(61) - client_side_stats_supported = _feature(62) - client_side_stats_on_by_default = _feature(63) - instrumentation_telemetry_enabled_by_default = _feature(64) - dd_instrumentation_telemetry_enabled_supported = _feature(65) - app_environment_collected = _feature(66) - dependencies_collected = _feature(67) - integrations_enabled_collected = _feature(68) - tracer_configurations_collected = _feature(69) - heart_beat_collected = _feature(70) - app_close_collected = _feature(71) - logs_collected = _feature(72) - metrics_collected = _feature(73) - api_v2_implemented = _feature(74) - app_client_configuration_change_event = _feature(75) - app_product_change_event = _feature(76) - app_extended_heartbeat_event = _feature(77) - message_batch = _feature(78) - app_started_changes = _feature(79) - dd_telemetry_dependency_collection_enabled_supported = _feature(80) - additional_http_headers_supported = _feature(81) - remote_config_object_supported = _feature(82) - w3c_headers_injection_and_extraction = _feature(83) - otel_api = _feature(84) - trace_id_generation_propagation_128_bit = _feature(85) - span_events = _feature(86) - span_links = _feature(87) - client_ip_adress_collection_dd_trace_client_ip_enabled = _feature(88) - host_auto_instrumentation = _feature(89) - container_auto_instrumentation = _feature(90) - collect_http_post_data_and_headers = _feature(94) - weak_hash_vulnerability_detection = _feature(96) - db_integrations = _feature(98) - http_threats_management = _feature(99) - weak_cipher_detection = _feature(100) - threats_alpha_preview = _feature(110) - procedure_to_debug_install = _feature(113) - security_events_metadata = _feature(124) - rate_limiter = _feature(134) - support_in_app_waf_metrics_report = _feature(140) - user_monitoring = _feature(141) - serialize_waf_rules_without_limiting_their_sizes = _feature(142) - threats_configuration = _feature(143) - sensitive_data_obfuscation = _feature(144) - propagation_of_user_id_rfc = _feature(146) - onboarding = _feature(154) - deactivate_rules_using_rc = _feature(157) - shell_execution_tracing = _feature(158) - appsec_userid_blocking = _feature(160) - custom_business_logic_events = _feature(161) - graphql_threats_detection = _feature(162) - iast_sink_sql_injection = _feature(165) - iast_sink_command_injection = _feature(166) - iast_sink_path_traversal = _feature(167) - iast_sink_ldap_injection = _feature(168) - iast_source_request_parameter_value = _feature(169) - iast_source_request_parameter_name = _feature(170) - iast_source_header_value = _feature(171) - iast_source_header_name = _feature(172) - iast_source_cookie_value = _feature(173) - iast_source_cookie_name = _feature(174) - iast_source_body = _feature(175) - grpc_threats_management = _feature(176) - telemetry = _feature(178) - kafkaspan_creationcontext_propagation_with_dd_trace_py = _feature(192) - - -def _main(): - """Display missing features""" - - existing_features = set() - for attr in dir(features): - if attr.startswith("__"): - continue - - decorator = getattr(features, attr) - - def obj(): - pass - - obj = decorator(obj) - - existing_features.add(obj.pytestmark[0].kwargs["feature_id"]) - - data = requests.get("https://dd-feature-parity.azurewebsites.net/Import/Features", timeout=10).json() - for feature in data: - feature_id = feature["id"] - if feature_id not in existing_features: - print(f" {feature['codeSafeName'].lower()} = _feature({feature_id})") - - -if __name__ == "__main__": - _main() + @staticmethod + def add_metadata_to_spans_via_tags_dd_trace_analytics_enabled(test_object): + """ + Add metadata to spans via tags (DD_TRACE_ANALYTICS_ENABLED) + + https://feature-parity.us1.prod.dog/#/?feature=3 + """ + pytest.mark.features(feature_id=3)(test_object) + return test_object + + @staticmethod + def trace_search_automatic_config(test_object): + """ + Trace Search - automatic config + + https://feature-parity.us1.prod.dog/#/?feature=4 + """ + pytest.mark.features(feature_id=4)(test_object) + return test_object + + @staticmethod + def manual_trace_id_injection_into_logs(test_object): + """ + Manual Trace-ID injection into Logs + + https://feature-parity.us1.prod.dog/#/?feature=5 + """ + pytest.mark.features(feature_id=5)(test_object) + return test_object + + @staticmethod + def unix_domain_sockets_support_for_traces(test_object): + """ + Unix Domain Sockets support for traces + + https://feature-parity.us1.prod.dog/#/?feature=6 + """ + + from utils._context.core import context + + if "uds" in context.weblog_variant: + pytest.mark.features(feature_id=6)(test_object) + + return test_object + + @staticmethod + def unix_domain_sockets_automatic_detection(test_object): + """ + Unix Domain Sockets automatic detection + + https://feature-parity.us1.prod.dog/#/?feature=7 + """ + pytest.mark.features(feature_id=7)(test_object) + return test_object + + @staticmethod + def twl_customer_controls_ingestion_dd_trace_sampling_rules(test_object): + """ + TwL - Customer Controls Ingestion (DD_TRACE_SAMPLING RULES) + + https://feature-parity.us1.prod.dog/#/?feature=8 + """ + pytest.mark.features(feature_id=8)(test_object) + return test_object + + @staticmethod + def synthetic_apm_http_header_span_tag_x_datadog_origin(test_object): + """ + Synthetic APM (http header ▶ span tag) (x-datadog-origin)) + + https://feature-parity.us1.prod.dog/#/?feature=9 + """ + pytest.mark.features(feature_id=9)(test_object) + return test_object + + @staticmethod + def log_tracer_status_at_startup(test_object): + """ + Log Tracer status at startup + + https://feature-parity.us1.prod.dog/#/?feature=10 + """ + pytest.mark.features(feature_id=10)(test_object) + return test_object + + @staticmethod + def fargate_14_tagging_support(test_object): + """ + Fargate 1.4 Tagging Support + + https://feature-parity.us1.prod.dog/#/?feature=11 + """ + pytest.mark.features(feature_id=11)(test_object) + return test_object + + @staticmethod + def container_tagging(test_object): + """ + Container tagging + + https://feature-parity.us1.prod.dog/#/?feature=12 + """ + pytest.mark.features(feature_id=12)(test_object) + return test_object + + @staticmethod + def b3_headers_injection_and_extraction(test_object): + """ + B3 headers injection and extraction + + https://feature-parity.us1.prod.dog/#/?feature=13 + """ + pytest.mark.features(feature_id=13)(test_object) + return test_object + + @staticmethod + def unix_domain_sockets_support_for_metrics(test_object): + """ + Unix Domain Sockets support for metrics + + https://feature-parity.us1.prod.dog/#/?feature=14 + """ + pytest.mark.features(feature_id=14)(test_object) + return test_object + + @staticmethod + def support_ddmeasured(test_object): + """ + Support _dd.measured + + https://feature-parity.us1.prod.dog/#/?feature=15 + """ + pytest.mark.features(feature_id=15)(test_object) + return test_object + + @staticmethod + def dd_service_mapping(test_object): + """ + DD_SERVICE_MAPPING + + https://feature-parity.us1.prod.dog/#/?feature=16 + """ + pytest.mark.features(feature_id=16)(test_object) + return test_object + + @staticmethod + def datadog_managed_dogstatsd_client(test_object): + """ + Datadog managed Dogstatsd client + + https://feature-parity.us1.prod.dog/#/?feature=17 + """ + pytest.mark.features(feature_id=17)(test_object) + return test_object + + @staticmethod + def http_headers_as_tags_dd_trace_header_tags(test_object): + """ + HTTP Headers as Tags (DD_TRACE_HEADER_TAGS) + + https://feature-parity.us1.prod.dog/#/?feature=18 + """ + pytest.mark.features(feature_id=18)(test_object) + return test_object + + @staticmethod + def dd_profiling_enabled(test_object): + """ + DD_PROFILING_ENABLED + + https://feature-parity.us1.prod.dog/#/?feature=19 + """ + pytest.mark.features(feature_id=19)(test_object) + return test_object + + @staticmethod + def dogstatsd_unified_service_tagging(test_object): + """ + Dogstatsd unified service tagging + + https://feature-parity.us1.prod.dog/#/?feature=20 + """ + pytest.mark.features(feature_id=20)(test_object) + return test_object + + @staticmethod + def trace_log_exporting_for_aws_lambda(test_object): + """ + Trace Log Exporting for AWS Lambda + + https://feature-parity.us1.prod.dog/#/?feature=21 + """ + pytest.mark.features(feature_id=21)(test_object) + return test_object + + @staticmethod + def runtime_id_in_span_metadata_for_service_entry_spans(test_object): + """ + Runtime-id in span metadata for service entry spans + + https://feature-parity.us1.prod.dog/#/?feature=22 + """ + pytest.mark.features(feature_id=22)(test_object) + return test_object + + @staticmethod + def partial_flush(test_object): + """ + Partial flush + + https://feature-parity.us1.prod.dog/#/?feature=23 + """ + pytest.mark.features(feature_id=23)(test_object) + return test_object + + @staticmethod + def partial_flush_on_by_default(test_object): + """ + Partial flush on by default + + https://feature-parity.us1.prod.dog/#/?feature=24 + """ + pytest.mark.features(feature_id=24)(test_object) + return test_object + + @staticmethod + def automatic_trace_id_injection_into_logs(test_object): + """ + Automatic Trace-id injection into Logs + + https://feature-parity.us1.prod.dog/#/?feature=25 + """ + pytest.mark.features(feature_id=25)(test_object) + return test_object + + @staticmethod + def mapping_http_status_codes_to_errors(test_object): + """ + Mapping HTTP status codes to errors + + https://feature-parity.us1.prod.dog/#/?feature=26 + """ + pytest.mark.features(feature_id=26)(test_object) + return test_object + + @staticmethod + def log_pipelines_updated_for_log_injection(test_object): + """ + Log Pipelines updated for log injection + + https://feature-parity.us1.prod.dog/#/?feature=27 + """ + pytest.mark.features(feature_id=27)(test_object) + return test_object + + @staticmethod + def inject_service_env_version_into_logs(test_object): + """ + Inject service, env, version into logs + + https://feature-parity.us1.prod.dog/#/?feature=28 + """ + pytest.mark.features(feature_id=28)(test_object) + return test_object + + @staticmethod + def top_level_detection_in_tracer(test_object): + """ + Top-level detection in tracer + + https://feature-parity.us1.prod.dog/#/?feature=29 + """ + pytest.mark.features(feature_id=29)(test_object) + return test_object + + @staticmethod + def use_sampling_priorities_2_1_in_rules_based_sampler(test_object): + """ + Use sampling priorities +2/-1 in rules-based sampler + + https://feature-parity.us1.prod.dog/#/?feature=30 + """ + pytest.mark.features(feature_id=30)(test_object) + return test_object + + @staticmethod + def trace_annotation(test_object): + """ + Trace Annotation + + https://feature-parity.us1.prod.dog/#/?feature=31 + """ + pytest.mark.features(feature_id=31)(test_object) + return test_object + + @staticmethod + def runtime_metrics(test_object): + """ + Runtime metrics + + https://feature-parity.us1.prod.dog/#/?feature=32 + """ + pytest.mark.features(feature_id=32)(test_object) + return test_object + + @staticmethod + def logs_throttling(test_object): + """ + Logs Throttling + + https://feature-parity.us1.prod.dog/#/?feature=33 + """ + pytest.mark.features(feature_id=33)(test_object) + return test_object + + @staticmethod + def post_processing_traces(test_object): + """ + Post-Processing Traces + + https://feature-parity.us1.prod.dog/#/?feature=34 + """ + pytest.mark.features(feature_id=34)(test_object) + return test_object + + @staticmethod + def span_baggage_item(test_object): + """ + Span Baggage item + + https://feature-parity.us1.prod.dog/#/?feature=35 + """ + pytest.mark.features(feature_id=35)(test_object) + return test_object + + @staticmethod + def tracer_health_metrics(test_object): + """ + Tracer Health Metrics + + https://feature-parity.us1.prod.dog/#/?feature=36 + """ + pytest.mark.features(feature_id=36)(test_object) + return test_object + + @staticmethod + def kafka_tracing(test_object): + """ + Kafka Tracing + + https://feature-parity.us1.prod.dog/#/?feature=37 + """ + pytest.mark.features(feature_id=37)(test_object) + return test_object + + @staticmethod + def numeric_tags_for_trace_search_analytics_step_1(test_object): + """ + Numeric tags for Trace Search/Analytics (step 1) + + https://feature-parity.us1.prod.dog/#/?feature=38 + """ + pytest.mark.features(feature_id=38)(test_object) + return test_object + + @staticmethod + def grpc_integration_tags(test_object): + """ + gRPC integration tags + + https://feature-parity.us1.prod.dog/#/?feature=39 + """ + pytest.mark.features(feature_id=39)(test_object) + return test_object + + @staticmethod + def dd_trace_config_file(test_object): + """ + DD_TRACE_CONFIG_FILE + + https://feature-parity.us1.prod.dog/#/?feature=40 + """ + pytest.mark.features(feature_id=40)(test_object) + return test_object + + @staticmethod + def dd_trace_methods(test_object): + """ + DD_TRACE_METHODS + + https://feature-parity.us1.prod.dog/#/?feature=41 + """ + pytest.mark.features(feature_id=41)(test_object) + return test_object + + @staticmethod + def dd_tags_space_separated_tags(test_object): + """ + DD_TAGS space-separated tags + + https://feature-parity.us1.prod.dog/#/?feature=42 + """ + pytest.mark.features(feature_id=42)(test_object) + return test_object + + @staticmethod + def report_tracer_drop_rate_ddtracer_kr(test_object): + """ + Report tracer drop rate (_dd.tracer_kr) + + https://feature-parity.us1.prod.dog/#/?feature=43 + """ + pytest.mark.features(feature_id=43)(test_object) + return test_object + + @staticmethod + def obfuscation_of_pii_from_web_span_resource_names(test_object): + """ + Obfuscation of PII from web span resource names + + https://feature-parity.us1.prod.dog/#/?feature=44 + """ + pytest.mark.features(feature_id=44)(test_object) + return test_object + + @staticmethod + def windows_named_pipe_support_for_traces(test_object): + """ + Windows named pipe support for traces + + https://feature-parity.us1.prod.dog/#/?feature=45 + """ + pytest.mark.features(feature_id=45)(test_object) + return test_object + + @staticmethod + def setting_to_rename_service_by_tag_split_by_tag(test_object): + """ + Setting to rename service by tag (split-by-tag) + + https://feature-parity.us1.prod.dog/#/?feature=46 + """ + pytest.mark.features(feature_id=46)(test_object) + return test_object + + @staticmethod + def collect_application_version_information(test_object): + """ + Collect application version information + + https://feature-parity.us1.prod.dog/#/?feature=47 + """ + pytest.mark.features(feature_id=47)(test_object) + return test_object + + @staticmethod + def ensure_that_sampling_is_consistent_across_languages(test_object): + """ + Ensure that sampling is consistent across languages + + https://feature-parity.us1.prod.dog/#/?feature=48 + """ + pytest.mark.features(feature_id=48)(test_object) + return test_object + + @staticmethod + def user_troubleshooting_tool(test_object): + """ + User Troubleshooting Tool + + https://feature-parity.us1.prod.dog/#/?feature=49 + """ + pytest.mark.features(feature_id=49)(test_object) + return test_object + + @staticmethod + def option_to_remap_apm_error_response_severity_eg_404_to_error(test_object): + """ + Option to remap APM error response severity (e.g. 404 to error) + + https://feature-parity.us1.prod.dog/#/?feature=50 + """ + pytest.mark.features(feature_id=50)(test_object) + return test_object + + @staticmethod + def obfuscation_of_httpurl_span_tag(test_object): + """ + Obfuscation of http.url span tag + + https://feature-parity.us1.prod.dog/#/?feature=51 + """ + pytest.mark.features(feature_id=51)(test_object) + return test_object + + @staticmethod + def dd_trace_report_hostname(test_object): + """ + DD_TRACE_REPORT_HOSTNAME + + https://feature-parity.us1.prod.dog/#/?feature=52 + """ + pytest.mark.features(feature_id=52)(test_object) + return test_object + + @staticmethod + def windows_named_pipe_support_for_metrics(test_object): + """ + Windows named pipe support for metrics + + https://feature-parity.us1.prod.dog/#/?feature=53 + """ + pytest.mark.features(feature_id=53)(test_object) + return test_object + + @staticmethod + def ensure_consistent_http_client_integration_tags(test_object): + """ + Ensure consistent HTTP client integration tags + + https://feature-parity.us1.prod.dog/#/?feature=54 + """ + pytest.mark.features(feature_id=54)(test_object) + return test_object + + @staticmethod + def aws_sdk_integration_tags(test_object): + """ + AWS SDK Integration Tags + + https://feature-parity.us1.prod.dog/#/?feature=55 + """ + pytest.mark.features(feature_id=55)(test_object) + return test_object + + @staticmethod + def dont_set_username_tag_because_its_pii(test_object): + """ + Don't set username tag because it's PII + + https://feature-parity.us1.prod.dog/#/?feature=56 + """ + pytest.mark.features(feature_id=56)(test_object) + return test_object + + @staticmethod + def trace_client_app_tagging(test_object): + """ + Trace Client App Tagging + + https://feature-parity.us1.prod.dog/#/?feature=57 + """ + pytest.mark.features(feature_id=57)(test_object) + return test_object + + @staticmethod + def client_split_by_domain_service_host(test_object): + """ + Client Split by domain/service/host + + https://feature-parity.us1.prod.dog/#/?feature=58 + """ + pytest.mark.features(feature_id=58)(test_object) + return test_object + + @staticmethod + def horizontal_propagation_of_x_datadog_tags_between_services(test_object): + """ + Horizontal propagation of `x-datadog-tags` between services + + https://feature-parity.us1.prod.dog/#/?feature=59 + """ + pytest.mark.features(feature_id=59)(test_object) + return test_object + + @staticmethod + def vertical_propagation_of_x_datadog_tags_onto_each_chunk_root_span(test_object): + """ + Vertical propagation of `x-datadog-tags` onto each chunk root span. + + https://feature-parity.us1.prod.dog/#/?feature=60 + """ + pytest.mark.features(feature_id=60)(test_object) + return test_object + + @staticmethod + def creation_and_propagation_of_ddpdm(test_object): + """ + Creation and propagation of `_dd.p.dm` + + https://feature-parity.us1.prod.dog/#/?feature=61 + """ + pytest.mark.features(feature_id=61)(test_object) + return test_object + + @staticmethod + def client_side_stats_supported(test_object): + """ + Client side stats supported + + https://feature-parity.us1.prod.dog/#/?feature=62 + """ + pytest.mark.features(feature_id=62)(test_object) + return test_object + + @staticmethod + def client_side_stats_on_by_default(test_object): + """ + Client side stats on by default + + https://feature-parity.us1.prod.dog/#/?feature=63 + """ + pytest.mark.features(feature_id=63)(test_object) + return test_object + + @staticmethod + def instrumentation_telemetry_enabled_by_default(test_object): + """ + Instrumentation Telemetry enabled by default + + https://feature-parity.us1.prod.dog/#/?feature=64 + """ + pytest.mark.features(feature_id=64)(test_object) + return test_object + + @staticmethod + def dd_instrumentation_telemetry_enabled_supported(test_object): + """ + DD_INSTRUMENTATION_TELEMETRY_ENABLED supported + + https://feature-parity.us1.prod.dog/#/?feature=65 + """ + pytest.mark.features(feature_id=65)(test_object) + return test_object + + @staticmethod + def app_environment_collected(test_object): + """ + App environment collected + + https://feature-parity.us1.prod.dog/#/?feature=66 + """ + pytest.mark.features(feature_id=66)(test_object) + return test_object + + @staticmethod + def dependencies_collected(test_object): + """ + Dependencies collected + + https://feature-parity.us1.prod.dog/#/?feature=67 + """ + pytest.mark.features(feature_id=67)(test_object) + return test_object + + @staticmethod + def integrations_enabled_collected(test_object): + """ + Integrations enabled collected + + https://feature-parity.us1.prod.dog/#/?feature=68 + """ + pytest.mark.features(feature_id=68)(test_object) + return test_object + + @staticmethod + def tracer_configurations_collected(test_object): + """ + Tracer Configurations collected + + https://feature-parity.us1.prod.dog/#/?feature=69 + """ + pytest.mark.features(feature_id=69)(test_object) + return test_object + + @staticmethod + def heart_beat_collected(test_object): + """ + Heart beat collected + + https://feature-parity.us1.prod.dog/#/?feature=70 + """ + pytest.mark.features(feature_id=70)(test_object) + return test_object + + @staticmethod + def app_close_collected(test_object): + """ + App close collected + + https://feature-parity.us1.prod.dog/#/?feature=71 + """ + pytest.mark.features(feature_id=71)(test_object) + return test_object + + @staticmethod + def redacted_error_logs_collected(test_object): + """ + Redacted Error Logs collected + + https://feature-parity.us1.prod.dog/#/?feature=72 + """ + pytest.mark.features(feature_id=72)(test_object) + return test_object + + @staticmethod + def metrics_collected(test_object): + """ + Metrics collected + + https://feature-parity.us1.prod.dog/#/?feature=73 + """ + pytest.mark.features(feature_id=73)(test_object) + return test_object + + @staticmethod + def api_v2_implemented(test_object): + """ + API V2 Implemented + + https://feature-parity.us1.prod.dog/#/?feature=74 + """ + pytest.mark.features(feature_id=74)(test_object) + return test_object + + @staticmethod + def app_client_configuration_change_event(test_object): + """ + app-client-configuration-change event + + https://feature-parity.us1.prod.dog/#/?feature=75 + """ + pytest.mark.features(feature_id=75)(test_object) + return test_object + + @staticmethod + def app_product_change_event(test_object): + """ + app-product-change event + + https://feature-parity.us1.prod.dog/#/?feature=76 + """ + pytest.mark.features(feature_id=76)(test_object) + return test_object + + @staticmethod + def app_extended_heartbeat_event(test_object): + """ + app-extended-heartbeat event + + https://feature-parity.us1.prod.dog/#/?feature=77 + """ + pytest.mark.features(feature_id=77)(test_object) + return test_object + + @staticmethod + def message_batch(test_object): + """ + message-batch + + https://feature-parity.us1.prod.dog/#/?feature=78 + """ + pytest.mark.features(feature_id=78)(test_object) + return test_object + + @staticmethod + def app_started_changes(test_object): + """ + app-started changes + + https://feature-parity.us1.prod.dog/#/?feature=79 + """ + pytest.mark.features(feature_id=79)(test_object) + return test_object + + @staticmethod + def dd_telemetry_dependency_collection_enabled_supported(test_object): + """ + DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED supported + + https://feature-parity.us1.prod.dog/#/?feature=80 + """ + pytest.mark.features(feature_id=80)(test_object) + return test_object + + @staticmethod + def additional_http_headers_supported(test_object): + """ + Additional HTTP Headers supported + + https://feature-parity.us1.prod.dog/#/?feature=81 + """ + pytest.mark.features(feature_id=81)(test_object) + return test_object + + @staticmethod + def remote_config_object_supported(test_object): + """ + remote-config object supported + + https://feature-parity.us1.prod.dog/#/?feature=82 + """ + pytest.mark.features(feature_id=82)(test_object) + return test_object + + @staticmethod + def w3c_headers_injection_and_extraction(test_object): + """ + W3C headers injection and extraction + + https://feature-parity.us1.prod.dog/#/?feature=83 + """ + pytest.mark.features(feature_id=83)(test_object) + return test_object + + @staticmethod + def otel_api(test_object): + """ + OTel API + + https://feature-parity.us1.prod.dog/#/?feature=84 + """ + pytest.mark.features(feature_id=84)(test_object) + return test_object + + @staticmethod + def trace_id_128_bit_generation_propagation(test_object): + """ + 128-bit trace id generation + propagation + + https://feature-parity.us1.prod.dog/#/?feature=85 + """ + pytest.mark.features(feature_id=85)(test_object) + return test_object + + @staticmethod + def span_events(test_object): + """ + Span Events + + https://feature-parity.us1.prod.dog/#/?feature=86 + """ + pytest.mark.features(feature_id=86)(test_object) + return test_object + + @staticmethod + def span_links(test_object): + """ + Span Links + + https://feature-parity.us1.prod.dog/#/?feature=87 + """ + pytest.mark.features(feature_id=87)(test_object) + return test_object + + @staticmethod + def client_ip_adress_collection_dd_trace_client_ip_enabled(test_object): + """ + Client IP adress collection (DD_TRACE_CLIENT_IP_ENABLED) + + https://feature-parity.us1.prod.dog/#/?feature=88 + """ + pytest.mark.features(feature_id=88)(test_object) + return test_object + + @staticmethod + def host_auto_instrumentation(test_object): + """ + Host auto-instrumentation + + https://feature-parity.us1.prod.dog/#/?feature=89 + """ + pytest.mark.features(feature_id=89)(test_object) + return test_object + + @staticmethod + def container_auto_instrumentation(test_object): + """ + Container auto-instrumentation + + https://feature-parity.us1.prod.dog/#/?feature=90 + """ + pytest.mark.features(feature_id=90)(test_object) + return test_object + + @staticmethod + def collect_http_post_data_and_headers(test_object): + """ + Collect HTTP Post Data and Headers + + https://feature-parity.us1.prod.dog/#/?feature=94 + """ + pytest.mark.features(feature_id=94)(test_object) + return test_object + + @staticmethod + def weak_hash_vulnerability_detection(test_object): + """ + Weak hash vulnerability detection + + https://feature-parity.us1.prod.dog/#/?feature=96 + """ + pytest.mark.features(feature_id=96)(test_object) + return test_object + + @staticmethod + def db_integrations(test_object): + """ + DB Integrations + + https://feature-parity.us1.prod.dog/#/?feature=98 + """ + pytest.mark.features(feature_id=98)(test_object) + return test_object + + @staticmethod + def http_threats_management(test_object): + """ + HTTP Threats Management + + https://feature-parity.us1.prod.dog/#/?feature=99 + """ + pytest.mark.features(feature_id=99)(test_object) + return test_object + + @staticmethod + def weak_cipher_detection(test_object): + """ + Weak cipher detection + + https://feature-parity.us1.prod.dog/#/?feature=100 + """ + pytest.mark.features(feature_id=100)(test_object) + return test_object + + @staticmethod + def threats_alpha_preview(test_object): + """ + Threats Alpha Preview + + https://feature-parity.us1.prod.dog/#/?feature=110 + """ + pytest.mark.features(feature_id=110)(test_object) + return test_object + + @staticmethod + def procedure_to_debug_install(test_object): + """ + Procedure to debug install + + https://feature-parity.us1.prod.dog/#/?feature=113 + """ + pytest.mark.features(feature_id=113)(test_object) + return test_object + + @staticmethod + def security_events_metadata(test_object): + """ + Security Events Metadata + + https://feature-parity.us1.prod.dog/#/?feature=124 + """ + pytest.mark.features(feature_id=124)(test_object) + return test_object + + @staticmethod + def rate_limiter(test_object): + """ + Rate limiter + + https://feature-parity.us1.prod.dog/#/?feature=134 + """ + pytest.mark.features(feature_id=134)(test_object) + return test_object + + @staticmethod + def support_in_app_waf_metrics_report(test_object): + """ + Support In-App WAF metrics report + + https://feature-parity.us1.prod.dog/#/?feature=140 + """ + pytest.mark.features(feature_id=140)(test_object) + return test_object + + @staticmethod + def user_monitoring(test_object): + """ + User Monitoring + + https://feature-parity.us1.prod.dog/#/?feature=141 + """ + pytest.mark.features(feature_id=141)(test_object) + return test_object + + @staticmethod + def serialize_waf_rules_without_limiting_their_sizes(test_object): + """ + Serialize WAF rules without limiting their sizes + + https://feature-parity.us1.prod.dog/#/?feature=142 + """ + pytest.mark.features(feature_id=142)(test_object) + return test_object + + @staticmethod + def threats_configuration(test_object): + """ + Threats Configuration + + https://feature-parity.us1.prod.dog/#/?feature=143 + """ + pytest.mark.features(feature_id=143)(test_object) + return test_object + + @staticmethod + def sensitive_data_obfuscation(test_object): + """ + Sensitive Data Obfuscation + + https://feature-parity.us1.prod.dog/#/?feature=144 + """ + pytest.mark.features(feature_id=144)(test_object) + return test_object + + @staticmethod + def propagation_of_user_id_rfc(test_object): + """ + Propagation of user Id RFC + + https://feature-parity.us1.prod.dog/#/?feature=146 + """ + pytest.mark.features(feature_id=146)(test_object) + return test_object + + @staticmethod + def appsec_onboarding(test_object): + """ + Onboarding + + https://feature-parity.us1.prod.dog/#/?feature=154 + """ + pytest.mark.features(feature_id=154)(test_object) + return test_object + + @staticmethod + def deactivate_rules_using_rc(test_object): + """ + Deactivate rules using RC + + https://feature-parity.us1.prod.dog/#/?feature=157 + """ + pytest.mark.features(feature_id=157)(test_object) + return test_object + + @staticmethod + def appsec_shell_execution_tracing(test_object): + """ + Shell execution tracing + + https://feature-parity.us1.prod.dog/#/?feature=158 + """ + pytest.mark.features(feature_id=158)(test_object) + return test_object + + @staticmethod + def appsec_userid_blocking(test_object): + """ + UserId Blocking + + https://feature-parity.us1.prod.dog/#/?feature=160 + """ + pytest.mark.features(feature_id=160)(test_object) + return test_object + + @staticmethod + def custom_business_logic_events(test_object): + """ + Custom Business Logic Events + + https://feature-parity.us1.prod.dog/#/?feature=161 + """ + pytest.mark.features(feature_id=161)(test_object) + return test_object + + @staticmethod + def graphql_threats_detection(test_object): + """ + GraphQL Threats Detection + + https://feature-parity.us1.prod.dog/#/?feature=162 + """ + pytest.mark.features(feature_id=162)(test_object) + return test_object + + @staticmethod + def iast_sink_sql_injection(test_object): + """ + IAST Sink: SQL Injection + + https://feature-parity.us1.prod.dog/#/?feature=165 + """ + pytest.mark.features(feature_id=165)(test_object) + return test_object + + @staticmethod + def iast_sink_command_injection(test_object): + """ + IAST Sink: Command Injection + + https://feature-parity.us1.prod.dog/#/?feature=166 + """ + pytest.mark.features(feature_id=166)(test_object) + return test_object + + @staticmethod + def iast_sink_path_traversal(test_object): + """ + IAST Sink: Path Traversal + + https://feature-parity.us1.prod.dog/#/?feature=167 + """ + pytest.mark.features(feature_id=167)(test_object) + return test_object + + @staticmethod + def iast_sink_ldap_injection(test_object): + """ + IAST Sink: LDAP Injection + + https://feature-parity.us1.prod.dog/#/?feature=168 + """ + pytest.mark.features(feature_id=168)(test_object) + return test_object + + @staticmethod + def iast_source_request_parameter_value(test_object): + """ + IAST Source: Request Parameter Value + + https://feature-parity.us1.prod.dog/#/?feature=169 + """ + pytest.mark.features(feature_id=169)(test_object) + return test_object + + @staticmethod + def iast_source_request_parameter_name(test_object): + """ + IAST Source: Request Parameter Name + + https://feature-parity.us1.prod.dog/#/?feature=170 + """ + pytest.mark.features(feature_id=170)(test_object) + return test_object + + @staticmethod + def iast_source_header_value(test_object): + """ + IAST Source: Header Value + + https://feature-parity.us1.prod.dog/#/?feature=171 + """ + pytest.mark.features(feature_id=171)(test_object) + return test_object + + @staticmethod + def iast_source_header_name(test_object): + """ + IAST Source: Header Name + + https://feature-parity.us1.prod.dog/#/?feature=172 + """ + pytest.mark.features(feature_id=172)(test_object) + return test_object + + @staticmethod + def iast_source_cookie_value(test_object): + """ + IAST Source: Cookie Value + + https://feature-parity.us1.prod.dog/#/?feature=173 + """ + pytest.mark.features(feature_id=173)(test_object) + return test_object + + @staticmethod + def iast_source_cookie_name(test_object): + """ + IAST Source: Cookie Name + + https://feature-parity.us1.prod.dog/#/?feature=174 + """ + pytest.mark.features(feature_id=174)(test_object) + return test_object + + @staticmethod + def iast_source_body(test_object): + """ + IAST Source: Body + + https://feature-parity.us1.prod.dog/#/?feature=175 + """ + pytest.mark.features(feature_id=175)(test_object) + return test_object + + @staticmethod + def grpc_threats_management(test_object): + """ + gRPC Threats Management + + https://feature-parity.us1.prod.dog/#/?feature=176 + """ + pytest.mark.features(feature_id=176)(test_object) + return test_object + + @staticmethod + def telemetry(test_object): + """ + Telemetry + + https://feature-parity.us1.prod.dog/#/?feature=178 + """ + pytest.mark.features(feature_id=178)(test_object) + return test_object + + @staticmethod + def kafkaspan_creationcontext_propagation_with_dd_trace_py(test_object): + """ + [Kafka][Span Creation][Context Propagation] with dd-trace-py + + https://feature-parity.us1.prod.dog/#/?feature=192 + """ + pytest.mark.features(feature_id=192)(test_object) + return test_object + + @staticmethod + def open_tracing_api(test_object): + """ + Open Tracing API + + https://feature-parity.us1.prod.dog/#/?feature=196 + """ + pytest.mark.features(feature_id=196)(test_object) + return test_object + + @staticmethod + def custom_tracing_api(test_object): + """ + Custom Tracing API + + https://feature-parity.us1.prod.dog/#/?feature=197 + """ + pytest.mark.features(feature_id=197)(test_object) + return test_object + + @staticmethod + def iast_sink_hardcoded_secrets(test_object): + """ + IAST sink: Hardcoded secrets detection + + https://feature-parity.us1.prod.dog/#/?feature=198 + """ + pytest.mark.features(feature_id=198)(test_object) + return test_object diff --git a/utils/scripts/update_features.py b/utils/scripts/update_features.py new file mode 100644 index 00000000000..b03ad9e4a82 --- /dev/null +++ b/utils/scripts/update_features.py @@ -0,0 +1,49 @@ +import requests + +from utils import features + + +def get_known_features(): + """ return an object feature_id -> attribute name in features decorator """ + result = {} + + for attr in dir(features): + if attr.startswith("__"): + continue + + def obj(): + pass + + obj = getattr(features, attr)(obj) + + if hasattr(obj, "pytestmark"): + result[obj.pytestmark[0].kwargs["feature_id"]] = attr + + return result + + +def _main(): + known_features = get_known_features() + + data = requests.get("https://dd-feature-parity.azurewebsites.net/Import/Features", timeout=10).json() + for feature in data: + feature_id = feature["id"] + if feature_id not in known_features: + docstring = f""" + {feature['name']} + + https://feature-parity.us1.prod.dog/#/?feature={feature_id}""" + print( + f""" + @staticmethod + def {feature['codeSafeName'].lower()}(test_object): + ""\"{docstring} + ""\" + pytest.mark.features(feature_id={feature_id})(test_object) + return test_object +""" + ) + + +if __name__ == "__main__": + _main()