diff --git a/.gitlab/generate-appsec.php b/.gitlab/generate-appsec.php index 7cc90045850..56b5ee2ec98 100644 --- a/.gitlab/generate-appsec.php +++ b/.gitlab/generate-appsec.php @@ -63,7 +63,7 @@ before_script: - - apt update && apt install -y default-jre + - apt update && apt install -y openjdk-17-jre "test appsec extension": stage: test @@ -130,7 +130,7 @@ script: - - apt update && apt install -y default-jre + - apt update && apt install -y openjdk-17-jre - find "$CI_PROJECT_DIR"/appsec/tests/integration/build || true - | cd appsec/tests/integration diff --git a/appsec/run-tests-internal.php b/appsec/run-tests-internal.php index 7eb24f23fb6..772131499d3 100644 --- a/appsec/run-tests-internal.php +++ b/appsec/run-tests-internal.php @@ -845,11 +845,11 @@ function write_information() $info_params = array(); settings2array($ini_overwrites, $info_params); $info_params = settings2params($info_params); - $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`; - define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`); + $php_info = shell_exec("$php $pass_options $info_params $no_file_cache \"$info_file\""); + define('TESTED_PHP_VERSION', shell_exec("$php -n -r \"echo PHP_VERSION;\"")); if ($php_cgi && $php != $php_cgi) { - $php_info_cgi = `$php_cgi $pass_options $info_params $no_file_cache -q "$info_file"`; + $php_info_cgi = shell_exec("$php_cgi $pass_options $info_params $no_file_cache -q \"$info_file\""); $php_info_sep = "\n---------------------------------------------------------------------"; $php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep"; } else { @@ -857,7 +857,7 @@ function write_information() } if ($phpdbg) { - $phpdbg_info = `$phpdbg $pass_options $info_params $no_file_cache -qrr "$info_file"`; + $phpdbg_info = shell_exec("$phpdbg $pass_options $info_params $no_file_cache -qrr \"$info_file\""); $php_info_sep = "\n---------------------------------------------------------------------"; $phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep"; } else { @@ -872,7 +872,7 @@ function write_information() // load list of enabled extensions save_text($info_file, ''); - $exts_to_test = explode(',', `$php $pass_options $info_params $no_file_cache "$info_file"`); + $exts_to_test = explode(',', shell_exec("$php $pass_options $info_params $no_file_cache \"$info_file\"")); // check for extensions that need special handling and regenerate $info_params_ex = array( 'session' => array('session.auto_start=0'), @@ -2171,9 +2171,9 @@ function run_test($php, $file, array $env) $ext_params = array(); settings2array($ini_overwrites, $ext_params); $ext_params = settings2params($ext_params); - $ext_dir = `$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r "echo ini_get('extension_dir');"`; + $ext_dir = shell_exec("$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r \"echo ini_get('extension_dir');\""); $extensions = preg_split("/[\n\r]+/", trim($section_text['EXTENSIONS'])); - $loaded = explode(",", `$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r "echo implode(',', get_loaded_extensions());"`); + $loaded = explode(",", shell_exec("$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r \"echo implode(',', get_loaded_extensions());\"")); $ext_prefix = IS_WINDOWS ? "php_" : ""; foreach ($extensions as $req_ext) { if (!in_array($req_ext, $loaded)) { @@ -3403,7 +3403,7 @@ function show_result( $tested, $tested_file, $extra = '', - array $temp_filenames = null + $temp_filenames = null ) { global $SHOW_ONLY_GROUPS, $colorize; diff --git a/appsec/src/extension/configuration.h b/appsec/src/extension/configuration.h index 691e70eebeb..50a0216a4db 100644 --- a/appsec/src/extension/configuration.h +++ b/appsec/src/extension/configuration.h @@ -29,7 +29,7 @@ extern bool runtime_config_first_init; #define DD_BASE(path) "/opt/datadog-php/" // clang-format off -#define DD_CONFIGURATION \ +#define DD_CONFIGURATION_GENERAL \ SYSCFG(BOOL, DD_APPSEC_ENABLED, "false") \ SYSCFG(BOOL, DD_APPSEC_CLI_START_ON_RINIT, "false") \ SYSCFG(STRING, DD_APPSEC_RULES, "") \ @@ -50,7 +50,6 @@ extern bool runtime_config_first_init; SYSCFG(BOOL, DD_APPSEC_RASP_ENABLED , "true") \ SYSCFG(INT, DD_APPSEC_MAX_STACK_TRACE_DEPTH, "32") \ SYSCFG(INT, DD_APPSEC_MAX_STACK_TRACES, "2") \ - CONFIG(STRING, DD_APPSEC_HELPER_RUNTIME_PATH, "/tmp", .ini_change = dd_on_runtime_path_update) \ SYSCFG(STRING, DD_APPSEC_HELPER_LOG_FILE, "/dev/null") \ SYSCFG(STRING, DD_APPSEC_HELPER_LOG_LEVEL, "info") \ CONFIG(CUSTOM(SET), DD_EXTRA_SERVICES, "", .parser = _parse_list) \ @@ -71,7 +70,17 @@ extern bool runtime_config_first_init; CONFIG(STRING, DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON, "") \ CONFIG(BOOL, DD_APM_TRACING_ENABLED, "true") \ CONFIG(BOOL, DD_API_SECURITY_ENABLED, "true", .ini_change = zai_config_system_ini_change) \ - CONFIG(DOUBLE, DD_API_SECURITY_SAMPLE_DELAY, "30.0", .ini_change = zai_config_system_ini_change) \ + CONFIG(DOUBLE, DD_API_SECURITY_SAMPLE_DELAY, "30.0", .ini_change = zai_config_system_ini_change) + +#ifdef __linux__ +#define DD_CONFIGURATION \ + DD_CONFIGURATION_GENERAL \ + CONFIG(STRING, DD_APPSEC_HELPER_RUNTIME_PATH, "@/ddappsec", .ini_change = dd_on_runtime_path_update) +#else +#define DD_CONFIGURATION \ + DD_CONFIGURATION_GENERAL \ + CONFIG(STRING, DD_APPSEC_HELPER_RUNTIME_PATH, "/tmp", .ini_change = dd_on_runtime_path_update) +#endif // clang-format on #define CALIAS CONFIG diff --git a/appsec/src/extension/helper_process.c b/appsec/src/extension/helper_process.c index 37f7cc016e7..844ff36d0db 100644 --- a/appsec/src/extension/helper_process.c +++ b/appsec/src/extension/helper_process.c @@ -41,8 +41,8 @@ typedef struct _dd_helper_mgr { bool connected_this_req; dd_helper_shared_state hss; - char *nonnull socket_path; - char *nonnull lock_path; + char *nonnull socket_path; // if abstract, starts with @ + char *nonnull lock_path; // set, but not used with abstract ns sockets } dd_helper_mgr; static _Atomic(dd_helper_shared_state) *_shared_state; diff --git a/appsec/src/extension/network.c b/appsec/src/extension/network.c index a43b57c1ba3..641853c6c1f 100644 --- a/appsec/src/extension/network.c +++ b/appsec/src/extension/network.c @@ -49,7 +49,13 @@ static long _timespec_delta_ms(struct timespec *ts1, struct timespec *ts2); int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) dd_conn *nonnull conn, const char *nonnull path, size_t path_len) { - if (path_len > sizeof(conn->addr.sun_path) - 1) { + if (path_len < 1) { + mlog(dd_log_error, "Socket path is too short"); + return dd_error; + } + const bool is_abstract = path[0] == '@'; + + if (path_len > sizeof(conn->addr.sun_path) - (is_abstract ? 0 : 1)) { mlog(dd_log_error, "Socket path is too long"); return dd_error; } @@ -64,9 +70,19 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) conn->addr.sun_family = AF_UNIX; - // NOLINTNEXTLINE - strncpy(conn->addr.sun_path, path, sizeof(conn->addr.sun_path) - 1); - conn->addr.sun_path[sizeof(conn->addr.sun_path) - 1] = '\0'; + size_t addr_size; + if (is_abstract) { + // abstract namespace socket; replace @ with NUL byte + conn->addr.sun_path[0] = '\0'; + strncpy( + conn->addr.sun_path + 1, path + 1, sizeof(conn->addr.sun_path) - 2); + conn->addr.sun_path[sizeof(conn->addr.sun_path) - 1] = '\0'; + addr_size = path_len + offsetof(struct sockaddr_un, sun_path); + } else { + strncpy(conn->addr.sun_path, path, sizeof(conn->addr.sun_path) - 1); + conn->addr.sun_path[sizeof(conn->addr.sun_path) - 1] = '\0'; + addr_size = sizeof(conn->addr); + } int flags_before = fcntl(conn->socket, F_GETFL, 0); if (flags_before == -1) { @@ -82,19 +98,18 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) mlog(dd_log_info, "Attempting to connect to UNIX socket %s", path); struct timespec deadline; - clock_gettime(CLOCK_MONOTONIC, &deadline); + UNUSED(clock_gettime(CLOCK_MONOTONIC, &deadline)); _timespec_add_ms(&deadline, CONNECT_TIMEOUT); try_again: - res = connect( - conn->socket, (struct sockaddr *)&conn->addr, sizeof(conn->addr)); + res = connect(conn->socket, (struct sockaddr *)&conn->addr, addr_size); if (res == -1) { int errno_copy = errno; if (errno_copy == EINPROGRESS) { struct pollfd pfds[] = { {.fd = conn->socket, .events = POLLIN | POLLOUT}}; struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + UNUSED(clock_gettime(CLOCK_MONOTONIC, &now)); long time_left = _timespec_delta_ms(&deadline, &now); if (time_left <= 0) { dd_conn_destroy(conn); @@ -131,7 +146,7 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) if (errno_copy == ENOENT || errno_copy == ECONNREFUSED) { // the socket does not exist or is not being listened on. Retry struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + UNUSED(clock_gettime(CLOCK_MONOTONIC, &now)); long time_left = _timespec_delta_ms(&deadline, &now); if (time_left <= 0) { dd_conn_destroy(conn); diff --git a/appsec/src/helper/config.hpp b/appsec/src/helper/config.hpp index aa0e35edfe2..0920247ce92 100644 --- a/appsec/src/helper/config.hpp +++ b/appsec/src/helper/config.hpp @@ -5,8 +5,6 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include -#include #include #include #include @@ -26,6 +24,15 @@ class config { return kv_.at(env_socket_file_path); } + [[nodiscard]] bool is_abstract_socket() const + { + std::string_view const sfp = socket_file_path(); + if (sfp.empty()) { + return false; + } + return sfp[0] == '@'; + } + [[nodiscard]] std::string_view lock_file_path() const { return kv_.at(env_lock_file_path); diff --git a/appsec/src/helper/main.cpp b/appsec/src/helper/main.cpp index 0f451f25d3a..47841b0121b 100644 --- a/appsec/src/helper/main.cpp +++ b/appsec/src/helper/main.cpp @@ -25,8 +25,10 @@ extern "C" { #include #include #include +#include #include #include +#include } namespace { @@ -60,7 +62,7 @@ void *pthread_wrapper(void *arg) return reinterpret_cast(ret); } -bool ensure_unique(const std::string &lock_path) +bool ensure_unique_lock(const std::string &lock_path) { // do not acquire the lock / assume we inherited it if (lock_path == "-") { @@ -88,6 +90,44 @@ bool ensure_unique(const std::string &lock_path) return true; } +bool ensure_unique_abstract_socket(std::string_view socket_path) +{ + if (socket_path.empty() || socket_path[0] != '@') { + return true; + } + + // Try to connect to the socket. If successful, another helper is running + // NOLINTNEXTLINE(android-cloexec-socket) + int const sock = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + SPDLOG_INFO("Failed to create test socket: errno {}", errno); + return false; + } + + struct sockaddr_un addr {}; + addr.sun_family = AF_UNIX; + addr.sun_path[0] = '\0'; + + std::size_t const size = + std::min(socket_path.size() - 1, sizeof(addr.sun_path) - 2); + std::copy_n(socket_path.data() + 1, size, &addr.sun_path[1]); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) + addr.sun_path[size + 1] = '\0'; + + int const res = ::connect( + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + sock, reinterpret_cast(&addr), sizeof(addr)); + ::close(sock); + + if (res == 0) { + SPDLOG_INFO("Another helper is already running on {}", socket_path); + return false; + } + + SPDLOG_DEBUG("No helper running on socket {}, proceeding", socket_path); + return true; +} + int appsec_helper_main_impl() { dds::config::config const config{ @@ -124,10 +164,18 @@ int appsec_helper_main_impl() dds::waf::initialise_logging(level); - if (!ensure_unique(std::string{config.lock_file_path()})) { - logger->warn("helper launched, but not unique, exiting"); - // There's another helper running - return 1; + if (config.is_abstract_socket()) { + if (!ensure_unique_abstract_socket(config.socket_file_path())) { + logger->warn("helper launched, but not unique (abstract socket " + "exists), exiting"); + return 1; + } + } else { + if (!ensure_unique_lock(std::string{config.lock_file_path()})) { + logger->warn( + "helper launched, but not unique (lock file exists), exiting"); + return 1; + } } // block SIGUSR1 (only used to interrupt the runner) diff --git a/appsec/src/helper/network/acceptor.cpp b/appsec/src/helper/network/acceptor.cpp index f387cc198cd..f2b80314886 100644 --- a/appsec/src/helper/network/acceptor.cpp +++ b/appsec/src/helper/network/acceptor.cpp @@ -29,42 +29,74 @@ acceptor::acceptor(const std::string_view &sv) } struct sockaddr_un addr {}; + std::size_t addr_size; addr.sun_family = AF_UNIX; - if (sv.size() > sizeof(addr.sun_path) - 1) { - throw std::invalid_argument{"socket path too long"}; - } - strcpy(static_cast(addr.sun_path), sv.data()); // NOLINT + bool const is_abstract = (!sv.empty() && sv[0] == '@'); - // Remove the existing socket - int res = ::unlink(static_cast(addr.sun_path)); - if (res == -1 && errno != ENOENT) { - SPDLOG_ERROR("Failed to unlink {}: errno {}", addr.sun_path, errno); - throw std::system_error(errno, std::generic_category()); + if (is_abstract) { +#ifdef __linux__ + if (sv.size() > sizeof(addr.sun_path)) { + throw std::invalid_argument{"socket path too long"}; + } + // Replace @ with null byte for abstract namespace + addr.sun_path[0] = '\0'; + std::copy_n(sv.data() + 1, sv.size() - 1, &addr.sun_path[1]); + addr_size = sv.size() + offsetof(struct sockaddr_un, sun_path); +#else + throw std::runtime_error{ + "Abstract namespace sockets are only supported on Linux"}; +#endif + } else { + // Filesystem socket + if (sv.size() > sizeof(addr.sun_path) - 1) { + throw std::invalid_argument{"socket path too long"}; + } + std::copy_n(sv.data(), sv.size(), &addr.sun_path[0]); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) + addr.sun_path[sv.size()] = '\0'; + addr_size = sizeof(addr); + + // Remove the existing socket + int const res = ::unlink(static_cast(addr.sun_path)); + if (res == -1 && errno != ENOENT) { + SPDLOG_ERROR("Failed to unlink {}: errno {}", addr.sun_path, errno); + throw std::system_error(errno, std::generic_category()); + } + SPDLOG_DEBUG("Unlinked {}", addr.sun_path); } - SPDLOG_DEBUG("Unlinked {}", addr.sun_path); - res = + int res = ::bind( // NOLINTNEXTLINE - ::bind(sock_.get(), reinterpret_cast(&addr), - sizeof(addr)); + sock_.get(), reinterpret_cast(&addr), addr_size); if (res == -1) { - SPDLOG_ERROR( - "Failed to bind socket to {}: errno {}", addr.sun_path, errno); + if (is_abstract) { + SPDLOG_ERROR("Failed to bind abstract socket: errno {}", errno); + } else { + SPDLOG_ERROR( + "Failed to bind socket to {}: errno {}", addr.sun_path, errno); + } throw std::system_error(errno, std::generic_category()); } - res = ::chmod(sv.data(), 0777); // NOLINT - if (res == -1) { - SPDLOG_ERROR( - "Failed to chmod socket {}: errno {}", addr.sun_path, errno); - throw std::system_error(errno, std::generic_category()); + if (!is_abstract) { + res = ::chmod(sv.data(), 0777); // NOLINT + if (res == -1) { + SPDLOG_ERROR( + "Failed to chmod socket {}: errno {}", addr.sun_path, errno); + throw std::system_error(errno, std::generic_category()); + } } static constexpr int backlog = 50; if (::listen(sock_.get(), backlog) == -1) { throw std::system_error(errno, std::generic_category()); } - SPDLOG_INFO("Started listening on {}", sv); + + if (is_abstract) { + SPDLOG_INFO("Started listening on abstract socket: {}", sv); + } else { + SPDLOG_INFO("Started listening on {}", sv); + } } void acceptor::set_accept_timeout(std::chrono::seconds timeout) diff --git a/appsec/tests/extension/module_order_opcache.phpt b/appsec/tests/extension/module_order_opcache.phpt index bda8badaa50..cc1cb13aa05 100644 --- a/appsec/tests/extension/module_order_opcache.phpt +++ b/appsec/tests/extension/module_order_opcache.phpt @@ -2,11 +2,13 @@ Verify ddappsec is always in the module registry after ddtrace when opcache is present --SKIPIF-- =')) +if (version_compare(PHP_VERSION, '8.5.0', '>=')) die('skip: opcache is loaded by default in PHP 8.5+'); + +// cover also pre-releases (to remove later) +if (strpos(phpversion(), "8.5") === 0) { + die('skip: opcache is loaded by default PHP 8.5 (prerelease)'); +} ?> --INI-- extension=ddtrace.so diff --git a/appsec/tests/extension/module_order_opcache_php85.phpt b/appsec/tests/extension/module_order_opcache_php85.phpt new file mode 100644 index 00000000000..48c64fea164 --- /dev/null +++ b/appsec/tests/extension/module_order_opcache_php85.phpt @@ -0,0 +1,21 @@ +--TEST-- +Verify ddappsec is always in the module registry after ddtrace when opcache is present +--SKIPIF-- + +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +ddtrace +ddappsec diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index 0547b8e6a8f..41a2edf02fd 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -1,46 +1,54 @@ import java.nio.file.Files import java.nio.file.Path +import javax.inject.Inject +import org.gradle.process.ExecOperations + +interface InjectedExecOps { + @Inject ExecOperations getExecOps() +} plugins { id 'groovy' id 'application' - id 'de.undercouch.download' version '4.0.4' + id 'de.undercouch.download' version '5.6.0' } repositories { mavenCentral() } -sourceCompatibility = '11' -targetCompatibility = '11' +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } dependencies { - implementation 'ch.qos.logback:logback-classic:1.5.6' - implementation 'org.slf4j:jul-to-slf4j:1.7.36' + implementation 'ch.qos.logback:logback-classic:1.5.18' + implementation 'org.slf4j:jul-to-slf4j:2.0.17' - implementation 'org.apache.groovy:groovy:4.0.21' - implementation 'org.apache.groovy:groovy-json:4.0.21' - implementation 'com.google.guava:guava:33.2.1-jre' - implementation 'org.msgpack:msgpack-core:0.9.8' - implementation 'io.javalin:javalin:6.1.4' + implementation 'org.apache.groovy:groovy:4.0.29' + implementation 'org.apache.groovy:groovy-json:4.0.29' + implementation 'com.google.guava:guava:33.4.8-jre' + implementation 'org.msgpack:msgpack-core:0.9.9' + implementation 'io.javalin:javalin:6.7.0' - implementation platform('org.testcontainers:testcontainers-bom:1.19.8') + implementation platform('org.testcontainers:testcontainers-bom:1.21.3') implementation 'org.testcontainers:mysql' implementation "org.testcontainers:junit-jupiter" implementation 'com.flipkart.zjsonpatch:zjsonpatch:0.4.16' - implementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' - implementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' + implementation 'org.junit.jupiter:junit-jupiter-engine:5.12.2' + implementation 'org.junit.jupiter:junit-jupiter-params:5.12.2' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.28' + testRuntimeOnly 'com.mysql:mysql-connector-j:9.3.0' } test { } -tasks['test'].enabled(false) +tasks['test'].enabled = false ext.testMatrix = ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'].collectMany { [[it, 'release'], [it, 'debug'], [it, 'release-zts']] @@ -91,6 +99,7 @@ def createVolumeTask = { String volumeName -> return tasks[taskName] } tasks.register(taskName, Exec) { Exec it -> + def injected = project.objects.newInstance(InjectedExecOps) onlyIf { Process proc = ['docker', 'volume', 'inspect', volumeName].execute() proc.waitForOrKill(5_000) @@ -98,7 +107,7 @@ def createVolumeTask = { String volumeName -> } it.commandLine 'docker', 'volume', 'create', volumeName doLast { - exec { + injected.execOps.exec { commandLine 'docker', 'run', '--rm', '-v', "${volumeName}:/vol", "$dockerMirror/library/busybox", 'sh', '-c', "chown -R ${uuid} /vol" @@ -108,22 +117,24 @@ def createVolumeTask = { String volumeName -> } task downloadComposerOld(type: Download) { + def injected = project.objects.newInstance(InjectedExecOps) src 'https://getcomposer.org/download/latest-2.2.x/composer.phar' dest 'build/composer-2.2.x.phar' overwrite false doLast { - exec { + injected.execOps.exec { commandLine 'chmod', '+x', outputFiles.first().toString() } } } task downloadComposer(type: Download) { + def injected = project.objects.newInstance(InjectedExecOps) src 'https://getcomposer.org/download/2.8.6/composer.phar' dest 'build/composer-2.8.6.phar' overwrite false doLast { - exec { + injected.execOps.exec { commandLine 'chmod', '+x', outputFiles.first().toString() } } diff --git a/appsec/tests/integration/gradle/tag_mappings.gradle b/appsec/tests/integration/gradle/tag_mappings.gradle index 7699017db7b..b21812e2d05 100644 --- a/appsec/tests/integration/gradle/tag_mappings.gradle +++ b/appsec/tests/integration/gradle/tag_mappings.gradle @@ -125,16 +125,16 @@ ext.tag_mappings = [ 'apache2-mod-php-8.4-release': 'sha256:79c2d7dd843b18ce2c5f929e6300e9a1f64a5e00a7edbaeb9f75d8a60b2be3d2', 'php-7.0-release': 'sha256:627c4f460fa1c11f3f5da44d9361eba5de9f38134189ca05b3bfdb9979011e44', 'php-7.1-release-zts': 'sha256:d997b9f99c28967872bd0949f8572b089daaadb6ceaa1e856ccc76e07e2ba6b7', - 'php-8.5-release': 'sha256:8728b0dd4fe83b1b0ea6e8d129d685dbd46e93d97782421bfdd549b9ecc57e9f', - 'php-8.5-release-zts': 'sha256:7521a31e22c9363ac681b71cd058046ade967ff35210a15083fbb1cc342b4188', - 'php-8.5-debug': 'sha256:badd675d5b86c52ea0100784adca65d728e8731ceae9693869763171f5b363bd', - 'apache2-fpm-php-8.5-release-zts': 'sha256:73f65188ffdd1a8fa0d14c8209f8a741fdb20b98db41f2a839fd49d6f567af45', - 'apache2-fpm-php-8.5-release': 'sha256:0897c1b7f60aa2424c4c893d89fffff6258c8cd798056f8f5efdd6b4b0e65ab3', - 'apache2-fpm-php-8.5-debug': 'sha256:229af7b98d64bb48438ca2ddaf3451da0e344649e01e0bdd15f9d03edda97e63', - 'apache2-mod-php-8.5-release-zts': 'sha256:c251da953a25e265551659a91798911948f882a959e9c926581824ca24618539', - 'apache2-mod-php-8.5-release': 'sha256:7be04bc4f4858dbfae4367c201e3e171ce085b14049bc00b5032fd32784de7da', - 'apache2-mod-php-8.5-debug': 'sha256:546eda07534d0c0221fc6636ff54ac102db36d6201a59621651a4f059611e1ff', - 'nginx-fpm-php-8.5-release-zts': 'sha256:0fdcee4b7c84f764789e825796115687843b72f667e73ddbb629a212a34333bd', - 'nginx-fpm-php-8.5-release': 'sha256:07573dae4a904c764ee8de036a62199a9f9dc106664b8e9bdb477c1391c74ca1', - 'nginx-fpm-php-8.5-debug': 'sha256:35ab8b5b90fcc43418a621a5f335fd724d41953cfca876aff4cce0c2cca02413' + 'php-8.5-release': 'sha256:2d3f721958476ac0837ae016d77a1fc796e48e0384e62721a6924c2fab204a82', + 'php-8.5-release-zts': 'sha256:b2096ab1477c3d90a53e62112774c525324ab2debc1cbfecc2cdd4c0a3599dc3', + 'php-8.5-debug': 'sha256:61a6a0d0fba6ad51dc39b3c8669e33498f78350e539a1ff7594d6a01e7ebdd0d', + 'apache2-fpm-php-8.5-release-zts': 'sha256:3434bfc2dee477bbf34c454b8b1fbb92a5dbdac7fc543e5d7a8714907493dea6', + 'apache2-fpm-php-8.5-release': 'sha256:f9e18353afcad74db9f56b4e46a8ffe9d628ac12f6fd491f525626d0e25429cb', + 'apache2-fpm-php-8.5-debug': 'sha256:86c7f37cb46200dbc73c6863a2255de7ed88fd01177cbaef69fa206719ba904c', + 'apache2-mod-php-8.5-release-zts': 'sha256:3b5132226a4f07628161f5a7a784b46426004c799f6a9e03d5397490b479d639', + 'apache2-mod-php-8.5-release': 'sha256:f6b156aaed21db2870cf1d452de1f0811d221b4ef250c777fecf2e96f6c9a161', + 'apache2-mod-php-8.5-debug': 'sha256:1ce0570b1e8fba34db0e4ec0f18134fb30c1d50ba795b6f42f0cbe3f19c7a68f', + 'nginx-fpm-php-8.5-release-zts': 'sha256:527f2ba273685ff7e3f72a74076fd6d99cbc87d51fb3485fef4dc80a0cfddeee', + 'nginx-fpm-php-8.5-release': 'sha256:fc2eea1ed06dfcf8102bdbfb2596f9a32bbe6493ca83d53370e1438289caa6aa', + 'nginx-fpm-php-8.5-debug': 'sha256:915d0155897e4f42cf1867b93edbb928afcc1c5b3bd3eccdb2d56ac50510cd82' ] diff --git a/appsec/tests/integration/gradle/wrapper/gradle-wrapper.jar b/appsec/tests/integration/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae..d64cd491770 100644 Binary files a/appsec/tests/integration/gradle/wrapper/gradle-wrapper.jar and b/appsec/tests/integration/gradle/wrapper/gradle-wrapper.jar differ diff --git a/appsec/tests/integration/gradle/wrapper/gradle-wrapper.properties b/appsec/tests/integration/gradle/wrapper/gradle-wrapper.properties index a5952066425..23449a2b543 100644 --- a/appsec/tests/integration/gradle/wrapper/gradle-wrapper.properties +++ b/appsec/tests/integration/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/appsec/tests/integration/gradlew b/appsec/tests/integration/gradlew index 1b6c787337f..1aa94a42690 100755 --- a/appsec/tests/integration/gradlew +++ b/appsec/tests/integration/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +214,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/appsec/tests/integration/gradlew.bat b/appsec/tests/integration/gradlew.bat index 107acd32c4e..93e3f59f135 100644 --- a/appsec/tests/integration/gradlew.bat +++ b/appsec/tests/integration/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/appsec/tests/integration/src/docker/php/build_dev_php.sh b/appsec/tests/integration/src/docker/php/build_dev_php.sh index 87d0c785cd4..be5da80a8f7 100755 --- a/appsec/tests/integration/src/docker/php/build_dev_php.sh +++ b/appsec/tests/integration/src/docker/php/build_dev_php.sh @@ -36,8 +36,6 @@ function download_php { local download_url if [[ $version_id -lt 50400 ]]; then download_url="http://museum.php.net/php5/php-${version}.tar.gz" - elif [[ $version_id -ge 80500 ]]; then - download_url="https://downloads.php.net/~daniels/php-8.5.0RC3.tar.gz" else download_url="https://www.php.net/distributions/php-${version}.tar.gz" fi @@ -109,7 +107,7 @@ function get_xdebug_version { elif [[ $version_id -lt 80400 ]]; then echo '3.3.2' elif [[ $version_id -ge 80400 ]]; then - echo '3.4.0' + echo '3.5.0' fi } @@ -199,7 +197,6 @@ function build_php { --enable-filter --enable-intl=shared --enable-mbstring=shared - --enable-opcache=shared "--enable-pdo=$([[ $(uname -o) != Darwin ]] && echo shared)" --enable-phar=shared --enable-xml @@ -226,6 +223,10 @@ function build_php { options+=(--enable-json) # not shared to make it consistent with php 8 fi + if [[ $version_id -lt 85000 ]]; then + options+=(--enable-opcache=shared) + fi + if [[ $minimal -eq 0 ]]; then local mysql_config= mysql_prefix= if contains_element nomysqlnd "${variants[@]}"; then