@contrast/core 1.60.0 → 1.61.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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright: 2025 Contrast Security, Inc
1
+ Copyright: 2026 Contrast Security, Inc
2
2
  Contact: support@contrastsecurity.com
3
3
  License: Commercial
4
4
 
package/lib/agent-info.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
package/lib/app-info.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
package/lib/build-id.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -32,7 +32,7 @@ module.exports = function init(core) {
32
32
  * Attempts to hash the contents of the `package-lock.json` neighbor of the
33
33
  * application's `package.json`. If no package-lock is detected, fall back to
34
34
  * the app's `package.json` to generate the build ID.
35
- * @returns {Promise<number | void>}
35
+ * @returns {Promise<string | void>}
36
36
  */
37
37
  return core.getBuildId = async function getBuildId() {
38
38
  if (_buildId) return _buildId;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -14,41 +14,25 @@
14
14
  */
15
15
 
16
16
  import { AppInfo, Messages, SystemInfo, ThreadTransferData } from '@contrast/common';
17
+ import Perf from '@contrast/perf';
18
+ import * as IOCCore from './ioc/core';
17
19
 
18
- interface Frame {
19
- eval: string | undefined;
20
- file: string | undefined;
21
- lineNumber: number | null;
22
- method: string | null;
23
- type: string | null;
24
- }
25
-
26
- /* eslint-disable @typescript-eslint/ban-types */
27
- interface CreateSnapshotOpts {
28
- constructorOpt?: Function;
29
- prependFrames?: (Function | Frame)[];
30
- }
20
+ export interface Core extends IOCCore {
21
+ // these should be defined on IOCCore but sometimes typescript isn't resolving them correctly
22
+ Perf: typeof Perf;
23
+ perf: Perf;
31
24
 
32
- export interface Core {
33
- threadTransferData: ThreadTransferData;
34
25
  agentName: string;
35
26
  agentVersion: string;
36
- reportingInstance: string;
37
-
38
27
  appInfo: AppInfo;
39
-
40
- captureStackTrace<T>(obj: T, opts: CreateSnapshotOpts, key: string): T;
41
- createSnapshot(opts?: CreateSnapshotOpts): () => Frame[];
42
-
43
- isAgentPath(path: string): boolean;
44
-
45
28
  messages: Messages;
46
-
29
+ reportingInstance: string;
47
30
  sensitiveDataMasking: SensitiveDataMasking;
31
+ threadTransferData: ThreadTransferData;
48
32
 
33
+ isAgentPath(path: string): boolean;
49
34
  getSystemInfo(): Promise<SystemInfo>;
50
- getBuildId(): Promse<number | void>;
51
- Perf: any;
35
+ getBuildId(): Promise<string | void>;
52
36
  }
53
37
 
54
38
  export interface SensitiveDataMasking {
package/lib/ioc/core.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -15,7 +15,9 @@
15
15
 
16
16
  'use strict';
17
17
 
18
+
18
19
  const EventEmitter = require('events');
20
+ const { set } = require('@contrast/common');
19
21
  const Perf = require('@contrast/perf');
20
22
 
21
23
  const kComponentName = Symbol('kComponentName');
@@ -23,8 +25,8 @@ const kComponentName = Symbol('kComponentName');
23
25
  class Core {
24
26
  constructor(initData = {}) {
25
27
  const core = this;
26
- // setup perf tools
27
28
 
29
+ // setup perf tools
28
30
  this.Perf = Perf;
29
31
  this.perf = new Perf('core');
30
32
  Perf.setInterval();
@@ -69,18 +71,26 @@ class Core {
69
71
  * If component factory is decorated with metadata, then we can
70
72
  * use the metadata to hook into core's extension events.
71
73
  * @param {function} component
72
- * @param {string} component.fullName
73
74
  * @throws {Error} if component doesn't have appropriate metadata
74
75
  */
75
76
  initComponentSync(component) {
76
- this.perf.wrapInit(component, component[kComponentName])(this);
77
+ if (component?.prototype instanceof ComponentBase) {
78
+ if (!component.prototype.constructor[kComponentName])
79
+ throw new Error(`no kComponentName on ${component.prototype.constructor.name}`);
80
+
81
+ const componentName = component.prototype.constructor[kComponentName];
82
+ this.perf.wrapInit(() => new component(this), componentName)(this);
83
+ } else {
84
+ this.perf.wrapInit(component, component[kComponentName])(this);
85
+ }
77
86
  this.messages.emit('ext:core.init', component[kComponentName]);
78
87
  }
79
88
 
80
89
  static isComponent(target) {
81
- return typeof target == 'function' && target[kComponentName]?.length > 0;
90
+ return typeof target == 'function' && target[kComponentName]?.length;
82
91
  }
83
92
 
93
+ /** @deprecated Use {@Link ComponentBase} instead*/
84
94
  static makeComponent(meta) {
85
95
  if (!meta.factory || !meta.name) throw new Error('makeComponent requires factory and name');
86
96
 
@@ -91,5 +101,27 @@ class Core {
91
101
  }
92
102
  }
93
103
 
104
+ /** @template {import('..').Core} T */
105
+ class ComponentBase {
106
+
107
+ /** @type {T} */
108
+ core;
109
+
110
+ /**
111
+ * @param {T} core
112
+ */
113
+ constructor(core) {
114
+ // avoid core being enumerable
115
+ Object.defineProperty(this, 'core', { value: core });
116
+
117
+ if (!this.constructor[kComponentName])
118
+ throw new Error(`no kComponentName on ${this.constructor}`);
119
+
120
+ set(core, this.constructor[kComponentName], this);
121
+ }
122
+ }
123
+
94
124
  module.exports = Core;
95
125
  module.exports.Core = Core;
126
+ module.exports.ComponentBase = ComponentBase;
127
+ module.exports.kComponentName = kComponentName;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
package/lib/messages.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  /*
22
- * Copyright: 2025 Contrast Security, Inc
22
+ * Copyright: 2026 Contrast Security, Inc
23
23
  * Contact: support@contrastsecurity.com
24
24
  * License: Commercial
25
25
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2025 Contrast Security, Inc
2
+ * Copyright: 2026 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/core",
3
- "version": "1.60.0",
3
+ "version": "1.61.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)",
@@ -19,12 +19,12 @@
19
19
  "test": "bash ../scripts/test.sh"
20
20
  },
21
21
  "dependencies": {
22
- "@contrast/common": "1.39.0",
23
- "@contrast/config": "1.55.0",
22
+ "@contrast/common": "1.40.0",
23
+ "@contrast/config": "1.56.0",
24
24
  "@contrast/find-package-json": "^1.1.0",
25
25
  "@contrast/fn-inspect": "^5.0.2",
26
- "@contrast/logger": "1.33.0",
27
- "@contrast/patcher": "1.32.0",
26
+ "@contrast/logger": "1.34.0",
27
+ "@contrast/patcher": "1.33.0",
28
28
  "@contrast/perf": "1.4.0",
29
29
  "@tsxper/crc32": "^2.1.3",
30
30
  "axios": "^1.12.2",
@@ -1,256 +0,0 @@
1
- /*
2
- * Copyright: 2025 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
- // @ts-check
16
- 'use strict';
17
-
18
- const process = require('process');
19
- const fnInspect = require('@contrast/fn-inspect');
20
- const { primordials: { StringPrototypeReplace, RegExpPrototypeExec } } = require('@contrast/common');
21
-
22
- /** @typedef {import('.').Frame} Frame */
23
- /** @typedef {import('.').CreateSnapshotOpts} CreateSnapshotOpts */
24
-
25
- const EVAL_ORIGIN_REGEX = /\((.*?):(\d+):\d+\)/;
26
-
27
- module.exports = function(core) {
28
- const { config, isAgentPath } = core;
29
-
30
- const stacktraceFactory = new StacktraceFactory({
31
- stackTraceLimit: config.agent.stack_trace_limit,
32
- isAgentPath
33
- });
34
-
35
- /** @type {StacktraceFactory['captureStacktrace']} */
36
- core.captureStacktrace = function (obj, opts, key) {
37
- return stacktraceFactory.captureStacktrace(obj, opts, key);
38
- };
39
-
40
- /** @type {StacktraceFactory['createSnapshot']} */
41
- core.createSnapshot = function (opts) {
42
- return stacktraceFactory.createSnapshot(opts);
43
- };
44
-
45
- return core.captureStacktrace;
46
- };
47
-
48
- class StacktraceFactory {
49
- /**
50
- * The factory will set stacktrace limit for stackframe lists created by it.
51
- * @param {Object} opts
52
- * @param {number} opts.stackTraceLimit set the stack trace limit
53
- * @param {(path: string) => boolean} opts.isAgentPath function indicating if path is agent code
54
- */
55
- constructor({ stackTraceLimit, isAgentPath }) {
56
- this.stackTraceLimit = stackTraceLimit;
57
- this.isAgentPath = isAgentPath;
58
- }
59
-
60
- /**
61
- * @template T
62
- * @param {T} obj
63
- * @param {CreateSnapshotOpts} opts
64
- * @param {string} key
65
- * @returns {T}
66
- */
67
- captureStacktrace(obj, opts, key = 'stack') {
68
- let stack;
69
- const snapshot = this.createSnapshot(opts);
70
- Object.defineProperty(obj, key, {
71
- enumerable: true,
72
- configurable: true,
73
- get() {
74
- if (!stack) {
75
- Object.defineProperty(obj, key, {
76
- configurable: true,
77
- writable: true,
78
- enumerable: true,
79
- value: snapshot()
80
- });
81
- }
82
- return obj[key];
83
- }
84
- });
85
- return obj;
86
- }
87
-
88
- /**
89
- * @param {Frame} frame
90
- * @returns {boolean}
91
- */
92
- shouldAppendFrame(frame) {
93
- return (
94
- !!frame.file &&
95
- !this.isAgentPath(frame.file) &&
96
- !`${frame.type}${frame.method}`.includes('ContrastMethods')
97
- );
98
- }
99
-
100
- /**
101
- * Creates a function that will build a stacktrace when invoked. It will keep
102
- * an error in a closure whose stack will be generated and processed when the
103
- * returned function executes. The result will be cached.
104
- * @param {CreateSnapshotOpts} params
105
- * @returns {() => Frame[]}
106
- */
107
- createSnapshot({ constructorOpt, prependFrames } = {}) {
108
- const target = {};
109
- this.appendStackGetter(target, constructorOpt);
110
-
111
- let frames;
112
-
113
- /**
114
- * Generates array of frames from `target`'s `stack` getter
115
- * @returns {Frame[]}
116
- */
117
- const snapshot = () => {
118
- if (!frames) {
119
- // @ts-expect-error target has had a stack getter appended above.
120
- const callsites = StacktraceFactory.generateCallsites(target) ?? [];
121
- frames = callsites.reduce(
122
- /**
123
- * @param {Frame[]} acc
124
- * @param {NodeJS.CallSite} callsite
125
- */
126
- (acc, callsite) => {
127
- if (StacktraceFactory.isCallsiteValid(callsite)) {
128
- const frame = StacktraceFactory.makeFrame(callsite);
129
- if (this.shouldAppendFrame(frame)) {
130
- acc.push(frame);
131
- }
132
- }
133
-
134
- return acc;
135
- },
136
- [],
137
- );
138
- }
139
-
140
- if (!prependFrames) return frames;
141
-
142
- return [
143
- ...prependFrames.map((f) => (typeof f == 'function' ? fnInspect.funcInfo(f) : f)),
144
- ...frames,
145
- ];
146
- };
147
-
148
- return snapshot;
149
- }
150
-
151
- /**
152
- * Based on stacktrace limit and constructor opt, will append a stack getter
153
- * @param {any} obj
154
- * @param {Function=} constructorOpt
155
- */
156
- appendStackGetter(obj, constructorOpt) {
157
- const { stackTraceLimit } = Error;
158
- Error.stackTraceLimit = this.stackTraceLimit;
159
- Error.captureStackTrace(obj, constructorOpt);
160
- Error.stackTraceLimit = stackTraceLimit;
161
- }
162
-
163
- /**
164
- * @param {any} callsite
165
- * @returns {boolean}
166
- */
167
- static isCallsiteValid(callsite) {
168
- return callsite instanceof Callsite;
169
- }
170
-
171
- /**
172
- * Creates an array frame objects from an array of Callsite instances
173
- * @param {NodeJS.CallSite} callsite
174
- * @returns {Frame}
175
- */
176
- static makeFrame(callsite) {
177
- /** @type {string | undefined} */
178
- let evalOrigin;
179
- /** @type {string | undefined} */
180
- let file;
181
- /** @type {number | null} */
182
- let lineNumber = null;
183
- /** @type {string | null} */
184
- let method = null;
185
- /** @type {string | null} */
186
- let type;
187
-
188
- if (callsite.isEval()) {
189
- evalOrigin = StacktraceFactory.formatFileName(callsite.getEvalOrigin());
190
- const matches = RegExpPrototypeExec.call(EVAL_ORIGIN_REGEX, evalOrigin);
191
- if (matches) {
192
- file = matches[1];
193
- lineNumber = parseInt(matches[2]);
194
- }
195
- }
196
-
197
- file = file ?? callsite.getFileName();
198
- lineNumber = lineNumber ?? callsite.getLineNumber();
199
- method = callsite.getFunctionName();
200
- type = callsite.getTypeName();
201
-
202
- if (method === null && type === 'Object') {
203
- method = '<anonymous>';
204
- type = null;
205
- }
206
-
207
- return { eval: evalOrigin, file, lineNumber, method, type };
208
- }
209
-
210
- /**
211
- * @param {string} fileName
212
- * @returns {string}
213
- */
214
- static formatFileName(fileName = '') {
215
- const cwd = `${process.cwd()}/`;
216
- const idx = fileName.indexOf(cwd);
217
-
218
- if (idx > -1) {
219
- return StringPrototypeReplace.call(fileName, cwd, ''); // + 1 to remove /
220
- }
221
- return fileName;
222
- }
223
-
224
- /**
225
- * Will access `stack` getter propery on the provided error to generate
226
- * a stacktrace. We capture the callsite instances passed to `prepareStacktrace`
227
- * using an ephemeral monkey patch.
228
- * @param {Error} error object with a `stack` getter property
229
- * @returns {NodeJS.CallSite[]}
230
- */
231
- static generateCallsites(error) {
232
- let callsites = [];
233
-
234
- const { prepareStackTrace } = Error;
235
-
236
- Error.prepareStackTrace = function contrastPrepareStackTrace(_, _callsites) {
237
- callsites = _callsites;
238
- return _callsites;
239
- };
240
-
241
- // accessing the getter will call `Error.prepareStacktrace`
242
- error.stack;
243
-
244
- // restore original method
245
- Error.prepareStackTrace = prepareStackTrace;
246
-
247
- return callsites;
248
- }
249
- }
250
-
251
- module.exports.StacktraceFactory = StacktraceFactory;
252
- module.exports.generateCallsites = StacktraceFactory.generateCallsites;
253
-
254
- const Callsite = (module.exports.Callsite = StacktraceFactory.generateCallsites(
255
- new Error()
256
- )[0].constructor);