From b8bb4c9d065ba82954a9c3383cf48eebfdf08f40 Mon Sep 17 00:00:00 2001 From: Eric Cornelissen Date: Sat, 25 Jan 2025 09:29:14 +0100 Subject: [PATCH] Remove lodash.get This refactors the code base to get rid of lodash.get, which has been deprecated in favor of the `?.` operator. Since this project promises support with node >= 8.12 (see package.json:16) and `?.` is in Node.js since v14 I do not use it in these changes. Rather, I use old school JavaScript to achieve the same effect as these two alternatives. In most cases where `lodash.get` was being used it was pretty much useless and thus would not even require `?.`. In some cases it side-stepped the type checker and the refactored code adds the appropriate type checks. --- package-lock.json | 16 +------------ package.json | 2 -- src/handlers/handleInput.ts | 13 +++++----- src/utils/color.ts | 5 ++-- src/utils/print.ts | 3 +-- src/utils/vulnerability.ts | 48 ++++++++++++++++++++++++------------- 6 files changed, 42 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index eb243c5..01f06f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "commander": "^8.0.0", "dayjs": "^1.10.6", - "lodash.get": "^4.4.2", "semver": "^7.6.3", "table": "^6.7.1" }, @@ -20,7 +19,6 @@ }, "devDependencies": { "@types/chai": "^4.2.19", - "@types/lodash.get": "^4.4.6", "@types/mocha": "^8.2.3", "@types/node": "^16.0.0", "@types/semver": "^7.5.8", @@ -287,19 +285,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/lodash": { - "version": "4.14.171", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash.get": { - "version": "4.4.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/mocha": { "version": "8.2.3", "dev": true, @@ -1622,6 +1607,7 @@ }, "node_modules/lodash.get": { "version": "4.4.2", + "dev": true, "license": "MIT" }, "node_modules/lodash.truncate": { diff --git a/package.json b/package.json index 9544da0..ee6d4d2 100644 --- a/package.json +++ b/package.json @@ -31,13 +31,11 @@ "dependencies": { "commander": "^8.0.0", "dayjs": "^1.10.6", - "lodash.get": "^4.4.2", "semver": "^7.6.3", "table": "^6.7.1" }, "devDependencies": { "@types/chai": "^4.2.19", - "@types/lodash.get": "^4.4.6", "@types/mocha": "^8.2.3", "@types/node": "^16.0.0", "@types/semver": "^7.5.8", diff --git a/src/handlers/handleInput.ts b/src/handlers/handleInput.ts index 9219324..43e3fba 100644 --- a/src/handlers/handleInput.ts +++ b/src/handlers/handleInput.ts @@ -1,4 +1,3 @@ -import get from 'lodash.get'; import semver from 'semver'; import { AuditLevel, CommandOptions } from 'src/types'; import { getNpmVersion } from '../utils/npm'; @@ -30,25 +29,25 @@ export default function handleInput( const auditCommand: string = [ 'npm audit', // flags - get(options, 'production') ? getProductionOnlyOption() : '', - get(options, 'registry') ? `--registry=${options.registry}` : '', + options.production ? getProductionOnlyOption() : '', + options.registry ? `--registry=${options.registry}` : '', ] .filter(Boolean) .join(' '); // Taking the audit level from the command or environment variable const envVar = process.env.NPM_CONFIG_AUDIT_LEVEL as AuditLevel; - const auditLevel: AuditLevel = get(options, 'level', envVar) || 'info'; + const auditLevel: AuditLevel = options.level || envVar || 'info'; // Get the exceptions const nsprc = readFile('.nsprc'); - const cmdExceptions: string[] = get(options, 'exclude', '') + const cmdExceptions: string[] = (options.exclude || '') .split(',') .map((each) => each.trim()) .filter((each) => each !== ''); const exceptionIds: string[] = getExceptionsIds(nsprc, cmdExceptions); - const cmdModuleIgnore: string[] = get(options, 'moduleIgnore', '').split(','); - const cmdIncludeColumns: string[] = get(options, 'includeColumns', '') + const cmdModuleIgnore: string[] = (options.moduleIgnore || '').split(','); + const cmdIncludeColumns: string[] = (options.includeColumns || '') .split(',') .map((each: string) => each.trim()) .filter((each: string) => !!each); diff --git a/src/utils/color.ts b/src/utils/color.ts index 6c3f9ed..192c104 100644 --- a/src/utils/color.ts +++ b/src/utils/color.ts @@ -1,4 +1,3 @@ -import get from 'lodash.get'; import { Severity, Color, ColorCode } from 'src/types'; const RESET = '\x1b[0m' as const; @@ -54,8 +53,8 @@ export function color(message: string, fgColor?: Color, bgColor?: Color): string } return [ - get(COLORS, `${fgColor}.fg`, ''), - get(COLORS, `${bgColor}.bg`, ''), + (fgColor ? COLORS[fgColor].fg : ''), + (bgColor ? COLORS[bgColor].bg : ''), message, RESET, // Reset the color at the end ].join(''); diff --git a/src/utils/print.ts b/src/utils/print.ts index 9f433ac..87c47ad 100644 --- a/src/utils/print.ts +++ b/src/utils/print.ts @@ -1,4 +1,3 @@ -import get from 'lodash.get'; import { table, TableUserConfig } from 'table'; import { SecurityReportHeader, ExceptionReportHeader } from 'src/types'; @@ -18,7 +17,7 @@ export function getColumnWidth(tableData: string[][], columnIndex: number, maxWi // Find the maximum length in the column const contentLength = tableData.reduce( (max, cur) => { - let content = JSON.stringify(get(cur, columnIndex, '')); + let content = JSON.stringify(cur[columnIndex] || ''); // Remove the color codes content = content.replace(/\\x1b\[\d{1,2}m/g, ''); content = content.replace(/\\u001b\[\d{1,2}m/g, ''); diff --git a/src/utils/vulnerability.ts b/src/utils/vulnerability.ts index 1feddf7..4660ae7 100644 --- a/src/utils/vulnerability.ts +++ b/src/utils/vulnerability.ts @@ -1,5 +1,3 @@ -import get from 'lodash.get'; - import { isJsonString, trimArray, shortenNodePath } from './common'; import { color, getSeverityBgColor } from './color'; import { printExceptionReport } from './print'; @@ -209,13 +207,18 @@ export function processAuditJson( return Object.values(vulnerabilities).reduce( (acc: ProcessedResult, cur: v7Vulnerability | string) => { // Inside `via` array, its either the related module name or the vulnerability source object. - get(cur, 'via', []).forEach((vul: v7VulnerabilityVia | string) => { + const via = typeof cur === 'string' ? [] : cur.via; + via.forEach((vul: v7VulnerabilityVia | string) => { + if (typeof vul === 'string') { + return; + } + // The vulnerability ID is labeled as `source` - const id = get(vul, 'source'); - const moduleName = get(vul, 'name', ''); + const id = vul.source; + const moduleName = vul.name || ''; // Let's skip if ID is a string (module name), and only focus on the root vulnerabilities - if (!id || typeof id === 'string' || typeof vul === 'string') { + if (!id || typeof id === 'string') { return; } @@ -236,7 +239,7 @@ export function processAuditJson( { key: 'ID', value: String(id) }, { key: 'Module', value: vul.name }, { key: 'Title', value: vul.title }, - { key: 'Paths', value: trimArray(get(cur, 'nodes', []).map(shortenNodePath), MAX_PATHS_SIZE).join('\n') }, + { key: 'Paths', value: trimArray((typeof cur !== 'string' ? cur.nodes : []).map(shortenNodePath), MAX_PATHS_SIZE).join('\n') }, { key: 'Severity', value: vul.severity, bgColor: getSeverityBgColor(vul.severity) }, { key: 'URL', value: vul.url }, { key: 'Ex.', value: isExcepted ? 'y' : 'n' }, @@ -317,9 +320,19 @@ export function getExceptionsIds(nsprc?: NsprcFile | boolean, cmdExceptions: str export function processExceptions(nsprc: NsprcFile, cmdExceptions: string[] = []): ProcessedReport { return Object.entries(nsprc).reduce( (acc: ProcessedReport, [id, details]: [string, string | NsprcConfigs]) => { - const isActive = Boolean(get(details, 'active', true)); // default to true - const notes = typeof details === 'string' ? details : get(details, 'notes', ''); - const { valid, expired, years } = analyzeExpiry(get(details, 'expiry')); + let isActive: boolean; + let notes: string; + let expiry: string | number | undefined; + if (typeof details === 'string') { + isActive = true; + notes = details; + } else { + isActive = Boolean(details.active === undefined ? true : details.active); + notes = details.notes || ''; + expiry = details.expiry; + } + + const { valid, expired, years } = analyzeExpiry(expiry); // Color the status accordingly let status = color('active', 'green'); @@ -332,12 +345,15 @@ export function processExceptions(nsprc: NsprcFile, cmdExceptions: string[] = [] } // Color the date accordingly - let expiryDate = get(details, 'expiry') ? new Date(get(details, 'expiry')).toUTCString() : ''; - // If it was expired for more than 5 years ago, warn by coloring the date in red - if (years && years <= -5) { - expiryDate = color(expiryDate, 'red'); - } else if (years && years <= -1) { - expiryDate = color(expiryDate, 'yellow'); + let expiryDate = ''; + if (typeof details !== 'string' && details.expiry) { + expiryDate = new Date(details.expiry).toUTCString(); + // If it was expired for more than 5 years ago, warn by coloring the date in red + if (years && years <= -5) { + expiryDate = color(expiryDate, 'red'); + } else if (years && years <= -1) { + expiryDate = color(expiryDate, 'yellow'); + } } acc.report.push([id, status, expiryDate, notes]);