release: WRNexusJS 0.7.0
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
import { basename, join, relative } from "node:path";
|
||||
|
||||
export interface StagedFileIntegrity {
|
||||
path: string;
|
||||
bytes: number;
|
||||
sha256: string;
|
||||
}
|
||||
|
||||
export interface StagedPackageIntegrity {
|
||||
name: string;
|
||||
version: string;
|
||||
files: StagedFileIntegrity[];
|
||||
}
|
||||
|
||||
const forbiddenStageName =
|
||||
/^(?:\.env(?:\..*)?|focus-shims\.d\.ts|tsconfig\.focus\.json|id_rsa|id_ed25519|.*\.(?:pem|key|p12|pfx))$/i;
|
||||
const highConfidenceSecret =
|
||||
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----|\bAKIA[0-9A-Z]{16}\b|\bnpm_[A-Za-z0-9]{30,}\b|\bgh[pousr]_[A-Za-z0-9]{30,}\b/;
|
||||
|
||||
function listFiles(root: string): string[] {
|
||||
const files: string[] = [];
|
||||
for (const entry of readdirSync(root, { recursive: true, withFileTypes: true })) {
|
||||
if (!entry.isFile()) continue;
|
||||
files.push(join(entry.parentPath, entry.name));
|
||||
}
|
||||
return files.sort((a, b) => {
|
||||
const left = relative(root, a).replace(/\\/g, "/");
|
||||
const right = relative(root, b).replace(/\\/g, "/");
|
||||
return left < right ? -1 : left > right ? 1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
export function validateAndHashStage(
|
||||
stage: string,
|
||||
manifest: Record<string, unknown>,
|
||||
): StagedPackageIntegrity {
|
||||
const name = String(manifest.name ?? "unknown package");
|
||||
const files = listFiles(stage);
|
||||
if (!files.length) throw new Error(`${name} staged no files.`);
|
||||
const integrity: StagedFileIntegrity[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
const path = relative(stage, file).replace(/\\/g, "/");
|
||||
const filename = basename(file);
|
||||
if (forbiddenStageName.test(filename)) {
|
||||
throw new Error(`${name} staged forbidden secret-like file ${path}.`);
|
||||
}
|
||||
if (path.endsWith(".map")) {
|
||||
throw new Error(`${name} staged source map ${path}; package source maps are disabled.`);
|
||||
}
|
||||
if (path.endsWith(".ts") && !path.endsWith(".d.ts")) {
|
||||
throw new Error(`${name} staged TypeScript source ${path}.`);
|
||||
}
|
||||
|
||||
const value = readFileSync(file);
|
||||
if (value.byteLength <= 2 * 1024 * 1024) {
|
||||
const source = value.toString("utf8");
|
||||
if (highConfidenceSecret.test(source)) {
|
||||
throw new Error(`${name} staged content matching a high-confidence secret in ${path}.`);
|
||||
}
|
||||
}
|
||||
integrity.push({
|
||||
path,
|
||||
bytes: value.byteLength,
|
||||
sha256: createHash("sha256").update(value).digest("hex"),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
version: String(manifest.version ?? "unknown"),
|
||||
files: integrity,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user