Skip to content

Commit

Permalink
DEBUG-2334 make serialize_value of Serializer public and add test cov…
Browse files Browse the repository at this point in the history
…erage

For serializing method return values, we will need to call serialize_value
from outside of Serializer and this call will not have a name provided.

Make serialize_value public and add some tests for it (simple cases that
were under serialize_vars previously + explicit redaction cases).
  • Loading branch information
p committed Sep 18, 2024
1 parent f6ec8b7 commit 9a225c9
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 18 deletions.
20 changes: 12 additions & 8 deletions lib/datadog/di/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,23 @@ def serialize_args(args, kwargs)
# of executed code.
def serialize_vars(vars)
vars.each_with_object({}) do |(k, v), agg|
agg[k] = serialize_value(k, v)
agg[k] = serialize_value(v, name: k)
end
end

private

# Serializes a single named value.
#
# The name is necessary to perform sensitive data redaction.
# The name is needed to perform sensitive data redaction.
#
# In some cases, the value being serialized does not have a name
# (for example, it is the return value of a method).
# In this case +name+ can be nil.
#
# Returns a data structure comprised of only values of basic types
# (integers, strings, arrays, hashes).
#
# Respects string length, collection size and traversal depth limits.
def serialize_value(name, value, depth: settings.dynamic_instrumentation.max_capture_depth)
def serialize_value(value, name: nil, depth: settings.dynamic_instrumentation.max_capture_depth)
if redactor.redact_type?(value)
return {type: class_name(value.class), notCapturedReason: "redactedType"}
end
Expand Down Expand Up @@ -109,7 +111,7 @@ def serialize_value(name, value, depth: settings.dynamic_instrumentation.max_cap
value = value[0...max] || []
end
entries = value.map do |elt|
serialize_value(nil, elt, depth: depth - 1)
serialize_value(elt, depth: depth - 1)
end
serialized.update(elements: entries)
end
Expand All @@ -126,7 +128,7 @@ def serialize_value(name, value, depth: settings.dynamic_instrumentation.max_cap
break
end
cur += 1
entries << [serialize_value(nil, k, depth: depth - 1), serialize_value(k, v, depth: depth - 1)]
entries << [serialize_value(k, depth: depth - 1), serialize_value(v, name: k, depth: depth - 1)]
end
serialized.update(entries: entries)
end
Expand Down Expand Up @@ -166,14 +168,16 @@ def serialize_value(name, value, depth: settings.dynamic_instrumentation.max_cap
break
end
cur += 1
fields[ivar] = serialize_value(ivar, value.instance_variable_get(ivar), depth: depth - 1)
fields[ivar] = serialize_value(value.instance_variable_get(ivar), name: ivar, depth: depth - 1)
end
serialized.update(fields: fields)
end
end
serialized
end

private

# Returns the name for the specified class object.
#
# Ruby can have nameless classes, e.g. Class.new is a class object
Expand Down
5 changes: 3 additions & 2 deletions sig/datadog/di/serializer.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ module Datadog

attr_reader settings: untyped

attr_reader redactor: untyped
attr_reader redactor: Datadog::DI::Redactor

def serialize_args: (untyped args, untyped kwargs) -> untyped
def serialize_vars: (untyped vars) -> untyped

private
def serialize_value: (untyped name, untyped value, ?depth: untyped) -> ({ type: untyped, notCapturedReason: "redactedType" } | { type: untyped, notCapturedReason: "redactedIdent" } | untyped)
def serialize_value: (untyped value, ?name: String, ?depth: untyped) -> ({ type: untyped, notCapturedReason: "redactedType" } | { type: untyped, notCapturedReason: "redactedIdent" } | untyped)
def class_name: (untyped cls) -> untyped
end
end
Expand Down
51 changes: 43 additions & 8 deletions spec/datadog/di/serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,49 @@ class DISerializerSpecTestClass; end
described_class.new(settings, redactor)
end

describe "#serialize_value" do
let(:serialized) do
serializer.serialize_value(value, **options)
end

def self.define_cases(cases)
cases.each do |c|
value = c.fetch(:input)
expected = c.fetch(:expected)
var_name = c[:var_name]

context c.fetch(:name) do
let(:value) { value }

let(:options) do
{name: var_name}
end

it "serializes as expected" do
expect(serialized).to eq(expected)
end
end
end
end

cases = [
{name: "nil value", input: nil, expected: {type: "NilClass", isNull: true}},
{name: "true value", input: true, expected: {type: "TrueClass", value: "true"}},
{name: "false value", input: false, expected: {type: "FalseClass", value: "false"}},
{name: "int value", input: 42, expected: {type: "Integer", value: "42"}},
{name: "bigint value", input: 420000000000000000000042, expected: {type: "Integer", value: "420000000000000000000042"}},
{name: "float value", input: 42.02, expected: {type: "Float", value: "42.02"}},
{name: "string value", input: "x", expected: {type: "String", value: "x"}},
{name: "symbol value", input: :x, expected: {type: "Symbol", value: "x"}},
{name: "redacted identifier in predefined list", input: "123", var_name: "password",
expected: {type: "String", notCapturedReason: "redactedIdent"}},
{name: "variable name given and is not a redacted identifier", input: "123", var_name: "normal",
expected: {type: "String", value: "123"}},
]

define_cases(cases)
end

describe "#serialize_vars" do
let(:serialized) do
serializer.serialize_vars(vars)
Expand All @@ -83,14 +126,6 @@ def self.define_cases(cases)
end

cases = [
{name: "nil value", input: {a: nil}, expected: {a: {type: "NilClass", isNull: true}}},
{name: "true value", input: {a: true}, expected: {a: {type: "TrueClass", value: "true"}}},
{name: "false value", input: {a: false}, expected: {a: {type: "FalseClass", value: "false"}}},
{name: "int value", input: {a: 42}, expected: {a: {type: "Integer", value: "42"}}},
{name: "bigint value", input: {a: 420000000000000000000042}, expected: {a: {type: "Integer", value: "420000000000000000000042"}}},
{name: "float value", input: {a: 42.02}, expected: {a: {type: "Float", value: "42.02"}}},
{name: "string value", input: {a: "x"}, expected: {a: {type: "String", value: "x"}}},
{name: "symbol value", input: {a: :x}, expected: {a: {type: "Symbol", value: "x"}}},
{name: "redacted value in predefined list", input: {password: "123"},
expected: {password: {type: "String", notCapturedReason: "redactedIdent"}}},
{name: "redacted type", input: {value: DISerializerSpecSensitiveType.new},
Expand Down

0 comments on commit 9a225c9

Please sign in to comment.