From 727812d6216c59946878fc53b62e4d8afd433b07 Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Tue, 22 Oct 2024 08:55:04 -0600 Subject: [PATCH 1/4] chore(agent): bump to 11.4 (#979) --- VERSION | 2 +- axiom/nr_version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index f628d2eaf..72773deb8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.3.0 +11.4.0 diff --git a/axiom/nr_version.c b/axiom/nr_version.c index 2c950be4f..5f09c86a7 100644 --- a/axiom/nr_version.c +++ b/axiom/nr_version.c @@ -23,7 +23,6 @@ /* * Current version naming scheme is gemstones * - * echinacea 03Oct2022 (10.2) * freesia 03Nov2022 (10.3) * goldenrod 12Dec2022 (10.4) * hydrangea 18Jan2023 (10.5) @@ -47,8 +46,9 @@ * zinnia 30Jul2024 (11.0) * amethyst 26Aug2024 (11.1) * bowenite 30Sep2024 (11.2) + * corundum 21Oct2024 (11.3) */ -#define NR_CODENAME "corundum" +#define NR_CODENAME "diamond" const char* nr_version(void) { return NR_STR2(NR_VERSION); From 467f79e4bce6be3caef6d129f4f44d3b6ad5c581 Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Tue, 12 Nov 2024 15:10:20 -0500 Subject: [PATCH 2/4] chore: enhance dockerized development environment (#977) 1. Add an option to control which PHP version is to be used in devenv service via PHP env var passed at the time when dev-shell is started (or any dev-* target is build). 2. Simplify `composer` and `go` install. 3. Fix `devenv` service image build for PHPs 7.2 and 7.3. 4. Apply `Dockerfile` best practices --- Makefile | 33 +++++---- docker-compose.yaml | 3 +- docs/dev_environment.md | 47 +++++++++---- files/Dockerfile | 152 +++++++++++++++++----------------------- files/set_path.sh | 5 -- 5 files changed, 116 insertions(+), 124 deletions(-) diff --git a/Makefile b/Makefile index 0e95237d5..11a871312 100644 --- a/Makefile +++ b/Makefile @@ -475,25 +475,28 @@ test-services-stop: # Docker Development Environment # -dev-shell: - docker compose --profile dev up --build --remove-orphans -d - docker exec -it agent-devenv bash -c "sh files/set_path.sh ; bash" +devenv-image: + @docker compose --profile dev build devenv -dev-build: - docker compose --profile dev up --build --remove-orphans -d - docker exec -it agent-devenv bash -c "sh files/set_path.sh ; make -j4 all" +dev-shell: devenv-image + docker compose --profile dev up --pull missing --remove-orphans -d + docker compose exec -it devenv bash -c "sh files/set_path.sh ; bash" -dev-unit-tests: - docker compose --profile dev up --build --remove-orphans -d - docker exec -it agent-devenv bash -c "sh files/set_path.sh ; make -j4 valgrind" +dev-build: devenv-image + docker compose --profile dev up --pull missing --remove-orphans -d + docker compose exec -it devenv bash -c "sh files/set_path.sh ; make -j4 all" -dev-integration-tests: - docker compose --profile dev up --build --remove-orphans -d - docker exec -it agent-devenv bash -c "sh files/set_path.sh ; ./bin/integration_runner -agent ./agent/.libs/newrelic.so" +dev-unit-tests: devenv-image + docker compose --profile dev up --pull missing --remove-orphans -d + docker compose exec -it devenv bash -c "sh files/set_path.sh ; make -j4 valgrind" -dev-all: - docker compose --profile dev up --build --remove-orphans -d - docker exec -it agent-devenv bash -c "sh files/set_path.sh ; make -j4 all valgrind; ./bin/integration_runner -agent ./agent/.libs/newrelic.so" +dev-integration-tests: devenv-image + docker compose --profile dev up --pull missing --remove-orphans -d + docker compose exec -it devenv bash -c "sh files/set_path.sh ; ./bin/integration_runner -agent ./agent/.libs/newrelic.so" + +dev-all: devenv-image + docker compose --profile dev up --pull missing --remove-orphans -d + docker compose exec -it devenv bash -c "sh files/set_path.sh ; make -j4 all valgrind; ./bin/integration_runner -agent ./agent/.libs/newrelic.so" dev-stop: docker compose --profile dev stop diff --git a/docker-compose.yaml b/docker-compose.yaml index 2ea1d11d3..315bedbfa 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,7 +2,6 @@ # Copyright 2021 New Relic Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # -version: '3.8' services: # The Database mysqldb: @@ -79,6 +78,8 @@ services: build: context: . dockerfile: files/Dockerfile + args: + PHP_VER: ${PHP:-8.3} user: ${UID}:${GID} environment: MEMCACHE_HOST: memcached diff --git a/docs/dev_environment.md b/docs/dev_environment.md index 7e6940d05..15948fc9f 100644 --- a/docs/dev_environment.md +++ b/docs/dev_environment.md @@ -6,19 +6,38 @@ The dockerized development environment prototype allows contributors to both dev docker-compose spins up `mysql` and `redis` and other databases in separate containers. -Two environment variables to note: -`NEWRELIC_LICENSE_KEY` is required to run the integration tests and should be set to your NR license key. -If your collector isn’t the default (collector.newrelic.com), set the `NEWRELIC_COLLECTOR_HOST` to the appropriate value. +## Prerequisites -PHP_VER can also be set to vary the PHP version being used. +### 1. Docker Compose -Set all environment variables prior to running the development environment. +Dockerized development environment for the New Relic PHP Agent uses following Docker Compose services as a runtime platform. So you need `docker` with `docker compose` installed. + +### 2. Environment variables + +Dockerized development environment for the New Relic PHP Agent needs a valid license key available in `NEW_RELIC_LICENSE_KEY` environment variable. +This environment variable must be set prior to starting Dockerized development environment for the New Relic PHP Agent. The easiest way to set +`NEW_RELIC_LICENSE_KEY` environment variable is via `.env` file. Simply create `.env` file in the top level directory, and add definition +of `NEW_RELIC_LICENSE_KEY` environment variable there, e.g.: +``` +NEW_RELIC_LICENSE_KEY=... +``` + +The second, optional environment variable, that controls PHP version in Dockerized development environment for the New Relic PHP Agent is `PHP`. `PHP` defaults to latest PHP supported by the agent. +This environment variable can be provided at the time when Dockerized development environment for the New Relic PHP Agent is started, e.g.: +``` +make dev-shell PHP=8.2 +``` ## Options for using the environment -## With a shell environment +### With a shell environment + +To start the dev environment type `make dev-shell`. This will spin up `devenv` service in `agent-devenv` container, with: + - latest PHP supported by the agent (this can be overriden with `PHP` environment variable like this: `make dev-shell PHP=8.2`) + - all the tools needed to build the agent + - all the tools needed to run unit tests + - all the tools and supporting services to run integration tests -To start the dev environment type `make dev-shell`. This will create a set of docker containers. A prompt will open and you’ll be able to compile and run all `make` commands right away with no additional setup (for example: `make -j4 all` or `make -j4 valgrind` or `make -j4 run_tests`). After compiling the agent, the integration tests can be run using the `integration_runner`. @@ -31,27 +50,27 @@ To end the session type `exit`. You can run `make dev-stop` to stop the docker- In the shell, you can run all `make` commands as you normally would. -## Build only +### Build only `make dev-build` -## Unit Tests only +### Unit Tests only `make dev-unit tests` -## Integration Tests only +### Integration Tests only `make dev-integration-tests` -## Build and test all +### Build and test all `make dev-all` -## Stop all containers +### Stop all containers `make dev-stop` -# Next steps and issues +## Next steps and issues -## There is possibly some incompatibility with mysql in the main build container as one of the mysql unit tests fails. Unless this is resolved, It might make sense at a future point to have the integration tests run from a different container than the build container. +### There is possibly some incompatibility with mysql in the main build container as one of the mysql unit tests fails. Unless this is resolved, It might make sense at a future point to have the integration tests run from a different container than the build container. diff --git a/files/Dockerfile b/files/Dockerfile index 4b50bebb8..2b327d439 100644 --- a/files/Dockerfile +++ b/files/Dockerfile @@ -14,95 +14,65 @@ FROM php:${PHP_VER:-8.3} RUN docker-php-source extract ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -RUN apt-get install -y build-essential - -# -# PHP dependencies -# -RUN apt-get update \ - && apt-get -y install gcc git netcat-openbsd \ - libpcre3 libpcre3-dev psmisc automake libtool \ - insserv procps vim ${PHP_USER_SPECIFIED_PACKAGES} \ - zlib1g-dev libmcrypt-dev +RUN apt-get update && apt-get install -y --no-install-recommends \ +# Install build entrypoint - make: + make \ +# Makefile's default shell: + bash \ +# git; techincally not needed to build agent but is required for successfull +# processing of top level Makefile: +# - make/version.mk if GIT_COMMIT is not defined, git is used to compute it: + git \ +# The following are required to build PHP extension: + $PHPIZE_DEPS \ +# valgrind, awk; required for agent-valgrind and axiom-valgrind targets + valgrind gawk \ +# Other useful developer tools: + vim lcov gdb strace ccache procps psmisc curl wget bzip2 zip unzip perl sqlite3 openssl \ +# Other build dependencies: + argon2 \ + automake \ + autotools-dev \ + dnsutils \ + gyp \ + insserv \ + libc6 libc6-dev libc6-dbg \ + libcurl4-openssl-dev \ + libedit-dev \ + libghc-argon2-dev \ + libgtest-dev \ + libmcrypt-dev \ + libonig-dev \ + libpcre3 libpcre3-dev \ + libreadline-dev \ + libssl-dev \ + libsqlite3-dev \ + libtool \ + libxml2 libxml2-dev \ + locales \ + locales-all \ + netcat-openbsd \ + python3-yaml \ + ${PHP_USER_SPECIFIED_PACKAGES} \ + zlib1g-dev # pgsql extension -RUN apt-get install -y libpq-dev +RUN apt-get install -y --no-install-recommends libpq-dev RUN docker-php-ext-install pgsql -# -# Other tools -# -RUN apt-get install -y gdb valgrind libcurl4-openssl-dev pkg-config libpq-dev libedit-dev libreadline-dev git - -# -# Install other packages. -# -RUN apt-get update && apt-get install -y \ - autoconf \ - autotools-dev \ - build-essential \ - bzip2 \ - ccache \ - curl \ - dnsutils \ - git \ - gyp \ - lcov \ - libc6 \ - libc6-dbg \ - libc6-dev \ - libgtest-dev \ - libtool \ - locales \ - locales-all \ - make \ - perl \ - strace \ - python-dev-is-python3 \ - python3-yaml \ - sqlite3 \ - libsqlite3-dev \ - openssl \ - libxml2 \ - libxml2-dev \ - libonig-dev \ - libssl-dev \ - unzip \ - wget \ - zip && apt-get clean - -# -# Download and install Go -# -RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && \ - wget https://go.dev/dl/go1.21.1.linux-${arch}.tar.gz -O- | tar -C /usr/local -zxvf -; - -RUN ln -s /usr/local/go/bin/go /usr/bin/go +# install latest go to work with the daemon +COPY --from=golang /usr/local/go /usr/local/go +ENV PATH /usr/local/go/bin:$PATH # -# If the debian version is jessie, don't install argon2 +# If the debian version is buster, don't install python-dev-is-python3 # -RUN if [ -z "$(grep '^8\.' /etc/debian_version)" ]; then \ - apt-get install -y argon2 libghc-argon2-dev; \ +RUN if [ -z "$(grep '^10\.' /etc/debian_version)" ]; then \ + apt-get install -y --no-install-recommends python-dev-is-python3; \ fi # install composer -WORKDIR /usr/src - -# based on https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md -RUN \ - EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" \ - && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ - && ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" \ - && if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; \ - then \ - >&2 echo 'ERROR: Invalid installer checksum'; \ - rm composer-setup.php; \ - exit 1; \ - fi \ - && php composer-setup.php \ - && php -r "unlink('composer-setup.php');" +COPY --from=composer ["/usr/bin/composer", "/usr/bin/composer"] # # The explain plan in the sql tests contain partition/filtered properties @@ -121,8 +91,16 @@ RUN \ # the mysql tests a separate machine running mysql server 5.6 is required. RUN docker-php-ext-install pdo pdo_mysql -# redis -RUN pecl install redis && docker-php-ext-enable redis +# install redis extension required by test_redis: +RUN \ + php_cmp=$(php -r "echo version_compare(PHP_VERSION, '7.4.0', '>=');"); \ + if [ "$php_cmp" = 1 ]; then \ + # install latest redis for PHPs >= 7.4 + echo 'no' | pecl install redis; \ + else \ + # install redis-4.3.0 - last one with support for php 7.2 + echo 'no' | pecl install redis-4.3.0; \ + fi && docker-php-ext-enable redis # memcache # Pre 8.0 requires 4.0.5.2 @@ -135,7 +113,7 @@ RUN \ fi # memcached -RUN apt-get install -y libmemcached-dev +RUN apt-get install -y --no-install-recommends libmemcached-dev RUN pecl install memcached && docker-php-ext-enable memcached # uopz @@ -152,13 +130,9 @@ RUN \ # install predis # installation will be in /usr/src/vendor/predis/predis # which is value which should be used for PREDIS_HOME -RUN php composer.phar require "predis/predis" -RUN php composer.phar update - -# -# install composer and make executable so it can be used in dev env -# -RUN cp composer.phar /usr/local/bin/composer && chmod +x /usr/local/bin/composer +WORKDIR /usr/src +RUN composer require "predis/predis" +RUN composer update # # These args need to be repeated so we can propagate the VARS within this build context. diff --git a/files/set_path.sh b/files/set_path.sh index 3a5cc965c..0b9e88c48 100644 --- a/files/set_path.sh +++ b/files/set_path.sh @@ -17,11 +17,6 @@ case ":$PATH:" in *) PATH=/usr/local/bin:$PATH esac -case ":$PATH:" in - *:/usr/local/go/bin:*) ;; - *) PATH=/usr/local/go/bin:$PATH -esac - export PATH From bae63754d546cbde31e2b7edff2fa66e6dc75dce Mon Sep 17 00:00:00 2001 From: Hitesh Ahuja <108540135+hahuja2@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:31:21 -0800 Subject: [PATCH 3/4] feat(agent): add support for CakePHP 4.x and 5.x (#983) This PR does the following: - Modifies how the action name is retrieved, so that it is compatible with CakePHP 4.x and 5.x - Adds correct detection file for CakePHP 4.x and 5.x - Adds call to `nr_txn_suggest_package_supportability_metric` in `nr_cakephp_enable` --------- Co-authored-by: Michal Nowacki --- agent/fw_cakephp.c | 282 ++++++++++---------------------------------- agent/fw_hooks.h | 8 +- agent/php_execute.c | 13 +- 3 files changed, 65 insertions(+), 238 deletions(-) diff --git a/agent/fw_cakephp.c b/agent/fw_cakephp.c index 1dfe88231..572f6f23f 100644 --- a/agent/fw_cakephp.c +++ b/agent/fw_cakephp.c @@ -4,6 +4,7 @@ */ #include "php_agent.h" +#include "php_error.h" #include "php_execute.h" #include "php_user_instrument.h" #include "php_wrapper.h" @@ -12,132 +13,14 @@ #include "util_logging.h" #include "util_memory.h" -nr_framework_classification_t nr_cakephp_special_1( - const char* filename TSRMLS_DC) { - NR_UNUSED_TSRMLS; - - if (nr_strcaseidx(filename, "cake/libs/object.php") >= 0) { - return FRAMEWORK_IS_SPECIAL; - } - - return FRAMEWORK_IS_NORMAL; -} - -nr_framework_classification_t nr_cakephp_special_2( - const char* filename TSRMLS_DC) { - NR_UNUSED_TSRMLS; - - if (nr_strcaseidx(filename, "cake/core/app.php") >= 0) { - return FRAMEWORK_IS_SPECIAL; - } - - return FRAMEWORK_IS_NORMAL; -} - -/* - * For CakePHP 1.2 and 1.3 (and possibly earlier versions too) we hook into - * Component::initialize(). This function takes a controller as a parameter - * and we look into the params array of that controller object, and pick up - * the controller and action out of that array. - * - * CakePHP 1.x is end-of-life and no longer supported by the agent. - * Cake PHP 1.x does not support PHP 8+ and this wrapper is not updated for OAPI - * compatibility. - * - */ -NR_PHP_WRAPPER(nr_cakephp_name_the_wt_pre20) { - zval* arg1 = 0; - zval* params; - zval* czval; - zval* azval; - char* controller = 0; - char* action = 0; - int clen = 0; - int alen = 0; - char* name; - - (void)wraprec; - NR_UNUSED_SPECIALFN; - - NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_CAKEPHP); - - arg1 = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); - if (!nr_php_is_zval_valid_object(arg1)) { - NR_PHP_WRAPPER_CALL; - goto end; - } - - NR_PHP_WRAPPER_CALL; - - params = nr_php_get_zval_object_property(arg1, "params" TSRMLS_CC); - if (0 == params) { - nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no params found in component"); - goto end; - } - - if (IS_ARRAY != Z_TYPE_P(params)) { - nrl_verbosedebug(NRL_FRAMEWORK, - "CakePHP: component params is not an array"); - goto end; - } - - czval = nr_php_get_zval_object_property(params, "controller" TSRMLS_CC); - if (0 == czval) { - nrl_verbosedebug(NRL_FRAMEWORK, - "CakePHP: no params['controller'] in component"); - } else { - clen = Z_STRLEN_P(czval); - controller = (char*)nr_alloca(clen + 1); - nr_strxcpy(controller, Z_STRVAL_P(czval), clen); - } - - azval = nr_php_get_zval_object_property(params, "action" TSRMLS_CC); - if (0 == azval) { - nrl_verbosedebug(NRL_FRAMEWORK, - "CakePHP: no params['action'] in component"); - } else { - alen = Z_STRLEN_P(azval); - action = (char*)nr_alloca(alen + 1); - nr_strxcpy(action, Z_STRVAL_P(azval), alen); - } - - if ((0 == clen) && (0 == alen)) { - nrl_verbosedebug(NRL_FRAMEWORK, - "CakePHP: nothing to call the transaction (yet?)"); - goto end; - } - - name = (char*)nr_alloca(alen + clen + 2); - if (clen) { - nr_strcpy(name, controller); - } - if (alen) { - if (clen) { - nr_strcat(name, "/"); - nr_strcat(name, action); - } else { - nr_strcpy(name, action); - } - } - - nr_txn_set_path("CakePHP", NRPRG(txn), name, NR_PATH_TYPE_ACTION, - NR_NOT_OK_TO_OVERWRITE); - -end: - nr_php_arg_release(&arg1); -} -NR_PHP_WRAPPER_END +#define PHP_PACKAGE_NAME "cakephp/cakephp" /* - * For CakePHP 2.0 and on, we do things a little differently as the params - * array doesn't exist in the component any more. Instead we hook the - * Controller's invokeAction method. This gets the request as a parameter - * and we get the action from the params array in that object. The - * controller object ($this) has a name, and that name is used (along - * with the word "Controller" appended which is what the CakePHP code does). - * - * CakePHP 2.x is end-of-life and in maintenance mode (critical bugfixes only). - * As such, functionality added in PHP 7.1+ is not well supported. + * For CakePHP 4.0 and on, we retrieve the current controller object + * and are able to extract the controller name from that. We then + * retrieve the request object from the controller and are able to + * extract the action name from that. We then concatenate the two + * strings to form the transaction name. * * txn naming scheme: * In this case, `nr_txn_set_path` is called after `NR_PHP_WRAPPER_CALL` with @@ -147,8 +30,7 @@ NR_PHP_WRAPPER_END * default way of calling the wrapped function in func_end. * */ -NR_PHP_WRAPPER(nr_cakephp_name_the_wt_2) { - zval* arg1 = 0; +NR_PHP_WRAPPER(nr_cakephp_name_the_wt_4) { zval* this_var = 0; zval* czval = 0; char* controller = 0; @@ -156,8 +38,9 @@ NR_PHP_WRAPPER(nr_cakephp_name_the_wt_2) { int clen = 0; int alen = 0; char* name = 0; - zval* params; - zval* azval; + zval* action_zval = NULL; + zval* request = NULL; + zval action_param; (void)wraprec; NR_UNUSED_SPECIALFN; @@ -193,37 +76,24 @@ NR_PHP_WRAPPER(nr_cakephp_name_the_wt_2) { } } - arg1 = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); - if (!nr_php_is_zval_valid_object(arg1)) { - NR_PHP_WRAPPER_CALL; - goto end; - } - NR_PHP_WRAPPER_CALL; - params = nr_php_get_zval_object_property(arg1, "params" TSRMLS_CC); - if (0 == params) { - nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no params found in request"); + request = nr_php_call(this_var, "getRequest"); + if (!nr_php_is_zval_valid_object(request)) { + nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no request found in controller"); goto end; } - if (IS_ARRAY != Z_TYPE_P(params)) { - nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: request params is not an array"); + nr_php_zval_str(&action_param, "action"); + action_zval = nr_php_call(request, "getParam", &action_param); + zval_dtor(&action_param); + if (!nr_php_is_zval_non_empty_string(action_zval)) { + nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no action param found in request"); goto end; - } - - azval = nr_php_get_zval_object_property(params, "action" TSRMLS_CC); - if (0 == azval) { - nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no params['action'] in request"); } else { - if (!nr_php_is_zval_valid_string(azval)) { - nrl_verbosedebug(NRL_FRAMEWORK, - "CakePHP: no string-valued params['action'] in request"); - } else { - alen = Z_STRLEN_P(azval); - action = (char*)nr_alloca(alen + 1); - nr_strxcpy(action, Z_STRVAL_P(azval), alen); - } + alen = Z_STRLEN_P(action_zval); + action = (char*)nr_alloca(alen + 1); + nr_strxcpy(action, Z_STRVAL_P(action_zval), alen); } if ((0 == clen) && (0 == alen)) { @@ -249,92 +119,64 @@ NR_PHP_WRAPPER(nr_cakephp_name_the_wt_2) { NR_NOT_OK_TO_OVERWRITE); end: - nr_php_arg_release(&arg1); nr_php_scope_release(&this_var); + nr_php_zval_free(&request); + nr_php_zval_free(&action_zval); } NR_PHP_WRAPPER_END /* - * CakePHP 1.2, 1.3 - * - * Dispatch::cakeError will be called if there is a problem during dispatch - * (action or controller not found). - * - * CakePHP 1.x is end-of-life and no longer supported by the agent. - * Cake PHP 1.x does not support PHP 8+ and this wrapper is not updated for OAPI - * compatibility. + * CakePHP 4.0+ * + * Report errors and exceptions caught by CakePHP's error handler. */ -NR_PHP_WRAPPER(nr_cakephp_problem_1) { - const char* name = "Dispatcher::cakeError"; +NR_PHP_WRAPPER(nr_cakephp_error_handler_wrapper) { + zval* exception = NULL; + char* request_uri = nr_php_get_server_global("REQUEST_URI"); - (void)wraprec; NR_UNUSED_SPECIALFN; + (void)wraprec; NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_CAKEPHP); - nr_txn_set_path("CakePHP", NRPRG(txn), name, NR_PATH_TYPE_ACTION, - NR_NOT_OK_TO_OVERWRITE); - - NR_PHP_WRAPPER_CALL; -} -NR_PHP_WRAPPER_END - -/* - * CakePHP 2.0+ - * - * If the action or controller is not found during the dispatch process, the - * appropriate Exception will be created and thrown. We wrap the CakeException - * constructor instead of the Exception handler, since CakePHP allows for the - * handler to be completely replaced. - * - * CakePHP 2.x is end-of-life and in maintenance mode (critical bugfixes only). - * As such, functionality added in PHP 7.1+ is not well supported. - * - * txn naming scheme: - * In this case, `nr_txn_set_path` is called before `NR_PHP_WRAPPER_CALL` with - * `NR_NOT_OK_TO_OVERWRITE` and as this corresponds to calling the wrapped - * function in func_begin it needs to be explicitly set as a before_callback to - * ensure OAPI compatibility. This entails that the first wrapped call gets to - * name the txn. - */ -NR_PHP_WRAPPER(nr_cakephp_problem_2) { - const char* name = "Exception"; - - (void)wraprec; - NR_UNUSED_SPECIALFN; + exception = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS); + if (!nr_php_is_zval_valid_object(exception)) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s: exception is NULL or not an object", + __func__); + goto end; + } - NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_CAKEPHP); + if (NR_SUCCESS + != nr_php_error_record_exception( + NRPRG(txn), exception, nr_php_error_get_priority(E_ERROR), true, + "Uncaught exception ", &NRPRG(exception_filters))) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s: unable to record exception", __func__); + } - nr_txn_set_path("CakePHP", NRPRG(txn), name, NR_PATH_TYPE_ACTION, - NR_NOT_OK_TO_OVERWRITE); + if (NULL != request_uri) { + nr_txn_set_path("CakePHP Exception", NRPRG(txn), request_uri, NR_PATH_TYPE_URI, + NR_OK_TO_OVERWRITE); + } else { + nrl_verbosedebug(NRL_FRAMEWORK, "%s: request uri is NULL", __func__); + } - NR_PHP_WRAPPER_CALL; +end: + nr_php_arg_release(&exception); + nr_free(request_uri); } NR_PHP_WRAPPER_END /* - * Enable CakePHP 1.2, 1.3 - */ -void nr_cakephp_enable_1(TSRMLS_D) { - nr_php_wrap_user_function(NR_PSTR("Component::initialize"), - nr_cakephp_name_the_wt_pre20 TSRMLS_CC); - nr_php_wrap_user_function(NR_PSTR("Dispatcher::cakeError"), - nr_cakephp_problem_1 TSRMLS_CC); -} - -/* - * Enable CakePHP 2.0+ + * Enable CakePHP 4.0+ */ -void nr_cakephp_enable_2(TSRMLS_D) { - nr_php_wrap_user_function(NR_PSTR("Controller::invokeAction"), - nr_cakephp_name_the_wt_2 TSRMLS_CC); -#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ - && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("CakeException::__construct"), nr_cakephp_problem_2, NULL, NULL); -#else - nr_php_wrap_user_function(NR_PSTR("CakeException::__construct"), - nr_cakephp_problem_2 TSRMLS_CC); -#endif +void nr_cakephp_enable(TSRMLS_D) { + nr_php_wrap_user_function( + NR_PSTR("Cake\\Controller\\Controller::invokeAction"), + nr_cakephp_name_the_wt_4); + nr_php_wrap_user_function( + NR_PSTR( + "Cake\\Error\\Middleware\\ErrorHandlerMiddleware::handleException"), + nr_cakephp_error_handler_wrapper); + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/fw_hooks.h b/agent/fw_hooks.h index c4500aeb2..711c3b618 100644 --- a/agent/fw_hooks.h +++ b/agent/fw_hooks.h @@ -12,13 +12,7 @@ */ #include "php_execute.h" -extern void nr_cakephp_enable_1(TSRMLS_D); -extern void nr_cakephp_enable_2(TSRMLS_D); -extern nr_framework_classification_t nr_cakephp_special_1( - const char* filename TSRMLS_DC); -extern nr_framework_classification_t nr_cakephp_special_2( - const char* filename TSRMLS_DC); - +extern void nr_cakephp_enable(TSRMLS_D); extern void nr_codeigniter_enable(TSRMLS_D); extern int nr_drupal_is_framework(nrframework_t fw); diff --git a/agent/php_execute.c b/agent/php_execute.c index e8e0db9ca..058fea30c 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -331,15 +331,8 @@ typedef struct _nr_framework_table_t { */ // clang-format: off static const nr_framework_table_t all_frameworks[] = { - /* - * Watch out: - * cake1.2 and cake1.3 use a subdirectory named 'cake' (lower case) - * cake2.0 and on use a subdirectory named 'Cake' (upper case file name) - */ - {"CakePHP", "cakephp", NR_PSTR("cake/libs/object.php"), nr_cakephp_special_1, - nr_cakephp_enable_1, NR_FW_CAKEPHP}, - {"CakePHP", "cakephp", NR_PSTR("cake/core/app.php"), nr_cakephp_special_2, - nr_cakephp_enable_2, NR_FW_CAKEPHP}, + {"CakePHP", "cakephp", NR_PSTR("cakephp/src/core/functions.php"), 0, + nr_cakephp_enable, NR_FW_CAKEPHP}, /* * Watch out: frameworks or CMS' build on top of CodeIgniter might not get @@ -522,8 +515,6 @@ static nr_library_table_t libraries[] = { * with other frameworks or even without a framework at all. */ {"Laminas_Http", NR_PSTR("laminas-http/src/client.php"), nr_laminas_http_enable}, - - {"CakePHP3", NR_PSTR("cakephp/src/core/functions.php"), NULL}, }; // clang-format: on From 99979665f938f8999315cc927dd9868244df8aa5 Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Thu, 5 Dec 2024 11:07:09 -0500 Subject: [PATCH 4/4] ci: add security scan with trivy (#985) Security scan with trivy will be run on push to main and dev, pull request, and weekly. trivy will skip scanning vendor subdirectory because the agent is not using code from that directory. --- .../actions/ubuntu16-build-action/Dockerfile | 21 -------- .../actions/ubuntu16-build-action/action.yml | 5 -- .../actions/ubuntu16-build-action/build.sh | 4 -- .../actions/ubuntu18-build-action/Dockerfile | 21 -------- .../actions/ubuntu18-build-action/action.yml | 5 -- .../actions/ubuntu18-build-action/build.sh | 4 -- .../actions/ubuntu20-build-action/Dockerfile | 21 -------- .../actions/ubuntu20-build-action/action.yml | 5 -- .../actions/ubuntu20-build-action/build.sh | 4 -- .github/workflows/security-scan.yml | 48 +++++++++++++++++++ .trivyignore | 2 + docker-compose.yaml | 1 - files/Dockerfile | 5 ++ trivy.yaml | 18 +++++++ 14 files changed, 73 insertions(+), 91 deletions(-) delete mode 100644 .github/actions/ubuntu16-build-action/Dockerfile delete mode 100644 .github/actions/ubuntu16-build-action/action.yml delete mode 100755 .github/actions/ubuntu16-build-action/build.sh delete mode 100644 .github/actions/ubuntu18-build-action/Dockerfile delete mode 100644 .github/actions/ubuntu18-build-action/action.yml delete mode 100755 .github/actions/ubuntu18-build-action/build.sh delete mode 100644 .github/actions/ubuntu20-build-action/Dockerfile delete mode 100644 .github/actions/ubuntu20-build-action/action.yml delete mode 100755 .github/actions/ubuntu20-build-action/build.sh create mode 100644 .github/workflows/security-scan.yml create mode 100644 .trivyignore create mode 100644 trivy.yaml diff --git a/.github/actions/ubuntu16-build-action/Dockerfile b/.github/actions/ubuntu16-build-action/Dockerfile deleted file mode 100644 index c43e2c564..000000000 --- a/.github/actions/ubuntu16-build-action/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ubuntu:16.04 - -# Uncomment deb-src lines for all enabled repos. First part of single-quoted -# string (up the the !) is the pattern of the lines that will be ignored. -# Needed for apt-get build-dep call later in script -RUN sed -Ei '/.*partner/! s/^# (deb-src .*)/\1/g' /etc/apt/sources.list - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -RUN apt-get install -y build-essential - -# PHP dependencies -RUN apt-get build-dep -y php7.0 -RUN apt-get install -y libmysqlclient-dev php-dev libmcrypt-dev libphp7.0-embed - -# Other tools -RUN apt-get install -y curl gdb valgrind libcurl4-openssl-dev pkg-config postgresql python-psycopg2 libpq-dev libedit-dev libreadline-dev git - -COPY build.sh /build.sh - -ENTRYPOINT ["/build.sh"] diff --git a/.github/actions/ubuntu16-build-action/action.yml b/.github/actions/ubuntu16-build-action/action.yml deleted file mode 100644 index 2a01f96da..000000000 --- a/.github/actions/ubuntu16-build-action/action.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: ubuntu16-build-action -description: 'Build the agent on ubuntu 16' -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/actions/ubuntu16-build-action/build.sh b/.github/actions/ubuntu16-build-action/build.sh deleted file mode 100755 index 079de1ba1..000000000 --- a/.github/actions/ubuntu16-build-action/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -make -j $(nproc) all -make -j $(nproc) run_tests - diff --git a/.github/actions/ubuntu18-build-action/Dockerfile b/.github/actions/ubuntu18-build-action/Dockerfile deleted file mode 100644 index b9bb190db..000000000 --- a/.github/actions/ubuntu18-build-action/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ubuntu:18.04 - -# Uncomment deb-src lines for all enabled repos. First part of single-quoted -# string (up the the !) is the pattern of the lines that will be ignored. -# Needed for apt-get build-dep call later in script -RUN sed -Ei '/.*partner/! s/^# (deb-src .*)/\1/g' /etc/apt/sources.list - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -RUN apt-get install -y build-essential - -# PHP dependencies -RUN apt-get build-dep -y php7.2 -RUN apt-get install -y libmysqlclient-dev php-dev libmcrypt-dev libphp7.2-embed - -# Other tools -RUN apt-get install -y curl gdb valgrind libcurl4-openssl-dev pkg-config postgresql python-psycopg2 libpq-dev libedit-dev libreadline-dev git - -COPY build.sh /build.sh - -ENTRYPOINT ["/build.sh"] diff --git a/.github/actions/ubuntu18-build-action/action.yml b/.github/actions/ubuntu18-build-action/action.yml deleted file mode 100644 index 5b8fc28d6..000000000 --- a/.github/actions/ubuntu18-build-action/action.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: ubuntu18-build-action -description: 'Build the agent on ubuntu 18' -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/actions/ubuntu18-build-action/build.sh b/.github/actions/ubuntu18-build-action/build.sh deleted file mode 100755 index 079de1ba1..000000000 --- a/.github/actions/ubuntu18-build-action/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -make -j $(nproc) all -make -j $(nproc) run_tests - diff --git a/.github/actions/ubuntu20-build-action/Dockerfile b/.github/actions/ubuntu20-build-action/Dockerfile deleted file mode 100644 index bbd6fe406..000000000 --- a/.github/actions/ubuntu20-build-action/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ubuntu:20.04 - -# Uncomment deb-src lines for all enabled repos. First part of single-quoted -# string (up the the !) is the pattern of the lines that will be ignored. -# Needed for apt-get build-dep call later in script -RUN sed -Ei '/.*partner/! s/^# (deb-src .*)/\1/g' /etc/apt/sources.list - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -RUN apt-get install -y build-essential - -# PHP dependencies -RUN apt-get build-dep -y php7.4 -RUN apt-get install -y libmysqlclient-dev php-dev libmcrypt-dev libphp7.4-embed - -# Other tools -RUN apt-get install -y curl gdb valgrind libcurl4-openssl-dev pkg-config postgresql libpq-dev libedit-dev libreadline-dev git - -COPY build.sh /build.sh - -ENTRYPOINT ["/build.sh"] diff --git a/.github/actions/ubuntu20-build-action/action.yml b/.github/actions/ubuntu20-build-action/action.yml deleted file mode 100644 index d42d71cf9..000000000 --- a/.github/actions/ubuntu20-build-action/action.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: ubuntu20-build-action -description: 'Build the agent on ubuntu 20' -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/actions/ubuntu20-build-action/build.sh b/.github/actions/ubuntu20-build-action/build.sh deleted file mode 100755 index 079de1ba1..000000000 --- a/.github/actions/ubuntu20-build-action/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -make -j $(nproc) all -make -j $(nproc) run_tests - diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml new file mode 100644 index 000000000..98e8039ba --- /dev/null +++ b/.github/workflows/security-scan.yml @@ -0,0 +1,48 @@ +name: Security scan +on: + push: + branches: + - main + - dev + pull_request: + schedule: + - cron: '0 0 * * 0' # Every Sunday at 12:00 AM + +jobs: + trivy-scan: + runs-on: ubuntu-latest + steps: + - name: Checkout newrelic-php-agent code + uses: actions/checkout@v4 + with: + path: php-agent + - name: Run Trivy in table mode + # Table output is only useful when running on a pull request or push. + if: contains(fromJSON('["push", "pull_request"]'), github.event_name) + uses: aquasecurity/trivy-action@0.28.0 + with: + scan-type: fs + scan-ref: ./php-agent + trivy-config: ./php-agent/trivy.yaml + trivyignores: ./php-agent/.trivyignore + format: table + exit-code: 1 + + - name: Run Trivy in report mode + # Only generate sarif when running nightly on the dev branch. + if: ${{ github.event_name == 'schedule' }} + uses: aquasecurity/trivy-action@0.28.0 + with: + scan-type: fs + scan-ref: ./php-agent + trivy-config: ./php-agent/trivy.yaml + trivyignores: ./php-agent/.trivyignore + format: sarif + output: trivy-results.sarif + + - name: Upload Trivy scan results to GitHub Security tab + # Only upload sarif when running nightly on the dev branch. + if: ${{ github.event_name == 'schedule' }} + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 000000000..01a5782ee --- /dev/null +++ b/.trivyignore @@ -0,0 +1,2 @@ +# Ignore missing HEALTHCHECK in Dockerfile - devenv service from files/Dockerfile doesn't need it: +AVD-DS-0026 diff --git a/docker-compose.yaml b/docker-compose.yaml index 315bedbfa..21044c7c7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -80,7 +80,6 @@ services: dockerfile: files/Dockerfile args: PHP_VER: ${PHP:-8.3} - user: ${UID}:${GID} environment: MEMCACHE_HOST: memcached diff --git a/files/Dockerfile b/files/Dockerfile index 2b327d439..3081cf1ab 100644 --- a/files/Dockerfile +++ b/files/Dockerfile @@ -148,5 +148,10 @@ ENV PS1="New Relic > " RUN echo 'alias integ="/usr/src/myapp/bin/integration_runner -agent /usr/src/myapp/agent/.libs/newrelic.so"' >> ~/.bashrc \ && echo 'alias rebuild="make -C agent clean && rm agent/Makefile && make && make tests"' >> ~/.bashrc +ARG USER=developer +ARG UID=501 +ARG GID=20 +RUN useradd --uid ${UID} --gid ${GID} --shell /bin/bash --create-home ${USER} +USER ${USER} WORKDIR /usr/src/myapp CMD ["bash"] diff --git a/trivy.yaml b/trivy.yaml new file mode 100644 index 000000000..e27bd8157 --- /dev/null +++ b/trivy.yaml @@ -0,0 +1,18 @@ +db: + repository: + - mirror.gcr.io/aquasec/trivy-db:2 + +scan: + scanners: + - vuln + - misconfig + skip-dirs: vendor + +severities: + - CRITICAL + - HIGH + - MEDIUM + - LOW + +vulnerability: + ignore-unfixed: true