Skip to content

Commit 42e42cf

Browse files
committed
Test workflow
1 parent b53e763 commit 42e42cf

File tree

3 files changed

+105
-14
lines changed

3 files changed

+105
-14
lines changed

.github/workflows/build_layer.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: build_layers
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
- "florentinl/APPSEC-58224/block-requests"
8+
pull_request:
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
14+
strategy:
15+
matrix:
16+
arch: [arm64, amd64]
17+
python_version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v3
22+
23+
- name: Build layer for Python ${{ matrix.python_version }} on ${{ matrix.arch }}
24+
run: |
25+
echo "Building layer for Python ${{ matrix.python_version }} on ${{ matrix.arch }}"
26+
ARCH=${{ matrix.arch }} PYTHON_VERSION=${{ matrix.python_version }} ./scripts/build_layers.sh
27+
28+
- name: Upload layer artifact
29+
uses: actions/upload-artifact@v3
30+
with:
31+
path: .layers/*.zip
32+
name: datadog-lambda-python-${{ matrix.python_version }}-${{ matrix.arch }}

datadog_lambda/asm.py

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
from copy import deepcopy
21
import logging
2+
import urllib.parse
3+
from copy import deepcopy
34
from typing import Any, Dict, List, Optional, Union
45

56
from ddtrace.contrib.internal.trace_utils import _get_request_header_client_ip
67
from ddtrace.internal import core
78
from ddtrace.internal.utils import get_blocked, set_blocked
9+
from ddtrace.internal.utils import http as http_utils
810
from ddtrace.trace import Span
911

1012
from datadog_lambda.trigger import (
@@ -51,8 +53,20 @@ def asm_set_context(event_source: _EventSource):
5153
This allows the AppSecSpanProcessor to know information about the event
5254
at the moment the span is created and skip it when not relevant.
5355
"""
56+
57+
def block_request_callable():
58+
"""Callable to block the request."""
59+
blocked_config = get_blocked()
60+
print("Blocking request before", blocked_config)
61+
set_blocked(blocked_config)
62+
print("Blocking request after", blocked_config)
63+
5464
if event_source.event_type not in _http_event_types:
5565
core.set_item("appsec_skip_next_lambda_event", True)
66+
core.set_item(
67+
"block_request_callable",
68+
block_request_callable,
69+
)
5670

5771

5872
def asm_start_request(
@@ -127,6 +141,14 @@ def asm_start_request(
127141
span.set_tag_str("http.client_ip", request_ip)
128142
span.set_tag_str("network.client.ip", request_ip)
129143

144+
# Encode the parsed query and append it to reconstruct the original raw URI expected by AppSec.
145+
if parsed_query:
146+
try:
147+
encoded_query = urllib.parse.urlencode(parsed_query, doseq=True)
148+
except Exception:
149+
pass
150+
raw_uri += "?" + encoded_query # type: ignore
151+
130152
core.dispatch(
131153
# The matching listener is registered in ddtrace.appsec._handlers
132154
"aws_lambda.start_request",
@@ -193,7 +215,33 @@ def get_asm_blocked_response(
193215
return None
194216

195217
blocked = get_blocked()
196-
if blocked:
197-
set_blocked(blocked)
198-
return blocked.get("type", "auto")
199-
return None
218+
if not blocked:
219+
return None
220+
221+
desired_type = blocked.get("type", "auto")
222+
print("desired_type", desired_type)
223+
if desired_type == "none":
224+
content = "".encode()
225+
response_headers = {
226+
"content-type": "text/plain; charset=utf-8",
227+
}
228+
else:
229+
ctype = blocked.get("content_type", "application/json")
230+
print("ctype", ctype)
231+
content = http_utils._get_blocked_template(ctype).encode()
232+
response_headers = {
233+
"content-type": ctype,
234+
}
235+
236+
status = blocked.get("status", 403)
237+
238+
location = blocked.get("location")
239+
if location:
240+
response_headers["location"] = location
241+
242+
return {
243+
"statusCode": status,
244+
"headers": response_headers,
245+
"body": content,
246+
"isBase64Encoded": False,
247+
}

datadog_lambda/wrapper.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from importlib import import_module
1010
from time import time_ns
1111

12+
from ddtrace.internal._exceptions import BlockingException
1213
from datadog_lambda.asm import (
1314
asm_set_context,
1415
asm_start_response,
@@ -126,6 +127,7 @@ def __init__(self, func):
126127
self.span = None
127128
self.inferred_span = None
128129
self.response = None
130+
self.asm_blocked = False
129131

130132
if config.profiling_enabled:
131133
self.prof = profiler.Profiler(env=config.env, service=config.service)
@@ -164,17 +166,19 @@ def __init__(self, func):
164166
def __call__(self, event, context, **kwargs):
165167
"""Executes when the wrapped function gets called"""
166168
self._before(event, context)
167-
if config.appsec_enabled:
168-
blocking_response = get_asm_blocked_response(self.event_source)
169-
if blocking_response:
170-
return blocking_response
171169
try:
172-
self.response = self.func(event, context, **kwargs)
173-
if config.appsec_enabled:
170+
if config.appsec_enabled and (
171+
blocking_response := get_asm_blocked_response(self.event_source)
172+
):
173+
return blocking_response
174+
175+
try:
176+
self.response = self.func(event, context, **kwargs)
177+
return self.response
178+
except BlockingException:
174179
blocking_response = get_asm_blocked_response(self.event_source)
175-
if blocking_response:
176-
return blocking_response
177-
return self.response
180+
return blocking_response
181+
178182
except Exception:
179183
from datadog_lambda.metric import submit_errors_metric
180184

@@ -185,6 +189,9 @@ def __call__(self, event, context, **kwargs):
185189
raise
186190
finally:
187191
self._after(event, context)
192+
if self.asm_blocked:
193+
self.asm_blocked = False
194+
return self.blocking_response
188195

189196
def _inject_authorizer_span_headers(self, request_id):
190197
reference_span = self.inferred_span if self.inferred_span else self.span
@@ -310,6 +317,10 @@ def _after(self, event, context):
310317
response=self.response,
311318
)
312319

320+
if blocking_response := get_asm_blocked_response(self.event_source):
321+
self.asm_blocked = True
322+
self.blocking_response = blocking_response
323+
313324
self.span.finish()
314325

315326
if self.inferred_span:

0 commit comments

Comments
 (0)