|
1 | 1 | # frozen_string_literal: true
|
2 | 2 |
|
| 3 | +require 'ostruct' |
| 4 | + |
| 5 | +require 'datadog/appsec/ext' |
3 | 6 | require 'datadog/appsec/spec_helper'
|
| 7 | +require 'support/thread_backtrace_helpers' |
4 | 8 |
|
5 | 9 | RSpec.describe Datadog::AppSec::ActionsHandler do
|
6 | 10 | describe '.handle' do
|
|
97 | 101 | end
|
98 | 102 | end
|
99 | 103 | end
|
| 104 | + |
| 105 | + describe '.generate_stack' do |
| 106 | + let(:generate_stack_action) { { 'stack_id' => 'foo' } } |
| 107 | + let(:trace_op) { Datadog::Tracing::TraceOperation.new } |
| 108 | + let(:trace_op_metastruct) { nil } |
| 109 | + let(:span_op) { Datadog::Tracing::SpanOperation.new('span_test') } |
| 110 | + let(:span_op_metastruct) { nil } |
| 111 | + let(:context) { OpenStruct.new(trace: trace_op, span: span_op) } |
| 112 | + let(:stack_trace_enabled) { true } |
| 113 | + let(:max_collect) { 0 } |
| 114 | + |
| 115 | + let(:stack_key) { Datadog::AppSec::Ext::TAG_STACK_TRACE } |
| 116 | + let(:exploit_category) { Datadog::AppSec::Ext::EXPLOIT_PREVENTION_EVENT_CATEGORY } |
| 117 | + |
| 118 | + before do |
| 119 | + Datadog.configure do |c| |
| 120 | + c.appsec.stack_trace.max_depth = 0 |
| 121 | + c.appsec.stack_trace.max_depth_top_percent = 0 |
| 122 | + end |
| 123 | + allow(Datadog.configuration.appsec.stack_trace).to receive(:enabled).and_return(stack_trace_enabled) |
| 124 | + allow(Datadog.configuration.appsec.stack_trace).to receive(:max_collect).and_return(max_collect) |
| 125 | + allow(Datadog::AppSec).to receive(:active_context).and_return(context) |
| 126 | + allow(Datadog::AppSec::ActionsHandler::StackTraceCollection).to receive(:collect).and_return( |
| 127 | + ThreadBacktraceHelper.locations_inside_nested_blocks.map.with_index do |location, index| |
| 128 | + { |
| 129 | + id: index, |
| 130 | + text: location.to_s.encode('UTF-8'), |
| 131 | + file: (location.absolute_path || location.path)&.encode('UTF-8'), |
| 132 | + line: location.lineno, |
| 133 | + function: location.label&.encode('UTF-8') |
| 134 | + } |
| 135 | + end |
| 136 | + ) |
| 137 | + trace_op.metastruct[Datadog::AppSec::Ext::TAG_STACK_TRACE] = trace_op_metastruct |
| 138 | + span_op.metastruct[Datadog::AppSec::Ext::TAG_STACK_TRACE] = span_op_metastruct |
| 139 | + described_class.generate_stack(generate_stack_action) |
| 140 | + end |
| 141 | + |
| 142 | + context 'when stack trace is enabled and context contains trace and span' do |
| 143 | + it 'adds stack trace to the trace' do |
| 144 | + trace_op_result = trace_op.metastruct[stack_key][exploit_category] |
| 145 | + expect(trace_op_result.size).to eq(1) |
| 146 | + expect(trace_op_result.first[:id]).to eq('foo') |
| 147 | + expect(trace_op_result.first[:frames].size).to eq(5) |
| 148 | + end |
| 149 | + |
| 150 | + context 'when max_collect is 2' do |
| 151 | + let(:max_collect) { 2 } |
| 152 | + |
| 153 | + context 'with two elements contained in same group in trace' do |
| 154 | + let(:trace_op_metastruct) { { exploit_category => [1, 2] } } |
| 155 | + |
| 156 | + it 'does not add stack trace to the trace nor the span' do |
| 157 | + trace_op_result = trace_op.metastruct.dig(stack_key, exploit_category) |
| 158 | + expect(trace_op_result.size).to eq(2) |
| 159 | + expect(trace_op_result[0]).to eq(1) |
| 160 | + expect(trace_op_result[1]).to eq(2) |
| 161 | + span_op_result = span_op.metastruct.dig(stack_key, exploit_category) |
| 162 | + expect(span_op_result).to be_nil |
| 163 | + end |
| 164 | + end |
| 165 | + |
| 166 | + context 'with two elements contained in same group in span' do |
| 167 | + let(:span_op_metastruct) { { exploit_category => [1, 2] } } |
| 168 | + |
| 169 | + it 'does not add stack trace to the trace nor the span' do |
| 170 | + span_op_result = span_op.metastruct.dig(stack_key, exploit_category) |
| 171 | + expect(span_op_result.size).to eq(2) |
| 172 | + expect(span_op_result[0]).to eq(1) |
| 173 | + expect(span_op_result[1]).to eq(2) |
| 174 | + trace_op_result = trace_op.metastruct.dig(stack_key, exploit_category) |
| 175 | + expect(trace_op_result).to be_nil |
| 176 | + end |
| 177 | + end |
| 178 | + |
| 179 | + context 'with one element contained in same group in span and trace' do |
| 180 | + let(:trace_op_metastruct) { { exploit_category => [1] } } |
| 181 | + let(:span_op_metastruct) { { exploit_category => [2] } } |
| 182 | + |
| 183 | + it 'does not add stack trace to the trace nor the span' do |
| 184 | + trace_op_result = trace_op.metastruct.dig(stack_key, exploit_category) |
| 185 | + expect(trace_op_result.size).to eq(1) |
| 186 | + expect(trace_op_result.first).to eq(1) |
| 187 | + span_op_result = span_op.metastruct.dig(stack_key, exploit_category) |
| 188 | + expect(span_op_result.size).to eq(1) |
| 189 | + expect(span_op_result.first).to eq(2) |
| 190 | + end |
| 191 | + end |
| 192 | + |
| 193 | + context 'with two elements contained in different group in trace' do |
| 194 | + let(:trace_op_metastruct) { { 'other_group' => [1, 2] } } |
| 195 | + |
| 196 | + it 'does add stack trace to the trace' do |
| 197 | + trace_op_result = trace_op.metastruct.dig(stack_key, exploit_category) |
| 198 | + expect(trace_op_result.size).to eq(1) |
| 199 | + expect(trace_op_result.first[:id]).to eq('foo') |
| 200 | + end |
| 201 | + end |
| 202 | + end |
| 203 | + end |
| 204 | + |
| 205 | + context 'when stack trace is enabled and context contains only span' do |
| 206 | + let(:context) { OpenStruct.new(span: span_op) } |
| 207 | + |
| 208 | + it 'adds stack trace to the span' do |
| 209 | + test_result = span_op.metastruct[stack_key][exploit_category] |
| 210 | + expect(test_result.size).to eq(1) |
| 211 | + expect(test_result.first[:id]).to eq('foo') |
| 212 | + expect(test_result.first[:frames].size).to eq(5) |
| 213 | + end |
| 214 | + end |
| 215 | + |
| 216 | + context 'when stack trace is disabled' do |
| 217 | + let(:stack_trace_enabled) { false } |
| 218 | + |
| 219 | + it 'does not add stack trace to the trace' do |
| 220 | + expect(trace_op.metastruct[stack_key]).to be_nil |
| 221 | + end |
| 222 | + end |
| 223 | + end |
100 | 224 | end
|
0 commit comments