Skip to content

Commit

Permalink
DEBUG-2334 Ruby DI system tests (#3516)
Browse files Browse the repository at this point in the history
Co-authored-by: Oleg Pudeyev <[email protected]>
  • Loading branch information
p-datadog and p authored Nov 21, 2024
1 parent 3ce4399 commit 86f272e
Show file tree
Hide file tree
Showing 12 changed files with 285 additions and 22 deletions.
18 changes: 18 additions & 0 deletions tests/debugger/probes/pii_line.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"language": "",
"pii": "",
"id": "log170aa-acda-4453-9111-1478a600line",
"where": {
"typeName": null,
"sourceFile": "ACTUAL_SOURCE_FILE",
"lines": [
"33"
]
},
"captureSnapshot": true,
"capture": {
"maxFieldCount": 200
}
}
]
52 changes: 37 additions & 15 deletions tests/debugger/test_debugger_pii.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,35 +121,38 @@ def filter(keys_to_filter):
@features.debugger_pii_redaction
@scenarios.debugger_pii_redaction
class Test_Debugger_PII_Redaction(base._Base_Debugger_Test):
def _setup(self):
probes = base.read_probes("pii")
def _setup(self, probes_file):
probes = base.read_probes(probes_file)
self.expected_probe_ids = base.extract_probe_ids(probes)
self.rc_state = rc.send_debugger_command(probes, version=1)

interfaces.agent.wait_for(self.wait_for_all_probes_installed, timeout=30)

self.weblog_responses = [weblog.get("/debugger/pii")]

def _test(self, redacted_keys, redacted_types):
def _test(self, redacted_keys, redacted_types, line_probe=False):
self.assert_all_states_not_error()
self.assert_all_probes_are_installed()
self.assert_all_weblog_responses_ok()

self._validate_pii_keyword_redaction(redacted_keys)
self._validate_pii_type_redaction(redacted_types)
self._validate_pii_keyword_redaction(redacted_keys, line_probe=line_probe)
self._validate_pii_type_redaction(redacted_types, line_probe=line_probe)

def setup_pii_redaction_full(self):
self._setup()
self._setup("pii")

@missing_feature(context.library < "[email protected]", reason="keywords are not fully redacted")
@missing_feature(context.library < "[email protected]", reason="keywords are not fully redacted")
@bug(context.library == "[email protected]", reason="DEBUG-3127")
@bug(context.library == "[email protected]", reason="DEBUG-3127")
# Ruby requires @irrelevant rather than @missing_feature to skip setup
# for this test (which will interfere with the line probe test).
@irrelevant(context.library == "ruby", reason="Local variable capture not implemented for method probes")
def test_pii_redaction_full(self):
self._test(REDACTED_KEYS, REDACTED_TYPES)

def setup_pii_redaction_java_1_33(self):
self._setup()
self._setup("pii")

@irrelevant(context.library != "[email protected]", reason="not relevant for other version")
def test_pii_redaction_java_1_33(self):
Expand All @@ -170,7 +173,7 @@ def test_pii_redaction_java_1_33(self):
)

def setup_pii_redaction_dotnet_2_50(self):
self._setup()
self._setup("pii")

