Skip to content

Commit

Permalink
Test asm standalone from appsec extension
Browse files Browse the repository at this point in the history
  • Loading branch information
estringana committed Nov 22, 2024
1 parent 8095baa commit a42e4ad
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 0 deletions.
26 changes: 26 additions & 0 deletions appsec/tests/extension/inc/distributed_tracing.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

function dt_decode_headers_from_httpbin($response)
{
$data = json_decode($response, true);
if (!$data || !isset($data['headers'])) {
echo 'Invalid response:' . PHP_EOL;
var_dump($response);
return [];
}
$headers = [];
foreach ($data['headers'] as $key => $value) {
$headers[strtolower($key)] = mb_convert_encoding($value, 'ISO-8859-1', 'UTF-8');
}
return $headers;
}

function dt_dump_headers_from_httpbin(array $headers, array $whitelist)
{
foreach ($headers as $key => $value) {
if (!in_array($key, $whitelist, true)) {
continue;
}
echo $key . ': ' . $value . PHP_EOL;
}
}
16 changes: 16 additions & 0 deletions appsec/tests/extension/inc/mock_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,22 @@ function response_config_sync() {
return response("config_sync", []);
}

function request_without_events()
{
return [
response_list(response_request_init([[['ok', []]], [], true])),
response_list(response_request_shutdown([[['ok', []]], [], true])),
];
}

function request_with_events()
{
return [
response_list(response_request_init([[['record', []]],['{"found":"attack"}','{"another":"attack"}'],true])),
response_list(response_request_shutdown([[['record', []]], ['{"yet another":"attack"}'], true]))
];
}


// vim: set et sw=4 ts=4:
?>
151 changes: 151 additions & 0 deletions appsec/tests/extension/inc/request_replayer.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

class RequestReplayer
{
/**
* @var string
*/
public $endpoint;

/**
* @var int
*/
public $flushInterval;

/**
* @var int
*/
public $maxIteration;

public function __construct()
{
$this->endpoint = sprintf(
'http://%s:%d',
getenv('DD_AGENT_HOST') ?: 'request-replayer',
getenv('DD_TRACE_AGENT_PORT') ?: '80'
);

$this->flushInterval = getenv('DD_TRACE_AGENT_FLUSH_INTERVAL')
? (int) getenv('DD_TRACE_AGENT_FLUSH_INTERVAL') * 100
: 50000;

$this->maxIteration = (strncasecmp(PHP_OS, "WIN", 3) === 0) ? 500 : 200;
}

public function waitForFlush()
{
usleep($this->flushInterval * 2);
}

public function waitForRequest($matcher)
{
$i = 0;
do {
if ($i++ == $this->maxIteration) {
throw new Exception("wait for replay timeout");
}
usleep($this->flushInterval);

$requests = $this->replayAllRequests();
if (is_array($requests)) {
foreach ($requests as $request) {
if ($matcher($request)) {
return $request;
}
}
}
} while (true);
}

public function waitForDataAndReplay($ignoreTelemetry = true)
{
$i = 0;
do {
if ($i++ == $this->maxIteration) {
throw new Exception("wait for replay timeout");
}
usleep($this->flushInterval);
} while (empty($data = $this->replayRequest($ignoreTelemetry)));
return $data;
}

public function replayRequest($ignoreTelemetry = false)
{
// Request replayer now returns as many requests as were sent during a session.
// For the scope of the tests, we are returning the very first one.
$allRequests = $this->replayAllRequests();
if ($allRequests && $ignoreTelemetry) {
$allRequests = array_values(array_filter($allRequests, function ($v) { return $v["uri"] != '/telemetry/proxy/api/v2/apmtelemetry'; }));
}
return $allRequests ? $allRequests[0] : [];
}

public function replayAllRequests()
{
return json_decode(file_get_contents($this->endpoint . '/replay', false, stream_context_create([
"http" => [
"header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"),
],
])), true);
}

public function replayHeaders($showOnly = [])
{
$request = $this->waitForDataAndReplay();
if (!isset($request['headers'])) {
return [];
}

ksort($request['headers']);

$headers = [];
foreach ($request['headers'] as $name => $value) {
$name = strtolower($name);
if ($showOnly && !in_array($name, $showOnly, true)) {
continue;
}
$headers[$name] = $value;
}
return $headers;
}

