@newrelic/security-agent 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +0 -0
  2. package/CONTRIBUTING.md +23 -0
  3. package/LICENSE +43 -0
  4. package/README.md +98 -0
  5. package/THIRD_PARTY_NOTICES.md +2909 -0
  6. package/index.js +24 -0
  7. package/lib/instrumentation-security/core/constants.js +66 -0
  8. package/lib/instrumentation-security/core/event-constants.js +49 -0
  9. package/lib/instrumentation-security/core/request-manager.js +73 -0
  10. package/lib/instrumentation-security/core/route-manager.js +30 -0
  11. package/lib/instrumentation-security/core/sec-trace.js +134 -0
  12. package/lib/instrumentation-security/core/sec-utils.js +212 -0
  13. package/lib/instrumentation-security/core/security-metadata.js +34 -0
  14. package/lib/instrumentation-security/core/stack-trace.js +156 -0
  15. package/lib/instrumentation-security/hooks/director/nr-director.js +98 -0
  16. package/lib/instrumentation-security/hooks/express/nr-express.js +106 -0
  17. package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +155 -0
  18. package/lib/instrumentation-security/hooks/fs/nr-fs.js +391 -0
  19. package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +131 -0
  20. package/lib/instrumentation-security/hooks/http/nr-http.js +461 -0
  21. package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +110 -0
  22. package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +81 -0
  23. package/lib/instrumentation-security/hooks/mongodb/common.js +239 -0
  24. package/lib/instrumentation-security/hooks/mongodb/constants.js +91 -0
  25. package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +49 -0
  26. package/lib/instrumentation-security/hooks/mongodb/v2-mongo.js +91 -0
  27. package/lib/instrumentation-security/hooks/mongodb/v3-mongo.js +36 -0
  28. package/lib/instrumentation-security/hooks/mongodb/v4-mongo.js +17 -0
  29. package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +232 -0
  30. package/lib/instrumentation-security/hooks/native/nr-childProcess.js +131 -0
  31. package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +131 -0
  32. package/lib/instrumentation-security/hooks/require/nr-require.js +93 -0
  33. package/lib/instrumentation-security/hooks/restify/nr-restify.js +104 -0
  34. package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +91 -0
  35. package/lib/instrumentation-security/index.js +171 -0
  36. package/lib/nr-security-agent/index.js +170 -0
  37. package/lib/nr-security-agent/lib/core/Auth-headers.js +58 -0
  38. package/lib/nr-security-agent/lib/core/ContainerProperties.js +42 -0
  39. package/lib/nr-security-agent/lib/core/ECSProperties.js +34 -0
  40. package/lib/nr-security-agent/lib/core/FuzzFailEvent.js +27 -0
  41. package/lib/nr-security-agent/lib/core/LinkingMetadata.js +21 -0
  42. package/lib/nr-security-agent/lib/core/LogMessage.js +47 -0
  43. package/lib/nr-security-agent/lib/core/PodProperties.js +36 -0
  44. package/lib/nr-security-agent/lib/core/Policy.js +105 -0
  45. package/lib/nr-security-agent/lib/core/agent-status.js +56 -0
  46. package/lib/nr-security-agent/lib/core/agent.js +146 -0
  47. package/lib/nr-security-agent/lib/core/applicationinfo.js +634 -0
  48. package/lib/nr-security-agent/lib/core/commonUtils.js +411 -0
  49. package/lib/nr-security-agent/lib/core/connections/aws-lambda-client.js +55 -0
  50. package/lib/nr-security-agent/lib/core/connections/restclient.js +20 -0
  51. package/lib/nr-security-agent/lib/core/connections/websocket/index.js +25 -0
  52. package/lib/nr-security-agent/lib/core/connections/websocket/response/collecotor-policy-error-handler.js +18 -0
  53. package/lib/nr-security-agent/lib/core/connections/websocket/response/collector-policy-handler.js +20 -0
  54. package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +171 -0
  55. package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +47 -0
  56. package/lib/nr-security-agent/lib/core/connections/websocket/response/no-op-handler.js +12 -0
  57. package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +27 -0
  58. package/lib/nr-security-agent/lib/core/connections/websocket/response/unsupported-agent-handler.js +18 -0
  59. package/lib/nr-security-agent/lib/core/connections/websocket/response/ws-reconnect.js +14 -0
  60. package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +443 -0
  61. package/lib/nr-security-agent/lib/core/event.js +186 -0
  62. package/lib/nr-security-agent/lib/core/health-check.js +159 -0
  63. package/lib/nr-security-agent/lib/core/json-file-handler.js +66 -0
  64. package/lib/nr-security-agent/lib/core/jsonValidator.js +149 -0
  65. package/lib/nr-security-agent/lib/core/lambda-delegated-task-service.js +90 -0
  66. package/lib/nr-security-agent/lib/core/logging.js +176 -0
  67. package/lib/nr-security-agent/lib/core/sec-agent-constants.js +173 -0
  68. package/lib/nr-security-agent/lib/core/sec-util.js +166 -0
  69. package/lib/nr-security-agent/lib/core/sha-size-util.js +338 -0
  70. package/lib/nr-security-agent/lib/core/sha_worker.js +24 -0
  71. package/lib/nr-security-agent/lib/core/startup-config.js +29 -0
  72. package/lib/nr-security-agent/lib/core/statusUtils.js +157 -0
  73. package/lib/nr-security-agent/lib/core/timeunit.js +44 -0
  74. package/lib/nr-security-agent/lib/core/websocket-client.js +335 -0
  75. package/lib/nr-security-agent/lib/core/worker-entities.js +82 -0
  76. package/lib/nr-security-agent/lib/core/worker.js +148 -0
  77. package/lib/nr-security-agent/lib/core/xss-utils.js +297 -0
  78. package/lib/nr-security-agent/lib/models/lambda-delegated-task/constants.js +12 -0
  79. package/lib/nr-security-agent/lib/models/lambda-delegated-task/index.js +15 -0
  80. package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-sha-calculation.js +37 -0
  81. package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-base.js +50 -0
  82. package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-type.js +17 -0
  83. package/lib/nr-security-agent/resources/cert.pem +31 -0
  84. package/lib/nr-security-agent/resources/config.js +19 -0
  85. package/lib/nr-security-agent/resources/default-policy.yaml +36 -0
  86. package/lib/nr-security-api/index.js +111 -0
  87. package/package.json +95 -0
@@ -0,0 +1,173 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const AWS_LAMBDA_NODE_EXEC_ENV_IDENTIFIER = /^AWS_Lambda_nodejs.*$/g;
9
+ const AWS_EXECUTION_ENV_VAR = 'AWS_EXECUTION_ENV';
10
+ const path = require('path');
11
+ const API = require('../../../nr-security-api');
12
+ const NRAgent = API.getNRAgent();
13
+
14
+ let CSEC_HOME = NRAgent && NRAgent.config.newrelic_home ? NRAgent.config.newrelic_home : require('path').join(process.cwd());
15
+ const CSEC_GROUP_NAME = NRAgent ? NRAgent.config.security.mode : 'IAST';
16
+ CSEC_HOME = path.resolve(CSEC_HOME);
17
+ const agentStartTime = new Date().toString();
18
+
19
+ module.exports = {
20
+ CSEC_HOME: CSEC_HOME,
21
+ CSEC_GROUP_NAME: CSEC_GROUP_NAME,
22
+ LOG_DIR: `${CSEC_HOME}/nr-security-home/logs/`,
23
+ CSEC_HOME_TMP: `${CSEC_HOME}/nr-security-home/tmp/language-agent/${process.env.applicationUUID}`,
24
+ AGENT_START_TIME: agentStartTime,
25
+ FRAMEWORK: '',
26
+ STATUS_LOG_FILE: `${CSEC_HOME}/nr-security-home/logs/snapshots/node-security-collector-status-${process.env.applicationUUID}.log`,
27
+
28
+ DB_LIST: {
29
+ MYSQL: 'MYSQL',
30
+ MYSQL2: 'MYSQL',
31
+ POSTGRES: 'POSTGRES',
32
+ MONGODB: 'MONGODB',
33
+ ORACLEDB: 'ORACLEDB',
34
+ MSSQL: 'MSSQL',
35
+ SQLITE3: 'SQLITE3'
36
+ },
37
+ SERVER_COMMAND: {
38
+ SET_LOG_LEVEL: 0,
39
+ SHUTDOWN_LANGUAGE_AGENT: 1,
40
+ SET_DEFAULT_LOG_LEVEL: 2,
41
+ ENABLE_HTTP_REQUEST_PRINTING: 3,
42
+ UPLOAD_LOGS: 4,
43
+ UNSUPPORTED_AGENT: 5,
44
+ VALIDATION_RESPONSE: 6,
45
+ START_VULNERABILITY_SCAN: 8,
46
+ STARTUP_WELCOME_MSG: 10,
47
+ FUZZ_REQUEST: 11,
48
+ COLLECTOR_CONFIG: 100,
49
+ BLOCKING: 101,
50
+ POLICY_ERROR: 102,
51
+ OBEY_WS_RECONNECT:12
52
+
53
+ },
54
+ JSON_NAME: {
55
+ EVENT: 'Event',
56
+ HC: 'LAhealthcheck',
57
+ SD: 'Shutdown',
58
+ APP_INFO: 'ApplicationInfo',
59
+ ICC: 'IntCodeControlCommand',
60
+ CONNSTAT: 'http-connection-stat'
61
+ },
62
+ COLLECTOR_TYPE: 'NODE',
63
+ LANGUAGE: 'JavaScript',
64
+ AWS_LAMBDA_QUALIFIED_ARN_ENV_IDENTIFIER: 'AWS_LAMBDA_ARN',
65
+ AWS_LAMBDA_FUNCTION_NAME_ENV_IDENTIFIER: 'AWS_LAMBDA_FUNCTION_NAME',
66
+ AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER: 'AWS_LAMBDA_FUNCTION_VERSION',
67
+ AWS_SESSION_TOKEN_ENV_IDENTIFIER: 'AWS_SESSION_TOKEN',
68
+ LAMBDA_TASK_ROOT_ENV_IDENTIFIER: 'LAMBDA_TASK_ROOT',
69
+ LAMBDA_ENABLE_RESPONSE_BLOCKING: 'LAMBDA_ENABLE_RESPONSE_BLOCKING',
70
+ AWS_LAMBDA_NODE_EXEC_ENV_IDENTIFIER,
71
+ AWS_EXECUTION_ENV_VAR,
72
+ IS_LAMBDA_ENV: Array.isArray(
73
+ (process.env[AWS_EXECUTION_ENV_VAR] || '').match(AWS_LAMBDA_NODE_EXEC_ENV_IDENTIFIER)
74
+ ),
75
+ LAMBDA_RUNTIME_DIR_VAR: 'LAMBDA_RUNTIME_DIR',
76
+ CSEC_LAST_FUZZ_REQUEST_TIME: 'CSEC_LAST_FUZZ_REQUEST_TIME',
77
+ NR_CSEC_FUZZ_REQUEST_ID: 'nr-csec-fuzz-request-id',
78
+ EMPTY_APPLICATION_UUID: '--0--',
79
+ QUESTIONMARK: '?',
80
+ PIPE_SEP: '|',
81
+ ATTHERATE: '@',
82
+ EMPTY_STR: '',
83
+ COLON: ':',
84
+ OPEN_PARANTHESIS: '(',
85
+ CLOSE_PARANTHESIS: ')',
86
+ SLASH: '/',
87
+ INCOMING_MSG: 'IncomingMessage',
88
+ OBJECT: 'object',
89
+ STRING: 'string',
90
+ TRIGGER_VIA_RCI: 'triggerViaRCI',
91
+ RCI_METHOD_CALLS: 'rciMethodsCalls',
92
+ VULNERABLE: 'VULNERABLE',
93
+ UTF8: 'utf8',
94
+ CONTENT_TYPE: 'content-type',
95
+ TEXT_HTML: 'text/html',
96
+ APPLICATION_JSON: 'application/json',
97
+ APPLICATION_XML: 'application/xml',
98
+ APPLICATION_XHTML: 'application/xhtml+xml',
99
+ TEXT_PLAIN: 'text/plain',
100
+ APPLICATION_X_FORM_URLENCODED: 'application/x-www-form-urlencoded',
101
+ MULTIPART_FORM_DATA: 'multipart/form-data',
102
+ XFORFOR: 'x-forwarded-for',
103
+ DOUBLE_DOLLAR: '$$',
104
+ LOADING_MODULE: 'Loading module ',
105
+ EXITEVENT: 'exit-event',
106
+
107
+ LOG_MESSAGES: {
108
+ LOADED_CSEC_ENVS_MSG: '[STEP-1][COMPLETE][env] Environment Information Gathering Done.',
109
+ COLLECTOR_INFO: ' CSEC Node version : %s , json version : %s and build number : %s',
110
+ APP_INFO_GATHERING_STARTED: '[STEP-1][BEGIN][APP_INFO] Gathering Application Info For Current Process.',
111
+ DEP_APP_GATHERING_STARTED: '[BEGIN][APP_INFO][DEPLOYED_APP] Gathering deployed application info for current process',
112
+ APP_INFO_DEP_APP: '[APP_INFO][DEPLOYED_APP] Deployed application info generated :%s',
113
+ AGENT_INIT_SUCCESSFUL: '[STEP-3][protection][BEGIN] Node CSEC Agent Attached To Process: PID = %s, With Generated Applicationuid = %s By Static Attachment',
114
+ AGENT_INIT_SUCCESSFUL_COMPLETE: '[STEP-3][PROTECTION][COMPLETE] Protecting new process with PID = %s and UUID = %s',
115
+ AGENT_INIT_FAILED: '[protection] Coudn\'t Initiate CSEC Agent For Process: : Pid = {pid}, With Generated Applicationuid = {application_uuid}.',
116
+ LOG_FILE_INITIATED_MSG: 'Init Log File Initiated.',
117
+ LOG_CONFIGURED_SUCCESSFULLY_MSG: 'Init Logger Configured Successfully With Level: %s And Rollover On Max Size %s.',
118
+ INIT_APPINFO_SEND: '[STEP-9][COMPLETE][APP_INFO][DEPLOYED_APP] Updated ApplicationInfo Will Be Sent To Prevent-web Service',
119
+ AGENT_ATTACH_FAIL: 'CSEC_GROUP_NAME Environment Not Present, Agent Attachment Failed',
120
+ APP_INFO_GENERATED: '[APP_INFO] Application Info Generated: %s ',
121
+ SCHED_SERVICE_START: '[STEP-5][BEGIN][MODULE] Start Threads/pools/scheduler',
122
+ SCHED_SERVICE_START_BEG: '[BEGIN][MODULE] Started Threads/pools/scheduler',
123
+ START_HC: '[BEGIN][MODULE] Starting Healthcheck',
124
+ START_IOM: '[BEGIN][MODULE] Starting Inboundoutboundmonitor.',
125
+ END_HC: '[COMPLETE][MODULE] Started Healthcheck',
126
+ END_IOM: '[COMPLETE][MODULE] Started Inboundoutboundmonitor.',
127
+ START_DIR_WATCH: '[BEGIN][MODULE] Starting DirectoryWatcher',
128
+ STARTED_DIR_WATCH: '[COMPLETE][MODULE] Started DirectoryWatcher',
129
+ NODE_VERSION_MSG: 'Node Version Is %s, Starting Websocket Client In Parent Process',
130
+ NODE_VERSION_MSG_WORKER: 'Node Version Is %s, Starting Websocket Client In Worker Thread',
131
+ MSG_FROM_WORKKER: 'Message From Worker Thread:',
132
+ RECV_POL_DATA: '[STEP-7][BEGIN][POLICY] Received Policy Data From Prevent-web Service:%s',
133
+ RECV_GLOBAL_POL_DATA: 'Received Global Policy Parameters From Prevent-web Service:%s',
134
+ EXP_RAISE_GLOBAL_POL: 'Exception Raised In Global Policy Validation:',
135
+ UNABLE_TO_WRITE_TO_POL_FILE: 'Unable To Write To Policy Yaml File',
136
+ POL_UPDATE_SUCCESS: 'Policy Updated To File Successfully',
137
+ CHANGE_DETECT_POL_FILE: 'Changes Detected In Policy File',
138
+ ERROR_READ_FROM_POL_FILE: 'ERROR In Reading Policy From File',
139
+ POL_UPDATE_RESP: 'Policy Update Response Is:',
140
+ UPDATE_POL_SENT_SERVER: 'Updated Policy Sent To Prevent Web',
141
+ EXP_RAISE_POL: 'Exception Raised In Policy Validation:',
142
+ FALLBACK_TO_CURR_POLICY: 'Fallback To Current Policy!!!:%s',
143
+ POLICY_APPLIED: '[STEP-7][COMPLETE][POLICY] Policy Applied :',
144
+ POL_PULL_INT_IS: 'Policy Pull Interval Is:',
145
+ LOG_LEVEL_UPDATED: 'Log Level Updated To:',
146
+ DEFAULT_POL_SET: 'Default Policy Has Been Set :',
147
+ UNABLE_SET_DEFAULT_POL: 'Unable To Set Default Policy',
148
+ COLLECTOR_INIT_WITH_PROP: 'Collector Is Initialized With Properties:',
149
+ PARSING_EXP_WELCOME_MSG: 'Parsing Exeception In Startup Welcome Command',
150
+ ACCESS_BLOCKED_IP_DETECTED: 'Access By Blocked Ip Address Detected : ',
151
+ FIRST_EVENT_INTERCEPTED: '[BEGIN][EVENT] First event intercepted',
152
+ FIRST_EVENT_PROCESSED: '[EVENT] First event processed :',
153
+ FIRST_EVENT_SENT: '[STEP-8][COMPLETE][EVENT] First Event Sent For Validation',
154
+ DETECTED_BROKEN_CONN: 'Detected Broken Connection. Will Reconnect Now!',
155
+ MAX_WS_RETRY: 'Maximum WS Retry Limit Reached. Unable To Connect To Prevent Web Service!!!',
156
+ EVENT_SENT: 'Event Sent:: ',
157
+ ERROR_WHILE_SEND_EVENT: 'ERROR While Sending Event: {}',
158
+ WS_CONNECTED: '[STEP-4][COMPLETE][WS] Connected To Prevent-web Service',
159
+ SENDING_APPINFO: '[APP_INFO] Sending Application Info To Prevent-web Service: ',
160
+ SENDING_APPINFO_COMPLETE: '[COMPLETE][APP_INFO] Application info sent to Prevent-Web service : {}',
161
+ APPLY_INSTRUMENTATION: '[STEP-6][BEGIN][instrumentation] Applying Instrumentation',
162
+ AVAIL_DISK: 'Available Disk Space Is: ',
163
+ AVAIL_DISK_ERROR: 'Insufficient Disk Space Available To The Location %s Is : %s',
164
+ OBJ_WRAP_MUST_BE_FUNC: 'original object and wrapper must be functions',
165
+ NO_ORIG_FUNC_TO_WRAP: 'no original function %s to wrap',
166
+ NO_WRAPPER_FUNC: 'no wrapper function',
167
+ ERR_IN_INIT_HOOK: 'Error in initial setup of hook',
168
+ ERR_IN_ONSTART: 'Error in execution onStart Hook',
169
+ ERR_IN_ONFINISH: 'Error in execution onFinish Hook',
170
+ COLLECTOR_SHUT_DOWN: 'Collector Is Shutting Down!!!!'
171
+
172
+ }
173
+ };
@@ -0,0 +1,166 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const { SecEvent } = require('./event');
9
+ const logs = require('./logging');
10
+ const { Agent } = require('./agent');
11
+ const shaUtil = require('./sha-size-util');
12
+ const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR } = require('./sec-agent-constants');
13
+ const API = require('../../../nr-security-api');
14
+ const NRAgent = API.getNRAgent();
15
+
16
+ const statusUtils = require('./statusUtils');
17
+
18
+ let firstEvent = false;
19
+ let firstEventSent = false;
20
+
21
+ const events = require('events');
22
+ const eventEmitter = undefined;
23
+
24
+ let logger = logs.getLogger();
25
+ const initLogger = logs.getInitLogger();
26
+
27
+ const NODE_SER = 'node-serialize/lib/serialize.js';
28
+
29
+ function getSecEventEmitter() {
30
+ if (eventEmitter) {
31
+ return eventEmitter;
32
+ } else {
33
+ return new events.EventEmitter();
34
+ }
35
+ }
36
+
37
+
38
+ /**
39
+ * Sets the logger instance.
40
+ *
41
+ * @param {*} loggerInstance
42
+ */
43
+ const setLogger = loggerInstance => {
44
+ logger = loggerInstance;
45
+ };
46
+
47
+
48
+ function promisify(thisArg, fn) {
49
+ return (...args) => new Promise((resolve, reject) => {
50
+ args.push((err, data) => {
51
+ if (err) {
52
+ reject(err);
53
+ } else {
54
+ resolve(data);
55
+ }
56
+ });
57
+ fn.apply(thisArg, args);
58
+ });
59
+ }
60
+
61
+ const sleep = (timeout) => new Promise((resolve) => {
62
+ setTimeout(resolve, timeout);
63
+ });
64
+
65
+
66
+ function generateSecEvent(securityMetadata) {
67
+ const traceObject = securityMetadata.traceObject;
68
+ const request = securityMetadata.request;
69
+
70
+ try {
71
+ let stakTrace = traceObject.stacktrace;
72
+ let uri = EMPTY_STR;
73
+ if (request.uri) {
74
+ uri += request.uri;
75
+ }
76
+
77
+ let apiId = shaUtil.getSHA256ForData(traceObject.stacktrace.join('|') + uri);
78
+ const agentModule = Agent.getAgent();
79
+ const metaData = {};
80
+ if (traceObject.sourceDetails && traceObject.sourceDetails.evalObj) {
81
+ metaData.triggerViaRCI = true;
82
+ metaData.rciMethodsCalls = traceObject.sourceDetails.evalObj.invokedCalls;
83
+ metaData.rciDetail = traceObject.sourceDetails.evalObj;
84
+ if (traceObject.sourceDetails.evalObj) {
85
+ const evalFile = traceObject.sourceDetails.evalObj.fileName;
86
+ if (evalFile && evalFile.includes(NODE_SER)) {
87
+ metaData.triggerViaDeserialisation = true;
88
+ }
89
+ }
90
+ }
91
+ const isClientDetectedFromXFF = false;
92
+
93
+ if (!firstEvent) {
94
+ initLogger.info(LOG_MESSAGES.FIRST_EVENT_INTERCEPTED, securityMetadata.eventCategory, JSON.stringify(request));
95
+ }
96
+ const isAPIBlocked = false;
97
+
98
+ const event = new SecEvent(traceObject.sourceDetails.fileName, traceObject.sourceDetails.funcName, traceObject.sourceDetails.lineNumber, securityMetadata.interceptedArgs, securityMetadata.executionId, agentModule.applicationInfo, securityMetadata.request, securityMetadata.eventType, traceObject.sourceDetails.source, securityMetadata.eventCategory, metaData, stakTrace, apiId, isAPIBlocked);
99
+ event.metaData.isClientDetectedFromXFF = isClientDetectedFromXFF;
100
+
101
+ if (!firstEvent) {
102
+ initLogger.info(LOG_MESSAGES.FIRST_EVENT_PROCESSED, JSON.stringify(event));
103
+ firstEvent = true;
104
+ }
105
+
106
+ return event;
107
+ } catch (e) {
108
+ statusUtils.addErrortoBuffer(e);
109
+ logger.error("Error in generating event:", e);
110
+ }
111
+
112
+ }
113
+
114
+ /**
115
+ *
116
+ * @param {current context} context
117
+ * @param {Sec agent module} agentModule
118
+ * Utility to generate exit events.
119
+ */
120
+ function generateExitEvent(secEvent) {
121
+ if (secEvent && secEvent.httpRequest && secEvent.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
122
+ try {
123
+ const fuzzHeader = secEvent.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID];
124
+ const apiId = fuzzHeader.split(COLON)[0].trim();
125
+ if (fuzzHeader.includes(VULNERABLE) && apiId === secEvent.apiId) {
126
+ const exitEvent = {};
127
+ exitEvent.jsonName = EXITEVENT;
128
+ exitEvent.applicationUUID = secEvent.applicationUUID;
129
+ exitEvent.groupName = secEvent.groupName;
130
+ exitEvent.nodeId = secEvent.nodeId;
131
+ exitEvent.jsonVersion = secEvent.jsonVersion;
132
+ exitEvent.buildNumber = secEvent.buildNumber;
133
+ exitEvent.policyVersion = secEvent.policyVersion;
134
+ exitEvent.executionId = secEvent.id;
135
+ exitEvent.caseType = secEvent.caseType;
136
+ exitEvent.k2RequestIdentifier = secEvent.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID];
137
+ sendEvent(exitEvent);
138
+ }
139
+ } catch (error) {
140
+ logger.error('Error in generating exit event', error);
141
+ }
142
+ }
143
+ }
144
+
145
+
146
+ function sendEvent(secEvent) {
147
+ const agentModule = Agent.getAgent();
148
+ if (agentModule.status.getStatus() === 'active' && NRAgent.config.security.enabled) {
149
+ agentModule.client.dispatcher(secEvent);
150
+ if (!firstEventSent) {
151
+ initLogger.info(LOG_MESSAGES.FIRST_EVENT_SENT, JSON.stringify(secEvent));
152
+ firstEventSent = true;
153
+ }
154
+ }
155
+
156
+ }
157
+
158
+ module.exports = {
159
+ setLogger,
160
+ getSecEventEmitter: getSecEventEmitter,
161
+ promisify,
162
+ sleep,
163
+ generateSecEvent,
164
+ sendEvent,
165
+ generateExitEvent
166
+ };
@@ -0,0 +1,338 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('fs');
9
+ const hash = require('hash.js');
10
+ const path = require('path');
11
+ const async_hooks = require('async_hooks');
12
+
13
+ const INCLUDED_FILE_EXT_REGEX = /(\.)(js|ts|ejs|html|properties|yml|yaml|json|hbs)$/;
14
+ const TWO_PIPES = '||';
15
+ const HEX_DIGEST = 'hex';
16
+ const thresh = 1024;
17
+ const units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
18
+
19
+ let logger;
20
+ let CurrentPromise = global.Promise;
21
+
22
+ module.exports = { getApplicationSHAAndSize, getApplicationSHAAndSizeSync, getSHA256ForFile, getSHA256ForFileSync, setPromise, setLogger, getSHA256ForData };
23
+
24
+ /**
25
+ * This is a utility which is used to calculate
26
+ * sha256 for the complete application context.
27
+ */
28
+ async function getApplicationSHAAndSize (appPath) {
29
+ if (CurrentPromise) {
30
+ try {
31
+ const shaMap = new Map();
32
+ const shaAndSizeObj = await getSHA256AndSizeForPath(appPath, shaMap);
33
+ return {
34
+ sha256: getSHA256ForData(shaAndSizeObj.shaList.join(TWO_PIPES)),
35
+ size: getHumanReadableSize(shaAndSizeObj.sizeList.reduce((result, value) => {
36
+ return result + value;
37
+ }, 0)),
38
+ shaMap: shaMap
39
+ };
40
+ } catch (err) {
41
+ logger.error('Error while calculating SHA and size for application!');
42
+ logger.debug(err);
43
+ return {};
44
+ }
45
+ } else {
46
+ logger.debug('Promise haven\'t been set!');
47
+ return {};
48
+ }
49
+ }
50
+
51
+ function getApplicationSHAAndSizeSync (appPath, skipRegex = null) {
52
+ try {
53
+ const shaMap = new Map();
54
+ const shaAndSizeObj = getSHA256AndSizeForPathSync(appPath, shaMap, skipRegex);
55
+ return {
56
+ sha256: getSHA256ForData(shaAndSizeObj.shaList.join(TWO_PIPES)),
57
+ size: getHumanReadableSize(shaAndSizeObj.sizeList.reduce((result, value) => {
58
+ return result + value;
59
+ }, 0)),
60
+ shaMap: shaMap
61
+ };
62
+ } catch (err) {
63
+ logger.error('Error while calculating SHA and size for application!');
64
+ logger.debug(err);
65
+ return {};
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Gets sha256 and Size for all the elements of
71
+ * given filePath.
72
+ *
73
+ * @param {String} filePath
74
+ */
75
+ async function getSHA256AndSizeForPath (filePath, shaMap) {
76
+ const pathStatObject = {};
77
+ pathStatObject.shaList = [];
78
+ pathStatObject.sizeList = [];
79
+ try {
80
+ const stats = await getStats(filePath);
81
+ if (stats.isDirectory()) {
82
+ const fileList = await getSortedDirContent(filePath);
83
+ if (fileList) {
84
+ for (const file of fileList) {
85
+ const calledStateObject = await getSHA256AndSizeForPath(file, shaMap);
86
+ pathStatObject.shaList = pathStatObject.shaList.concat(calledStateObject.shaList);
87
+ pathStatObject.sizeList = pathStatObject.sizeList.concat(calledStateObject.sizeList);
88
+ }
89
+ }
90
+ } else if (filePath.match(INCLUDED_FILE_EXT_REGEX)) {
91
+ const sha = await getSHA256ForFile(filePath);
92
+ const fileSizeInBytes = stats.size;
93
+ if (sha && sha !== null) {
94
+ pathStatObject.shaList.push(sha);
95
+ if (fileSizeInBytes) {
96
+ pathStatObject.sizeList.push(fileSizeInBytes);
97
+ }
98
+ }
99
+ }
100
+ } catch (err) {
101
+ logger.error('Something bad happened while geting sha for ' + filePath);
102
+ logger.debug(err);
103
+ }
104
+ return pathStatObject;
105
+ }
106
+
107
+ /**
108
+ * Gets sha256 and Size for all the elements of
109
+ * given filePath Synchronusly.
110
+ *
111
+ * @param {String} filePath
112
+ */
113
+ function getSHA256AndSizeForPathSync (filePath, shaMap, skipRegex) {
114
+ const pathStatObject = {
115
+ shaList: [],
116
+ sizeList: []
117
+ };
118
+ if (skipRegex && filePath && filePath.match(skipRegex)) {
119
+ return pathStatObject;
120
+ }
121
+ try {
122
+ const stats = getStatsSync(filePath);
123
+ if (stats.isDirectory()) {
124
+ const fileList = getSortedDirContentSync(filePath);
125
+ if (fileList) {
126
+ for (const file of fileList) {
127
+ const calledStateObject = getSHA256AndSizeForPathSync(file, shaMap, skipRegex);
128
+ pathStatObject.shaList = pathStatObject.shaList.concat(calledStateObject.shaList);
129
+ pathStatObject.sizeList = pathStatObject.sizeList.concat(calledStateObject.sizeList);
130
+ }
131
+ }
132
+ } else if (filePath.match(INCLUDED_FILE_EXT_REGEX)) {
133
+ const sha = getSHA256ForFileSync(filePath);
134
+ const fileSizeInBytes = stats.size;
135
+ if (sha && sha !== null) {
136
+ pathStatObject.shaList.push(sha);
137
+ if (fileSizeInBytes) {
138
+ pathStatObject.sizeList.push(fileSizeInBytes);
139
+ }
140
+ }
141
+ }
142
+ } catch (err) {
143
+ logger.error('Something bad happened while geting sha for ' + filePath);
144
+ logger.debug(err);
145
+ }
146
+ return pathStatObject;
147
+ }
148
+
149
+ /**
150
+ * Gets all the source and prop files
151
+ * in the passed directory.
152
+ * @param {String} dirPath
153
+ */
154
+ async function getSortedDirContent (dirPath) {
155
+ return new CurrentPromise(async function (resolve, reject) {
156
+ try {
157
+ const stats = await getStats(dirPath);
158
+ if (stats.isDirectory()) {
159
+ fs.readdir(dirPath, function (err, files) {
160
+ if (err) {
161
+ logger.error('Unable to get directory content: ', dirPath);
162
+ reject(err);
163
+ }
164
+
165
+ // resolve file paths
166
+ for (let counter = 0; counter < files.length; counter++) {
167
+ files[counter] = path.resolve(dirPath, files[counter]);
168
+ }
169
+
170
+ // sort
171
+ files = files.sort();
172
+
173
+ resolve(files);
174
+ });
175
+ } else {
176
+ const err = new Error('Not a directory: ' + dirPath);
177
+ logger.error(err);
178
+ reject(err);
179
+ }
180
+ } catch (err) {
181
+ logger.error('Unable to get directory content: ', dirPath);
182
+ reject(err);
183
+ }
184
+ });
185
+ }
186
+
187
+ /**
188
+ * Gets all the source and prop files
189
+ * in the passed directory Sync.
190
+ * @param {String} dirPath
191
+ */
192
+ function getSortedDirContentSync (dirPath) {
193
+ try {
194
+ const stats = getStatsSync(dirPath);
195
+ if (stats.isDirectory()) {
196
+ try {
197
+ let files = fs.readdirSync(dirPath);
198
+
199
+ // resolve file paths
200
+ for (let counter = 0; counter < files.length; counter++) {
201
+ files[counter] = path.resolve(dirPath, files[counter]);
202
+ }
203
+
204
+ // sort
205
+ files = files.sort();
206
+
207
+ return files;
208
+ } catch (err) {
209
+ logger.error('Unable to get directory content: ', dirPath);
210
+ throw err;
211
+ }
212
+ } else {
213
+ const err = new Error('Not a directory: ' + dirPath);
214
+ logger.error(err);
215
+ throw err;
216
+ }
217
+ } catch (err) {
218
+ logger.error('Unable to get directory content: ', dirPath);
219
+ throw err;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Calculates SHA256 for a file.
225
+ * @param {String} filePath
226
+ */
227
+ async function getSHA256ForFile (filePath) {
228
+ if (CurrentPromise) {
229
+ return new CurrentPromise((resolve, reject) => {
230
+ if (filePath) {
231
+ } else {
232
+ const err = new Error('Empty FilePath!');
233
+ reject(err);
234
+ }
235
+ });
236
+ } else {
237
+ logger.debug('Promise haven\'t been set!');
238
+ return {};
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Calculates SHA256 for a file synchronously.
244
+ * @param {String} filePath
245
+ */
246
+ function getSHA256ForFileSync (filePath) {
247
+ if (!filePath) {
248
+ throw new Error('Empty FilePath!');
249
+ }
250
+ try {
251
+ const fd = fs.openSync(filePath);
252
+ const content = fs.readFileSync(fd, { encoding: 'UTF-8' });
253
+ fs.closeSync(fd);
254
+ return getSHA256ForData(content);
255
+ } catch (err) {
256
+ logger.error('Unable to calculate SHA256 for file: ' + filePath);
257
+ throw err;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Generates SHA256 for passed data
263
+ * @param {String} data
264
+ */
265
+ function getSHA256ForData (data) {
266
+ try {
267
+ if (!process.traceMap) {
268
+ return hash.sha256().update(data).digest(HEX_DIGEST); ;
269
+ }
270
+ const cachedSha = process.traceMap.get(data);
271
+
272
+ if (cachedSha) {
273
+ return cachedSha;
274
+ } else {
275
+ const calSha = hash.sha256().update(data).digest(HEX_DIGEST);
276
+ if (process.traceMap.size < 2000 && (httpreq && httpreq.data)) {
277
+ process.traceMap.set(data, calSha);
278
+ }
279
+ return calSha;
280
+ }
281
+ } catch (err) {
282
+ logger.error('Unable to calculate SHA256 for data.');
283
+ logger.debug(err);
284
+ return null;
285
+ }
286
+ }
287
+
288
+ /**
289
+ * returns HumanReadable form of passed Bytes
290
+ * @param {Number} bytes
291
+ */
292
+ function getHumanReadableSize (bytes) {
293
+ if (Math.abs(bytes) < thresh) {
294
+ return bytes + ' B';
295
+ }
296
+ let u = -1;
297
+ do {
298
+ bytes /= thresh;
299
+ ++u;
300
+ } while (Math.abs(bytes) >= thresh && u < units.length - 1);
301
+ return bytes.toFixed(1) + ' ' + units[u];
302
+ }
303
+
304
+ /**
305
+ * Sets logger for this module.
306
+ */
307
+ function setLogger (logr) {
308
+ logger = logr;
309
+ }
310
+
311
+ /**
312
+ * Sets promise for this module.
313
+ */
314
+ function setPromise (promise) {
315
+ CurrentPromise = promise;
316
+ }
317
+
318
+ /**
319
+ * Checks if the given path represents a directory
320
+ */
321
+ async function getStats (path) {
322
+ return new CurrentPromise((resolve, reject) => {
323
+ fs.lstat(path, function (err, stat) {
324
+ if (err) {
325
+ logger.error('Unable to get stats for: ', path);
326
+ reject(err);
327
+ }
328
+ resolve(stat);
329
+ });
330
+ });
331
+ }
332
+
333
+ /**
334
+ * Checks if the given path represents a directory
335
+ */
336
+ function getStatsSync (path) {
337
+ return fs.lstatSync(path);
338
+ }
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const { workerData, parentPort } = require('worker_threads');
7
+ const shaSizeUtil = require('./sha-size-util');
8
+ const logs = require('./logging');
9
+ const logger = logs.getLogger(workerData.applicationUUID);
10
+ shaSizeUtil.setLogger(logger);
11
+ const MESSAGE = 'message';
12
+
13
+ async function computeSHA (path) {
14
+ logger.info('Started computing SHA and size in worker thread');
15
+ const data = await shaSizeUtil.getApplicationSHAAndSize(path, global.Promise);
16
+ parentPort.postMessage(data);
17
+ parentPort.unref();
18
+ logger.debug('Terminating SHA worker');
19
+ process.exit(0);
20
+ }
21
+
22
+ parentPort.on(MESSAGE, (param) => {
23
+ computeSHA(param);
24
+ });