@irrelevant(context.library != "[email protected]", reason="not relevant for other version")
@bug(
Expand All @@ -179,7 +182,14 @@ def setup_pii_redaction_dotnet_2_50(self):
def test_pii_redaction_dotnet_2_50(self):
self._test(filter(["applicationkey", "connectionstring"]), REDACTED_TYPES)

def _validate_pii_keyword_redaction(self, should_redact_field_names):
def setup_pii_redaction_line(self):
self._setup("pii_line")

@irrelevant(context.library != "ruby", reason="Ruby needs to use line probes to capture variables")
def test_pii_redaction_line(self):
self._test(REDACTED_KEYS, REDACTED_TYPES, True)

def _validate_pii_keyword_redaction(self, should_redact_field_names, line_probe=False):
agent_logs_endpoint_requests = list(interfaces.agent.get_data(path_filters="/api/v2/logs"))
not_redacted = []
not_found = list(set(should_redact_field_names))
Expand All @@ -193,12 +203,21 @@ def _validate_pii_keyword_redaction(self, should_redact_field_names):

if snapshot:
for field_name in should_redact_field_names:
fields = snapshot["captures"]["return"]["locals"]["pii"]["fields"]

if field_name in fields:
if line_probe:
fields = snapshot["captures"]["lines"]["33"]["locals"]["pii"]["fields"]
else:
fields = snapshot["captures"]["return"]["locals"]["pii"]["fields"]

# Ruby prefixes instance variable names with @
if context.library == "ruby":
check_field_name = "@" + field_name
else:
check_field_name = field_name

if check_field_name in fields:
not_found.remove(field_name)

if "value" in fields[field_name]:
if "value" in fields[check_field_name]:
not_redacted.append(field_name)
error_message = ""
if not_redacted:
Expand All @@ -212,7 +231,7 @@ def _validate_pii_keyword_redaction(self, should_redact_field_names):
if error_message != "":
raise ValueError(error_message)

def _validate_pii_type_redaction(self, should_redact_types):
def _validate_pii_type_redaction(self, should_redact_types, line_probe=False):
agent_logs_endpoint_requests = list(interfaces.agent.get_data(path_filters="/api/v2/logs"))
not_redacted = []

Expand All @@ -225,7 +244,10 @@ def _validate_pii_type_redaction(self, should_redact_types):

if snapshot:
for type_name in should_redact_types:
type_info = snapshot["captures"]["return"]["locals"][type_name]
if line_probe:
type_info = snapshot["captures"]["lines"]["33"]["locals"][type_name]
else:
type_info = snapshot["captures"]["return"]["locals"][type_name]

if "fields" in type_info:
not_redacted.append(type_name)
Expand Down
5 changes: 4 additions & 1 deletion tests/debugger/test_debugger_probe_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import tests.debugger.utils as base

from utils import scenarios, interfaces, weblog, features, remote_config as rc, bug
from utils import scenarios, interfaces, weblog, features, remote_config as rc, bug, missing_feature, context


@features.debugger
Expand Down Expand Up @@ -41,6 +41,7 @@ def setup_span_method_probe_snaphots(self):
]

@bug(library="python", reason="DEBUG-2708, DEBUG-2709")
@missing_feature(context.library == "ruby", reason="Not yet implemented")
def test_span_method_probe_snaphots(self):
self.assert_all_states_not_error()
self.assert_all_probes_are_installed()
Expand All @@ -61,6 +62,7 @@ def setup_span_decoration_method_probe_snaphots(self):
]

@bug(library="python", reason="DEBUG-2708, DEBUG-2709")
@missing_feature(context.library == "ruby", reason="Not yet implemented")
def test_span_decoration_method_probe_snaphots(self):
self.assert_all_states_not_error()
self.assert_all_probes_are_installed()
Expand Down Expand Up @@ -105,6 +107,7 @@ def setup_span_decoration_line_probe_snaphots(self):
weblog.get("/debugger/span-decoration/asd/1"),
]

@missing_feature(context.library == "ruby", reason="Not yet implemented")
def test_span_decoration_line_probe_snaphots(self):
self.assert_all_states_not_error()
self.assert_all_probes_are_installed()
Expand Down
5 changes: 4 additions & 1 deletion tests/debugger/test_debugger_probe_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import tests.debugger.utils as base

from utils import scenarios, features, remote_config as rc, bug, context
from utils import weblog, scenarios, features, remote_config as rc, bug, context, missing_feature


@features.debugger
Expand Down Expand Up @@ -40,6 +40,7 @@ def setup_probe_status_metric(self):

@bug(context.library == "[email protected]", reason="DEBUG-3127")
@bug(context.library == "[email protected]", reason="DEBUG-3127")
@missing_feature(context.library == "ruby", reason="Not yet implemented")
def test_probe_status_metric(self):
self._assert()

Expand All @@ -49,6 +50,7 @@ def setup_probe_status_span(self):

self._setup(probes)

@missing_feature(context.library == "ruby", reason="Not yet implemented")
def test_probe_status_span(self):
self._assert()

Expand All @@ -60,6 +62,7 @@ def setup_probe_status_spandecoration(self):

@bug(context.library == "[email protected]", reason="DEBUG-3127")
@bug(context.library == "[email protected]", reason="DEBUG-3127")
@missing_feature(context.library == "ruby", reason="Not yet implemented")
def test_probe_status_spandecoration(self):
self._assert()