public function setResponse($array) {
file_get_contents($this->endpoint . '/next-response', false, stream_context_create([
"http" => [
"method" => "POST",
"content" => json_encode($array),
"header" => [
"Content-Type: application/json",
"X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"),
]
],
]));
}

public static function launchUnixProxy($socketPath) {
@unlink($socketPath);
$code = str_replace("\n", "", '
ignore_user_abort(true); /* prevent bailout... */
$server = stream_socket_server("unix://' . $socketPath . '");
print "1\n"; /* ready marker */
if (!$client = stream_socket_accept($server, 5)) {
return;
}
$replayer = stream_socket_client("request-replayer:80");
$all = $read = [$client, $replayer];
foreach ($read as $fp) stream_set_blocking($fp, false);
while (stream_select($read, $w, $e, null)) {
$data = fread($fp = reset($read), 4096);
if ($data == "") {
return;
}
fwrite($fp == $replayer ? $client : $replayer, $data);
$read = $all;
}
');

static $unix_proxy_process_reference;
$unix_proxy_process_reference = popen(PHP_BINARY . " -r '$code'", 'r');
fread($unix_proxy_process_reference, 1); // ready
}
}
49 changes: 49 additions & 0 deletions appsec/tests/extension/standalone/appsec_upstream_no_attack.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
Appsec upstream and no attack, should leave the priorit as it comes on the upstream
--ENV--
DD_AGENT_HOST=request-replayer
DD_TRACE_AGENT_PORT=80
DD_TRACE_AGENT_FLUSH_INTERVAL=333
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_INSTRUMENTATION_TELEMETRY_ENABLED=0
DD_TRACE_SIDECAR_TRACE_SENDER=0
DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED=1
DD_APPSEC_ENABLED=1
DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec
HTTP_X_DATADOG_TRACE_ID=42
HTTP_X_DATADOG_TAGS=_dd.p.appsec=1
HTTP_X_DATADOG_SAMPLING_PRIORITY=2
--INI--
extension=ddtrace.so
datadog.trace.agent_test_session_token=background-sender/agent_sampling_c
--SKIPIF--
<?php if (!extension_loaded('curl')) die('skip: curl extension required'); ?>
<?php if (!getenv('HTTPBIN_HOSTNAME')) die('skip: HTTPBIN_HOSTNAME env var required'); ?>
--FILE--
<?php
include __DIR__ . '/simulate_request.inc';
include __DIR__ . '/../inc/mock_helper.php';

$helper = Helper::createInitedRun([
...request_without_events()
]);

$rr = new RequestReplayer();
$result = simulate_request($rr);
var_dump("Appsec should be present", isset($result['spans'][0]["meta"]["_dd.p.appsec"]));
var_dump("Sampling priority should be 2", $result['spans'][0]["metrics"]["_sampling_priority_v1"]);
var_dump("Apm should be disabled", $result['spans'][0]["metrics"]["_dd.apm.enabled"]);
var_dump("Propagated sampling priority should be 2", $result['curl_headers']['x-datadog-sampling-priority']);
var_dump("Appsec tag should be propagated", strpos($result['curl_headers']['x-datadog-tags'], '_dd.p.appsec=1') !== false);
?>
--EXPECTF--
string(24) "Appsec should be present"
bool(true)
string(29) "Sampling priority should be 2"
int(2)
string(22) "Apm should be disabled"
int(0)
string(40) "Propagated sampling priority should be 2"
string(1) "2"
string(31) "Appsec tag should be propagated"
bool(true)
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
--TEST--
No appsec upstream and no attack, set priority to auto reject
--ENV--
DD_AGENT_HOST=request-replayer
DD_TRACE_AGENT_PORT=80
DD_TRACE_AGENT_FLUSH_INTERVAL=333
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_INSTRUMENTATION_TELEMETRY_ENABLED=0
DD_TRACE_SIDECAR_TRACE_SENDER=0
DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED=1
DD_APPSEC_ENABLED=1
DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec
HTTP_X_DATADOG_TRACE_ID=42
HTTP_X_DATADOG_TAGS=_dd.p.other=1
--INI--
extension=ddtrace.so
datadog.trace.agent_test_session_token=background-sender/agent_sampling_a
--SKIPIF--
<?php if (!extension_loaded('curl')) die('skip: curl extension required'); ?>
<?php if (!getenv('HTTPBIN_HOSTNAME')) die('skip: HTTPBIN_HOSTNAME env var required'); ?>
--FILE--
<?php
include __DIR__ . '/simulate_request.inc';
include __DIR__ . '/../inc/mock_helper.php';

