Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/pnpm-audit-resolver/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @knime/pnpm-audit-resolver
710 changes: 710 additions & 0 deletions packages/pnpm-audit-resolver/LICENSE

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions packages/pnpm-audit-resolver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# ![Image](https://www.knime.com/sites/default/files/knime_logo_github_40x40_4layers.png)@knime/pnpm-audit-resolver

PNPM Audit Resolver

## How It Works

- The script looks for an `audit-resolve.json` file in your project’s root directory.
- It reads each GHSA ID, a `decision` (e.g. `"ignore"`), and an expiration timestamp (`expiresAt`).
- It compares the expiration timestamp with the current date (`Date.now()`):
- **If the GHSA is not expired** (i.e., `Date.now() < expiresAt`), it ensures that GHSA ID is listed in `pnpm.auditConfig.ignoreGhsas`.
- **If expired**, it removes that GHSA ID from the ignore list.
- It then updates your `package.json` accordingly to ensure that temporary ignore rules are automatically removed once their grace period has passed.

## Usage

1. Create a file named `audit-resolve.json` in your project root. When you run `pnpm audit`, each entry has a `GHSA ID`. You should use this ID as a key, mapping to an object with:

- decision: Typically "ignore".
- expiresAt: A Unix timestamp (milliseconds) indicating when the ignore decision expires.

```
{
"GHSA-1234-abcd-wxyz": {
"decision": "ignore",
"expiresAt": 1726923450122
},
"GHSA-4321-dcba-zywx": {
"decision": "ignore",
"expiresAt": 1726923450122
}
}
```

2. Use the exposed `audit-resolve` command line tool in your repository's `package.json` which can be used with pnpm to run the resolver script

```
{
"scripts":{
"audit:resolve": "pnpm audit-resolve"
}
}
```

3. Append this command to the audit command, so that it always runs before the auditing

```
"audit": "pnpm audit:resolve && pnpm audit --prod",
```
30 changes: 30 additions & 0 deletions packages/pnpm-audit-resolver/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@knime/pnpm-audit-resolver",
"version": "1.0.0",
"description": "Resolves audit issues be defining actions to it",
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammatical error in description. Should be 'by defining actions for them' instead of 'be defining actions to it'.

Suggested change
"description": "Resolves audit issues be defining actions to it",
"description": "Resolves audit issues by defining actions for them",

Copilot uses AI. Check for mistakes.
"homepage": "https://knime.github.io/webapps-common/",
"license": "GPL 3 and Additional Permissions according to Sec. 7 (SEE the file LICENSE)",
"author": "KNIME AG, Zurich, Switzerland",
"type": "module",
"scripts": {
"audit:resolve": "node src/index"
},
"main": "src/index.js",
"files": [
"src",
"CHANGELOG.md"
],
"exports": {
".": {
"import": "./src/index.js"
}
},
"bin": {
"audit-resolve": "./src/index.js"
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The binary entry points to a file that lacks a shebang line. Add #!/usr/bin/env node as the first line of src/index.js to make it executable as a binary.

Copilot uses AI. Check for mistakes.
},
"pnpm": {
"auditConfig": {
"ignoreGhsas": []
}
}
}
68 changes: 68 additions & 0 deletions packages/pnpm-audit-resolver/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable no-console */
import fs from "fs/promises";
import path from "path";

const updateAuditConfig = async () => {
const rootDir = process.cwd();
const auditResolvePath = path.join(rootDir, "audit-resolve.json");
const packageJsonPath = path.join(rootDir, "package.json");

// Read and parse audit-resolve.json
let auditResolveRaw;
try {
auditResolveRaw = await fs.readFile(auditResolvePath, "utf-8");
} catch {
throw "Could not find 'audit-resolve.json' file in your root directory";
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing a string is not recommended. Use a proper Error object instead: throw new Error(\"Could not find 'audit-resolve.json' file in your root directory\");

Suggested change
throw "Could not find 'audit-resolve.json' file in your root directory";
throw new Error("Could not find 'audit-resolve.json' file in your root directory");

Copilot uses AI. Check for mistakes.
}

const auditData = JSON.parse(auditResolveRaw);
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON.parse can throw if the file contains invalid JSON. Wrap this in a try-catch block to provide a more helpful error message for malformed audit-resolve.json files.

Suggested change
const auditData = JSON.parse(auditResolveRaw);
let auditData;
try {
auditData = JSON.parse(auditResolveRaw);
} catch (err) {
throw new Error(
"Malformed 'audit-resolve.json': " + err.message
);
}

Copilot uses AI. Check for mistakes.

// Read and parse package.json
const packageJsonRaw = await fs.readFile(packageJsonPath, "utf-8");
const packageJson = JSON.parse(packageJsonRaw);
Comment on lines +21 to +22
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for reading and parsing package.json. These operations can fail if the file doesn't exist or contains invalid JSON.

Suggested change
const packageJsonRaw = await fs.readFile(packageJsonPath, "utf-8");
const packageJson = JSON.parse(packageJsonRaw);
let packageJsonRaw;
try {
packageJsonRaw = await fs.readFile(packageJsonPath, "utf-8");
} catch {
throw "Could not find 'package.json' file in your root directory";
}
let packageJson;
try {
packageJson = JSON.parse(packageJsonRaw);
} catch {
throw "Could not parse 'package.json': invalid JSON format";
}

Copilot uses AI. Check for mistakes.

// Initialize pnpm audit config if not already present
packageJson.pnpm = packageJson.pnpm || {};
packageJson.pnpm.auditConfig = packageJson.pnpm.auditConfig || {};
let ignoreGhsas = Array.isArray(packageJson.pnpm.auditConfig.ignoreGhsas)
? packageJson.pnpm.auditConfig.ignoreGhsas
: [];

const currentTime = Date.now();

// Process each audit resolution from audit-resolve.json
for (const [ghsaId, resolution] of Object.entries(auditData)) {
if (resolution.decision === "ignore") {
// If the ignore decision is still valid (not expired), add the GHSA id if it's missing
if (currentTime < resolution.expiresAt) {
if (!ignoreGhsas.includes(ghsaId)) {
ignoreGhsas.push(ghsaId);
console.log(`Added ${ghsaId} to ignore list.`);
}
} else {
// If expired, remove it from the list if present
if (ignoreGhsas.includes(ghsaId)) {
ignoreGhsas = ignoreGhsas.filter((id) => id !== ghsaId);
console.log(`Removed expired ${ghsaId} from ignore list.`);
}
}
} else {
console.log(`Decision for ${ghsaId} is not 'ignore'; skipping.`);
}
}

// Update the package.json audit configuration
packageJson.pnpm.auditConfig.ignoreGhsas = ignoreGhsas;

// Write the updated package.json back to disk
await fs.writeFile(
packageJsonPath,
JSON.stringify(packageJson, null, 2),
"utf-8",
);
console.log("Updated package.json audit configuration.");
};

updateAuditConfig().catch((error) => {
console.error("Error updating audit configuration:", error);
});
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.