@contrast/agentify
Advanced tools
Comparing version 1.15.0 to 1.16.0
@@ -17,5 +17,6 @@ /* | ||
import { Config } from '@contrast/config'; | ||
import { Installable } from '@contrast/common'; | ||
import { Logger } from '@contrast/logger'; | ||
import { Rewriter } from '@contrast/rewriter'; | ||
import RequireHook from '@contrast/require-hook'; | ||
import { Rewriter } from '@contrast/rewriter'; | ||
@@ -39,37 +40,24 @@ declare module 'module' { | ||
interface AgentifyOptions { | ||
install: boolean; | ||
svcList: string[]; | ||
// TODO: this is now much larger with all of the existing core deps | ||
export interface Core { | ||
readonly config: Config; | ||
readonly depHooks: RequireHook; | ||
readonly logger: Logger; | ||
readonly rewriter: Rewriter; | ||
} | ||
interface AgentOptions<T> { | ||
install: boolean; | ||
preRunMain: PreRunMain<T>; | ||
export interface AgentifyOptions { | ||
installOrder?: string[]; | ||
} | ||
declare class Agent<T> { | ||
constructor(deps: T, opts: AgentOptions<T>); | ||
hookRunMain(): void; | ||
handleInstallFailure(err: Error): Promise<void>; | ||
install(): Promise<void>; | ||
export interface PreRunMain { | ||
(core: any): Installable | void | Promise<Installable | void>; | ||
} | ||
interface PreRunMain<T> { | ||
(core: T, agent: Agent<T>): void | Promise<void>; | ||
export interface Agentify { | ||
(preRunMain: PreRunMain, opts?: AgentifyOptions): any; | ||
} | ||
export interface Agentify<T> { | ||
(preRunMain: PreRunMain<T>, opts?: AgentifyOptions): Agent<T>; | ||
} | ||
declare function init(core: any): Agentify; | ||
export interface Core<T> { | ||
readonly config: Config; | ||
readonly depHooks: RequireHook; | ||
readonly logger: Logger; | ||
readonly rewriter: Rewriter; | ||
agentify: Agentify<T>; | ||
} | ||
declare function init<T>(core: Core<T>): Agentify<T>; | ||
export = init; |
230
lib/index.js
@@ -18,152 +18,126 @@ /* | ||
const fs = require('fs').promises; | ||
const path = require('path'); | ||
const Module = require('module'); | ||
const defaultOpts = { | ||
installOrder: [ | ||
'reporter', | ||
'contrastMethods', | ||
'deadzones', | ||
'scopes', | ||
'sources', | ||
'architectureComponents', | ||
'assess', | ||
'protect', | ||
'depHooks', | ||
'routeCoverage', | ||
'libraryAnalysis', | ||
'heapSnapshots', | ||
'rewriteHooks', | ||
'functionHooks' | ||
] | ||
}; | ||
const ERROR_MESSAGE = 'A fatal agent installation error has occurred. The application will be run without instrumentation.'; | ||
const DEFAULT_INSTALL_ORDER = [ | ||
'reporter', | ||
'contrastMethods', | ||
'deadzones', | ||
'scopes', | ||
'sources', | ||
'architectureComponents', | ||
'assess', | ||
'protect', | ||
'depHooks', | ||
'routeCoverage', | ||
'libraryAnalysis', | ||
'heapSnapshots', | ||
'rewriteHooks', | ||
'functionHooks', | ||
'metrics', | ||
]; | ||
module.exports = function (core) { | ||
// compose add'l local services | ||
require('./heap-snapshots')(core); | ||
require('./sources')(core); | ||
require('./function-hooks')(core); | ||
require('./rewrite-hooks')(core); | ||
/** | ||
* @param {any} core | ||
* @returns {import('.').Agentify} | ||
*/ | ||
module.exports = function init(core = {}) { | ||
try { | ||
require('@contrast/core/lib/events')(core); | ||
require('@contrast/config')(core); | ||
require('@contrast/logger').default(core); | ||
// @contrast/info ? | ||
require('@contrast/core/lib/agent-info')(core); | ||
require('@contrast/core/lib/system-info')(core); | ||
require('@contrast/core/lib/app-info')(core); | ||
require('@contrast/core/lib/sensitive-data-masking')(core); | ||
require('@contrast/core/lib/is-agent-path')(core); | ||
require('@contrast/core/lib/capture-stacktrace')(core); | ||
/** | ||
* The interface is a function, which when called, will hook runMain | ||
* @param {function} preRunMain | ||
* @param {object} opts | ||
*/ | ||
function agentify(preRunMain, opts = defaultOpts) { | ||
if (typeof preRunMain !== 'function') { | ||
throw new Error('Invalid usage: first argument must be a function'); | ||
} | ||
require('@contrast/patcher')(core); | ||
require('@contrast/rewriter')(core); // merge contrast-methods? | ||
require('@contrast/core/lib/contrast-methods')(core); // can we remove dependency on patcher? | ||
opts.preRunMain = preRunMain; | ||
require('@contrast/dep-hooks')(core); | ||
require('@contrast/scopes')(core); | ||
require('@contrast/deadzones')(core); | ||
require('@contrast/reporter').default(core); | ||
require('@contrast/instrumentation')(core); | ||
require('@contrast/metrics')(core); | ||
if (opts !== defaultOpts) { | ||
opts = { ...defaultOpts, ...opts }; | ||
} | ||
// compose add'l local services | ||
require('./heap-snapshots')(core); | ||
require('./sources')(core); | ||
require('./function-hooks')(core); | ||
require('./log-diagnostic-files')(core); // this doesn't really belong in agentify | ||
require('./rewrite-hooks')(core); | ||
return new Agent(core, opts); | ||
} | ||
/** | ||
* The interface is a function, which when called, will hook runMain | ||
* @type {import('.').Agentify} | ||
*/ | ||
core.agentify = function agentify( | ||
preRunMain, | ||
{ installOrder = DEFAULT_INSTALL_ORDER } = {}, | ||
) { | ||
if (typeof preRunMain !== 'function') { | ||
throw new Error('Invalid usage: first argument must be a function'); | ||
} | ||
core.agentify = agentify; | ||
const { runMain } = Module; | ||
const { config, logger } = core; | ||
return agentify; | ||
}; | ||
/** | ||
* This is one side effect that will always occur, even with `installOrder: []`. | ||
* The act of calling `agentify()` is enough to cause this side effect, by design. | ||
*/ | ||
Module.runMain = async function (...args) { | ||
try { | ||
for (const err of config._errors) { | ||
throw err; // move this into config itself since we now handle errors | ||
} | ||
class Agent { | ||
/** | ||
* | ||
* @param {object} core dependencies | ||
* @param {object} opts | ||
* @param {function} opts.preRunMain custom function for registering services | ||
* @param {boolean} opts.install whether to automatically install | ||
*/ | ||
constructor(core, opts) { | ||
const self = this; | ||
const { config, logger } = core; | ||
logger.info('Starting %s v%s', core.agentName, core.agentVersion); | ||
logger.info({ config }, 'Agent configuration'); | ||
this.core = core; | ||
this.opts = opts; | ||
this.runMain = Module.runMain; | ||
const plugin = await preRunMain(core); | ||
/** | ||
* This is one side effect that will always occur, even with `install: false`. | ||
* The act of calling `agentify()` is enough to cause this side effect, by design. | ||
*/ | ||
Module.runMain = async function (...args) { | ||
try { | ||
for (const err of config._errors) { | ||
throw err; | ||
} | ||
for (const svcName of installOrder ?? []) { | ||
const svc = core[svcName]; | ||
if (svc?.install) { | ||
logger.trace('installing service: %s', svcName); | ||
await svc.install(); | ||
} | ||
} | ||
logger.info('Starting the Contrast agent'); | ||
logger.info({ config }, 'Agent configuration'); | ||
if (plugin?.install) { | ||
await plugin.install(); | ||
} | ||
const plugin = await opts.preRunMain(core); | ||
if (opts.installOrder?.length) { | ||
await self.install(); | ||
core.logDiagnosticFiles(); // should this be moved into a separate install side-effect? | ||
} catch (err) { | ||
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent | ||
logger.error({ err }, ERROR_MESSAGE); | ||
} | ||
if (plugin?.install) { | ||
await plugin.install(); | ||
} | ||
} catch (err) { | ||
await self.handleInstallFailure(err); | ||
} | ||
return runMain.apply(this, args); | ||
}; | ||
self.logDiagnosticFiles(); | ||
return self.runMain.apply(this, args); | ||
return core; | ||
}; | ||
} | ||
/** | ||
* Original `runMain` will get called after this. | ||
* | ||
* TODO: Consider proper UNINSTALLATION and normal startup w/o agent | ||
* | ||
* @param {error} err Error thrown during install | ||
*/ | ||
async handleInstallFailure(err) { | ||
this.core.logger.error( | ||
{ err }, | ||
'A fatal agent installation error has occurred. The application will be run without instrumentation.' | ||
); | ||
} | ||
async install() { | ||
for (const svcName of this.opts.installOrder) { | ||
const svc = this.core[svcName]; | ||
if (svc?.install) { | ||
this.core.logger.trace('installing service: %s', svcName); | ||
await svc.install(); | ||
} | ||
} catch (err) { | ||
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent | ||
if (core.logger) { | ||
core.logger.error({ err }, ERROR_MESSAGE); | ||
} else { | ||
console.error(new Error(ERROR_MESSAGE, { cause: err })); | ||
} | ||
} | ||
logDiagnosticFiles() { | ||
const { config, logger } = this.core; | ||
if (!config.agent.diagnostics.enable) return; | ||
const { report_path } = config.agent.diagnostics; | ||
// let these run async so they don't block agent startup. | ||
fs.writeFile( | ||
path.join(report_path, 'contrast_effective_config.json'), | ||
JSON.stringify(config.getReport({ redact: true }), null, 2) | ||
).catch((err) => { | ||
logger.warn({ err }, 'unable to write effective config file'); | ||
}); | ||
fs.writeFile( | ||
path.join(report_path, 'contrast_system_info.json'), | ||
JSON.stringify(this.core.getSystemInfo(), null, 2) | ||
).catch((err) => { | ||
logger.warn({ err }, 'unable to write system info file'); | ||
}); | ||
core.agentify = function agentify() { | ||
return core; | ||
}; | ||
} | ||
} | ||
module.exports.Agent = Agent; | ||
return core.agentify; | ||
}; |
{ | ||
"name": "@contrast/agentify", | ||
"version": "1.15.0", | ||
"version": "1.16.0", | ||
"description": "Configures Contrast agent services and instrumentation within an application", | ||
@@ -21,5 +21,14 @@ "license": "SEE LICENSE IN LICENSE", | ||
"@contrast/common": "1.15.1", | ||
"@contrast/config": "1.20.0", | ||
"@contrast/logger": "1.6.0" | ||
"@contrast/config": "1.21.0", | ||
"@contrast/core": "1.26.1", | ||
"@contrast/deadzones": "1.1.1", | ||
"@contrast/dep-hooks": "^1.3.0", | ||
"@contrast/instrumentation": "^1.2.1", | ||
"@contrast/logger": "1.7.0", | ||
"@contrast/metrics": "1.1.0", | ||
"@contrast/patcher": "1.7.1", | ||
"@contrast/reporter": "1.21.1", | ||
"@contrast/rewriter": "^1.4.2", | ||
"@contrast/scopes": "^1.4.0" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20120
10
491
12
+ Added@contrast/core@1.26.1
+ Added@contrast/deadzones@1.1.1
+ Added@contrast/dep-hooks@^1.3.0
+ Added@contrast/metrics@1.1.0
+ Added@contrast/patcher@1.7.1
+ Added@contrast/reporter@1.21.1
+ Added@contrast/rewriter@^1.4.2
+ Added@contrast/scopes@^1.4.0
+ Added@contrast/agent-swc-plugin@2.0.0(transitive)
+ Added@contrast/common@1.26.0(transitive)
+ Added@contrast/config@1.21.01.34.0(transitive)
+ Added@contrast/core@1.26.11.39.0(transitive)
+ Added@contrast/deadzones@1.1.1(transitive)
+ Added@contrast/dep-hooks@1.7.0(transitive)
+ Added@contrast/find-package-json@1.2.0(transitive)
+ Added@contrast/fn-inspect@3.4.04.3.0(transitive)
+ Added@contrast/instrumentation@1.17.0(transitive)
+ Added@contrast/logger@1.12.01.7.0(transitive)
+ Added@contrast/metrics@1.1.0(transitive)
+ Added@contrast/patcher@1.11.01.7.1(transitive)
+ Added@contrast/reporter@1.21.1(transitive)
+ Added@contrast/rewriter@1.15.0(transitive)
+ Added@contrast/scopes@1.8.0(transitive)
+ Added@swc/core@1.5.29(transitive)
+ Added@swc/core-darwin-arm64@1.5.29(transitive)
+ Added@swc/core-darwin-x64@1.5.29(transitive)
+ Added@swc/core-linux-arm-gnueabihf@1.5.29(transitive)
+ Added@swc/core-linux-arm64-gnu@1.5.29(transitive)
+ Added@swc/core-linux-arm64-musl@1.5.29(transitive)
+ Added@swc/core-linux-x64-gnu@1.5.29(transitive)
+ Added@swc/core-linux-x64-musl@1.5.29(transitive)
+ Added@swc/core-win32-arm64-msvc@1.5.29(transitive)
+ Added@swc/core-win32-ia32-msvc@1.5.29(transitive)
+ Added@swc/core-win32-x64-msvc@1.5.29(transitive)
+ Added@swc/counter@0.1.3(transitive)
+ Added@swc/types@0.1.13(transitive)
+ Addedasynckit@0.4.0(transitive)
+ Addedaxios@0.27.21.7.7(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addedcrc-32@1.2.2(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addedfollow-redirects@1.15.9(transitive)
+ Addedform-data@4.0.0(transitive)
+ Addedhpagent@1.2.0(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addednan@2.20.0(transitive)
+ Addednode-gyp-build@4.8.2(transitive)
+ Addedproxy-from-env@1.1.0(transitive)
+ Addedsemver@7.6.3(transitive)
- Removed@contrast/config@1.20.0(transitive)
- Removed@contrast/logger@1.6.0(transitive)
Updated@contrast/config@1.21.0
Updated@contrast/logger@1.7.0