$helper = Helper::createInitedRun([
...request_without_events(),
...request_without_events(),
]);
$rr = new RequestReplayer();

//We need to make two requests. The first one is allow by the sampler, but not the second
$result = simulate_request($rr);
var_dump("First call: Appsec should not be present", isset($result['spans'][0]["meta"]["_dd.p.appsec"]));
var_dump("First call: Sampling priority should be 1", $result['spans'][0]["metrics"]["_sampling_priority_v1"]);
var_dump("First call: Apm should be disabled", $result['spans'][0]["metrics"]["_dd.apm.enabled"]);
var_dump("First call: There should not be header propagation", $result['curl_headers']);

$result = simulate_request($rr);
var_dump("Second call: Appsec should not be present", isset($result['spans'][0]["meta"]["_dd.p.appsec"]));
var_dump("Second call: Sampling priority should be 0", $result['spans'][0]["metrics"]["_sampling_priority_v1"]);
var_dump("Second call: Apm should be disabled", $result['spans'][0]["metrics"]["_dd.apm.enabled"]);
var_dump("Second call: There should not be header propagation", $result['curl_headers']);
?>
--EXPECTF--
string(40) "First call: Appsec should not be present"
bool(false)
string(41) "First call: Sampling priority should be 1"
int(1)
string(34) "First call: Apm should be disabled"
int(0)
string(50) "First call: There should not be header propagation"
array(0) {
}
string(41) "Second call: Appsec should not be present"
bool(false)
string(42) "Second call: Sampling priority should be 0"
int(0)
string(35) "Second call: Apm should be disabled"
int(0)
string(51) "Second call: There should not be header propagation"
array(0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
--TEST--
No appsec upstream with attack, set priority to keep
--ENV--
DD_AGENT_HOST=request-replayer
DD_TRACE_AGENT_PORT=80
DD_TRACE_AGENT_FLUSH_INTERVAL=333
DD_TRACE_GENERATE_ROOT_SPAN=1
DD_INSTRUMENTATION_TELEMETRY_ENABLED=0
DD_TRACE_SIDECAR_TRACE_SENDER=0
DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED=1
DD_APPSEC_ENABLED=1
DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec
HTTP_X_DATADOG_TRACE_ID=42
HTTP_X_DATADOG_TAGS=_dd.p.other=1
--INI--
extension=ddtrace.so
datadog.trace.agent_test_session_token=background-sender/agent_sampling_b
--FILE--
<?php
include __DIR__ . '/simulate_request.inc';
include __DIR__ . '/../inc/mock_helper.php';

$helper = Helper::createInitedRun([
...request_with_events()
]);

$rr = new RequestReplayer();
$result = simulate_request($rr);
var_dump("Appsec should be present", isset($result['spans'][0]["meta"]["_dd.p.appsec"]));
var_dump("Sampling priority should be 2", $result['spans'][0]["metrics"]["_sampling_priority_v1"]);
var_dump("Apm should be disabled", $result['spans'][0]["metrics"]["_dd.apm.enabled"]);
var_dump("Propagated sampling priority should be 2", $result['curl_headers']['x-datadog-sampling-priority']);
var_dump("Appsec tag should be propagated", strpos($result['curl_headers']['x-datadog-tags'], '_dd.p.appsec=1') !== false);
?>
--EXPECTF--
string(24) "Appsec should be present"
bool(true)
string(29) "Sampling priority should be 2"
int(2)
string(22) "Apm should be disabled"
int(0)
string(40) "Propagated sampling priority should be 2"
string(1) "2"
string(31) "Appsec tag should be propagated"
bool(true)
Loading

0 comments on commit a42e4ad

Please sign in to comment.