Expand Down
12 changes: 7 additions & 5 deletions tests/debugger/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from utils.dd_constants import RemoteConfigApplyState as ApplyState

_CONFIG_PATH = "/v0.7/config"
_DEBUGER_PATH = "/api/v2/debugger"
_DEBUGGER_PATH = "/api/v2/debugger"
_LOGS_PATH = "/api/v2/logs"
_TRACES_PATH = "/api/v0.2/traces"

Expand Down Expand Up @@ -51,16 +51,18 @@ def read_diagnostic_data():
tracer_version = version.parse(re.sub(r"[^0-9.].*$", "", tracer["tracer_version"]))
if tracer["language"] == "java":
if tracer_version > version.parse("1.27.0"):
path = _DEBUGER_PATH
path = _DEBUGGER_PATH
else:
path = _LOGS_PATH
elif tracer["language"] == "dotnet":
if tracer_version > version.parse("2.49.0"):
path = _DEBUGER_PATH
path = _DEBUGGER_PATH
else:
path = _LOGS_PATH
elif tracer["language"] == "python":
path = _DEBUGER_PATH
path = _DEBUGGER_PATH
elif tracer["language"] == "ruby":
path = _DEBUGGER_PATH
else:
path = _LOGS_PATH

Expand Down Expand Up @@ -133,7 +135,7 @@ def _all_probes_installed(self, probes_map):
return False

if not self.all_probes_installed:
if data["path"] == _DEBUGER_PATH or data["path"] == _LOGS_PATH:
if data["path"] == _DEBUGGER_PATH or data["path"] == _LOGS_PATH:
self.all_probes_installed = _all_probes_installed(self, get_probes_map([data]))

return self.all_probes_installed
Expand Down
7 changes: 7 additions & 0 deletions utils/_remote_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,20 @@ def _get_probe_type(probe_id):
probe["where"]["methodName"] = re.sub(
r"([a-z])([A-Z])", r"\1_\2", probe["where"]["methodName"]
).lower()
elif library_name == "ruby":
probe["where"]["typeName"] = "DebuggerController"
probe["where"]["methodName"] = re.sub(
r"([a-z])([A-Z])", r"\1_\2", probe["where"]["methodName"]
).lower()
elif probe["where"]["sourceFile"] == "ACTUAL_SOURCE_FILE":
if library_name == "dotnet":
probe["where"]["sourceFile"] = "DebuggerController.cs"
elif library_name == "java":
probe["where"]["sourceFile"] = "DebuggerController.java"
elif library_name == "python":
probe["where"]["sourceFile"] = "debugger_controller.py"
elif library_name == "ruby":
probe["where"]["sourceFile"] = "debugger_controller.rb"

logger.debug(f"RC probe is:\n{json.dumps(probe, indent=2)}")
probe_type = _get_probe_type(probe["id"])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Padding
# Padding
# Padding
# Padding

class DebuggerController < ActionController::Base
def init
# This method does nothing.
# When the endpoint corresponding to it is invoked however,
# the middleware installed by dd-trace-rb initializes remote configuration.
render inline: 'debugger init'
end

# Padding
# Padding
# Padding
# Padding

def log_probe
render inline: 'Log probe' # This needs to be line 20
end

# Padding
# Padding
# Padding
# Padding

def pii
pii = Pii.new
customPii = CustomPii.new
value = pii.test_value
custom_value = customPii.test_value
render inline: "PII #{value}. CustomPII #{custom_value}" # must be line 33
end

# Padding
# Padding
# Padding
# Padding
# Padding
# Padding
# Padding
# Padding
# Padding
# Padding
# Padding
# Padding
# Padding

def mix_probe
value = params[:string_arg].length * Integer(params[:int_arg])
render inline: "Mixed result #{value}" # must be line 52
end
end
7 changes: 7 additions & 0 deletions utils/build/docker/ruby/rails70/app/models/base_pii.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class BasePii
def initialize
@test_value = 'should be redacted'
end

attr_reader :test_value
end
5 changes: 5 additions & 0 deletions utils/build/docker/ruby/rails70/app/models/custom_pii.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class CustomPii < BasePii
def initialize
@custom_key = 'should be redacted'
end
end
Loading

0 comments on commit 86f272e

Please sign in to comment.