@contrast/protect 1.9.1 → 1.11.0
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.
|
@@ -29,6 +29,26 @@ module.exports = function(core) {
|
|
|
29
29
|
|
|
30
30
|
const express4ErrorHandler = protect.errorHandlers.express4ErrorHandler = {};
|
|
31
31
|
|
|
32
|
+
function aroundFn(name) {
|
|
33
|
+
return function around(orig, data) {
|
|
34
|
+
const [err] = data.args;
|
|
35
|
+
const sourceContext = protect.getSourceContext(name);
|
|
36
|
+
const isSecurityException = SecurityException.isSecurityException(err);
|
|
37
|
+
|
|
38
|
+
if (isSecurityException && sourceContext) {
|
|
39
|
+
const blockInfo = sourceContext.securityException;
|
|
40
|
+
|
|
41
|
+
sourceContext.block(...blockInfo);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!sourceContext && isSecurityException) {
|
|
46
|
+
logger.info('source context not found; unable to handle response');
|
|
47
|
+
}
|
|
48
|
+
return orig();
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
32
52
|
express4ErrorHandler.install = function () {
|
|
33
53
|
depHooks.resolve({ name: 'finalhandler' }, (finalhandler) =>
|
|
34
54
|
patcher.patch(finalhandler, {
|
|
@@ -38,23 +58,7 @@ module.exports = function(core) {
|
|
|
38
58
|
data.result = patcher.patch(data.result, {
|
|
39
59
|
name: 'finalHandler.returnedFunction',
|
|
40
60
|
patchType,
|
|
41
|
-
around(
|
|
42
|
-
const [err] = data.args;
|
|
43
|
-
const sourceContext = protect.getSourceContext('finalHandler');
|
|
44
|
-
const isSecurityException = SecurityException.isSecurityException(err);
|
|
45
|
-
|
|
46
|
-
if (isSecurityException && sourceContext) {
|
|
47
|
-
const blockInfo = sourceContext.securityException;
|
|
48
|
-
|
|
49
|
-
sourceContext.block(...blockInfo);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!sourceContext && isSecurityException) {
|
|
54
|
-
logger.info('source context not found; unable to handle response');
|
|
55
|
-
}
|
|
56
|
-
return orig();
|
|
57
|
-
},
|
|
61
|
+
around: aroundFn('finalHandler')
|
|
58
62
|
});
|
|
59
63
|
},
|
|
60
64
|
})
|
|
@@ -64,23 +68,7 @@ module.exports = function(core) {
|
|
|
64
68
|
patcher.patch(Layer.prototype, 'handle_error', {
|
|
65
69
|
name: 'Layer.prototype.handle_error',
|
|
66
70
|
patchType,
|
|
67
|
-
around(
|
|
68
|
-
const [err] = data.args;
|
|
69
|
-
const sourceContext = protect.getSourceContext('express.Layer.handle_error');
|
|
70
|
-
const isSecurityException = SecurityException.isSecurityException(err);
|
|
71
|
-
|
|
72
|
-
if (isSecurityException && sourceContext) {
|
|
73
|
-
const blockInfo = sourceContext.securityException;
|
|
74
|
-
|
|
75
|
-
sourceContext.block(...blockInfo);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!sourceContext && isSecurityException) {
|
|
80
|
-
logger.info('source context not found; unable to handle response');
|
|
81
|
-
}
|
|
82
|
-
return orig();
|
|
83
|
-
}
|
|
71
|
+
around: aroundFn('express.Layer.handle_error')
|
|
84
72
|
});
|
|
85
73
|
|
|
86
74
|
// This should be revisited after the research ticket NODE-2556
|
package/lib/index.d.ts
CHANGED
|
@@ -26,28 +26,6 @@ type Http = typeof http;
|
|
|
26
26
|
type Https = typeof https;
|
|
27
27
|
|
|
28
28
|
export type Block = (mode: string, ruleId: string) => void;
|
|
29
|
-
export class HttpInstrumentation {
|
|
30
|
-
messages: Messages;
|
|
31
|
-
scope: Sources;
|
|
32
|
-
config: Config;
|
|
33
|
-
logger: Logger;
|
|
34
|
-
depHooks: RequireHook;
|
|
35
|
-
protect: ProtectMessage;
|
|
36
|
-
makeSourceContext: Protect['makeSourceContext'];
|
|
37
|
-
maxBodySize: number;
|
|
38
|
-
installed: boolean;
|
|
39
|
-
|
|
40
|
-
constructor(core: any);
|
|
41
|
-
|
|
42
|
-
install(): void;
|
|
43
|
-
uninstall(): void; //NYI
|
|
44
|
-
hookHttp(): void;
|
|
45
|
-
hookHttps(): void;
|
|
46
|
-
hookServer(xport: Http | Https): void;
|
|
47
|
-
initiateRequestHandling(fnContext: { instance: any, method: any, args: any }): void; //TODO
|
|
48
|
-
removeCookies(headers: string[]): string[];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
29
|
export interface ProtectRequestStore {
|
|
52
30
|
reqData: ReqData;
|
|
53
31
|
block: Block;
|
|
@@ -95,7 +73,7 @@ export interface Protect {
|
|
|
95
73
|
expressInstrumentation: { install: () => void },
|
|
96
74
|
fastifyInstrumentation: { install: () => void },
|
|
97
75
|
koaInstrumentation: { install: () => void },
|
|
98
|
-
httpInstrumentation:
|
|
76
|
+
httpInstrumentation: { install: () => void },
|
|
99
77
|
install: () => void,
|
|
100
78
|
},
|
|
101
79
|
inputTracing: {
|
|
@@ -15,203 +15,57 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
+
const { patchType } = require('../constants');
|
|
18
19
|
const { Event } = require('@contrast/common');
|
|
19
20
|
|
|
20
|
-
// Instruments http `Server` and `IncomingMessage` instances to support input
|
|
21
|
-
// analysis in framework-agnostic manner.
|
|
22
|
-
|
|
23
21
|
module.exports = function(core) {
|
|
24
|
-
const {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.makeSourceContext = this.protect.makeSourceContext;
|
|
38
|
-
this.maxBodySize = 16 * 1024 * 1024;
|
|
39
|
-
this.installed = false;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* After checking whether the sensor is enabled, will set up `require` hooks
|
|
44
|
-
* for instrumenting both `http` and `https` modules when they load.
|
|
45
|
-
*/
|
|
46
|
-
install() {
|
|
47
|
-
if (this.installed) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
this.installed = true;
|
|
52
|
-
this.hookHttp();
|
|
53
|
-
this.hookHttps();
|
|
54
|
-
this.hookHttp2();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
uninstall() {
|
|
58
|
-
return null; //NYI
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Sets hooks to instrument `http.Server.prototype`.
|
|
63
|
-
*/
|
|
64
|
-
hookHttp() {
|
|
65
|
-
this.logger.debug('hooking library: http');
|
|
66
|
-
this.depHooks.resolve({ name: 'http' }, (http) => this.hookServerEmit.call(this, http, 'httpServer'));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Sets hooks to instrument `https.Server.prototype`.
|
|
71
|
-
*/
|
|
72
|
-
hookHttps() {
|
|
73
|
-
this.logger.debug('hooking library: https');
|
|
74
|
-
this.depHooks.resolve({ name: 'https' }, (https) => this.hookServerEmit.call(this, https, 'httpsServer'));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Sets hooks to instrument `http2 Servers`.
|
|
79
|
-
*/
|
|
80
|
-
hookHttp2() {
|
|
81
|
-
this.logger.debug('hooking library: http2');
|
|
82
|
-
// http2 library does not expose its Server class, so we need to hook the createServer function
|
|
83
|
-
this.depHooks.resolve({ name: 'http2' }, (http2) => this.hookCreateServer.call(this, http2, 'http2Server'));
|
|
84
|
-
this.depHooks.resolve({ name: 'http2' }, (http2) => this.hookCreateServer.call(this, http2, 'http2SecureServer', 'createSecureServer'));
|
|
85
|
-
|
|
86
|
-
this.logger.debug('hooking library: spdy');
|
|
87
|
-
this.depHooks.resolve({ name: 'spdy' }, (spdy) => this.hookServerEmit.call(this, spdy, 'spdyServer'));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Instruments the `Server` prototype from `http(s)` or spdy's http2 Server. This patches `emit` and
|
|
92
|
-
* invokes the protect service to do analysis when appropriate.
|
|
93
|
-
*/
|
|
94
|
-
hookServerEmit(serverSource, sourceName) {
|
|
95
|
-
serverSource.Server.prototype = this.patcher.patch(serverSource.Server.prototype, 'emit', {
|
|
96
|
-
name: `${sourceName}.Server.prototype.emit`,
|
|
97
|
-
patchType: 'initiate-handling',
|
|
98
|
-
around: this.emitAroundHook.bind(this)
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Instruments the `Http2Server` prototype which results from the http2.createServer/createSecureServer() call.
|
|
104
|
-
* This also patches `emit` and
|
|
105
|
-
* invokes the protect service to do analysis when appropriate.
|
|
106
|
-
*/
|
|
107
|
-
hookCreateServer(serverSource, sourceName, constructorName = 'createServer') {
|
|
108
|
-
const self = this;
|
|
109
|
-
|
|
110
|
-
return this.patcher.patch(serverSource, constructorName, {
|
|
111
|
-
name: sourceName,
|
|
112
|
-
patchType: 'initiate-handling',
|
|
113
|
-
post(data) {
|
|
114
|
-
|
|
115
|
-
const { result: server } = data;
|
|
116
|
-
const serverPrototype = server ? Object.getPrototypeOf(server) : null;
|
|
117
|
-
|
|
118
|
-
if (!serverPrototype) {
|
|
119
|
-
self.logger.error('Unable to patch server prototype, continue without instrumentation');
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
self.patcher.patch(serverPrototype, 'emit', {
|
|
124
|
-
name: `${sourceName}.Server.prototype.emit`,
|
|
125
|
-
patchType: 'req-async-storage',
|
|
126
|
-
around: self.emitAroundHook.bind(self)
|
|
127
|
-
});
|
|
22
|
+
const {
|
|
23
|
+
logger,
|
|
24
|
+
messages,
|
|
25
|
+
scopes: { sources },
|
|
26
|
+
instrumentation: { instrument },
|
|
27
|
+
protect: { inputAnalysis },
|
|
28
|
+
} = core;
|
|
29
|
+
|
|
30
|
+
function removeCookies(headers) {
|
|
31
|
+
for (let i = 0; i < headers.length; i += 2) {
|
|
32
|
+
if (headers[i] === 'cookies') {
|
|
33
|
+
headers = headers.slice();
|
|
34
|
+
headers.splice(i, 2);
|
|
128
35
|
}
|
|
129
|
-
}
|
|
36
|
+
}
|
|
37
|
+
return headers;
|
|
130
38
|
}
|
|
131
39
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*/
|
|
136
|
-
emitAroundHook(next, data) {
|
|
137
|
-
const [type] = data.args;
|
|
40
|
+
function around(next, data) {
|
|
41
|
+
let store, block;
|
|
42
|
+
const { args: [type, req, res] } = data;
|
|
138
43
|
|
|
139
44
|
if (type !== 'request') {
|
|
140
45
|
return next();
|
|
141
46
|
}
|
|
142
47
|
|
|
143
|
-
const context = { instance: data.obj, method: next, args: data.args };
|
|
144
|
-
this.initiateRequestHandling(context);
|
|
145
|
-
return !!data.obj._events[type];
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Creates the sourceContext for the request and invokes the handler for
|
|
150
|
-
* inputs that are present in the 'incomingMessage' object at the time of
|
|
151
|
-
* the 'connect' event.
|
|
152
|
-
*
|
|
153
|
-
* @param {Object} context Function invocation context
|
|
154
|
-
*/
|
|
155
|
-
initiateRequestHandling(fnContext) {
|
|
156
|
-
const {
|
|
157
|
-
instance,
|
|
158
|
-
method,
|
|
159
|
-
args,
|
|
160
|
-
args: [, req, res]
|
|
161
|
-
} = fnContext;
|
|
162
|
-
|
|
163
|
-
let store;
|
|
164
|
-
let block;
|
|
165
|
-
|
|
166
48
|
try {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// this must be invoked by the patching code using scope.sources.run({}, ...)
|
|
170
|
-
// so that an async context is present.
|
|
171
|
-
store = this.scope.getStore();
|
|
172
|
-
// nothing can be done if async context is not available.
|
|
173
|
-
|
|
49
|
+
store = sources.getStore();
|
|
174
50
|
if (!store) {
|
|
175
|
-
|
|
176
|
-
setImmediate(() => method.call(instance, ...args));
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
store.protect = this.makeSourceContext(req, res);
|
|
180
|
-
|
|
181
|
-
if (store.protect.allowed) {
|
|
182
|
-
setImmediate(() => method.call(instance, ...args));
|
|
51
|
+
logger.debug('cannot acquire store for around()');
|
|
183
52
|
return;
|
|
184
53
|
}
|
|
185
54
|
|
|
186
|
-
|
|
55
|
+
store.protect = core.protect.makeSourceContext(req, res);
|
|
56
|
+
const {
|
|
57
|
+
reqData: { headers, uriPath, method }
|
|
58
|
+
} = store.protect;
|
|
187
59
|
|
|
188
60
|
res.on('finish', () => {
|
|
189
|
-
|
|
61
|
+
inputAnalysis.handleRequestEnd(store.protect);
|
|
190
62
|
messages.emit(Event.PROTECT, store);
|
|
191
63
|
});
|
|
192
64
|
|
|
193
|
-
// don't put inputs in the store; they are a param to each handler. findings
|
|
194
|
-
// associated with inputs do go into the store. why not put the inputs
|
|
195
|
-
// into the store? after all, the inputs come from the store. mostly because
|
|
196
|
-
// they can really add up to a lot of data that isn't going to be used.
|
|
197
|
-
//
|
|
198
|
-
// how to replace result in resultsList, e.g., queries find something
|
|
199
|
-
// but then framework emits parsed queries? does this only matter for
|
|
200
|
-
// no-sql? index-lookup or hash?
|
|
201
|
-
//
|
|
202
|
-
// create inputs for this handler. we defer cookies until the framework
|
|
203
|
-
// parses them because there is no way to be certain of their formatting
|
|
204
|
-
// and encoding.
|
|
205
|
-
//
|
|
206
|
-
// the primary reason for this is to avoid passing the incomingMessage,
|
|
207
|
-
// req, to all the handlers allowing direct access to it and tightly
|
|
208
|
-
// coupling all handlers to an extensive collection of data.
|
|
209
65
|
const connectInputs = {
|
|
210
|
-
headers:
|
|
211
|
-
uriPath
|
|
212
|
-
|
|
213
|
-
// TODO AGENT-203 - need to handle method-tampering rule.
|
|
214
|
-
method: reqData.method,
|
|
66
|
+
headers: removeCookies(headers),
|
|
67
|
+
uriPath,
|
|
68
|
+
method
|
|
215
69
|
};
|
|
216
70
|
|
|
217
71
|
if (inputAnalysis.virtualPatchesEvaluators?.length) {
|
|
@@ -229,25 +83,61 @@ class HttpInstrumentation {
|
|
|
229
83
|
|
|
230
84
|
block = block || inputAnalysis.handleConnect(store.protect, connectInputs);
|
|
231
85
|
} catch (err) {
|
|
232
|
-
|
|
86
|
+
logger.error({ err }, 'Error during input analysis');
|
|
233
87
|
}
|
|
234
88
|
|
|
235
89
|
if (!block) {
|
|
236
|
-
setImmediate(() =>
|
|
90
|
+
setImmediate(() => next.call(data.obj, ...data.args));
|
|
237
91
|
} else {
|
|
238
92
|
store.protect.block(...block);
|
|
239
|
-
|
|
93
|
+
logger.debug({ block }, 'request blocked by not emitting request event');
|
|
240
94
|
}
|
|
241
95
|
}
|
|
242
96
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
97
|
+
function install() {
|
|
98
|
+
[{
|
|
99
|
+
moduleName: 'http'
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
moduleName: 'https'
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
moduleName: 'spdy'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
moduleName: 'http2',
|
|
109
|
+
patchObjectsProps: [
|
|
110
|
+
{
|
|
111
|
+
methods: ['createServer', 'createSecureServer'],
|
|
112
|
+
patchType,
|
|
113
|
+
patchObjects: [
|
|
114
|
+
{
|
|
115
|
+
name: 'Server.prototype',
|
|
116
|
+
methods: ['emit'],
|
|
117
|
+
patchType,
|
|
118
|
+
around
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}].forEach(({ moduleName, patchObjectsProps }) => {
|
|
124
|
+
const patchObjects = patchObjectsProps || [
|
|
125
|
+
{
|
|
126
|
+
name: 'Server.prototype',
|
|
127
|
+
methods: ['emit'],
|
|
128
|
+
patchType,
|
|
129
|
+
around
|
|
130
|
+
}
|
|
131
|
+
];
|
|
132
|
+
instrument({
|
|
133
|
+
moduleName,
|
|
134
|
+
patchObjects
|
|
135
|
+
});
|
|
136
|
+
});
|
|
252
137
|
}
|
|
253
|
-
|
|
138
|
+
|
|
139
|
+
return inputAnalysis.httpInstrumentation = {
|
|
140
|
+
install,
|
|
141
|
+
around
|
|
142
|
+
};
|
|
143
|
+
};
|
|
@@ -149,9 +149,8 @@ module.exports = function(core) {
|
|
|
149
149
|
if (typeof sinkContext.value === 'object') {
|
|
150
150
|
traverseKeysAndValues(sinkContext.value, function(path, type, value) {
|
|
151
151
|
if (type !== 'Key' && !agentLib.isMongoQueryType(value)) return;
|
|
152
|
-
|
|
153
|
-
stringFindings = handleStringValue(result,
|
|
154
|
-
|
|
152
|
+
const cmdVal = sinkContext.value[value];
|
|
153
|
+
stringFindings = handleStringValue(result, cmdVal?.['$function']?.body || cmdVal, agentLib);
|
|
155
154
|
// halt traversal
|
|
156
155
|
return true;
|
|
157
156
|
});
|
|
@@ -289,9 +288,11 @@ function handleStringValue(result, cmd, agentLib) {
|
|
|
289
288
|
if (typeof cmd !== 'string') {
|
|
290
289
|
return null;
|
|
291
290
|
}
|
|
291
|
+
|
|
292
292
|
let findings = null;
|
|
293
|
+
let inputIndex = -1;
|
|
294
|
+
inputIndex = cmd.indexOf(result.value);
|
|
293
295
|
|
|
294
|
-
const inputIndex = cmd.indexOf(result.value);
|
|
295
296
|
// if the user input is not in the sink input, there is nothing to do.
|
|
296
297
|
if (inputIndex === -1) {
|
|
297
298
|
return findings;
|
|
@@ -27,105 +27,49 @@ module.exports = function(core) {
|
|
|
27
27
|
protect: { inputTracing }
|
|
28
28
|
} = core;
|
|
29
29
|
|
|
30
|
+
function pre({ args, hooked, orig, name }) {
|
|
31
|
+
if (instrumentation.isLocked()) return;
|
|
32
|
+
|
|
33
|
+
const sourceContext = protect.getSourceContext(name);
|
|
34
|
+
if (!sourceContext) return;
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < (name === 'vm.runInNewContext' ? 2 : 1); i++) {
|
|
37
|
+
const arg = args[i];
|
|
38
|
+
if (!((arg && isString(arg)) || isNonEmptyObject(arg))) continue;
|
|
39
|
+
|
|
40
|
+
const sinkContext = {
|
|
41
|
+
name,
|
|
42
|
+
value: arg,
|
|
43
|
+
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] },
|
|
44
|
+
};
|
|
45
|
+
inputTracing.ssjsInjection(sourceContext, sinkContext);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
function install() {
|
|
31
50
|
depHooks.resolve({ name: 'vm' }, (vm) => {
|
|
32
|
-
[
|
|
51
|
+
[
|
|
52
|
+
'Script',
|
|
53
|
+
'createScript',
|
|
54
|
+
'runInContext',
|
|
55
|
+
'runInThisContext',
|
|
56
|
+
'createContext',
|
|
57
|
+
'runInNewContext'
|
|
58
|
+
].forEach(
|
|
33
59
|
(method) => {
|
|
34
60
|
const name = `vm.${method}`;
|
|
35
|
-
|
|
36
61
|
patcher.patch(vm, method, {
|
|
37
62
|
name,
|
|
38
63
|
patchType,
|
|
39
|
-
pre
|
|
40
|
-
if (instrumentation.isLocked()) return;
|
|
41
|
-
|
|
42
|
-
const sourceContext = protect.getSourceContext(name);
|
|
43
|
-
if (!sourceContext) return;
|
|
44
|
-
|
|
45
|
-
const codeString = args[0];
|
|
46
|
-
if (!codeString || !isString(codeString)) return;
|
|
47
|
-
|
|
48
|
-
const sinkContext = {
|
|
49
|
-
name,
|
|
50
|
-
value: codeString,
|
|
51
|
-
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] },
|
|
52
|
-
};
|
|
53
|
-
inputTracing.ssjsInjection(sourceContext, sinkContext);
|
|
54
|
-
}
|
|
64
|
+
pre
|
|
55
65
|
});
|
|
56
66
|
}
|
|
57
67
|
);
|
|
58
68
|
|
|
59
|
-
patcher.patch(vm, 'runInNewContext', {
|
|
60
|
-
name: 'vm.runInNewContext',
|
|
61
|
-
patchType,
|
|
62
|
-
pre: ({ args, hooked, orig }) => {
|
|
63
|
-
if (instrumentation.isLocked()) return;
|
|
64
|
-
|
|
65
|
-
const sourceContext = protect.getSourceContext('vm.runInNewContext');
|
|
66
|
-
if (!sourceContext) return;
|
|
67
|
-
|
|
68
|
-
const codeString = args[0];
|
|
69
|
-
const envObj = args[1];
|
|
70
|
-
|
|
71
|
-
if ((!codeString || !isString(codeString)) && (!isNonEmptyObject(envObj))) return;
|
|
72
|
-
|
|
73
|
-
const codeStringSinkContext = (codeString && isString(codeString)) ? {
|
|
74
|
-
name: 'vm.runInNewContext',
|
|
75
|
-
value: codeString,
|
|
76
|
-
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }
|
|
77
|
-
} : null;
|
|
78
|
-
const envObjSinkContext = isNonEmptyObject(envObj) ? {
|
|
79
|
-
name: 'vm.runInNewContext',
|
|
80
|
-
value: envObj,
|
|
81
|
-
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }
|
|
82
|
-
} : null;
|
|
83
|
-
|
|
84
|
-
codeStringSinkContext && inputTracing.ssjsInjection(sourceContext, codeStringSinkContext);
|
|
85
|
-
envObjSinkContext && inputTracing.ssjsInjection(sourceContext, envObjSinkContext);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
patcher.patch(vm, 'createContext', {
|
|
90
|
-
name: 'vm.createContext',
|
|
91
|
-
patchType,
|
|
92
|
-
pre: ({ args, hooked, orig }) => {
|
|
93
|
-
if (instrumentation.isLocked()) return;
|
|
94
|
-
|
|
95
|
-
const sourceContext = protect.getSourceContext('vm.createContext');
|
|
96
|
-
if (!sourceContext) return;
|
|
97
|
-
|
|
98
|
-
const envObj = args[0];
|
|
99
|
-
if (!isNonEmptyObject(envObj)) return;
|
|
100
|
-
|
|
101
|
-
const sinkContext = {
|
|
102
|
-
name: 'vm.createContext',
|
|
103
|
-
value: envObj,
|
|
104
|
-
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] },
|
|
105
|
-
};
|
|
106
|
-
inputTracing.ssjsInjection(sourceContext, sinkContext);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
69
|
patcher.patch(vm.Script.prototype, 'runInNewContext', {
|
|
111
70
|
name: 'vm.Script.prototype.runInNewContext',
|
|
112
71
|
patchType,
|
|
113
|
-
pre
|
|
114
|
-
if (instrumentation.isLocked()) return;
|
|
115
|
-
|
|
116
|
-
const sourceContext = protect.getSourceContext('vm.Script.prototype.runInNewContext');
|
|
117
|
-
if (!sourceContext) return;
|
|
118
|
-
|
|
119
|
-
const envObj = args[0];
|
|
120
|
-
if (!isNonEmptyObject(envObj)) return;
|
|
121
|
-
|
|
122
|
-
const sinkContext = {
|
|
123
|
-
name: 'vm.Script.prototype.runInNewContext',
|
|
124
|
-
value: envObj,
|
|
125
|
-
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] },
|
|
126
|
-
};
|
|
127
|
-
inputTracing.ssjsInjection(sourceContext, sinkContext);
|
|
128
|
-
}
|
|
72
|
+
pre
|
|
129
73
|
});
|
|
130
74
|
});
|
|
131
75
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/protect",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Contrast service providing framework-agnostic Protect support",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@contrast/agent-lib": "^5.1.0",
|
|
21
|
-
"@contrast/common": "1.
|
|
22
|
-
"@contrast/core": "1.
|
|
23
|
-
"@contrast/esm-hooks": "1.
|
|
21
|
+
"@contrast/common": "1.3.1",
|
|
22
|
+
"@contrast/core": "1.10.0",
|
|
23
|
+
"@contrast/esm-hooks": "1.6.0",
|
|
24
24
|
"@contrast/scopes": "1.2.0",
|
|
25
25
|
"ipaddr.js": "^2.0.1",
|
|
26
26
|
"semver": "^7.3.7"
|
|
27
27
|
}
|
|
28
|
-
}
|
|
28
|
+
}
|