@0xrama/bnpm 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -0
- package/dist/package.json +45 -0
- package/dist/src/cache/archive.d.ts +15 -0
- package/dist/src/cache/archive.js +163 -0
- package/dist/src/cache/archive.js.map +1 -0
- package/dist/src/cache/commands.d.ts +15 -0
- package/dist/src/cache/commands.js +168 -0
- package/dist/src/cache/commands.js.map +1 -0
- package/dist/src/cache/quarantine.d.ts +18 -0
- package/dist/src/cache/quarantine.js +104 -0
- package/dist/src/cache/quarantine.js.map +1 -0
- package/dist/src/cache/store.d.ts +10 -0
- package/dist/src/cache/store.js +155 -0
- package/dist/src/cache/store.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +4 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/diff.d.ts +34 -0
- package/dist/src/commands/diff.js +223 -0
- package/dist/src/commands/diff.js.map +1 -0
- package/dist/src/commands/doctor.d.ts +14 -0
- package/dist/src/commands/doctor.js +42 -0
- package/dist/src/commands/doctor.js.map +1 -0
- package/dist/src/commands/edit.d.ts +12 -0
- package/dist/src/commands/edit.js +100 -0
- package/dist/src/commands/edit.js.map +1 -0
- package/dist/src/commands/fund.d.ts +10 -0
- package/dist/src/commands/fund.js +32 -0
- package/dist/src/commands/fund.js.map +1 -0
- package/dist/src/commands/graph.d.ts +29 -0
- package/dist/src/commands/graph.js +90 -0
- package/dist/src/commands/graph.js.map +1 -0
- package/dist/src/commands/index.d.ts +20 -0
- package/dist/src/commands/index.js +1753 -0
- package/dist/src/commands/index.js.map +1 -0
- package/dist/src/commands/navigation.d.ts +2 -0
- package/dist/src/commands/navigation.js +47 -0
- package/dist/src/commands/navigation.js.map +1 -0
- package/dist/src/commands/outdated.d.ts +17 -0
- package/dist/src/commands/outdated.js +69 -0
- package/dist/src/commands/outdated.js.map +1 -0
- package/dist/src/commands/process.d.ts +11 -0
- package/dist/src/commands/process.js +189 -0
- package/dist/src/commands/process.js.map +1 -0
- package/dist/src/commands/prompts.d.ts +6 -0
- package/dist/src/commands/prompts.js +86 -0
- package/dist/src/commands/prompts.js.map +1 -0
- package/dist/src/commands/query.d.ts +48 -0
- package/dist/src/commands/query.js +639 -0
- package/dist/src/commands/query.js.map +1 -0
- package/dist/src/commands/rebuild.d.ts +16 -0
- package/dist/src/commands/rebuild.js +44 -0
- package/dist/src/commands/rebuild.js.map +1 -0
- package/dist/src/commands/sbom.d.ts +6 -0
- package/dist/src/commands/sbom.js +31 -0
- package/dist/src/commands/sbom.js.map +1 -0
- package/dist/src/commands/script-approvals.d.ts +22 -0
- package/dist/src/commands/script-approvals.js +128 -0
- package/dist/src/commands/script-approvals.js.map +1 -0
- package/dist/src/commands/shrinkwrap.d.ts +6 -0
- package/dist/src/commands/shrinkwrap.js +97 -0
- package/dist/src/commands/shrinkwrap.js.map +1 -0
- package/dist/src/commands/stage.d.ts +20 -0
- package/dist/src/commands/stage.js +40 -0
- package/dist/src/commands/stage.js.map +1 -0
- package/dist/src/commands/token.d.ts +2 -0
- package/dist/src/commands/token.js +47 -0
- package/dist/src/commands/token.js.map +1 -0
- package/dist/src/commands/trust.d.ts +5 -0
- package/dist/src/commands/trust.js +41 -0
- package/dist/src/commands/trust.js.map +1 -0
- package/dist/src/config/commands.d.ts +5 -0
- package/dist/src/config/commands.js +91 -0
- package/dist/src/config/commands.js.map +1 -0
- package/dist/src/config/configuration.d.ts +40 -0
- package/dist/src/config/configuration.js +157 -0
- package/dist/src/config/configuration.js.map +1 -0
- package/dist/src/config/interactive.d.ts +16 -0
- package/dist/src/config/interactive.js +19 -0
- package/dist/src/config/interactive.js.map +1 -0
- package/dist/src/config/paths.d.ts +22 -0
- package/dist/src/config/paths.js +46 -0
- package/dist/src/config/paths.js.map +1 -0
- package/dist/src/config/types.d.ts +15 -0
- package/dist/src/config/types.js +6 -0
- package/dist/src/config/types.js.map +1 -0
- package/dist/src/core/cli-parser.d.ts +102 -0
- package/dist/src/core/cli-parser.js +1148 -0
- package/dist/src/core/cli-parser.js.map +1 -0
- package/dist/src/core/cli-runner.d.ts +15 -0
- package/dist/src/core/cli-runner.js +261 -0
- package/dist/src/core/cli-runner.js.map +1 -0
- package/dist/src/core/exit-codes.d.ts +13 -0
- package/dist/src/core/exit-codes.js +13 -0
- package/dist/src/core/exit-codes.js.map +1 -0
- package/dist/src/core/output.d.ts +23 -0
- package/dist/src/core/output.js +123 -0
- package/dist/src/core/output.js.map +1 -0
- package/dist/src/installer/install.d.ts +73 -0
- package/dist/src/installer/install.js +596 -0
- package/dist/src/installer/install.js.map +1 -0
- package/dist/src/installer/mutations.d.ts +5 -0
- package/dist/src/installer/mutations.js +160 -0
- package/dist/src/installer/mutations.js.map +1 -0
- package/dist/src/linker/project-linker.d.ts +9 -0
- package/dist/src/linker/project-linker.js +223 -0
- package/dist/src/linker/project-linker.js.map +1 -0
- package/dist/src/lockfile/index.d.ts +27 -0
- package/dist/src/lockfile/index.js +221 -0
- package/dist/src/lockfile/index.js.map +1 -0
- package/dist/src/package/authoring.d.ts +31 -0
- package/dist/src/package/authoring.js +125 -0
- package/dist/src/package/authoring.js.map +1 -0
- package/dist/src/package/pack.d.ts +22 -0
- package/dist/src/package/pack.js +238 -0
- package/dist/src/package/pack.js.map +1 -0
- package/dist/src/package/provenance.d.ts +28 -0
- package/dist/src/package/provenance.js +162 -0
- package/dist/src/package/provenance.js.map +1 -0
- package/dist/src/package/publish.d.ts +28 -0
- package/dist/src/package/publish.js +189 -0
- package/dist/src/package/publish.js.map +1 -0
- package/dist/src/package/trusted-publishing.d.ts +7 -0
- package/dist/src/package/trusted-publishing.js +84 -0
- package/dist/src/package/trusted-publishing.js.map +1 -0
- package/dist/src/project/dependency-error.d.ts +3 -0
- package/dist/src/project/dependency-error.js +7 -0
- package/dist/src/project/dependency-error.js.map +1 -0
- package/dist/src/project/dev-engines.d.ts +10 -0
- package/dist/src/project/dev-engines.js +70 -0
- package/dist/src/project/dev-engines.js.map +1 -0
- package/dist/src/project/discovery.d.ts +6 -0
- package/dist/src/project/discovery.js +51 -0
- package/dist/src/project/discovery.js.map +1 -0
- package/dist/src/project/global.d.ts +3 -0
- package/dist/src/project/global.js +43 -0
- package/dist/src/project/global.js.map +1 -0
- package/dist/src/project/invalidation.d.ts +3 -0
- package/dist/src/project/invalidation.js +21 -0
- package/dist/src/project/invalidation.js.map +1 -0
- package/dist/src/project/link.d.ts +21 -0
- package/dist/src/project/link.js +152 -0
- package/dist/src/project/link.js.map +1 -0
- package/dist/src/project/manifest.d.ts +27 -0
- package/dist/src/project/manifest.js +378 -0
- package/dist/src/project/manifest.js.map +1 -0
- package/dist/src/project/pkg.d.ts +5 -0
- package/dist/src/project/pkg.js +94 -0
- package/dist/src/project/pkg.js.map +1 -0
- package/dist/src/project/recovery.d.ts +2 -0
- package/dist/src/project/recovery.js +82 -0
- package/dist/src/project/recovery.js.map +1 -0
- package/dist/src/project/workspaces.d.ts +4 -0
- package/dist/src/project/workspaces.js +59 -0
- package/dist/src/project/workspaces.js.map +1 -0
- package/dist/src/registry/account.d.ts +79 -0
- package/dist/src/registry/account.js +305 -0
- package/dist/src/registry/account.js.map +1 -0
- package/dist/src/registry/audit.d.ts +18 -0
- package/dist/src/registry/audit.js +74 -0
- package/dist/src/registry/audit.js.map +1 -0
- package/dist/src/registry/client.d.ts +19 -0
- package/dist/src/registry/client.js +102 -0
- package/dist/src/registry/client.js.map +1 -0
- package/dist/src/registry/configuration.d.ts +36 -0
- package/dist/src/registry/configuration.js +133 -0
- package/dist/src/registry/configuration.js.map +1 -0
- package/dist/src/registry/operations.d.ts +163 -0
- package/dist/src/registry/operations.js +502 -0
- package/dist/src/registry/operations.js.map +1 -0
- package/dist/src/registry/types.d.ts +30 -0
- package/dist/src/registry/types.js +2 -0
- package/dist/src/registry/types.js.map +1 -0
- package/dist/src/resolver/registry-resolver.d.ts +40 -0
- package/dist/src/resolver/registry-resolver.js +354 -0
- package/dist/src/resolver/registry-resolver.js.map +1 -0
- package/dist/src/resolver/source-provider.d.ts +17 -0
- package/dist/src/resolver/source-provider.js +336 -0
- package/dist/src/resolver/source-provider.js.map +1 -0
- package/dist/src/resolver/types.d.ts +28 -0
- package/dist/src/resolver/types.js +2 -0
- package/dist/src/resolver/types.js.map +1 -0
- package/dist/src/security/analyzer.d.ts +38 -0
- package/dist/src/security/analyzer.js +241 -0
- package/dist/src/security/analyzer.js.map +1 -0
- package/dist/src/security/audit.d.ts +19 -0
- package/dist/src/security/audit.js +61 -0
- package/dist/src/security/audit.js.map +1 -0
- package/dist/src/security/findings.d.ts +23 -0
- package/dist/src/security/findings.js +2 -0
- package/dist/src/security/findings.js.map +1 -0
- package/dist/src/security/integrity.d.ts +14 -0
- package/dist/src/security/integrity.js +40 -0
- package/dist/src/security/integrity.js.map +1 -0
- package/dist/src/security/policy.d.ts +16 -0
- package/dist/src/security/policy.js +24 -0
- package/dist/src/security/policy.js.map +1 -0
- package/dist/src/security/recent-release.d.ts +19 -0
- package/dist/src/security/recent-release.js +27 -0
- package/dist/src/security/recent-release.js.map +1 -0
- package/dist/src/security/script-runner.d.ts +13 -0
- package/dist/src/security/script-runner.js +73 -0
- package/dist/src/security/script-runner.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { open, readFile, readdir, stat } from "node:fs/promises";
|
|
3
|
+
import { join, relative, resolve, sep } from "node:path";
|
|
4
|
+
export const lifecycleStages = ["preinstall", "install", "postinstall"];
|
|
5
|
+
export const gitBuildStages = ["preinstall", "install", "postinstall", "prepare"];
|
|
6
|
+
const rules = [
|
|
7
|
+
{ id: "BNPM-SEC-001", severity: "dangerous", behavior: "Possible reverse-shell behavior", expression: /(?:\/dev\/tcp\/|\bnc\s+[^\n]*\s-e\s|bash\s+-i[^\n]*(?:>&|\|))/i, remediation: "Remove or independently verify the reverse-shell behavior." },
|
|
8
|
+
{ id: "BNPM-SEC-002", severity: "dangerous", behavior: "Remote payload download followed by execution", expression: /(?:(?:curl|wget)\b[^\n|;&]*(?:\||&&|;)\s*(?:sh|bash|node|python)|(?:curl|wget)\b[^\n]*(?:chmod\s+\+x|child_process))/i, remediation: "Pin and verify downloaded content instead of executing a remote payload." },
|
|
9
|
+
{ id: "BNPM-SEC-003", severity: "warning", behavior: "Credential or secret material is targeted", expression: /(?:\.npmrc|\.ssh(?:\/|\\)|AWS_SECRET_ACCESS_KEY|AWS_ACCESS_KEY_ID|NPM_TOKEN|GITHUB_TOKEN|keychain|(?:credentials|secrets)\.json)/i, remediation: "Confirm why installation needs access to credentials or secret stores." },
|
|
10
|
+
{ id: "BNPM-SEC-004", severity: "dangerous", behavior: "Persistence mechanism is installed", expression: /(?:crontab|LaunchAgents|LaunchDaemons|systemctl\s+enable|schtasks\s+\/create)/i, remediation: "Remove persistence changes from package installation." },
|
|
11
|
+
{ id: "BNPM-SEC-005", severity: "dangerous", behavior: "Destructive filesystem command is present", expression: /(?:rm\s+-rf\s+(?:\/|~)|mkfs\b|diskutil\s+erase|format\s+[A-Z]:)/i, remediation: "Remove destructive filesystem operations." },
|
|
12
|
+
{ id: "BNPM-SEC-006", severity: "dangerous", behavior: "Cryptocurrency miner behavior is present", expression: /(?:xmrig|stratum\+(?:tcp|ssl)|cryptonight|minerd\b)/i, remediation: "Remove miner download or execution behavior." },
|
|
13
|
+
{ id: "BNPM-SEC-007", severity: "warning", behavior: "Obfuscated content is connected to process execution", expression: /(?:eval\s*\(\s*(?:Buffer\.from|atob)|child_process[^\n]{0,160}(?:base64|fromCharCode))/i, remediation: "Replace obfuscated execution with reviewable source." },
|
|
14
|
+
];
|
|
15
|
+
const capabilityRules = [
|
|
16
|
+
{ kind: "ai-history-read", severity: "warning", behavior: "May read local AI assistant chat logs and session history", expression: /(?:CODEX_HOME|GEMINI_CLI_HOME|HERMES_HOME|JCODE_HOME|GROK_HOME|\.codex(?:\/|\\)|\.claude\/projects|\.config\/opencode|opencode\/storage\/message|\.cursor|\.gemini|amp\/threads|droid\.factory\/sessions|\.kimi\/sessions|\.qwen\/projects|roocode|kilocode|cline|antigravity-cache\/sessions|zed\/threads|trae-cache\/sessions|warp-cache)/ig },
|
|
17
|
+
{ kind: "credential-read", severity: "warning", behavior: "May read authentication tokens, credentials, cookies, or account configuration", expression: /(?:auth\.json|credentials\.json|codex-credentials\.json|secrets\.json|hosts\.yml|oauth[_-]?token|refresh[_-]?token|session[_-]?token|api[_-]?token|keychain|credential store)/ig },
|
|
18
|
+
{ kind: "network-access", severity: "notice", behavior: "May make outbound network requests", expression: /https:\/\/[A-Za-z0-9._~:/?#@!$&'()*+,;=%-]+/ig },
|
|
19
|
+
{ kind: "local-write", severity: "notice", behavior: "May write local configuration, cache, database, or report files", expression: /(?:writeFile(?:Sync)?|create_dir(?:_all)?|\.config\/tokscale|stats-cache\.json|tui-data-cache\.json|\.sqlite|\.db\b)/ig },
|
|
20
|
+
{ kind: "process-spawn", severity: "warning", behavior: "May launch subprocesses or platform-native helper programs", expression: /(?:child_process|spawnSync|execSync|Command::new|std::process::Command)/ig },
|
|
21
|
+
];
|
|
22
|
+
const hash = (value) => `sha256-${createHash("sha256").update(value).digest("base64")}`;
|
|
23
|
+
function passiveEvidencePath(path) {
|
|
24
|
+
return /(?:^|\/)(?:readme|changelog|changes|license)(?:\.[^/]*)?$|\.(?:md|mdx|map|d\.ts|d\.mts|d\.cts)$/i.test(path);
|
|
25
|
+
}
|
|
26
|
+
function referencedCommandFiles(command) {
|
|
27
|
+
const matches = command.matchAll(/(?:^|\s)(?:node|sh|bash|zsh|python(?:3)?|\.\/)(?:\s+)?([A-Za-z0-9_./-]+\.(?:js|cjs|mjs|sh|py))(?:\s|$)/g);
|
|
28
|
+
return [...new Set([...matches].map((match) => match[1]).filter((value) => value !== undefined))].sort();
|
|
29
|
+
}
|
|
30
|
+
function safeReference(root, path) {
|
|
31
|
+
const target = resolve(root, path);
|
|
32
|
+
if (target !== root && !target.startsWith(`${root}${sep}`))
|
|
33
|
+
throw new Error(`Lifecycle script references a file outside the package: ${path}`);
|
|
34
|
+
return target;
|
|
35
|
+
}
|
|
36
|
+
function finding(rule, packageName, packageVersion, path, content, index) {
|
|
37
|
+
const before = content.slice(0, index);
|
|
38
|
+
const line = before.split("\n").length;
|
|
39
|
+
const column = index - before.lastIndexOf("\n");
|
|
40
|
+
const evidence = content.slice(Math.max(0, index - 40), Math.min(content.length, index + 120)).replace(/\s+/g, " ").slice(0, 160);
|
|
41
|
+
return { ruleId: rule.id, severity: rule.severity, packageName, packageVersion, behavior: rule.behavior, evidence, location: { path, line, column }, remediation: rule.remediation };
|
|
42
|
+
}
|
|
43
|
+
export async function analyzePackage(options) {
|
|
44
|
+
const maxFiles = options.maxFiles ?? 20_000;
|
|
45
|
+
const maxBytes = options.maxBytes ?? 64 * 1024 * 1024;
|
|
46
|
+
let files = 0;
|
|
47
|
+
let bytes = 0;
|
|
48
|
+
let limitedAt;
|
|
49
|
+
const findings = [];
|
|
50
|
+
const capabilityEvidence = new Map();
|
|
51
|
+
const recordCapability = (kind, severity, behavior, evidence) => {
|
|
52
|
+
let current = capabilityEvidence.get(kind);
|
|
53
|
+
if (!current) {
|
|
54
|
+
current = { severity, behavior, evidence: new Set() };
|
|
55
|
+
capabilityEvidence.set(kind, current);
|
|
56
|
+
}
|
|
57
|
+
if (current.evidence.size < 64)
|
|
58
|
+
current.evidence.add(evidence.replace(/[^\x20-\x7E]+/g, " ").slice(0, 160));
|
|
59
|
+
};
|
|
60
|
+
const scanCapabilities = (path, content, inspectMagic = false) => {
|
|
61
|
+
const magic = content.subarray(0, 4).toString("hex");
|
|
62
|
+
if (inspectMagic && (["cffaedfe", "cefaedfe", "feedfacf", "feedface", "7f454c46"].includes(magic) || content.subarray(0, 2).toString("ascii") === "MZ"))
|
|
63
|
+
recordCapability("native-code", "warning", "Contains native executable code that cannot be fully inspected as JavaScript", path);
|
|
64
|
+
const searchable = content.toString("latin1");
|
|
65
|
+
for (const rule of capabilityRules) {
|
|
66
|
+
if (passiveEvidencePath(path) || (rule.kind === "network-access" && /(?:^|\/)package\.json$/i.test(path)))
|
|
67
|
+
continue;
|
|
68
|
+
rule.expression.lastIndex = 0;
|
|
69
|
+
let matches = 0;
|
|
70
|
+
for (const match of searchable.matchAll(rule.expression)) {
|
|
71
|
+
const value = match[0];
|
|
72
|
+
if (!value)
|
|
73
|
+
continue;
|
|
74
|
+
let displayed = value;
|
|
75
|
+
if (rule.kind === "network-access") {
|
|
76
|
+
try {
|
|
77
|
+
displayed = new URL(value.replace(/[\"'`]+$/g, "")).origin;
|
|
78
|
+
}
|
|
79
|
+
catch { /* retain bounded source evidence */ }
|
|
80
|
+
}
|
|
81
|
+
else if (rule.kind === "ai-history-read") {
|
|
82
|
+
if (/CODEX_HOME|\.codex/i.test(value))
|
|
83
|
+
displayed = "Codex: $CODEX_HOME or ~/.codex";
|
|
84
|
+
else if (/claude/i.test(value))
|
|
85
|
+
displayed = "Claude Code: ~/.claude/projects";
|
|
86
|
+
else if (/opencode/i.test(value))
|
|
87
|
+
displayed = "OpenCode session storage";
|
|
88
|
+
else if (/cursor/i.test(value))
|
|
89
|
+
displayed = "Cursor session storage";
|
|
90
|
+
else if (/GEMINI|\.gemini/i.test(value))
|
|
91
|
+
displayed = "Gemini CLI session storage";
|
|
92
|
+
}
|
|
93
|
+
recordCapability(rule.kind, rule.severity, rule.behavior, `${path}: ${displayed}`);
|
|
94
|
+
if (++matches >= 256)
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const scan = (path, content) => {
|
|
100
|
+
if (passiveEvidencePath(path))
|
|
101
|
+
return;
|
|
102
|
+
for (const rule of rules) {
|
|
103
|
+
rule.expression.lastIndex = 0;
|
|
104
|
+
const match = rule.expression.exec(content);
|
|
105
|
+
if (match?.index === undefined)
|
|
106
|
+
continue;
|
|
107
|
+
const lineStart = content.lastIndexOf("\n", match.index - 1) + 1;
|
|
108
|
+
const prefix = content.slice(lineStart, match.index);
|
|
109
|
+
if (rule.id === "BNPM-SEC-002" && /\b(?:echo|printf|console\.(?:log|warn|error))\b/i.test(prefix))
|
|
110
|
+
continue;
|
|
111
|
+
findings.push(finding(rule, options.packageName, options.packageVersion, path, content, match.index));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const walk = async (directory) => {
|
|
115
|
+
for (const entry of (await readdir(directory, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
116
|
+
if (["node_modules", ".git", ".bnpm-store.json"].includes(entry.name))
|
|
117
|
+
continue;
|
|
118
|
+
const path = join(directory, entry.name);
|
|
119
|
+
if (entry.isDirectory()) {
|
|
120
|
+
await walk(path);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (!entry.isFile())
|
|
124
|
+
continue;
|
|
125
|
+
files += 1;
|
|
126
|
+
if (files > maxFiles)
|
|
127
|
+
throw new Error(`Package analysis exceeds ${maxFiles} files`);
|
|
128
|
+
const size = (await stat(path)).size;
|
|
129
|
+
const displayPath = relative(options.root, path).split(sep).join("/");
|
|
130
|
+
if (passiveEvidencePath(displayPath))
|
|
131
|
+
continue;
|
|
132
|
+
const handle = await open(path, "r");
|
|
133
|
+
try {
|
|
134
|
+
const chunks = [];
|
|
135
|
+
let position = 0;
|
|
136
|
+
let overlap = Buffer.alloc(0);
|
|
137
|
+
const chunkSize = 1024 * 1024;
|
|
138
|
+
while (position < size && bytes < maxBytes) {
|
|
139
|
+
const length = Math.min(chunkSize, size - position, maxBytes - bytes);
|
|
140
|
+
if (length <= 0)
|
|
141
|
+
break;
|
|
142
|
+
const chunk = Buffer.allocUnsafe(length);
|
|
143
|
+
const result = await handle.read(chunk, 0, length, position);
|
|
144
|
+
if (result.bytesRead === 0)
|
|
145
|
+
break;
|
|
146
|
+
const content = chunk.subarray(0, result.bytesRead);
|
|
147
|
+
if (size <= 2 * 1024 * 1024)
|
|
148
|
+
chunks.push(content);
|
|
149
|
+
bytes += result.bytesRead;
|
|
150
|
+
scanCapabilities(displayPath, overlap.length === 0 ? content : Buffer.concat([overlap, content]), position === 0);
|
|
151
|
+
overlap = content.subarray(Math.max(0, content.length - 256));
|
|
152
|
+
position += result.bytesRead;
|
|
153
|
+
}
|
|
154
|
+
if (position < size)
|
|
155
|
+
limitedAt ??= displayPath;
|
|
156
|
+
if (position === size && size <= 2 * 1024 * 1024) {
|
|
157
|
+
const content = Buffer.concat(chunks);
|
|
158
|
+
if (!content.includes(0))
|
|
159
|
+
scan(displayPath, content.toString("utf8"));
|
|
160
|
+
}
|
|
161
|
+
if (position === 0) {
|
|
162
|
+
const magic = Buffer.allocUnsafe(Math.min(4, size));
|
|
163
|
+
const result = await handle.read(magic, 0, magic.length, 0);
|
|
164
|
+
scanCapabilities(displayPath, magic.subarray(0, result.bytesRead), true);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await handle.close();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
await walk(options.root);
|
|
173
|
+
if (limitedAt !== undefined)
|
|
174
|
+
findings.push({
|
|
175
|
+
ruleId: "BNPM-SEC-009",
|
|
176
|
+
severity: "warning",
|
|
177
|
+
packageName: options.packageName,
|
|
178
|
+
packageVersion: options.packageVersion,
|
|
179
|
+
behavior: "Static content inspection reached its bounded scan budget",
|
|
180
|
+
evidence: `Scanned ${bytes} bytes; remaining content starts at or after ${limitedAt}`.slice(0, 160),
|
|
181
|
+
location: { path: limitedAt },
|
|
182
|
+
remediation: "Use package-specific review or sandboxing when complete static inspection is required.",
|
|
183
|
+
});
|
|
184
|
+
const lifecycles = [];
|
|
185
|
+
for (const stage of options.stages ?? lifecycleStages) {
|
|
186
|
+
const declaredCommand = options.scripts?.[stage];
|
|
187
|
+
let command = declaredCommand;
|
|
188
|
+
let implicitNodeGyp = false;
|
|
189
|
+
if (stage === "install" && declaredCommand === undefined) {
|
|
190
|
+
try {
|
|
191
|
+
implicitNodeGyp = (await stat(join(options.root, "binding.gyp"))).isFile();
|
|
192
|
+
if (implicitNodeGyp)
|
|
193
|
+
command = "node-gyp rebuild";
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error.code !== "ENOENT")
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!command)
|
|
201
|
+
continue;
|
|
202
|
+
scan(`package.json#scripts.${stage}`, command);
|
|
203
|
+
const referencedFiles = implicitNodeGyp ? ["binding.gyp"] : referencedCommandFiles(command);
|
|
204
|
+
const referencedContent = [];
|
|
205
|
+
for (const path of referencedFiles) {
|
|
206
|
+
try {
|
|
207
|
+
const content = await readFile(safeReference(options.root, path));
|
|
208
|
+
referencedContent.push(Buffer.from(`${path}\0`), content, Buffer.from("\0"));
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
if (error.code !== "ENOENT")
|
|
212
|
+
throw error;
|
|
213
|
+
referencedContent.push(Buffer.from(`${path}\0[MISSING]\0`));
|
|
214
|
+
findings.push({
|
|
215
|
+
ruleId: "BNPM-SEC-008",
|
|
216
|
+
severity: "warning",
|
|
217
|
+
packageName: options.packageName,
|
|
218
|
+
packageVersion: options.packageVersion,
|
|
219
|
+
behavior: "Lifecycle command references a file absent from the package",
|
|
220
|
+
evidence: path.slice(0, 160),
|
|
221
|
+
location: { path: `package.json#scripts.${stage}` },
|
|
222
|
+
remediation: "Publish the referenced script or remove the lifecycle command.",
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
lifecycles.push({
|
|
227
|
+
packageName: options.packageName,
|
|
228
|
+
packageVersion: options.packageVersion,
|
|
229
|
+
integrity: options.integrity,
|
|
230
|
+
stage,
|
|
231
|
+
command,
|
|
232
|
+
commandHash: hash(command),
|
|
233
|
+
contentHash: hash(Buffer.concat(referencedContent)),
|
|
234
|
+
referencedFiles,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
findings.sort((left, right) => left.ruleId.localeCompare(right.ruleId) || (left.location?.path ?? "").localeCompare(right.location?.path ?? ""));
|
|
238
|
+
const capabilities = [...capabilityEvidence].sort(([left], [right]) => left.localeCompare(right)).map(([kind, value]) => ({ kind, severity: value.severity, packageName: options.packageName, packageVersion: options.packageVersion, behavior: value.behavior, evidence: [...value.evidence].sort() }));
|
|
239
|
+
return { analysis: { packageName: options.packageName, packageVersion: options.packageVersion, integrity: options.integrity, ruleSetVersion: "1", findings }, lifecycles, capabilities };
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../../src/security/analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BnpmPaths } from "../config/paths.js";
|
|
2
|
+
import { type RegistryAdvisory } from "../registry/audit.js";
|
|
3
|
+
import { type AnalyzedPackage } from "./analyzer.js";
|
|
4
|
+
import { RegistryConfiguration } from "../registry/configuration.js";
|
|
5
|
+
import type { ResolutionGraph } from "../resolver/types.js";
|
|
6
|
+
export interface AuditResult {
|
|
7
|
+
readonly analyzedAt: string;
|
|
8
|
+
readonly packages: ReadonlyMap<string, AnalyzedPackage>;
|
|
9
|
+
readonly advisories: readonly RegistryAdvisory[];
|
|
10
|
+
readonly graph: ResolutionGraph;
|
|
11
|
+
}
|
|
12
|
+
export declare function auditProject(options: {
|
|
13
|
+
readonly paths: BnpmPaths;
|
|
14
|
+
readonly registry?: URL;
|
|
15
|
+
readonly registryConfiguration?: RegistryConfiguration;
|
|
16
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
17
|
+
readonly signal?: AbortSignal;
|
|
18
|
+
readonly now?: Date;
|
|
19
|
+
}): Promise<AuditResult>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { storePath } from "../cache/store.js";
|
|
2
|
+
import { readLockfileGraph } from "../lockfile/index.js";
|
|
3
|
+
import { fetchBulkAdvisories } from "../registry/audit.js";
|
|
4
|
+
import { analyzePackage } from "./analyzer.js";
|
|
5
|
+
import { loadRegistryConfiguration, RegistryConfiguration } from "../registry/configuration.js";
|
|
6
|
+
async function forEachConcurrent(values, concurrency, worker) {
|
|
7
|
+
let index = 0;
|
|
8
|
+
const next = async () => { while (true) {
|
|
9
|
+
const value = values[index++];
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return;
|
|
12
|
+
await worker(value);
|
|
13
|
+
} };
|
|
14
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, () => next()));
|
|
15
|
+
}
|
|
16
|
+
export async function auditProject(options) {
|
|
17
|
+
const { graph } = await readLockfileGraph(options.paths.lockfile, options.paths.store);
|
|
18
|
+
const packages = new Map();
|
|
19
|
+
await forEachConcurrent([...graph.packages], 8, async ([id, pkg]) => {
|
|
20
|
+
try {
|
|
21
|
+
packages.set(id, await analyzePackage({
|
|
22
|
+
root: storePath(options.paths.store, pkg.integrity),
|
|
23
|
+
packageName: pkg.name,
|
|
24
|
+
packageVersion: pkg.version,
|
|
25
|
+
integrity: pkg.integrity,
|
|
26
|
+
...(pkg.manifest.scripts === undefined ? {} : { scripts: pkg.manifest.scripts }),
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error.code !== "ENOENT")
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const configuration = options.registryConfiguration ?? await loadRegistryConfiguration({
|
|
35
|
+
userNpmrc: options.paths.userNpmrc,
|
|
36
|
+
projectNpmrc: options.paths.projectNpmrc,
|
|
37
|
+
...(options.registry === undefined ? {} : { defaultRegistry: options.registry }),
|
|
38
|
+
});
|
|
39
|
+
const packagesByRegistry = new Map();
|
|
40
|
+
for (const [id, pkg] of graph.packages) {
|
|
41
|
+
if (pkg.source !== "registry" && (pkg.source !== undefined || pkg.localPath !== undefined || pkg.preparedPath !== undefined))
|
|
42
|
+
continue;
|
|
43
|
+
const registry = configuration.registryForPackage(pkg.name).href;
|
|
44
|
+
let packages = packagesByRegistry.get(registry);
|
|
45
|
+
if (!packages) {
|
|
46
|
+
packages = new Map();
|
|
47
|
+
packagesByRegistry.set(registry, packages);
|
|
48
|
+
}
|
|
49
|
+
packages.set(id, pkg);
|
|
50
|
+
}
|
|
51
|
+
const advisoryGroups = await Promise.all([...packagesByRegistry].sort(([left], [right]) => left.localeCompare(right)).map(([registry, packages]) => fetchBulkAdvisories({
|
|
52
|
+
graph: { roots: new Map(), packages },
|
|
53
|
+
registry: new URL(registry),
|
|
54
|
+
headers: (url) => configuration.headersFor(url),
|
|
55
|
+
...(options.fetch === undefined ? {} : { fetch: options.fetch }),
|
|
56
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
57
|
+
})));
|
|
58
|
+
const advisories = advisoryGroups.flat().sort((left, right) => left.packageName.localeCompare(right.packageName) || String(left.id).localeCompare(String(right.id)));
|
|
59
|
+
return { analyzedAt: (options.now ?? new Date()).toISOString(), packages, advisories, graph };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/security/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type FindingSeverity = "notice" | "warning" | "dangerous";
|
|
2
|
+
export interface SourceLocation {
|
|
3
|
+
path: string;
|
|
4
|
+
line?: number;
|
|
5
|
+
column?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SecurityFinding {
|
|
8
|
+
ruleId: string;
|
|
9
|
+
severity: FindingSeverity;
|
|
10
|
+
packageName: string;
|
|
11
|
+
packageVersion: string;
|
|
12
|
+
behavior: string;
|
|
13
|
+
evidence: string;
|
|
14
|
+
location?: SourceLocation;
|
|
15
|
+
remediation?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PackageAnalysis {
|
|
18
|
+
packageName: string;
|
|
19
|
+
packageVersion: string;
|
|
20
|
+
integrity: string;
|
|
21
|
+
ruleSetVersion: string;
|
|
22
|
+
findings: readonly SecurityFinding[];
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.js","sourceRoot":"","sources":["../../../src/security/findings.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const supportedAlgorithms: readonly ["sha512", "sha384", "sha256"];
|
|
2
|
+
type SupportedAlgorithm = (typeof supportedAlgorithms)[number];
|
|
3
|
+
export interface ExpectedIntegrity {
|
|
4
|
+
algorithm: SupportedAlgorithm;
|
|
5
|
+
digest: Buffer;
|
|
6
|
+
serialized: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class IntegrityError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare function selectExpectedIntegrity(integrity: string): ExpectedIntegrity;
|
|
12
|
+
export declare function verifyBufferIntegrity(content: Uint8Array, integrity: string): ExpectedIntegrity;
|
|
13
|
+
export declare function assertDigest(expected: ExpectedIntegrity, actual: Uint8Array): void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
|
+
const supportedAlgorithms = ["sha512", "sha384", "sha256"];
|
|
3
|
+
export class IntegrityError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "IntegrityError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function selectExpectedIntegrity(integrity) {
|
|
10
|
+
const entries = integrity.trim().split(/\s+/);
|
|
11
|
+
for (const algorithm of supportedAlgorithms) {
|
|
12
|
+
const prefix = `${algorithm}-`;
|
|
13
|
+
const entry = entries.find((candidate) => candidate.startsWith(prefix));
|
|
14
|
+
if (!entry)
|
|
15
|
+
continue;
|
|
16
|
+
const encoded = entry.slice(prefix.length).split("?", 1)[0];
|
|
17
|
+
if (!encoded || !/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)) {
|
|
18
|
+
throw new IntegrityError(`Malformed ${algorithm} integrity digest`);
|
|
19
|
+
}
|
|
20
|
+
const digest = Buffer.from(encoded, "base64");
|
|
21
|
+
if (digest.length !== createHash(algorithm).digest().length) {
|
|
22
|
+
throw new IntegrityError(`Malformed ${algorithm} integrity digest length`);
|
|
23
|
+
}
|
|
24
|
+
return { algorithm, digest, serialized: `${algorithm}-${encoded}` };
|
|
25
|
+
}
|
|
26
|
+
throw new IntegrityError("No supported strong integrity digest was provided");
|
|
27
|
+
}
|
|
28
|
+
export function verifyBufferIntegrity(content, integrity) {
|
|
29
|
+
const expected = selectExpectedIntegrity(integrity);
|
|
30
|
+
const actual = createHash(expected.algorithm).update(content).digest();
|
|
31
|
+
assertDigest(expected, actual);
|
|
32
|
+
return expected;
|
|
33
|
+
}
|
|
34
|
+
export function assertDigest(expected, actual) {
|
|
35
|
+
const actualBuffer = Buffer.from(actual);
|
|
36
|
+
if (actualBuffer.length !== expected.digest.length || !timingSafeEqual(actualBuffer, expected.digest)) {
|
|
37
|
+
throw new IntegrityError(`Content does not match ${expected.algorithm} integrity`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../../src/security/integrity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AASpE,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACvD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9C,KAAK,MAAM,SAAS,IAAI,mBAAmB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,GAAG,SAAS,GAAG,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,OAAO,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,cAAc,CAAC,aAAa,SAAS,mBAAmB,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC;YAC5D,MAAM,IAAI,cAAc,CAAC,aAAa,SAAS,0BAA0B,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,IAAI,OAAO,EAAE,EAAE,CAAC;IACtE,CAAC;IAED,MAAM,IAAI,cAAc,CAAC,mDAAmD,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAmB,EAAE,SAAiB;IAC1E,MAAM,QAAQ,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;IACvE,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAA2B,EAAE,MAAkB;IAC1E,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,YAAY,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtG,MAAM,IAAI,cAAc,CAAC,0BAA0B,QAAQ,CAAC,SAAS,YAAY,CAAC,CAAC;IACrF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TrustedPackageApproval } from "../config/types.js";
|
|
2
|
+
import type { AnalyzedPackage, LifecycleFact } from "./analyzer.js";
|
|
3
|
+
export interface PackagePolicyDecision {
|
|
4
|
+
readonly packageId: string;
|
|
5
|
+
readonly identity: string;
|
|
6
|
+
readonly blocked: boolean;
|
|
7
|
+
readonly approvedLifecycles: readonly LifecycleFact[];
|
|
8
|
+
readonly skippedLifecycles: readonly LifecycleFact[];
|
|
9
|
+
readonly reasons: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function decidePackagePolicy(options: {
|
|
12
|
+
readonly analyzed: AnalyzedPackage;
|
|
13
|
+
readonly allowedDangerous: ReadonlySet<string>;
|
|
14
|
+
readonly trustedApproval?: TrustedPackageApproval;
|
|
15
|
+
readonly packageId?: string;
|
|
16
|
+
}): PackagePolicyDecision;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function decidePackagePolicy(options) {
|
|
2
|
+
const identity = `${options.analyzed.analysis.packageName}@${options.analyzed.analysis.packageVersion}`;
|
|
3
|
+
const dangerous = options.analyzed.analysis.findings.filter((finding) => finding.severity === "dangerous");
|
|
4
|
+
const blocked = dangerous.length > 0 && !options.allowedDangerous.has(identity);
|
|
5
|
+
const approvedLifecycles = [];
|
|
6
|
+
const skippedLifecycles = [];
|
|
7
|
+
for (const lifecycle of options.analyzed.lifecycles) {
|
|
8
|
+
const trusted = options.trustedApproval;
|
|
9
|
+
const stage = trusted?.scripts[lifecycle.stage];
|
|
10
|
+
if (trusted?.version === lifecycle.packageVersion &&
|
|
11
|
+
trusted.integrity === lifecycle.integrity &&
|
|
12
|
+
stage?.commandHash === lifecycle.commandHash &&
|
|
13
|
+
stage.contentHash === lifecycle.contentHash)
|
|
14
|
+
approvedLifecycles.push(lifecycle);
|
|
15
|
+
else
|
|
16
|
+
skippedLifecycles.push(lifecycle);
|
|
17
|
+
}
|
|
18
|
+
const reasons = [
|
|
19
|
+
...(blocked ? [`${dangerous.length} dangerous finding${dangerous.length === 1 ? "" : "s"} require an exact override`] : []),
|
|
20
|
+
...(skippedLifecycles.length > 0 ? [`${skippedLifecycles.length} lifecycle script${skippedLifecycles.length === 1 ? "" : "s"} lack an exact trusted approval`] : []),
|
|
21
|
+
];
|
|
22
|
+
return { packageId: options.packageId ?? identity, identity, blocked, approvedLifecycles, skippedLifecycles, reasons };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/security/policy.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,mBAAmB,CAAC,OAKnC;IACC,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC;IACxG,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,WAAW,CAAC,CAAC;IAC3G,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChF,MAAM,kBAAkB,GAAoB,EAAE,CAAC;IAC/C,MAAM,iBAAiB,GAAoB,EAAE,CAAC;IAC9C,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;QACxC,MAAM,KAAK,GAAG,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAChD,IACE,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC,cAAc;YAC7C,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,SAAS;YACzC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC,WAAW;YAC5C,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,WAAW;YAC3C,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;;YAChC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,OAAO,GAAG;QACd,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,qBAAqB,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,4BAA4B,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3H,GAAG,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,MAAM,oBAAoB,iBAAiB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,iCAAiC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACrK,CAAC;IACF,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC;AACzH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RecentReleaseHours } from "../config/types.js";
|
|
2
|
+
export interface ReleaseIdentity {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
publishedAt?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RecentReleaseDecision {
|
|
8
|
+
identity: string;
|
|
9
|
+
status: "allowed" | "blocked" | "unknown";
|
|
10
|
+
ageMilliseconds?: number;
|
|
11
|
+
thresholdHours: RecentReleaseHours;
|
|
12
|
+
reason: string;
|
|
13
|
+
}
|
|
14
|
+
export interface RecentReleasePolicy {
|
|
15
|
+
thresholdHours: RecentReleaseHours;
|
|
16
|
+
now?: Date;
|
|
17
|
+
allowedExactVersions?: ReadonlySet<string>;
|
|
18
|
+
}
|
|
19
|
+
export declare function evaluateRecentRelease(release: ReleaseIdentity, policy: RecentReleasePolicy): RecentReleaseDecision;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function evaluateRecentRelease(release, policy) {
|
|
2
|
+
const identity = `${release.name}@${release.version}`;
|
|
3
|
+
const base = { identity, thresholdHours: policy.thresholdHours };
|
|
4
|
+
if (!release.publishedAt) {
|
|
5
|
+
return { ...base, status: "unknown", reason: "Registry metadata has no publication timestamp" };
|
|
6
|
+
}
|
|
7
|
+
const publishedAt = Date.parse(release.publishedAt);
|
|
8
|
+
if (!Number.isFinite(publishedAt)) {
|
|
9
|
+
return { ...base, status: "unknown", reason: "Registry publication timestamp is invalid" };
|
|
10
|
+
}
|
|
11
|
+
const ageMilliseconds = (policy.now ?? new Date()).getTime() - publishedAt;
|
|
12
|
+
const thresholdMilliseconds = policy.thresholdHours * 60 * 60 * 1_000;
|
|
13
|
+
const isRecent = ageMilliseconds < thresholdMilliseconds;
|
|
14
|
+
if (!isRecent) {
|
|
15
|
+
return { ...base, status: "allowed", ageMilliseconds, reason: "Release is older than the configured window" };
|
|
16
|
+
}
|
|
17
|
+
if (policy.allowedExactVersions?.has(identity)) {
|
|
18
|
+
return { ...base, status: "allowed", ageMilliseconds, reason: "Exact recent version was explicitly allowed" };
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
...base,
|
|
22
|
+
status: "blocked",
|
|
23
|
+
ageMilliseconds,
|
|
24
|
+
reason: ageMilliseconds < 0 ? "Publication timestamp is in the future" : "Release is inside the configured window",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=recent-release.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-release.js","sourceRoot":"","sources":["../../../src/security/recent-release.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,qBAAqB,CACnC,OAAwB,EACxB,MAA2B;IAE3B,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,MAAM,IAAI,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC;IAEjE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACzB,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,gDAAgD,EAAE,CAAC;IAClG,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,2CAA2C,EAAE,CAAC;IAC7F,CAAC;IAED,MAAM,eAAe,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC;IAC3E,MAAM,qBAAqB,GAAG,MAAM,CAAC,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IACtE,MAAM,QAAQ,GAAG,eAAe,GAAG,qBAAqB,CAAC;IAEzD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,6CAA6C,EAAE,CAAC;IAChH,CAAC;IAED,IAAI,MAAM,CAAC,oBAAoB,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,6CAA6C,EAAE,CAAC;IAChH,CAAC;IAED,OAAO;QACL,GAAG,IAAI;QACP,MAAM,EAAE,SAAS;QACjB,eAAe;QACf,MAAM,EAAE,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,yCAAyC;KACnH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LifecycleFact } from "./analyzer.js";
|
|
2
|
+
export declare class ScriptExecutionError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare function runLifecycle(options: {
|
|
6
|
+
readonly fact: LifecycleFact;
|
|
7
|
+
readonly cwd: string;
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
9
|
+
readonly timeoutMilliseconds?: number;
|
|
10
|
+
readonly maxOutputBytes?: number;
|
|
11
|
+
readonly onOutput?: (stream: "stdout" | "stderr", text: string) => void;
|
|
12
|
+
readonly initialCwd?: string;
|
|
13
|
+
}): Promise<void>;
|