@contrast/agent 4.10.4 → 4.10.5

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/bin/VERSION CHANGED
@@ -1 +1 @@
1
- 2.28.12
1
+ 2.28.13
Binary file
Binary file
Binary file
package/esm.mjs CHANGED
@@ -24,13 +24,45 @@ if (enabled) {
24
24
  await loader.resetArgs(process.argv[0], process.argv[1]);
25
25
  const { readFile } = require('fs').promises;
26
26
 
27
+ const path = require('path');
27
28
  const agent = require(`./lib/agent.js`);
28
29
  const logger = require(`./lib/core/logger/index.js`)('contrast:esm-loaders');
29
30
  const rewriter = require(`./lib/core/rewrite/index.js`)(agent);
30
31
  const helpers = require(`./lib/hooks/module/helpers.js`);
32
+ const parent = require('parent-package-json');
31
33
 
32
34
  const loadedFromCache = new Set();
33
35
 
36
+ function getType(url) {
37
+ const {protocol, pathname} = new URL(url);
38
+
39
+ let parentType = 'commonjs';
40
+ try {
41
+ parentType = parent(pathname).parse().type;
42
+ } catch (err) {
43
+ // Node assumes `commonjs ` if there's no `type` set in package.json
44
+ }
45
+
46
+ if (protocol === 'node:') {
47
+ return 'builtin';
48
+ }
49
+ if (protocol === 'file:') {
50
+ const ext = path.extname(pathname);
51
+ if (
52
+ ext === '.mjs' ||
53
+ (ext === '.js' && parentType === 'module')
54
+ ){
55
+ return 'module';
56
+ }
57
+ else if (
58
+ ext === '.cjs' ||
59
+ (ext === '.js' && parentType !== 'module')
60
+ ){
61
+ return 'commonjs';
62
+ }
63
+ }
64
+ return 'unknown';
65
+ }
34
66
  /**
35
67
  * The `getSource` hook is used to provide a custom method for retrieving source
36
68
  * code. In our case, we check for previously rewritten ESM files in our cache
@@ -110,15 +142,25 @@ export async function transformSource(source, context, defaultTransformSource) {
110
142
  * @returns {Promise<{ format: string, source: string | SharedArrayBuffer | Uint8Array }>}
111
143
  */
112
144
  export async function load(url, context, defaultLoad) {
145
+ const type = getType(url);
146
+
147
+ if (type === 'builtin' || type === 'unknown') {
148
+ logger.debug(
149
+ 'Skipping rewrite for %s module %s, loading original code',
150
+ type,
151
+ url
152
+ );
153
+ return defaultLoad(url, context, defaultLoad);
154
+ }
155
+
113
156
  const filename = fileURLToPath(url);
114
157
 
115
158
  try {
116
159
  const cached = helpers.find(agent, filename);
117
160
  const source = cached || await readFile(filename, 'utf8');
118
- const result = rewriter.rewriteFile(source, filename, { sourceType: 'module' });
161
+ const result = rewriter.rewriteFile(source, filename, { sourceType: type === 'commonjs' ? 'script' : 'module' });
119
162
  helpers.cacheWithSourceMap(agent, filename, result);
120
- return { format: context.format, source: result.code };
121
-
163
+ return { format: type, source: result.code };
122
164
  } catch (err) {
123
165
  logger.error(
124
166
  'Failed to load rewritten code for %s, err: %o, rewritten code %s, loading original code.',
@@ -59,7 +59,7 @@ class Path extends Stack {
59
59
 
60
60
  super.push({
61
61
  key,
62
- array
62
+ array,
63
63
  });
64
64
  }
65
65
  }
@@ -74,7 +74,16 @@ class Visitor {
74
74
  this.cache = new WeakSet();
75
75
  }
76
76
 
77
- visit(key, value) {
77
+ visit(key, value, isMaxDepthReached) {
78
+ if (isMaxDepthReached) {
79
+ this.updateStacks(
80
+ { counter: this.counter, path: this.path },
81
+ null,
82
+ null,
83
+ isMaxDepthReached,
84
+ );
85
+ return;
86
+ }
78
87
  // skip circular objects
79
88
  if (typeof value === 'object' && value !== null) {
80
89
  if (this.cache.has(value)) {
@@ -96,7 +105,11 @@ class Visitor {
96
105
  return newVal || value;
97
106
  }
98
107
 
99
- updateStacks(stacks, key, value) {
108
+ updateStacks(stacks, key, value, isMaxDepthReached) {
109
+ if (isMaxDepthReached) {
110
+ stacks.counter.pop();
111
+ stacks.path.pop();
112
+ }
100
113
  // We're decending into the object, so here we're
101
114
  // just building up the stacks.
102
115
 
@@ -123,7 +136,7 @@ class Visitor {
123
136
  return this.updateStacks(
124
137
  { counter: this.counter, path: this.path },
125
138
  key,
126
- value
139
+ value,
127
140
  );
128
141
  }
129
142
  return;
@@ -136,16 +149,34 @@ function traverse(obj, fn, visitor, maxDepth) {
136
149
  maxDepth = visitor;
137
150
  visitor = undefined;
138
151
  }
139
-
140
152
  visitor =
141
153
  visitor ||
142
- new Visitor(function(key, value, depth, paths) {
154
+ new Visitor(function (key, value, depth, paths) {
143
155
  fn(key, value, depth, paths);
144
156
  }, maxDepth);
145
157
 
146
- JSON.stringify(obj, function(k, v) {
147
- // if is traversable and empty, call traverse on value and pass in existing visitor
148
- return visitor.visit(k, v);
158
+ traverseObject(obj, visitor, maxDepth);
159
+ }
160
+
161
+ function traverseObject(obj, visitor, remainingDepth = Infinity) {
162
+ if (!obj) {
163
+ return;
164
+ }
165
+ if (visitor.path.length() < 1) {
166
+ visitor.visit('', obj);
167
+ }
168
+
169
+ // This is done just to reset the stack, without changing the current impletion
170
+ if (remainingDepth == 0) {
171
+ visitor.visit(null, null, true);
172
+ return;
173
+ }
174
+
175
+ Object.entries(obj).forEach(([key, value]) => {
176
+ visitor.visit(key, value);
177
+ if (value && typeof value === 'object') {
178
+ traverseObject(value, visitor, remainingDepth - 1);
179
+ }
149
180
  });
150
181
  }
151
182
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agent",
3
- "version": "4.10.4",
3
+ "version": "4.10.5",
4
4
  "description": "Node.js security instrumentation by Contrast Security",
5
5
  "keywords": [
6
6
  "security",