Socket
Socket
Sign inDemoInstall

@contrast/library-analysis

Package Overview
Dependencies
4
Maintainers
9
Versions
30
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.15.0 to 1.15.1

2

lib/index.js
/*
* Copyright: 2023 Contrast Security, Inc
* Copyright: 2024 Contrast Security, Inc
* Contact: support@contrastsecurity.com

@@ -4,0 +4,0 @@ * License: Commercial

/*
* Copyright: 2023 Contrast Security, Inc
* Copyright: 2024 Contrast Security, Inc
* Contact: support@contrastsecurity.com

@@ -142,3 +142,3 @@ * License: Commercial

if (!nodeModulesPath) {
logger.warn('Unable to determine the location of node_modules, aborting library analysis. Ensure `agent.node.app_root` is set to the directory containing your node_modules folder.');
logger.warn('Unable to determine the location of the `node_modules` directory; aborting library analysis. Ensure the `agent.node.app_root` configuration variable is set to the directory containing your `node_modules` folder.');
return;

@@ -149,2 +149,3 @@ }

const npmData = await listInstalled(
config.agent.node.npm_path,
nodeModulesPath,

@@ -151,0 +152,0 @@ logger,

/*
* Copyright: 2023 Contrast Security, Inc
* Copyright: 2024 Contrast Security, Inc
* Contact: support@contrastsecurity.com

@@ -15,2 +15,3 @@ * License: Commercial

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

@@ -36,2 +37,3 @@

/**
* @param {string} npm the path to the npm executable
* @param {string} cwd directory in which we want to execute `npm ls`

@@ -42,3 +44,3 @@ * @param {import('@contrast/logger').Logger} logger

*/
module.exports = async function listInstalled(cwd, logger, npmVersionRange) {
module.exports = async function listInstalled(npm, cwd, logger, npmVersionRange) {
const execFileOpts = {

@@ -50,18 +52,17 @@ cwd,

};
let stdout;
let version, location, cause;
try {
const result = await execFile('npm', ['help'], execFileOpts);
stdout = result.stdout;
const result = await execFile(npm, ['help'], execFileOpts);
[, version, location] = result.stdout.match(VERSION_REGEX) || [];
} catch (err) {
logger.trace({ err }, '`npm help` returned an error');
// If npm encounters any errors whatsoever it will return with a non-zero
// exit code but still output the relevant information to stdout.
// If an even worse error occurs, we may not be able to parse stdout.
stdout = err.stdout ?? '{}';
cause = err;
}
const [, version, location] = stdout.match(VERSION_REGEX) || [];
if (!version) {
throw new Error("Unable to locate `npm`. `npm` is required for your application's libraries to be reported to Contrast for analysis. Please enable debug or trace level logs for more information.");
throw new Error(
"Unable to locate an `npm` executable. `npm` is required for your application's libraries to be reported to Contrast for analysis. You can use the `agent.node.npm_path` configuration variable to specify the path to the npm executable if it is not correctly detected by the agent.",
// @ts-expect-error error.cause added in 16.9.0
{ cause }
);
}

@@ -80,19 +81,29 @@

const lsArgs = ['ls', '--json', '--long'];
// This will be needs to be updated once node 14 is no longer LTS
if (semver.gte(version, '8.0.0')) lsArgs.push('--all');
if (semver.gte(version, '7.0.0')) lsArgs.push('--all');
let list;
try {
const result = await execFile('npm', lsArgs, execFileOpts);
stdout = result.stdout;
const result = await execFile(npm, lsArgs, execFileOpts);
list = result.stdout;
} catch (err) {
logger.trace({ err }, '`npm ls` returned an error');
stdout = err.stdout ?? '{}';
// If npm encounters any errors whatsoever it will return with a non-zero
// exit code but still output the relevant information to stdout.
// If an even worse error occurs, we may not be able to parse stdout.
list = err.stdout ?? '{}';
// Since stdout includes the entire JSON object describing npm dependencies,
// it makes the log entry enormous. We omit that content and log the rest of
// the error here.
delete err.stdout;
logger.debug({ err }, 'error occured when executing `npm ls`');
}
try {
return JSON.parse(stdout);
return JSON.parse(list);
} catch (err) {
logger.trace({ err }, 'parsing the output of `npm ls` failed');
throw new Error('`npm ls` failed to provide a list of installed dependencies. Please enable trace level logs for more information.');
throw new Error(
'`npm ls` failed to provide a valid list of installed dependencies.',
// @ts-expect-error error.cause added in 16.9.0
{ cause: err },
);
}
};
/*
* Copyright: 2023 Contrast Security, Inc
* Copyright: 2024 Contrast Security, Inc
* Contact: support@contrastsecurity.com

@@ -18,5 +18,4 @@ * License: Commercial

const path = require('path');
const semver = require('semver');
const { Event, substring, substr, replace, split } = require('@contrast/common');
const { setCodeEventListener } = require('@contrast/fn-inspect');
const { setCodeEventListener } = require('@contrast/code-events');
const { buildLibraryHash } = require('../../util');

@@ -29,3 +28,2 @@

const libInfoMap = libraryUsage.libInfoMap = new Map();
const type = semver.gte(process.version, '20.0.0') ? 'Function' : 'LazyCompile';

@@ -72,4 +70,3 @@ libraryUsage.readPackageFile = function readPackageFile(installPath) {

if (
codeEvent.type !== type ||
codeEvent.script?.indexOf(`node_modules${path.sep}`) === -1 ||
!codeEvent.script?.includes(`node_modules${path.sep}`) ||
!codeEvent.func

@@ -102,5 +99,7 @@ ) return;

libraryUsage.install = function () {
const evalInterval = config.agent.node.library_usage.reporting.interval_ms;
const interval = config.agent.node.library_usage.reporting.interval_ms;
setCodeEventListener(libraryUsage.codeEventListener, evalInterval);
// by default it will not report node: internal scripts or non-Function/LazyCompile
// types.
setCodeEventListener(libraryUsage.codeEventListener, { interval });
setInterval(libraryUsage.report, 2000).unref();

@@ -107,0 +106,0 @@ };

/*
* Copyright: 2023 Contrast Security, Inc
* Copyright: 2024 Contrast Security, Inc
* Contact: support@contrastsecurity.com

@@ -15,9 +15,23 @@ * License: Commercial

*/
// @ts-check
'use strict';
const fs = require('fs').promises;
const pathModule = require('path');
const os = require('os');
const fs = require('fs/promises');
const { EOL } = require('os');
const { join } = require('path');
const APPLICABLE_FILE_REGEX = /\.([cm]?js|node)$/;
function buildLibraryHash(data) {
if (data._shasum) {
return data._shasum;
}
if (data.dist) {
return data.dist.shasum;
}
return `${data.name}:${data.name}:${data.version}`;
}
function createLibData(data, tags) {

@@ -39,30 +53,19 @@ return {

function buildLibraryHash(data) {
if (data._shasum) {
return data._shasum;
}
if (data.dist) {
return data.dist.shasum;
}
return `${data.name}:${data.name}:${data.version}`;
}
function serializeLibrary(library) {
let manifest = `${library.name}${os.EOL}${os.EOL}`;
let manifest = `${library.name}${EOL}${EOL}`;
const { description, license, dependencies, requiredBy } = library;
if (description) {
manifest += `${description}${os.EOL}`;
manifest += `${description}${EOL}`;
}
if (license) {
manifest += `license: ${license}${os.EOL}`;
manifest += `license: ${license}${EOL}`;
}
if (dependencies) {
manifest += `dependencies:${os.EOL}`;
manifest += `dependencies:${EOL}`;
Object.entries(dependencies).forEach(([name, version]) => {
manifest += ` ${name}: ${version}${os.EOL}`;
manifest += ` ${name}: ${version}${EOL}`;
});

@@ -72,5 +75,5 @@ }

if (requiredBy && requiredBy.length > 0) {
manifest += `dependants:${os.EOL}`;
manifest += `dependants:${EOL}`;
requiredBy.forEach((d) => {
manifest += ` ${d}${os.EOL}`;
manifest += ` ${d}${EOL}`;
});

@@ -92,51 +95,49 @@ }

function applicableFile(file) {
return (
['.js', '.node', '.cjs', '.mjs'].filter((validExtension) =>
file.endsWith(validExtension)
).length > 0
);
/**
* @param {string} path
* @returns {boolean}
*/
function applicableFile(path) {
return APPLICABLE_FILE_REGEX.test(path);
}
async function getFileCount(path) {
const files = await readdir(path);
return files.filter((file) => applicableFile(file)
).length;
}
/**
* recursive fs.readdir, since the recursive option is only added in node 18.
* @param {string} path
* @returns {Promise<string[]>}
*/
async function readdir(path) {
let list = [];
const files = await fs.readdir(path);
let pending = files.length;
const list = [];
if (!pending) {
return list;
}
for (const file of files) {
const filePath = pathModule.join(path, file);
const stats = await fs.stat(filePath);
const filePath = join(path, file);
const fileStat = await fs.stat(filePath);
if (stats.isDirectory() && !filePath.endsWith(`${pathModule.sep}node_modules`)) {
if (fileStat.isDirectory() && file !== 'node_modules') {
const subList = await readdir(filePath);
list = list.concat(subList);
pending -= 1;
list.push(...subList);
} else {
list.push(filePath);
pending -= 1;
}
}
if (!pending) {
return list;
}
}
return list;
}
/**
* @param {string} path
* @returns {Promise<number>}
*/
async function getFileCount(path) {
const files = await readdir(path);
return files.filter(applicableFile).length;
}
module.exports = {
buildLibraryHash,
createLibData,
serializeLibrary,
getFileCount,
createLibData
};
{
"name": "@contrast/library-analysis",
"version": "1.15.0",
"version": "1.15.1",
"description": "Handles library reporting and library usage analysis",

@@ -20,3 +20,3 @@ "license": "SEE LICENSE IN LICENSE",

"dependencies": {
"@contrast/fn-inspect": "^3.4.0",
"@contrast/code-events": "^2.0.0",
"@contrast/common": "1.16.0",

@@ -23,0 +23,0 @@ "semver": "^7.3.8"

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc