Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions agent/php_autorum.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ static char* nr_php_rum_malloc(int len) {
return (char*)emalloc(len);
}

static char* nr_php_rum_produce_header_with_ini_nonce(nrtxn_t* txn,
int tags,
int autorum) {
char* nonce = nr_php_zend_ini_string(
NR_PSTR("newrelic.browser_monitoring.nonce_rum_header"), 0);
if (nonce && '\0' != nonce[0]) {
return nr_rum_produce_header_with_nonce(txn, tags, autorum, nonce);
}
return nr_rum_produce_header(txn, tags, autorum);
}

static char* nr_php_rum_produce_footer_with_ini_nonce(nrtxn_t* txn,
int tags,
int autorum) {
char* nonce = nr_php_zend_ini_string(
NR_PSTR("newrelic.browser_monitoring.nonce_rum_footer"), 0);
if (nonce && '\0' != nonce[0]) {
return nr_rum_produce_footer_with_nonce(txn, tags, autorum, nonce);
}
return nr_rum_produce_footer(txn, tags, autorum);
}

/*
* Callback function for iterating the list of response headers that
* prints the value of the Content-Type header, if present. This is so
Expand Down Expand Up @@ -91,8 +113,8 @@ void nr_php_rum_output_handler(
}

nr_memset(&control_block, 0, sizeof(control_block));
control_block.produce_header = nr_rum_produce_header;
control_block.produce_footer = nr_rum_produce_footer;
control_block.produce_header = nr_php_rum_produce_header_with_ini_nonce;
control_block.produce_footer = nr_php_rum_produce_footer_with_ini_nonce;
control_block.malloc_worker = nr_php_rum_malloc;

has_response_content_length = nr_php_has_response_content_length(TSRMLS_C);
Expand Down
10 changes: 10 additions & 0 deletions agent/php_nrini.c
Original file line number Diff line number Diff line change
Expand Up @@ -2897,6 +2897,16 @@ STD_PHP_INI_ENTRY_EX("newrelic.browser_monitoring.attributes.exclude",
zend_newrelic_globals,
newrelic_globals,
0)
PHP_INI_ENTRY_EX("newrelic.browser_monitoring.nonce_rum_header",
"",
NR_PHP_REQUEST,
0,
0)
PHP_INI_ENTRY_EX("newrelic.browser_monitoring.nonce_rum_footer",
"",
NR_PHP_REQUEST,
0,
0)
/*
* newrelic.browser_monitoring.ssl_for_http is omitted.
*/
Expand Down
75 changes: 65 additions & 10 deletions axiom/nr_rum.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ static char* nr_rum_obfuscate(const char* input, const char* key) {
return nr_obfuscate(input, key, NR_RUM_OBFUSCATION_KEY_LENGTH);
}

char* nr_rum_encode_nonce(const char* nonce) {
char* encoded = 0;
if ((0 == nonce) || ('\0' == nonce[0])) {
encoded = (char*)nr_malloc(1);
encoded[0] = '\0';
return encoded;
}
{
size_t needed = (size_t)snprintf(NULL, 0, " nonce=\"%s\"", nonce) + 1;
encoded = (char*)nr_malloc(needed);
snprintf(encoded, needed, " nonce=\"%s\"", nonce);
}
return encoded;
}

/*
* Do not terminate these strings with a \n. If the fragments below end up
* being inserted into the middle of a JavaScript string (for example the user
Expand All @@ -50,7 +65,7 @@ static char* nr_rum_obfuscate(const char* input, const char* key) {
* Otherwise they will end up breaking the string in the middle of a line which
* will cause JavaScript errors, which will break a user's web site.
*/
static const char rum_start_tag[] = "<script type=\"text/javascript\">";
static const char rum_start_tag[] = "<script type=\"text/javascript\"%s>";
static const char rum_end_tag[] = "</script>";

/*
Expand All @@ -59,10 +74,11 @@ static const char rum_end_tag[] = "</script>";
*/
const char nr_rum_footer_prefix[] = "window.NREUM||(NREUM={});NREUM.info=";

char* nr_rum_produce_header(nrtxn_t* txn, int tags, int autorum) {
static char* nr_rum_produce_header_impl(nrtxn_t* txn, int tags, int autorum, const char* nonce) {
char* return_header;
size_t header_length;
const char* loader;
char* formatted_rum_start_tag = 0;

if (0 == txn) {
return 0;
Expand All @@ -89,18 +105,37 @@ char* nr_rum_produce_header(nrtxn_t* txn, int tags, int autorum) {

txn->status.rum_header = 1 + (autorum != 0);

header_length
= (tags ? (nr_strlen(rum_start_tag) + nr_strlen(rum_end_tag)) : 0)
+ nr_strlen(loader) + 1;
if (tags) {
char* encoded_nonce = nr_rum_encode_nonce(nonce);
size_t needed = (size_t)snprintf(NULL, 0, rum_start_tag, encoded_nonce) + 1;
formatted_rum_start_tag = (char*)nr_malloc(needed);
snprintf(formatted_rum_start_tag, needed, rum_start_tag, encoded_nonce);
nr_free(encoded_nonce);
}

header_length = (tags ? (nr_strlen(formatted_rum_start_tag) + nr_strlen(rum_end_tag)) : 0)
+ nr_strlen(loader) + 1;

return_header = (char*)nr_malloc(header_length);
return_header[0] = '\0';
snprintf(return_header, header_length, "%s%s%s", tags ? rum_start_tag : "",
snprintf(return_header, header_length, "%s%s%s", tags ? formatted_rum_start_tag : "",
loader, tags ? rum_end_tag : "");

if (formatted_rum_start_tag) {
nr_free(formatted_rum_start_tag);
}

return return_header;
}

char* nr_rum_produce_header(nrtxn_t* txn, int tags, int autorum) {
return nr_rum_produce_header_impl(txn, tags, autorum, NULL);
}

char* nr_rum_produce_header_with_nonce(nrtxn_t* txn, int tags, int autorum, const char* nonce) {
return nr_rum_produce_header_impl(txn, tags, autorum, nonce);
}

char* nr_rum_get_attributes(const nr_attributes_t* attributes) {
char* json;
nrobj_t* user;
Expand Down Expand Up @@ -152,13 +187,14 @@ static char* nr_rum_get_attributes_obfuscated(const nr_attributes_t* attributes,
return json_obfuscated;
}

char* nr_rum_produce_footer(nrtxn_t* txn, int tags, int autorum) {
static char* nr_rum_produce_footer_impl(nrtxn_t* txn, int tags, int autorum, const char* nonce) {
char* txn_name;
nrtime_t queue_time;
nrtime_t app_time;
nrobj_t* hash;
char* hash_json;
char* obfuscated_attributes;
char* formatted_rum_start_tag = 0;

if (0 == txn) {
return 0;
Expand Down Expand Up @@ -222,21 +258,40 @@ char* nr_rum_produce_footer(nrtxn_t* txn, int tags, int autorum) {
char* footer;
size_t footer_len;

footer_len = (tags ? (sizeof(rum_start_tag) + sizeof(rum_end_tag)) : 0)
+ sizeof(nr_rum_footer_prefix) + nr_strlen(hash_json) + 1;
if (tags) {
char* encoded_nonce = nr_rum_encode_nonce(nonce);
size_t needed = (size_t)snprintf(NULL, 0, rum_start_tag, encoded_nonce) + 1;
formatted_rum_start_tag = (char*)nr_malloc(needed);
snprintf(formatted_rum_start_tag, needed, rum_start_tag, encoded_nonce);
nr_free(encoded_nonce);
}

footer_len = (tags ? (nr_strlen(formatted_rum_start_tag) + nr_strlen(rum_end_tag)) : 0)
+ sizeof(nr_rum_footer_prefix) - 1 + nr_strlen(hash_json) + 1;

footer = (char*)nr_malloc(footer_len);
footer[0] = '\0';
snprintf(footer, footer_len, "%s%s%s%s", tags ? rum_start_tag : "",
snprintf(footer, footer_len, "%s%s%s%s", tags ? formatted_rum_start_tag : "",
nr_rum_footer_prefix, hash_json, tags ? rum_end_tag : "");

nr_free(hash_json);
if (formatted_rum_start_tag) {
nr_free(formatted_rum_start_tag);
}

txn->status.rum_footer = 1 + (autorum != 0);
return footer;
}
}

char* nr_rum_produce_footer(nrtxn_t* txn, int tags, int autorum) {
return nr_rum_produce_footer_impl(txn, tags, autorum, NULL);
}

char* nr_rum_produce_footer_with_nonce(nrtxn_t* txn, int tags, int autorum, const char* nonce) {
return nr_rum_produce_footer_impl(txn, tags, autorum, nonce);
}

static const char nr_rum_x_ua_compatible_regex[]
= "<\\s*meta[^>]+http-equiv\\s*=\\s*['\"]x-ua-compatible['\"][^>]*>";
static const char nr_rum_charset_regex[] = "<\\s*meta[^>]+charset\\s*=[^>]*>";
Expand Down
14 changes: 14 additions & 0 deletions axiom/nr_rum.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,39 @@ extern int nr_rum_do_autorum(const nrtxn_t* txn);
* Params : 1. The transaction pointer.
* 2. Whether or not to use start tags.
* 3. Whether or not this is being inserted by auto-RUM.
* 4. (optional) See nr_rum_produce_header_with_nonce.
*
* Returns : The string to insert, allocated. If the header has been produced
* before, returns NULL. That is, this function can only be called
* once per transaction.
*/
extern char* nr_rum_produce_header(nrtxn_t* txn, int tags, int autorum);
extern char* nr_rum_produce_header_with_nonce(nrtxn_t* txn, int tags, int autorum, const char* nonce);

/*
* Purpose : Produce the RUM footer for a transaction.
*
* Params : 1. The transaction pointer.
* 2. Whether or not to use tags.
* 3. Whether or not this is being inserted by auto-RUM.
* 4. (optional) See nr_rum_produce_footer_with_nonce.
*
* Returns : The string to insert, allocated. If the header has been produced
* before, returns NULL. That is, this function can only be called
* once per transaction.
*/
extern char* nr_rum_produce_footer(nrtxn_t* txn, int tags, int autorum);
extern char* nr_rum_produce_footer_with_nonce(nrtxn_t* txn, int tags, int autorum, const char* nonce);

/*
* Purpose : Encode a CSP nonce attribute for insertion into a script tag.
*
* Params : 1. The raw nonce value, or NULL/empty.
*
* Returns : An allocated string: "nonce=\"VALUE\"" if nonce is provided,
* otherwise "" (empty). Caller must free with nr_free.
*/
extern char* nr_rum_encode_nonce(const char* nonce);

/*
* Purpose : Scan html looking for a heuristically good place in <head> to put
Expand Down