@contrast/core 1.1.1 → 1.3.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.
- package/lib/app-info.js +15 -0
- package/lib/capture-stacktrace.js +33 -11
- package/lib/index.d.ts +16 -1
- package/lib/index.js +16 -1
- package/lib/is-agent-path.js +15 -0
- package/lib/sensitive-data-masking/constants.js +20 -0
- package/lib/sensitive-data-masking/index.js +69 -0
- package/lib/sensitive-data-masking/protect-listener.js +99 -0
- package/lib/sensitive-data-masking/server-settings-listener.js +44 -0
- package/package.json +11 -10
- package/lib/cli-rewriter/dependency-rewriter.js +0 -206
- package/lib/cli-rewriter/index.js +0 -24
package/lib/app-info.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
3
18
|
const os = require('os');
|
|
@@ -1,6 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
3
18
|
const process = require('process');
|
|
19
|
+
const fnInspect = require('@contrast/fn-inspect');
|
|
20
|
+
|
|
4
21
|
const EVAL_ORIGIN_REGEX = /\((.*?):(\d+):\d+\)/;
|
|
5
22
|
|
|
6
23
|
module.exports = function(core) {
|
|
@@ -92,7 +109,12 @@ class StacktraceFactory {
|
|
|
92
109
|
}, []);
|
|
93
110
|
}
|
|
94
111
|
|
|
95
|
-
|
|
112
|
+
if (!prependFrames) return ret;
|
|
113
|
+
|
|
114
|
+
return [
|
|
115
|
+
...prependFrames.map((f) => (typeof f == 'function' ? fnInspect.funcInfo(f) : f)),
|
|
116
|
+
...ret
|
|
117
|
+
];
|
|
96
118
|
}
|
|
97
119
|
|
|
98
120
|
return snapshot;
|
|
@@ -120,9 +142,7 @@ class StacktraceFactory {
|
|
|
120
142
|
* @returns {}
|
|
121
143
|
*/
|
|
122
144
|
static makeFrame(callsite) {
|
|
123
|
-
let evalOrigin;
|
|
124
|
-
let file;
|
|
125
|
-
let lineNumber;
|
|
145
|
+
let evalOrigin, file, lineNumber, method, type;
|
|
126
146
|
|
|
127
147
|
if (callsite.isEval()) {
|
|
128
148
|
evalOrigin = StacktraceFactory.formatFileName(callsite.getEvalOrigin());
|
|
@@ -132,13 +152,15 @@ class StacktraceFactory {
|
|
|
132
152
|
file = file || callsite.getFileName();
|
|
133
153
|
lineNumber = lineNumber || callsite.getLineNumber();
|
|
134
154
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
method
|
|
140
|
-
type
|
|
141
|
-
}
|
|
155
|
+
method = callsite.getFunctionName();
|
|
156
|
+
type = callsite.getTypeName();
|
|
157
|
+
|
|
158
|
+
if (method === null && type === 'Object') {
|
|
159
|
+
method = '<anonymous>';
|
|
160
|
+
type = null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { eval: evalOrigin, file, lineNumber, method, type };
|
|
142
164
|
}
|
|
143
165
|
|
|
144
166
|
static formatFileName(fileName = '') {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
import { Agentify } from '@contrast/agentify';
|
|
2
17
|
import { Config } from '@contrast/config';
|
|
3
18
|
import { Logger } from '@contrast/logger';
|
|
@@ -38,7 +53,7 @@ export interface Core {
|
|
|
38
53
|
messages: Messages;
|
|
39
54
|
patcher: Patcher;
|
|
40
55
|
reporter: ReporterBus;
|
|
41
|
-
protect:
|
|
56
|
+
protect: ProtectMessage;
|
|
42
57
|
rewriter: Rewriter;
|
|
43
58
|
scopes: Scopes;
|
|
44
59
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
3
18
|
const EventEmitter = require('events');
|
|
@@ -7,10 +22,10 @@ module.exports = function init(core = {}) {
|
|
|
7
22
|
|
|
8
23
|
require('@contrast/config')(core);
|
|
9
24
|
require('@contrast/logger').default(core);
|
|
25
|
+
require('./sensitive-data-masking')(core);
|
|
10
26
|
require('./app-info')(core);
|
|
11
27
|
require('./is-agent-path')(core);
|
|
12
28
|
require('./capture-stacktrace')(core);
|
|
13
|
-
require('./cli-rewriter')(core);
|
|
14
29
|
require('@contrast/patcher')(core);
|
|
15
30
|
require('@contrast/rewriter')(core);
|
|
16
31
|
require('@contrast/dep-hooks')(core);
|
package/lib/is-agent-path.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
3
18
|
/**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
CONTRAST_REDACTED: 'contrast-redacted',
|
|
20
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { Event, simpleTraverse } = require('@contrast/common');
|
|
19
|
+
const { CONTRAST_REDACTED } = require('./constants');
|
|
20
|
+
|
|
21
|
+
module.exports = function(core) {
|
|
22
|
+
const {
|
|
23
|
+
logger,
|
|
24
|
+
messages
|
|
25
|
+
} = core;
|
|
26
|
+
|
|
27
|
+
const idMap = new Map();
|
|
28
|
+
const keywordSets = [];
|
|
29
|
+
|
|
30
|
+
function getRedactedText(key) {
|
|
31
|
+
key = key.toLowerCase();
|
|
32
|
+
for (const set of keywordSets) {
|
|
33
|
+
if (set.has(key)) {
|
|
34
|
+
return `${CONTRAST_REDACTED}-${idMap.get(set)}`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function traverseAndMask(target) {
|
|
40
|
+
let redactedText;
|
|
41
|
+
if (!target) return;
|
|
42
|
+
|
|
43
|
+
simpleTraverse(target, (path, type, value, obj) => {
|
|
44
|
+
if (type === 'Key') {
|
|
45
|
+
redactedText = getRedactedText(value);
|
|
46
|
+
if (redactedText) {
|
|
47
|
+
obj[value] = redactedText;
|
|
48
|
+
redactedText = undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const sensitiveDataMasking = core.sensitiveDataMasking = {
|
|
55
|
+
policy: {
|
|
56
|
+
idMap,
|
|
57
|
+
keywordSets,
|
|
58
|
+
maskHttpBody: false,
|
|
59
|
+
maskAttackVector: false,
|
|
60
|
+
},
|
|
61
|
+
getRedactedText,
|
|
62
|
+
traverseAndMask
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
require('./server-settings-listener')(core);
|
|
66
|
+
require('./protect-listener')(core);
|
|
67
|
+
|
|
68
|
+
return sensitiveDataMasking;
|
|
69
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { URLSearchParams } = require('url');
|
|
19
|
+
const { Event } = require('@contrast/common');
|
|
20
|
+
|
|
21
|
+
const { CONTRAST_REDACTED } = require('./constants');
|
|
22
|
+
|
|
23
|
+
module.exports = function(core) {
|
|
24
|
+
const {
|
|
25
|
+
messages,
|
|
26
|
+
logger,
|
|
27
|
+
sensitiveDataMasking: {
|
|
28
|
+
policy,
|
|
29
|
+
getRedactedText,
|
|
30
|
+
traverseAndMask
|
|
31
|
+
}
|
|
32
|
+
} = core;
|
|
33
|
+
|
|
34
|
+
messages.on(Event.PROTECT, (msg) => {
|
|
35
|
+
if (!msg.protect || !policy.keywordSets.length) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
logger.trace(`masking sensitive fields in ${Event.PROTECT} message`);
|
|
40
|
+
|
|
41
|
+
if (policy.maskHttpBody) {
|
|
42
|
+
msg.protect.parsedBody = `${CONTRAST_REDACTED}-body`;
|
|
43
|
+
} else {
|
|
44
|
+
traverseAndMask(msg.protect?.parsedBody);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
traverseAndMask(msg.protect?.parsedCookies);
|
|
48
|
+
traverseAndMask(msg.protect?.parsedQuery);
|
|
49
|
+
|
|
50
|
+
// Do parsed URL path params and urlPath together
|
|
51
|
+
const params = msg.protect?.parsedParams;
|
|
52
|
+
if (params) {
|
|
53
|
+
for (const [key, value] of Object.entries(params)) {
|
|
54
|
+
const redactedText = getRedactedText(key);
|
|
55
|
+
if (redactedText) {
|
|
56
|
+
const encoded = encodeURIComponent(value);
|
|
57
|
+
msg.protect.reqData.uriPath = msg.protect.reqData.uriPath.replace(encoded, redactedText);
|
|
58
|
+
msg.protect.parsedParams[key] = redactedText;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// raw headers
|
|
64
|
+
const headers = msg.protect?.reqData.headers;
|
|
65
|
+
for (let i = 0; i <= headers.length - 2; i += 2) {
|
|
66
|
+
const key = headers[i];
|
|
67
|
+
|
|
68
|
+
const redactedText = getRedactedText(key);
|
|
69
|
+
if (redactedText) {
|
|
70
|
+
headers[i + 1] = redactedText;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// raw queries
|
|
75
|
+
if (msg.protect?.reqData?.queries) {
|
|
76
|
+
const searchParams = new URLSearchParams(msg.protect.reqData.queries);
|
|
77
|
+
for (const [key, value] of searchParams) {
|
|
78
|
+
const redactedText = getRedactedText(key);
|
|
79
|
+
if (redactedText) {
|
|
80
|
+
searchParams.set(key, redactedText);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
msg.protect.reqData.queries = searchParams.toString();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (policy.maskAttackVector) {
|
|
87
|
+
// attack values
|
|
88
|
+
const inputAnalysis = Object.entries(msg.protect?.findings.resultsMap);
|
|
89
|
+
for (const [, results] of inputAnalysis) {
|
|
90
|
+
for (const result of results) {
|
|
91
|
+
const redactedText = getRedactedText(result.key);
|
|
92
|
+
if (redactedText) {
|
|
93
|
+
result.value = redactedText;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { Event } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = function(core) {
|
|
21
|
+
const {
|
|
22
|
+
logger,
|
|
23
|
+
messages,
|
|
24
|
+
sensitiveDataMasking: { policy },
|
|
25
|
+
} = core;
|
|
26
|
+
|
|
27
|
+
messages.on(Event.SERVER_SETTINGS_UPDATE, (settingsMsg) => {
|
|
28
|
+
const dtm = settingsMsg?.sensitive_data_masking_policy;
|
|
29
|
+
|
|
30
|
+
if (!dtm) return;
|
|
31
|
+
|
|
32
|
+
logger.trace('updating sensitive data masking policy');
|
|
33
|
+
|
|
34
|
+
policy.maskHttpBody = dtm.mask_http_body;
|
|
35
|
+
policy.maskAttackVector = dtm.mask_attack_vector;
|
|
36
|
+
policy.keywordSets.length = 0;
|
|
37
|
+
policy.idMap.clear();
|
|
38
|
+
dtm.rules.forEach(({ id, keywords }) => {
|
|
39
|
+
const kwSet = new Set(keywords);
|
|
40
|
+
policy.keywordSets.push(kwSet);
|
|
41
|
+
policy.idMap.set(kwSet, id);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Preconfigured Contrast agent core services and models",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -10,21 +10,22 @@
|
|
|
10
10
|
"main": "lib/index.js",
|
|
11
11
|
"types": "lib/index.d.ts",
|
|
12
12
|
"engines": {
|
|
13
|
-
"npm": ">=6.13.7 <7 || >= 8.
|
|
13
|
+
"npm": ">=6.13.7 <7 || >= 8.3.1",
|
|
14
14
|
"node": ">= 14.15.0"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"test": "../scripts/test.sh"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@contrast/agentify": "1.0.
|
|
21
|
-
"@contrast/config": "1.1.
|
|
22
|
-
"@contrast/dep-hooks": "1.0.
|
|
23
|
-
"@contrast/
|
|
24
|
-
"@contrast/
|
|
25
|
-
"@contrast/
|
|
26
|
-
"@contrast/
|
|
27
|
-
"@contrast/
|
|
20
|
+
"@contrast/agentify": "1.0.5",
|
|
21
|
+
"@contrast/config": "1.1.4",
|
|
22
|
+
"@contrast/dep-hooks": "1.0.4",
|
|
23
|
+
"@contrast/fn-inspect": "^3.1.0",
|
|
24
|
+
"@contrast/logger": "1.0.4",
|
|
25
|
+
"@contrast/patcher": "1.0.4",
|
|
26
|
+
"@contrast/reporter": "1.2.0",
|
|
27
|
+
"@contrast/rewriter": "1.0.4",
|
|
28
|
+
"@contrast/scopes": "1.1.1",
|
|
28
29
|
"builtin-modules": "^3.2.0",
|
|
29
30
|
"semver": "^7.3.7"
|
|
30
31
|
}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Module = require('module');
|
|
4
|
-
const process = require('process');
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const util = require('util');
|
|
8
|
-
const builtins = require('builtin-modules');
|
|
9
|
-
const readFile = util.promisify(fs.readFile);
|
|
10
|
-
|
|
11
|
-
module.exports = function(core) {
|
|
12
|
-
const dependencyRewriter = new RecursiveDependencyRewriter(core);
|
|
13
|
-
core.dependencyRewriter = dependencyRewriter;
|
|
14
|
-
return dependencyRewriter;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Ported from node agent lib/cli-rewriter/index.js
|
|
18
|
-
class RecursiveDependencyRewriter {
|
|
19
|
-
/**
|
|
20
|
-
* Receives entrypoint used to initialize agent.
|
|
21
|
-
* Sets up config, logging, agent, and rewriter state.
|
|
22
|
-
* @param {string} entrypoint application's entrypoint script
|
|
23
|
-
*/
|
|
24
|
-
constructor(core) {
|
|
25
|
-
this.deps = core;
|
|
26
|
-
this.logger = core.logger;
|
|
27
|
-
this.rewriter = core.rewriter;
|
|
28
|
-
this.rewriter.visitors.push(RecursiveDependencyRewriter.requireDetector);
|
|
29
|
-
// keep track of files rewritten - don't rewrite if file is required more than once
|
|
30
|
-
this.visited = new Set();
|
|
31
|
-
this.entrypoint = core.config.entrypoint;
|
|
32
|
-
this.filename = path.resolve(process.cwd(), this.entrypoint);
|
|
33
|
-
try {
|
|
34
|
-
fs.statSync(this.filename);
|
|
35
|
-
} catch (err) {
|
|
36
|
-
this.logger.error('entrypoint file not found: %s', this.filename);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Starting at the provided entrypoint will rewrite it and any dependencies
|
|
43
|
-
* detected. Recursively continues until all files are rewritten.
|
|
44
|
-
* @param {string} entrypoint provided application entrypoint script
|
|
45
|
-
*/
|
|
46
|
-
async rewrite() {
|
|
47
|
-
|
|
48
|
-
const parent = RecursiveDependencyRewriter.getModuleData(this.filename);
|
|
49
|
-
const start = Date.now();
|
|
50
|
-
|
|
51
|
-
await this.traverse(this.filename, parent);
|
|
52
|
-
|
|
53
|
-
this.logger.info('rewriting complete [%ss]', (Date.now() - start) / 1000);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Visits the filename in order to rewrite and cache. Visitor returns found
|
|
58
|
-
* dependencies in the file. Recursively traverses the absolute file paths of
|
|
59
|
-
* dependencies found by the require detector.
|
|
60
|
-
* @param {string} filename file to rewrite
|
|
61
|
-
* @param {object} parent parent module data
|
|
62
|
-
*/
|
|
63
|
-
async traverse(filename, parent) {
|
|
64
|
-
const fileDependencies = await this.visitDependency(filename);
|
|
65
|
-
|
|
66
|
-
return Promise.all(
|
|
67
|
-
fileDependencies.map((request) => {
|
|
68
|
-
try {
|
|
69
|
-
const _filename = Module._resolveFilename(request, parent);
|
|
70
|
-
if (_filename.endsWith('.js')) {
|
|
71
|
-
const _parent = RecursiveDependencyRewriter.getModuleData(_filename);
|
|
72
|
-
return this.traverse(_filename, _parent);
|
|
73
|
-
}
|
|
74
|
-
} catch (err) {
|
|
75
|
-
if (err.code === 'MODULE_NOT_FOUND') {
|
|
76
|
-
this.logger.debug(
|
|
77
|
-
`module not found. skipping ${request} required by ${filename}`
|
|
78
|
-
);
|
|
79
|
-
} else {
|
|
80
|
-
this.logger.error('error resolving filename: %o', err);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* For each file dependency rewrite and cache it. Returns array of found
|
|
89
|
-
* dependencies.
|
|
90
|
-
* @param {string} filename name of file to rewrite / cache
|
|
91
|
-
* @returns {array[string]} list of the file's dependencies
|
|
92
|
-
*/
|
|
93
|
-
async visitDependency(filename) {
|
|
94
|
-
if (this.visited.has(filename)) {
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
this.visited.add(filename);
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const content = await readFile(filename, 'utf8');
|
|
102
|
-
const rewriteData = this.rewriter.rewriteFile({
|
|
103
|
-
content,
|
|
104
|
-
filename,
|
|
105
|
-
opts: {
|
|
106
|
-
inject: true,
|
|
107
|
-
sourceType: 'script',
|
|
108
|
-
wrap: true,
|
|
109
|
-
deps: []
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
if (!rewriteData.deps) {
|
|
114
|
-
return [];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return rewriteData.deps.filter((dep) => !builtins.includes(dep));
|
|
118
|
-
} catch (e) {
|
|
119
|
-
console.log('visit error\n', e);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Gets module data in order to resolve abs paths of deps.
|
|
126
|
-
* @param {string} filename absolute path of file being rewritten
|
|
127
|
-
* @returns {object}
|
|
128
|
-
*/
|
|
129
|
-
static getModuleData(filename) {
|
|
130
|
-
return {
|
|
131
|
-
id: filename,
|
|
132
|
-
filename,
|
|
133
|
-
paths: Module._nodeModulePaths(path.dirname(filename))
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Added to agent's static visitors. Runs during rewriting to detect require
|
|
139
|
-
* calls to discover dependencies.
|
|
140
|
-
* @param {object} node AST node being visited during rewrite
|
|
141
|
-
* @param {string} filename file being rewritten
|
|
142
|
-
* @param {object} state rewriter state
|
|
143
|
-
*/
|
|
144
|
-
static requireDetector(path, state) {
|
|
145
|
-
const { node } = path;
|
|
146
|
-
if (isRequire(node)) {
|
|
147
|
-
if (isLiteralRequire(node) || isStaticTemplateRequire(node)) {
|
|
148
|
-
const request = getRequireArg(node);
|
|
149
|
-
if (request) {
|
|
150
|
-
state.deps.push(request);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Whether AST node looks like a `require([request])` call.
|
|
159
|
-
* @param {object} node AST node
|
|
160
|
-
* @returns {boolean}
|
|
161
|
-
*/
|
|
162
|
-
function isRequire(node) {
|
|
163
|
-
return (
|
|
164
|
-
node.type === 'CallExpression' &&
|
|
165
|
-
node.callee &&
|
|
166
|
-
node.callee.type === 'Identifier' &&
|
|
167
|
-
node.callee.name === 'require' &&
|
|
168
|
-
node.arguments &&
|
|
169
|
-
node.arguments.length === 1
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Whether node is static template require
|
|
175
|
-
* @param {object} node AST node
|
|
176
|
-
* @returns {boolean}
|
|
177
|
-
*/
|
|
178
|
-
function isStaticTemplateRequire(node) {
|
|
179
|
-
return (
|
|
180
|
-
node.arguments[0].type === 'TemplateLiteral' &&
|
|
181
|
-
node.arguments[0].expressions.length === 0
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Whether node is literal require
|
|
187
|
-
* @param {object} node AST node
|
|
188
|
-
* @returns {boolean}
|
|
189
|
-
*/
|
|
190
|
-
function isLiteralRequire(node) {
|
|
191
|
-
return (
|
|
192
|
-
node.arguments[0].type === 'Literal' ||
|
|
193
|
-
node.arguments[0].type === 'StringLiteral'
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Gets value of argument to `require([request])`
|
|
199
|
-
* @param {object} node AST node
|
|
200
|
-
* @returns {string}
|
|
201
|
-
*/
|
|
202
|
-
function getRequireArg(node) {
|
|
203
|
-
return node.arguments[0].type === 'TemplateLiteral'
|
|
204
|
-
? node.arguments[0].quasis[0].value.raw
|
|
205
|
-
: node.arguments[0].value;
|
|
206
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
module.exports = function(deps) {
|
|
4
|
-
const cliRewriter = deps.cliRewriter = async function (preHook) {
|
|
5
|
-
// fix this - config doesn't resolve this properly for this cli use case
|
|
6
|
-
deps.config.entrypoint = process.argv[2];
|
|
7
|
-
|
|
8
|
-
require('./dependency-rewriter')(deps);
|
|
9
|
-
|
|
10
|
-
/*
|
|
11
|
-
* Callers just need to set up their rewriter policy e.g.
|
|
12
|
-
* deps.depHooks.rewriting.install()
|
|
13
|
-
* deps.assess.rewriting.install()
|
|
14
|
-
*/
|
|
15
|
-
preHook(deps);
|
|
16
|
-
|
|
17
|
-
// once clients configure their stuff
|
|
18
|
-
await deps.dependencyRewriter.rewrite();
|
|
19
|
-
|
|
20
|
-
deps.logger.info('done');
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
return cliRewriter;
|
|
24
|
-
};
|