@contrast/esm-hooks 1.0.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/README.md +21 -0
- package/lib/get-file-type.mjs +27 -0
- package/lib/index.mjs +66 -0
- package/package.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
### Using [ECMAScript Modules (ESM)](https://nodejs.org/docs/latest-v12.x/api/esm.html#esm_ecmascript_modules)
|
|
2
|
+
|
|
3
|
+
> NOTE: ECMAScript instrumentation is experimental and Contrast requires at least Node version 14.15.0 for support.
|
|
4
|
+
|
|
5
|
+
ECMAScript modules are the _new_ official standard format to package JavaScript
|
|
6
|
+
code for reuse. ES Modules are loaded with the `import module from 'module'`
|
|
7
|
+
syntax.
|
|
8
|
+
|
|
9
|
+
When instrumenting an application that utilizes ECMAScript Modules, use the
|
|
10
|
+
following method to start the application. This is the appropriate method for
|
|
11
|
+
instrumenting an application that uses CJS, ESM, or a combination of both.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Usage: node --experimental-loader @contrast/protect/esm-loader.mjs index.mjs [agent arguments] -- [app arguments]
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
|
|
18
|
+
-h, --help output usage information
|
|
19
|
+
-V, --version output the version number
|
|
20
|
+
-c, --configFile <path> path to agent config file
|
|
21
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import parent from 'parent-package-json';
|
|
3
|
+
|
|
4
|
+
export default function getType(filename) {
|
|
5
|
+
|
|
6
|
+
let pathname = filename.pathname || filename;
|
|
7
|
+
|
|
8
|
+
if (pathname.startsWith('node:')) {
|
|
9
|
+
return 'builtin';
|
|
10
|
+
}
|
|
11
|
+
pathname = pathname.replace('file://', '');
|
|
12
|
+
|
|
13
|
+
let parentType = 'commonjs';
|
|
14
|
+
try {
|
|
15
|
+
parentType = parent(pathname).parse().type;
|
|
16
|
+
} catch (err) {
|
|
17
|
+
// Node assumes `commonjs ` if there's no `type` set in package.json
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const ext = path.extname(pathname);
|
|
21
|
+
if (ext === '.mjs' || (ext === '.js' && parentType === 'module')) {
|
|
22
|
+
return 'module';
|
|
23
|
+
} else if (ext === '.cjs' || (ext === '.js' && parentType !== 'module')) {
|
|
24
|
+
return 'commonjs';
|
|
25
|
+
}
|
|
26
|
+
return 'unknown';
|
|
27
|
+
}
|
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import initCore from '@contrast/core';
|
|
4
|
+
import getType from './get-file-type.mjs';
|
|
5
|
+
|
|
6
|
+
export default function (core = initCore()) {
|
|
7
|
+
|
|
8
|
+
const esmHooks = {
|
|
9
|
+
|
|
10
|
+
async getSource(url, context, defaultGetSource) {
|
|
11
|
+
const filename = fileURLToPath(url);
|
|
12
|
+
core.logger.trace('getSource %s', filename);
|
|
13
|
+
return defaultGetSource(url, context, defaultGetSource);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
async transformSource(source, context, defaultTransformSource) {
|
|
17
|
+
let result;
|
|
18
|
+
const filename = fileURLToPath(context.url);
|
|
19
|
+
core.logger.trace('transformSource %s', filename);
|
|
20
|
+
try {
|
|
21
|
+
result = core.rewriter.rewrite(source.toString(), { filename, sourceType: 'module' });
|
|
22
|
+
return { source: result.code };
|
|
23
|
+
} catch (err) {
|
|
24
|
+
core.logger.error(
|
|
25
|
+
{ err, result },
|
|
26
|
+
'Failed to compile rewritten code for %s. compiling original code.',
|
|
27
|
+
filename,
|
|
28
|
+
);
|
|
29
|
+
return defaultTransformSource(source, context, defaultTransformSource);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
async load(url, context, defaultLoad) {
|
|
34
|
+
const type = getType(url);
|
|
35
|
+
|
|
36
|
+
if (type === 'builtin' || type === 'unknown') {
|
|
37
|
+
core.logger.debug(
|
|
38
|
+
'Skipping rewrite for %s module %s, loading original code',
|
|
39
|
+
type,
|
|
40
|
+
url
|
|
41
|
+
);
|
|
42
|
+
return defaultLoad(url, context, defaultLoad);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const filename = fileURLToPath(url);
|
|
46
|
+
core.logger.trace('load %s', filename);
|
|
47
|
+
|
|
48
|
+
let result;
|
|
49
|
+
try {
|
|
50
|
+
const source = await fs.readFile(filename, 'utf8');
|
|
51
|
+
result = core.rewriter.rewrite(source, { filename, sourceType: type === 'commonjs' ? 'script' : 'module' });
|
|
52
|
+
return { format: type, source: result.code };
|
|
53
|
+
} catch (err) {
|
|
54
|
+
core.logger.error(
|
|
55
|
+
{ err, result },
|
|
56
|
+
'Failed to load rewritten code for %s. loading original code.',
|
|
57
|
+
filename,
|
|
58
|
+
);
|
|
59
|
+
return defaultLoad(url, context, defaultLoad);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
core.esmHooks = esmHooks;
|
|
65
|
+
return core.esmHooks;
|
|
66
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contrast/esm-hooks",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Support for loading and instrumenting ECMAScript modules",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
8
|
+
"main": "lib/index.mjs",
|
|
9
|
+
"engines": {
|
|
10
|
+
"npm": ">= 8.4.0",
|
|
11
|
+
"node": ">= 14.15.0"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "../scripts/test.sh"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@contrast/core": "^1.0.0",
|
|
18
|
+
"parent-package-json": "^2.0.1"
|
|
19
|
+
}
|
|
20
|
+
}
|