New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@contrast/dep-hooks

Package Overview
Dependencies
Maintainers
0
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@contrast/dep-hooks - npm Package Compare versions

Comparing version 1.10.0 to 1.11.0

32

lib/export-handler-registry.d.ts

@@ -18,7 +18,14 @@ export = ExportHandlerRegistry;

/**
* @param {import('pino').Logger=} logger
* @param {Object} core
* @param {import('pino').Logger} core.logger
* @param {import('@contrast/common').Messages} core.messages
*/
constructor(logger?: import('pino').Logger | undefined);
/** @type {import('pino').Logger=} */
logger: import('pino').Logger | undefined;
constructor({ logger, messages }: {
logger: import('pino').Logger;
messages: import('@contrast/common').Messages;
});
/** @type {import('pino').Logger} */
logger: import('pino').Logger;
/** @type {import('@contrast/common').Messages} */
messages: import('@contrast/common').Messages;
/**

@@ -37,2 +44,19 @@ * Contains all registered hooks that have been added by `.update()`

/**
* Keep track of package versions targeted by registered hooks.
* This is used to help to detect when unsupported package versions are loaded.
* ```
* Map(1) { '@some/lib' => Set(3) { '2', '3', '>=5 <9' } }
* ```
* @type {Map<string, Set<string>>}
*/
hookedLibVersions: Map<string, Set<string>>;
/**
* Keep track of which unspported libs we've logged/reported.
* ```
* Set(2) { 'mongodb@45.67.89', 'express@0.0.1' }
* ```
* @type {Set<string>}
*/
unsupportedLibsDetected: Set<string>;
/**
* Update the registry with the handler info for the given module name.

@@ -39,0 +63,0 @@ * This gets called when an agent registers a handler for a given module.

@@ -21,2 +21,3 @@ /*

const { getShortname } = require('./helpers');
const { Event } = require('@contrast/common');
const PackageFinder = require('./package-finder');

@@ -39,7 +40,11 @@ /**

/**
* @param {import('pino').Logger=} logger
* @param {Object} core
* @param {import('pino').Logger} core.logger
* @param {import('@contrast/common').Messages} core.messages
*/
constructor(logger) {
/** @type {import('pino').Logger=} */
constructor({ logger, messages }) {
/** @type {import('pino').Logger} */
this.logger = logger;
/** @type {import('@contrast/common').Messages} */
this.messages = messages;
/**

@@ -57,2 +62,19 @@ * Contains all registered hooks that have been added by `.update()`

this.resolved = {};
/**
* Keep track of package versions targeted by registered hooks.
* This is used to help to detect when unsupported package versions are loaded.
* ```
* Map(1) { '@some/lib' => Set(3) { '2', '3', '>=5 <9' } }
* ```
* @type {Map<string, Set<string>>}
*/
this.hookedLibVersions = new Map();
/**
* Keep track of which unspported libs we've logged/reported.
* ```
* Set(2) { 'mongodb@45.67.89', 'express@0.0.1' }
* ```
* @type {Set<string>}
*/
this.unsupportedLibsDetected = new Set();
}

@@ -73,2 +95,9 @@ /**

}
if (!this.hookedLibVersions.has(info.name)) {
this.hookedLibVersions.set(info.name, new Set([info.version]));
}
else {
// @ts-expect-error we've validated this above
this.hookedLibVersions.get(info.name).add(info.version);
}
return this.registered[info.shortname];

@@ -111,6 +140,4 @@ }

});
if (!satisfies) {
this.logger?.trace({ metadata }, 'skipping handlers since package %s@%s does not satisfy semver range %s', metadata.name, metadata.version, descriptor.version);
if (!satisfies)
continue;
}
const { handlers } = descriptor;

@@ -159,2 +186,20 @@ if (!this.resolved[metadata.file]) {

return;
const versions = this.hookedLibVersions.get(metadata.name);
// no hooks registred for this package - exit early
if (!versions)
return;
const pkgKey = `${metadata.name}@${metadata.version}`;
let supported = false;
for (const version of versions) {
if (semver.satisfies(metadata.version, version)) {
supported = true;
break;
}
}
// make sure we only log/report once
if (!supported && !this.unsupportedLibsDetected.has(pkgKey)) {
this.unsupportedLibsDetected.add(pkgKey);
this.logger.trace({ metadata }, 'skipping handlers since package %s does not match any registered versions', pkgKey);
this.messages.emit(Event.UNSUPPORTED_LIBRARY, metadata);
}
const registered = this.registered[getShortname(metadata)];

@@ -161,0 +206,0 @@ if (!registered)

52

lib/export-hook-descriptor.d.ts

@@ -12,11 +12,9 @@ export = ExportHookDescriptor;

* @param {Metadata & HookControls} metadata
* @returns {T}
* @returns {T | void}
*/
/**
* @template {Object} T
* @typedef {Object} HookDescriptorOptions
* @property {string} name
* @property {string=} file
* @property {string=} version
* @property {Handler<T>[]=} handlers
* @typedef {Object} Descriptor
* @property {string} name module name, as passed to `require`, to handle.
* @property {string=} file if provided, the file under the module's root that we want to hook. otherwise, the module's `main` will be hooked.
* @property {string} version if provided, hooks will only execute against an installed module that matches the semver version range
*/

@@ -30,23 +28,14 @@ /**

/**
* Validates the constructor params.
* @param {Object} opts
* @param {string=} opts.name required
* @param {string} opts.version must be a valid semver range
* @throws {Error}
*/
static validate({ name, version }: {
name?: string | undefined;
version: string;
}): void;
/**
* A static factory function for creating descriptors with different params.
* @template {Object} T
* @param {HookDescriptorOptions<T> | string} descriptor export info and handlers
* @param {Descriptor | string} descriptor export info and handlers
* @param {Handler<T>[]=} handlers
* @returns {ExportHookDescriptor<T>}
*/
static create<T_1 extends Object>(descriptor: string | HookDescriptorOptions<T_1>): ExportHookDescriptor<T_1>;
static create<T_1 extends Object>(descriptor: Descriptor | string, handlers?: Handler<T_1>[] | undefined): ExportHookDescriptor<T_1>;
/**
* @param {HookDescriptorOptions<T>} options
* @param {Descriptor} options
* @param {Handler<T>[]} handlers
*/
constructor({ name, file, version, handlers, }?: HookDescriptorOptions<T>);
constructor({ name, file, version }: Descriptor, handlers: Handler<T>[]);
/** @type {string} */

@@ -64,6 +53,5 @@ name: string;

declare namespace ExportHookDescriptor {
export { DEFAULT_VERSION, Metadata, HookControls, Handler, HookDescriptorOptions };
export { Metadata, HookControls, Handler, Descriptor };
}
type Handler<T extends Object> = (mod: T, metadata: Metadata & HookControls) => T;
declare const DEFAULT_VERSION: "*";
type Handler<T extends Object> = (mod: T, metadata: Metadata & HookControls) => T | void;
type Metadata = import('./package-finder').Metadata;

@@ -73,8 +61,16 @@ type HookControls = {

};
type HookDescriptorOptions<T extends Object> = {
type Descriptor = {
/**
* module name, as passed to `require`, to handle.
*/
name: string;
/**
* if provided, the file under the module's root that we want to hook. otherwise, the module's `main` will be hooked.
*/
file?: string | undefined;
version?: string | undefined;
handlers?: Handler<T>[] | undefined;
/**
* if provided, hooks will only execute against an installed module that matches the semver version range
*/
version: string;
};
//# sourceMappingURL=export-hook-descriptor.d.ts.map

@@ -19,6 +19,5 @@ /*

const path = require('node:path');
const Semver = require('semver');
const semver = require('semver');
const { isBuiltin } = require('./helpers');
const { primordials: { StringPrototypeReplace } } = require('@contrast/common');
const DEFAULT_VERSION = '*';
/** @typedef {import('./package-finder').Metadata} Metadata */

@@ -34,11 +33,9 @@ /**

* @param {Metadata & HookControls} metadata
* @returns {T}
* @returns {T | void}
*/
/**
* @template {Object} T
* @typedef {Object} HookDescriptorOptions
* @property {string} name
* @property {string=} file
* @property {string=} version
* @property {Handler<T>[]=} handlers
* @typedef {Object} Descriptor
* @property {string} name module name, as passed to `require`, to handle.
* @property {string=} file if provided, the file under the module's root that we want to hook. otherwise, the module's `main` will be hooked.
* @property {string} version if provided, hooks will only execute against an installed module that matches the semver version range
*/

@@ -52,12 +49,11 @@ /**

/**
* @param {HookDescriptorOptions<T>} options
* @param {Descriptor} options
* @param {Handler<T>[]} handlers
*/
// @ts-expect-error typescript doesn't play well with the validation we do.
constructor({ name, file, version = DEFAULT_VERSION, handlers = [], } = {}) {
ExportHookDescriptor.validate({ name, version });
constructor({ name, file, version }, handlers) {
/** @type {string} */
// @ts-ignore
// @ts-ignore ts does not handle method overloads well
this.name = isBuiltin(name) ? StringPrototypeReplace.call(name, /^(node:)?/, 'node:') : name;
/** @type {string=} */
// @ts-ignore
// @ts-ignore ts does not handle method overloads well
this.file = file ? StringPrototypeReplace.call(file, /\/?(index)?(\.js)?$/, '') : undefined;

@@ -72,33 +68,25 @@ /** @type {string} */

/**
* Validates the constructor params.
* @param {Object} opts
* @param {string=} opts.name required
* @param {string} opts.version must be a valid semver range
* @throws {Error}
*/
static validate({ name, version }) {
if (!name) {
throw new Error('Required option missing: `name`');
}
if (!Semver.validRange(version)) {
throw new Error(`Invalid version range provided: "${version}"`);
}
}
/**
* A static factory function for creating descriptors with different params.
* @template {Object} T
* @param {HookDescriptorOptions<T> | string} descriptor export info and handlers
* @param {Descriptor | string} descriptor export info and handlers
* @param {Handler<T>[]=} handlers
* @returns {ExportHookDescriptor<T>}
*/
static create(descriptor) {
static create(descriptor, handlers = []) {
if (!descriptor) {
throw new Error('Required option missing: `name`');
}
if (typeof descriptor === 'string') {
return Reflect.construct(ExportHookDescriptor, [{ name: descriptor }]);
descriptor = { name: descriptor, version: '*' };
}
else {
return Reflect.construct(ExportHookDescriptor, [descriptor]);
if (!descriptor.name) {
throw new Error('Required option missing: `name`');
}
if (!semver.validRange(descriptor.version)) {
throw new Error(`Invalid version range provided: "${descriptor.version}"`);
}
return Reflect.construct(ExportHookDescriptor, [descriptor, handlers]);
}
}
module.exports = ExportHookDescriptor;
module.exports.DEFAULT_VERSION = DEFAULT_VERSION;
//# sourceMappingURL=export-hook-descriptor.js.map

@@ -9,7 +9,7 @@ export = HandlerInvoker;

/**
* @param {import('pino').Logger=} logger
* @param {import('pino').Logger} logger
*/
constructor(logger?: import('pino').Logger | undefined);
/** @type {import('pino').Logger=} */
logger: import('pino').Logger | undefined;
constructor(logger: import('pino').Logger);
/** @type {import('pino').Logger} */
logger: import('pino').Logger;
/** @type {WeakMap<Object, WeakSet<Handler<any>>>} */

@@ -16,0 +16,0 @@ seen: WeakMap<Object, WeakSet<Handler<any>>>;

@@ -25,6 +25,6 @@ /*

/**
* @param {import('pino').Logger=} logger
* @param {import('pino').Logger} logger
*/
constructor(logger) {
/** @type {import('pino').Logger=} */
/** @type {import('pino').Logger} */
this.logger = logger;

@@ -80,7 +80,7 @@ /** @type {WeakMap<Object, WeakSet<Handler<any>>>} */

};
this.logger?.trace({ metadata }, 'invoking handler: %s', metadata.name);
this.logger.trace({ metadata }, 'invoking handler: %s', metadata.name);
return handler(acc, { rerun, ...metadata }) ?? acc;
}
catch (err) {
this.logger?.error({ err, metadata }, 'error invoking handler: %s', metadata.name);
this.logger.error({ err, metadata }, 'error invoking handler: %s', metadata.name);
return acc;

@@ -87,0 +87,0 @@ }

declare function _exports(core: {
readonly logger: import('@contrast/logger').Logger;
readonly messages: import('@contrast/common').Messages;
depHooks: DepHooks;

@@ -7,23 +8,4 @@ }): DepHooks;

export { DepHooks };
export { Descriptor };
}
export = _exports;
export type Descriptor = {
/**
* module name, as passed to `require`, to handle.
*/
name: string;
/**
* alternative to `name`, taking precedent when provided.
*/
module?: string | undefined;
/**
* if provided, the file under the module's root that we want to hook. otherwise, the module's `main` will be hooked.
*/
file?: string | undefined;
/**
* if provided, hooks will only execute against an installed module that matches the SemVer version range
*/
version?: string | undefined;
};
/**

@@ -35,7 +17,14 @@ * Allows clients to register function handlers which run as a 'post-hook' at

/**
* @param {import('pino').Logger=} logger
* @param {Object} opts
* @param {import('pino').Logger} opts.logger
* @param {import('@contrast/common').Messages} opts.messages
*/
constructor(logger?: import('pino').Logger | undefined);
/** @type {import('pino').Logger=} */
logger: import('pino').Logger | undefined;
constructor({ logger, messages }: {
logger: import('pino').Logger;
messages: import('@contrast/common').Messages;
});
/** @type {import('pino').Logger} */
logger: import('pino').Logger;
/** @type {import('@contrast/common').Messages} */
messages: import('@contrast/common').Messages;
originalLoad: any;

@@ -53,6 +42,8 @@ /** @type {HandlerInvoker} */

* @template {Object} T
* @param {Descriptor | string} descriptor describes the module to hook
* @param {ExportHookDescriptor.Descriptor | string} descriptor describes the module to hook
* @param {ExportHookDescriptor.Handler<T>[]} handlers the function hooks to execute after require
*/
resolve<T extends Object>(descriptor: Descriptor | string, ...handlers: ExportHookDescriptor.Handler<T>[]): void;
register<T extends Object>(descriptor: ExportHookDescriptor.Descriptor | string, ...handlers: ExportHookDescriptor.Handler<T>[]): void;
/** Alias for `DepHooks#resolve` */
resolve: <T extends Object>(descriptor: ExportHookDescriptor.Descriptor | string, ...handlers: ExportHookDescriptor.Handler<T>[]) => void;
/**

@@ -59,0 +50,0 @@ * Provided with an export, a collection of handlers, and metadata, will

@@ -23,23 +23,2 @@ /*

/**
* @typedef {Object} Descriptor
* @property {string} name module name, as passed to `require`, to handle.
* @property {string=} module alternative to `name`, taking precedent when provided.
* @property {string=} file if provided, the file under the module's root that we want to hook. otherwise, the module's `main` will be hooked.
* @property {string=} version if provided, hooks will only execute against an installed module that matches the SemVer version range
*/
/**
* Coerces a string into a minimal object that can be made into a descriptor.
* @param {Descriptor | string} descriptor The export descriptor
* @returns {Descriptor}
*/
const normalizeDescriptor = (descriptor) => {
if (typeof descriptor === 'string') {
return { name: descriptor };
}
if (descriptor.module) {
descriptor.name = descriptor.module;
}
return descriptor;
};
/**
* Allows clients to register function handlers which run as a 'post-hook' at

@@ -50,7 +29,13 @@ * require-time.

/**
* @param {import('pino').Logger=} logger
* @param {Object} opts
* @param {import('pino').Logger} opts.logger
* @param {import('@contrast/common').Messages} opts.messages
*/
constructor(logger) {
/** @type {import('pino').Logger=} */
constructor({ logger, messages }) {
/** Alias for `DepHooks#resolve` */
this.resolve = this.register;
/** @type {import('pino').Logger} */
this.logger = logger;
/** @type {import('@contrast/common').Messages} */
this.messages = messages;
// set this up before we start patching Module methods

@@ -61,3 +46,3 @@ this.originalLoad = Reflect.get(Module, '_load');

/** @type {ExportHandlerRegistry} */
this.registry = new ExportHandlerRegistry(logger);
this.registry = new ExportHandlerRegistry({ logger, messages });
/** @type {WeakMap<Object, Object>} */

@@ -71,8 +56,7 @@ this.requiredModules = new WeakMap();

* @template {Object} T
* @param {Descriptor | string} descriptor describes the module to hook
* @param {ExportHookDescriptor.Descriptor | string} descriptor describes the module to hook
* @param {ExportHookDescriptor.Handler<T>[]} handlers the function hooks to execute after require
*/
resolve(descriptor, ...handlers) {
const normalized = normalizeDescriptor(descriptor);
const info = ExportHookDescriptor.create({ ...normalized, handlers });
register(descriptor, ...handlers) {
const info = ExportHookDescriptor.create(descriptor, handlers);
this.registry.update(info);

@@ -112,3 +96,3 @@ }

install() {
this.logger?.trace('Applying Module._load override');
this.logger.trace('Applying Module._load override');
const self = this;

@@ -142,3 +126,3 @@ /**

uninstall() {
this.logger?.trace('Removing Module._load override');
this.logger.trace('Removing Module._load override');
Reflect.set(Module, '_load', this.originalLoad);

@@ -158,2 +142,3 @@ }

* readonly logger: import('@contrast/logger').Logger;
* readonly messages: import('@contrast/common').Messages;
* depHooks: DepHooks;

@@ -164,3 +149,6 @@ * }} core

module.exports = function init(core) {
core.depHooks = new DepHooks(core.logger.child({ name: 'contrast:dep-hooks' }));
core.depHooks = new DepHooks({
logger: core.logger.child({ name: 'contrast:dep-hooks' }),
messages: core.messages,
});
return core.depHooks;

@@ -167,0 +155,0 @@ };

@@ -15,3 +15,3 @@ /*

*/
// @ts-nocheck
// @ts-check
'use strict';

@@ -43,2 +43,3 @@ Object.defineProperty(exports, "__esModule", { value: true });

if (isBuiltin(filename)) {
// @ts-ignore ts does not handle method overloads well
filename = StringPrototypeReplace.call(filename, /^(node:)?/, 'node:');

@@ -45,0 +46,0 @@ return {

{
"name": "@contrast/dep-hooks",
"version": "1.10.0",
"version": "1.11.0",
"description": "Post hooks for Module.prototype.require",

@@ -21,7 +21,7 @@ "license": "SEE LICENSE IN LICENSE",

"dependencies": {
"@contrast/common": "1.26.0",
"@contrast/common": "1.27.0",
"@contrast/find-package-json": "^1.1.0",
"@contrast/logger": "1.14.0",
"@contrast/logger": "1.15.0",
"semver": "^7.6.3"
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc