Comparing version 0.0.3 to 0.1.0
@@ -5,3 +5,3 @@ import yargs from "yargs"; | ||
import { analyzeLockfile, analyzePackageJson } from "./index.js"; | ||
import { writeToConsole } from "./formatter.js"; | ||
import { writeResultsToConsole } from "./formatter.js"; | ||
await yargs(hideBin(process.argv)) | ||
@@ -12,26 +12,71 @@ .parserConfiguration({ | ||
.scriptName("no-scripts") | ||
.command({ | ||
command: "$0 [projectDir]", | ||
desc: "Check lockfile", | ||
builder: (yargs) => { | ||
yargs | ||
.positional("projectDir", { | ||
describe: "Project directory to scan. Defaults to the current working directory", | ||
type: "string", | ||
}); | ||
}, | ||
handler: async (argv) => { | ||
const cwd = argv.projectDir ? path.resolve(argv.projectDir) : process.cwd(); | ||
const lockfileResults = await analyzeLockfile({ cwd, ignorePackages: argv.ignore || [] }); | ||
let packageJsonResult; | ||
if (argv.includeLocal) { | ||
.command("$0 [projectDir]", "Checks all npm dependencies and fail if any of them define automatically executed npm lifecycle scripts", (yargs) => { | ||
yargs | ||
.positional("projectDir", { | ||
describe: "Project directory to scan. Defaults to the current working directory", | ||
type: "string", | ||
}); | ||
}, async (argv) => { | ||
const cwd = argv.projectDir ? path.resolve(argv.projectDir) : process.cwd(); | ||
let lockfileResults; | ||
if (!argv.offline) { | ||
lockfileResults = await analyzeLockfile({ cwd, ignorePackages: argv.ignore || [] }); | ||
writeResultsToConsole(lockfileResults); | ||
console.log(""); | ||
} | ||
let packageJsonResult; | ||
if (argv.includeLocal || argv.offline) { | ||
packageJsonResult = await analyzePackageJson({ cwd, ignorePackages: argv.ignore || [] }); | ||
writeResultsToConsole(packageJsonResult); | ||
console.log(""); | ||
if (argv.verbose && lockfileResults) { | ||
// If verbose logging is enabled, compare the analyzed sets of packages and log the differences | ||
console.log("Comparing analyzed package sets..."); | ||
const lockfilePackages = new Set(); | ||
const packageJsonPackages = new Set(); | ||
lockfileResults.packages.forEach((packageAnalysisResult) => { | ||
lockfilePackages.add(packageAnalysisResult.packageName); | ||
}); | ||
packageJsonResult.packages.forEach((packageAnalysisResult) => { | ||
packageJsonPackages.add(packageAnalysisResult.packageName); | ||
}); | ||
const localOnly = new Set(); | ||
for (const packageName of packageJsonPackages) { | ||
if (lockfilePackages.has(packageName)) { | ||
lockfilePackages.delete(packageName); | ||
} | ||
else { | ||
localOnly.add(packageName); | ||
} | ||
} | ||
if (lockfilePackages.size) { | ||
console.log(`Packages listed in lockfile but not found locally (${lockfilePackages.size}):`); | ||
for (const pkgName of lockfilePackages.values()) { | ||
console.log(` * ${pkgName}`); | ||
} | ||
console.log(""); | ||
} | ||
else { | ||
console.log(`All packages listed in the lockfile where analyzed locally too`); | ||
} | ||
if (localOnly.size) { | ||
console.log(`Packages found locally but not listed in lockfile (${localOnly.size}):`); | ||
for (const pkgName of localOnly.values()) { | ||
console.log(` * ${pkgName}`); | ||
} | ||
console.log(`(this might indicate an outdated lockfile)`); | ||
} | ||
else { | ||
console.log(`All packages analyzed locally are also listed in the lockfile`); | ||
} | ||
console.log(""); | ||
packageJsonResult = await analyzePackageJson({ cwd, ignorePackages: argv.ignore || [] }); | ||
} | ||
console.log(""); | ||
writeToConsole(lockfileResults, packageJsonResult); | ||
if (lockfileResults.numberOfFindings || (packageJsonResult && packageJsonResult.numberOfFindings)) { | ||
process.exit(1); | ||
} | ||
} | ||
if (lockfileResults?.numberOfFindings || packageJsonResult?.numberOfFindings) { | ||
console.log("Exiting with status FAILED(1)"); | ||
process.exit(1); | ||
} | ||
else { | ||
console.log("Exiting with status SUCCESS(0)"); | ||
} | ||
}) | ||
@@ -47,2 +92,10 @@ .option("ignore", { | ||
}) | ||
.option("offline", { | ||
describe: "Only scan local dependencies. Implies '--include-local'", | ||
boolean: true, | ||
}) | ||
.option("verbose", { | ||
describe: "Enables additional logging", | ||
boolean: true, | ||
}) | ||
.showHelpOnFail(true) | ||
@@ -61,4 +114,5 @@ .strict(true) | ||
} | ||
process.exit(1); | ||
}) | ||
.parse(); | ||
//# sourceMappingURL=cli.js.map |
@@ -1,21 +0,9 @@ | ||
export function writeToConsole(lockfileResults, packageJsonResults) { | ||
for (const packageResult of lockfileResults.packages) { | ||
if (packageResult.messages.length) { | ||
console.log(`Findings for package ${packageResult.packageInfo.packageJson.name}:`); | ||
for (const msg of packageResult.messages) { | ||
console.log(` ${msg}`); | ||
} | ||
console.log(""); | ||
} | ||
} | ||
console.log(`${lockfileResults.numberOfFindings} Findings`); | ||
if (packageJsonResults) { | ||
export function writeResultsToConsole(results) { | ||
if (results) { | ||
console.log(""); | ||
console.log("------------------------"); | ||
console.log("Local results:"); | ||
for (const packageResult of packageJsonResults.packages) { | ||
for (const packageResult of results.packages) { | ||
if (packageResult.messages.length) { | ||
console.log(`Findings for package ${packageResult.packageInfo.packageJson.name}:`); | ||
console.log(`# Findings for package ${packageResult.packageInfo.packageJson.name}:`); | ||
for (const msg of packageResult.messages) { | ||
console.log(` ${msg}`); | ||
console.log(` * ${msg}`); | ||
} | ||
@@ -25,5 +13,5 @@ console.log(""); | ||
} | ||
console.log(`${packageJsonResults.numberOfFindings} Findings`); | ||
console.log(`Findings: ${results.numberOfFindings}`); | ||
} | ||
} | ||
//# sourceMappingURL=formatter.js.map |
{ | ||
"name": "no-scripts", | ||
"version": "0.0.3", | ||
"version": "0.1.0", | ||
"description": "", | ||
@@ -38,2 +38,5 @@ "type": "module", | ||
"@types/node": "^20.8.10", | ||
"@types/pacote": "^11.1.8", | ||
"@types/resolve": "^1.20.5", | ||
"@types/yargs": "^17.0.31", | ||
"@typescript-eslint/eslint-plugin": "^6.10.0", | ||
@@ -40,0 +43,0 @@ "@typescript-eslint/parser": "^6.10.0", |
# no-scripts | ||
**Note: This tool is still in development** | ||
A CLI tool that checks your project's npm dependencies and alerts you if any of them define one of the [npm lifecycle script](https://docs.npmjs.com/cli/v10/using-npm/scripts) that are automatically executed during `npm install`. Namely `preinstall`, `install`, `postinstall`, `preuninstall` and `postuninstall`. | ||
@@ -11,5 +9,16 @@ | ||
To further secure your system from such attacks you should consider having npm ignore **all** implicit scripts using the configuration command: [**`npm config set ignore-scripts true`**](https://docs.npmjs.com/cli/v10/using-npm/config#ignore-scripts) | ||
To further secure your system from such attacks you should consider having npm ignore **all** implicit scripts using the configuration command: [**`npm config set ignore-scripts true`**](https://docs.npmjs.com/cli/v10/using-npm/config#ignore-scripts) | ||
Also make sure to update your CI jobs to run `npm ci --ignore-scripts` and `npm publish --ignore-scripts` (as also suggested by [snyk](https://snyk.io/blog/github-actions-to-securely-publish-npm-packages/) for example) | ||
Note however that this disables **any** [pre- and post-scripts](https://docs.npmjs.com/cli/v10/using-npm/scripts#pre--post-scripts), such as for example the commonly used `pretest` script. | ||
Also, in case one of your dependencies does need an install script to run, you will have to manually execute [`npm rebuild <package name>`](https://docs.npmjs.com/cli/v10/commands/npm-rebuild) to run those after install. | ||
## Install | ||
```sh | ||
npm install --global no-scripts | ||
``` | ||
## Usage | ||
@@ -23,10 +32,14 @@ | ||
**Note:** Local dependencies which are referenced via links or workspaces are not analyzed. You can use the [`--include-local`](#include-local-dependencies) option to additionally check them. | ||
If any install-scripts have been found, the tools exit code will be set to `1`. | ||
**Note:** Local dependencies which are referenced via links or workspaces are not analyzed. You can use the [`--include-local`](#include-local-dependencies) option to additionally check those. | ||
### Ignore Dependencies | ||
```sh | ||
no-scripts --ignore esbuild | ||
no-scripts --ignore <package name> <package name> <...> | ||
``` | ||
In case one of your project's dependencies requires an install script you can ignore that package from the analysis using the `--ignore` flag. For example `no-scripts --ignore esbuild>` | ||
### Include Local Dependencies | ||
@@ -39,4 +52,11 @@ ```sh | ||
### Offline | ||
```sh | ||
no-scripts --offline | ||
``` | ||
This option implies [`--include-local`](#include-local-dependencies) as it completely skips the default behavior of resolving the packages listed in the lockfile using a remote registry. | ||
## Similar Projects | ||
* [naugtur/can-i-ignore-scripts](https://github.com/naugtur/can-i-ignore-scripts) | ||
* [spaceraccoon/npm-scan](https://github.com/spaceraccoon/npm-scan) *(archived)* |
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 too big to display
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
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
174060
4531
60
9