Skip to content

Commit

Permalink
feat(agent): Add supportability metrics for packages that provide maj…
Browse files Browse the repository at this point in the history
…or version (#868)

This PR creates a new supportability metric that will provide
information on what major version of the package is being used. The
packages that provide the major version and will send up this
supportability metric are:
 - Drupal
 - Guzzle
 - Laravel
 - Monolog
 - Predis
 - Slim
 - Wordpress.
  • Loading branch information
hahuja2 authored Apr 19, 2024
1 parent 539dd4f commit 5b964b4
Show file tree
Hide file tree
Showing 115 changed files with 295 additions and 26 deletions.
10 changes: 8 additions & 2 deletions agent/fw_drupal8.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "util_memory.h"
#include "util_strings.h"

#define PHP_PACKAGE_NAME "drupal/core"

/*
* Purpose : Convenience function to handle adding a callback to a method,
* given a class entry and a method name. This will check the
Expand Down Expand Up @@ -684,7 +686,11 @@ void nr_drupal_version() {
// Add php package to transaction
if (nr_php_is_zval_valid_string(zval_version)) {
char* version = Z_STRVAL_P(zval_version);
nr_txn_add_php_package(NRPRG(txn), "drupal/core", version);
if (NRINI(vulnerability_management_package_detection_enabled)) {
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
}
nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
version);
}

nr_php_zval_free(&zval_version);
Expand Down Expand Up @@ -753,7 +759,7 @@ void nr_drupal8_enable(TSRMLS_D) {
}

if (NRINI(vulnerability_management_package_detection_enabled)) {
nr_txn_add_php_package(NRPRG(txn), "drupal/core",
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME,
PHP_PACKAGE_VERSION_UNKNOWN);
}
}
6 changes: 5 additions & 1 deletion agent/fw_laravel.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "ext/standard/php_versioning.h"
#include "Zend/zend_exceptions.h"

#define PHP_PACKAGE_NAME "laravel/framework"

/*
* This instruments Laravel 4.0-5.0, inclusive.
* There is no support for Laravel 3.X or earlier.
Expand Down Expand Up @@ -959,8 +961,10 @@ NR_PHP_WRAPPER(nr_laravel_application_construct) {

if (NRINI(vulnerability_management_package_detection_enabled)) {
// Add php package to transaction
nr_txn_add_php_package(NRPRG(txn), "laravel/framework", version);
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
}
nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
version);

if (version) {
nrl_debug(NRL_FRAMEWORK, "Laravel version is " NRP_FMT, NRP_PHP(version));
Expand Down
25 changes: 15 additions & 10 deletions agent/fw_slim.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "fw_support.h"
#include "util_logging.h"

#define PHP_PACKAGE_NAME "slim/slim"

static char* nr_slim_path_from_route(zval* route TSRMLS_DC) {
zval* name = NULL;
zval* pattern = NULL;
Expand Down Expand Up @@ -119,8 +121,13 @@ NR_PHP_WRAPPER(nr_slim_application_construct) {

version = nr_php_get_object_constant(this_var, "VERSION");

// Add php package to transaction
nr_txn_add_php_package(NRPRG(txn), "slim/slim", version);
if (NRINI(vulnerability_management_package_detection_enabled)) {
// Add php package to transaction
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
}

nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
version);

nr_free(version);
nr_php_scope_release(&this_var);
Expand All @@ -139,13 +146,11 @@ void nr_slim_enable(TSRMLS_D) {
nr_php_wrap_user_function(NR_PSTR("Slim\\Routing\\Route::run"),
nr_slim3_4_route_run TSRMLS_CC);

if (NRINI(vulnerability_management_package_detection_enabled)) {
/* Slim 2 does not have the same path as Slim 3/4 which is why
we need to separate these*/
nr_php_wrap_user_function(NR_PSTR("Slim\\Slim::__construct"),
nr_slim_application_construct);
/* Slim 2 does not have the same path as Slim 3/4 which is why
we need to separate these*/
nr_php_wrap_user_function(NR_PSTR("Slim\\Slim::__construct"),
nr_slim_application_construct);

nr_php_wrap_user_function(NR_PSTR("Slim\\App::__construct"),
nr_slim_application_construct);
}
nr_php_wrap_user_function(NR_PSTR("Slim\\App::__construct"),
nr_slim_application_construct);
}
35 changes: 35 additions & 0 deletions agent/fw_support.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "util_memory.h"
#include "util_strings.h"

#define MAJOR_VERSION_LENGTH 8

void nr_php_framework_add_supportability_metric(const char* framework_name,
const char* name TSRMLS_DC) {
char buf[512];
Expand Down Expand Up @@ -52,3 +54,36 @@ void nr_fw_support_add_logging_supportability_metric(nrtxn_t* txn,
nrm_force_add(txn->unscoped_metrics, metname, 0);
nr_free(metname);
}

void nr_fw_support_add_package_supportability_metric(
nrtxn_t* txn,
const char* package_name,
const char* package_version) {
if (NULL == txn || NULL == package_name || NULL == package_version) {
return;
}

char* metname = NULL;
char major_version[MAJOR_VERSION_LENGTH] = {0};

/* The below for loop checks if the major version of the package is more than
* one digit and keeps looping until a '.' is encountered or one of the
* conditions is met.
*/
for (int i = 0; package_version[i] && i < MAJOR_VERSION_LENGTH - 1; i++) {
if ('.' == package_version[i]) {
break;
}
major_version[i] = package_version[i];
}

if (NR_FW_UNSET == NRINI(force_framework)) {
metname = nr_formatf("Supportability/PHP/package/%s/%s/detected",
package_name, major_version);
} else {
metname = nr_formatf("Supportability/PHP/package/%s/%s/forced",
package_name, major_version);
}
nrm_force_add(txn->unscoped_metrics, metname, 0);
nr_free(metname);
}
13 changes: 13 additions & 0 deletions agent/fw_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,17 @@ extern void nr_fw_support_add_logging_supportability_metric(
const char* library_name,
const bool is_enabled);

/*
* Purpose: Add 'Supportability/PHP/package/{package}/{version}/detected' metric
*
* Params : 1. Transaction object
* 2. Package name
* 3. Package version
*
*/
extern void nr_fw_support_add_package_supportability_metric(
nrtxn_t* txn,
const char* package_name,
const char* package_version);

#endif /* FW_SUPPORT_HDR */
9 changes: 7 additions & 2 deletions agent/fw_wordpress.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#define NR_WORDPRESS_HOOK_PREFIX "Framework/WordPress/Hook/"
#define NR_WORDPRESS_PLUGIN_PREFIX "Framework/WordPress/Plugin/"
#define PHP_PACKAGE_NAME "wordpress"

static nr_regex_t* wordpress_hook_regex;

Expand Down Expand Up @@ -809,7 +810,11 @@ void nr_wordpress_version() {
if (SUCCESS == result) {
if (nr_php_is_zval_valid_string(&retval)) {
char* version = Z_STRVAL(retval);
nr_txn_add_php_package(NRPRG(txn), "wordpress", version);
if (NRINI(vulnerability_management_package_detection_enabled)) {
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
}
nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
version);
}
zval_dtor(&retval);
}
Expand Down Expand Up @@ -865,7 +870,7 @@ void nr_wordpress_enable(TSRMLS_D) {
#endif /* OAPI */

if (NRINI(vulnerability_management_package_detection_enabled)) {
nr_txn_add_php_package(NRPRG(txn), "wordpress",
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME,
PHP_PACKAGE_VERSION_UNKNOWN);
}
}
Expand Down
14 changes: 11 additions & 3 deletions agent/lib_guzzle6.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@

#include "ext/standard/php_var.h"

#define PHP_PACKAGE_NAME "guzzlehttp/guzzle"

/*
* Since Guzzle 6 requires PHP 5.5.0 or later, we just won't build the Guzzle 6
* support on older versions and will instead provide simple stubs for the two
Expand Down Expand Up @@ -350,12 +352,18 @@ NR_PHP_WRAPPER_START(nr_guzzle6_client_construct) {
zval* retval;
zval* this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS);

char* version = nr_php_get_object_constant(this_var, "VERSION");
if (NULL == version) {
version = nr_php_get_object_constant(this_var, "MAJOR_VERSION");
}

if (NRINI(vulnerability_management_package_detection_enabled)) {
char* version = nr_php_get_object_constant(this_var, "VERSION");
// Add php package to transaction
nr_txn_add_php_package(NRPRG(txn), "guzzlehttp/guzzle", version);
nr_free(version);
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
}
nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
version);
nr_free(version);

(void)wraprec;
NR_UNUSED_SPECIALFN;
Expand Down
9 changes: 8 additions & 1 deletion agent/lib_monolog.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
#define LOG_DECORATE_PROC_FUNC_NAME \
"newrelic_phpagent_monolog_decorating_processor"

#define PHP_PACKAGE_NAME "monolog/monolog"
#define MAJOR_VERSION_LENGTH 8

/*
* Purpose : Convert Monolog\Logger::API to integer
*
Expand Down Expand Up @@ -373,6 +376,10 @@ NR_PHP_WRAPPER(nr_monolog_logger_addrecord) {
api = nr_monolog_version(this_var TSRMLS_CC);
timestamp
= nr_monolog_get_timestamp(api, argc, NR_EXECUTE_ORIG_ARGS TSRMLS_CC);
char version[MAJOR_VERSION_LENGTH];
snprintf(version, sizeof(version), "%d", api);
nr_fw_support_add_package_supportability_metric(NRPRG(txn),
PHP_PACKAGE_NAME, version);
}

/* Record the log event */
Expand Down Expand Up @@ -513,7 +520,7 @@ void nr_monolog_enable(TSRMLS_D) {
nr_monolog_logger_addrecord TSRMLS_CC);

if (NRINI(vulnerability_management_package_detection_enabled)) {
nr_txn_add_php_package(NRPRG(txn), "monolog/monolog",
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME,
PHP_PACKAGE_VERSION_UNKNOWN);
}
}
11 changes: 8 additions & 3 deletions agent/lib_predis.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "util_strings.h"
#include "lib_predis_private.h"

#define PHP_PACKAGE_NAME "predis/predis"

/*
* Predis instrumentation
* ======================
Expand Down Expand Up @@ -646,12 +648,15 @@ NR_PHP_WRAPPER(nr_predis_client_construct) {
(void)wraprec;

NR_PHP_WRAPPER_CALL;

char* version = nr_php_get_object_constant(scope, "VERSION");
if (NRINI(vulnerability_management_package_detection_enabled)) {
char* version = nr_php_get_object_constant(scope, "VERSION");
// Add php package to transaction
nr_txn_add_php_package(NRPRG(txn), "predis/predis", version);
nr_free(version);
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
}
nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
version);
nr_free(version);

/*
* Grab the connection object from the client, since we actually instrument
Expand Down
11 changes: 8 additions & 3 deletions agent/php_agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -727,10 +727,15 @@ char* nr_php_get_object_constant(zval* app, const char* name) {

if (nr_php_is_zval_valid_string(version)) {
retval = nr_strndup(Z_STRVAL_P(version), Z_STRLEN_P(version));
} else if (nr_php_is_zval_valid_integer(version)) {
zend_string* zstr = zend_long_to_str(Z_LVAL_P(version));
retval = nr_strndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr));
zend_string_release(zstr);
} else {
nrl_verbosedebug(NRL_FRAMEWORK,
"%s: expected VERSION be a valid string, got type %d",
__func__, Z_TYPE_P(version));
nrl_verbosedebug(
NRL_FRAMEWORK,
"%s: expected VERSION to be a valid string or int, got type %d",
__func__, Z_TYPE_P(version));
}

nr_php_zval_free(&version);
Expand Down
69 changes: 69 additions & 0 deletions agent/tests/test_fw_support.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ tlib_parallel_info_t parallel_info

static void test_fw_supportability_metrics(void) {
#define LIBRARY_NAME "php-package"
#define LIBRARY_MAJOR_VERSION "7"
#define LIBRARY_MAJOR_VERSION_2 "10"
#define LIBRARY_MAJOR_VERSION_3 "100"
#define LIBRARY_MAJOR_VERSION_4 "1.23"
#define LIBRARY_MAJOR_VERSION_5 "12.34"
#define LIBRARY_MAJOR_VERSION_6 "123.45"
#define LIBRARY_MAJOR_VERSION_7 "0.4.5"
#define LIBRARY_METRIC "Supportability/library/" LIBRARY_NAME "/detected"
#define LOGGING_LIBRARY_METRIC "Supportability/Logging/PHP/" LIBRARY_NAME
#define PACKAGE_METRIC "Supportability/PHP/package/" LIBRARY_NAME
nrtxn_t t;
nrtxn_t* txn = &t;
txn->unscoped_metrics = nrm_table_create(10);
Expand All @@ -36,6 +44,18 @@ static void test_fw_supportability_metrics(void) {
tlib_pass_if_int_equal("NULL logging library metric not created", 0,
nrm_table_size(txn->unscoped_metrics));

nr_fw_support_add_package_supportability_metric(NULL, LIBRARY_NAME, LIBRARY_MAJOR_VERSION);
tlib_pass_if_int_equal("package metric not created in NULL metrics",
0, nrm_table_size(txn->unscoped_metrics));

nr_fw_support_add_package_supportability_metric(txn, NULL, LIBRARY_MAJOR_VERSION);
tlib_pass_if_int_equal("NULL package name, metric not created", 0,
nrm_table_size(txn->unscoped_metrics));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, NULL);
tlib_pass_if_int_equal("NULL major version, metric not created", 0,
nrm_table_size(txn->unscoped_metrics));

/* Happy path */
nr_fw_support_add_library_supportability_metric(txn, LIBRARY_NAME);
tlib_pass_if_not_null("happy path: library metric created",
Expand All @@ -51,6 +71,55 @@ static void test_fw_supportability_metrics(void) {
"happy path: logging library metric created",
nrm_find(txn->unscoped_metrics, LOGGING_LIBRARY_METRIC "/disabled"));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION);
tlib_pass_if_not_null("happy path test 1: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC
"/" LIBRARY_MAJOR_VERSION "/detected"));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION_2);
tlib_pass_if_not_null("happy path test 2: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC
"/" LIBRARY_MAJOR_VERSION_2 "/detected"));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION_3);
tlib_pass_if_not_null("happy path test 3: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC
"/" LIBRARY_MAJOR_VERSION_3 "/detected"));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION_4);
tlib_pass_if_not_null(
"happy path test 4: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/1/detected"));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION_5);
tlib_pass_if_not_null(
"happy path test 5: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/12/detected"));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION_6);
tlib_pass_if_not_null(
"happy path test 6: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/123/detected"));

nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION_7);
tlib_pass_if_not_null(
"happy path test 7: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/0/detected"));

NRINI(force_framework) = true;
nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME,
LIBRARY_MAJOR_VERSION);
tlib_pass_if_not_null(
"happy path test 8: package metric created",
nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/7/forced"));

nrm_table_destroy(&txn->unscoped_metrics);
}

Expand Down
Loading

0 comments on commit 5b964b4

Please sign in to comment.