@pho9ubenaa/siro 0.1.3 → 0.2.0
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/CHANGELOG.md +17 -0
- package/dist/cli.mjs +414 -3
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +8 -1
- package/dist/node-io-Cb8nixgE.mjs +2509 -0
- package/package.json +3 -3
- package/dist/node-io-BLwef7p6.mjs +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pho9ubenaa/siro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Security best-practices linter for the npm ecosystem (npm, pnpm, deno, bun, yarn, aube).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aube",
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"cac": "7.0.0",
|
|
50
50
|
"ini": "7.0.0",
|
|
51
|
-
"jiti": "2.7.0",
|
|
52
51
|
"picocolors": "1.1.1",
|
|
53
52
|
"smol-toml": "1.6.1",
|
|
54
53
|
"valibot": "1.4.1",
|
|
@@ -73,12 +72,13 @@
|
|
|
73
72
|
"vitest": "4.1.7"
|
|
74
73
|
},
|
|
75
74
|
"engines": {
|
|
76
|
-
"node": ">=
|
|
75
|
+
"node": "^22.18.0 || ^23.6.0 || >=24.0.0"
|
|
77
76
|
},
|
|
78
77
|
"scripts": {
|
|
79
78
|
"bench": "node ./scripts/bench/run.mjs",
|
|
80
79
|
"build": "node ./scripts/gen/version.mjs && tsdown && node ./scripts/gen/bin-wrapper.mjs",
|
|
81
80
|
"check": "oxlint --deny-warnings && oxfmt --check",
|
|
81
|
+
"check:oss-coverage": "node ./oss-benchmarks/check.mjs",
|
|
82
82
|
"format": "oxfmt --write",
|
|
83
83
|
"gen": "pnpm gen:version && pnpm gen:comparison && pnpm gen:rules && pnpm gen:api",
|
|
84
84
|
"gen:api": "typedoc",
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import e from"picocolors";import t from"ini";import{parse as n}from"smol-toml";import{parseDocument as r}from"yaml";import*as i from"valibot";import{accessSync as a,constants as o,readFileSync as s}from"node:fs";import c from"node:path";import{createJiti as l}from"jiti";const u=e=>e,d=e=>e;var f=class extends Error{exitCode;constructor(e,t){super(e),this.name=`SiroError`,this.exitCode=t}},p=class extends f{constructor(e){super(e,2),this.name=`ConfigError`}},m=class extends f{constructor(e){super(e,2),this.name=`UsageError`}};const h=(e,t)=>{try{return t()}catch(t){if(t instanceof p)throw t;let n=String(t);throw t instanceof Error&&(n=t.message),new p(`${e}: ${n}`)}},g=[`npm`,`pnpm`,`yarn`,`bun`,`deno`,`aube`],_=[`error`,`warn`,`info`],ee={error:3,info:1,warn:2},te=new Set(g),ne=new Set(_),v=e=>te.has(e),re=e=>ne.has(e),ie=/^(?<pmName>[a-z]+)(?:@.+)?$/u,ae=e=>{let t=ie.exec(e.trim()),n;if(t&&t.groups&&(n=t.groups.pmName),n!==void 0&&v(n))return n},y=e=>typeof e!=`object`||!e||!(`name`in e)||typeof e.name!=`string`?!1:`format`in e&&typeof e.format==`function`,oe={error:`error`,info:`notice`,warn:`warning`},b=e=>e.replaceAll(`%`,`%25`).replaceAll(`\r`,`%0D`).replaceAll(`
|
|
2
|
-
`,`%0A`).replaceAll(`:`,`%3A`).replaceAll(`,`,`%2C`),se=e=>e.replaceAll(`%`,`%25`).replaceAll(`\r`,`%0D`).replaceAll(`
|
|
3
|
-
`,`%0A`),ce={format(e,t){for(let n of e.findings){let e=``;n.file&&(e=`file=${b(n.file)},`);let r=`[${n.pm}] ${n.message}`;n.docs&&(r=`[${n.pm}] ${n.message} (${n.docs})`),t.stdout(`::${oe[n.severity]} ${e}title=${b(n.ruleId)}::${se(r)}`)}},name:`github`},le=`0.1.3`,ue={format(e,t){t.stdout(JSON.stringify(Object.assign({},{schemaVersion:1,siroVersion:le},e),void 0,2))},name:`json`},de={error:`✖ error`,info:`ℹ info`,warn:`⚠ warn`},fe=()=>{let{env:t}=process;return t.NO_COLOR!==void 0&&t.NO_COLOR!==``?!1:`FORCE_COLOR`in t?t.FORCE_COLOR!==`0`&&t.FORCE_COLOR!==``:e.isColorSupported},pe=(e,t)=>{let n=``;e.file&&(n=t.colors.dim(` (${e.file})`)),t.io.stdout(`${t.tag[e.severity](de[e.severity])} [${e.pm}] ${t.colors.bold(e.ruleId)}${n}`),t.io.stdout(` ${e.message}`);for(let n of e.manualSteps??[])t.io.stdout(` ↳ ${n}`);e.docs&&t.io.stdout(t.colors.dim(` → ${e.docs}`))},me=t=>{let n=e.createColors(fe());return{colors:n,io:t,tag:{error:n.red,info:n.cyan,warn:n.yellow}}},he={format(e,t){let n=me(t);if(e.findings.length===0){t.stdout(n.colors.green(`✔ No security best-practice issues found.`));return}for(let t of e.findings)pe(t,n);let{error:r,warn:i,info:a}=e.summary;t.stdout(``),t.stdout(`Summary: ${r} error, ${i} warn, ${a} info`)},name:`pretty`},ge=[he,ue,ce],_e=ge.map(e=>e.name),ve=(e=[])=>{let t=new Map;for(let n of[...ge,...e]){if(!y(n))throw TypeError(`createRegistry: each reporter needs a string 'name' and a 'format' function.`);t.set(n.name,n)}return{get:e=>t.get(e),list:()=>[...t.keys()]}},x=e=>e instanceof Error&&`code`in e&&typeof e.code==`string`,ye=(e,t)=>ee[e]>=ee[t],be=(e,t)=>{let n=[],r={error:0,info:0,warn:0};for(let i of e.findings)ye(i.severity,t)&&(n.push(i),r[i.severity]+=1);return{findings:n,summary:r}},S=(e,t=`error`)=>+!!e.findings.some(e=>ye(e.severity,t)),C=e=>{if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype},w=(e,t)=>{let n=e;for(let e of t){if(!C(n))return;n=n[e]}return n},T=e=>C(e)?e:{},xe=/^-?\d+$/u,Se=e=>{if(e===`true`)return!0;if(e===`false`)return!1},Ce=e=>{if(xe.test(e))return Number(e)},E=e=>{if(typeof e==`boolean`||typeof e==`number`)return e;let t=String(e??``);return typeof e==`string`&&(t=e),Se(t)??Ce(t)??t},we={json:{parse(e){let t=e.trim();return t===``?{}:T(JSON.parse(t))}},npmrc:{parse(e){let n=T(t.parse(e)),r={};for(let[e,t]of Object.entries(n))Array.isArray(t)?r[e]=t.map(e=>E(e)):typeof t==`object`&&t?r[e]=T(t):r[e]=E(t);return r}},toml:{parse(e){return e.trim()===``?{}:T(n(e))}},yaml:{parse(e){return e.trim()===``?{}:T(r(e).toJS())}}},Te=e=>we[e],Ee=e=>(t,n)=>{let r=e(t,n);return r.state===`violation`&&r.severity!==void 0?{actual:r.actual,expected:r.expected,manualSteps:r.manualSteps,message:r.message,state:`violation`}:r},De=(e,t)=>{let n={};for(let t of g){let r=e.bindings[t];r!==void 0&&(n[t]=Object.assign({},r,{check:Ee(r.check),severity:void 0}))}return Object.assign({},e,{bindings:n,severity:t})},Oe=(e,t,n)=>{let r=t[e.id];r!==`off`&&(r===void 0?n.push(e):n.push(De(e,r)))},ke=(e,t)=>{let n;return t&&({customRules:n}=t),Array.from(e).concat(n??[])},Ae=(e,t)=>{let n=ke(e,t),r;if(t&&(r=t.rules),r===void 0)return n;let i=[];for(let e of n)Oe(e,r,i);return i},D={aubeWorkspace:{kind:`yaml`,path:d(`aube-workspace.yaml`)},bunfig:{kind:`toml`,path:d(`bunfig.toml`)},denoJson:{kind:`json`,path:d(`deno.json`)},npmrc:{kind:`npmrc`,path:d(`.npmrc`)},packageJson:{kind:`json`,path:d(`package.json`)},pnpmWorkspace:{kind:`yaml`,path:d(`pnpm-workspace.yaml`)},yarnrc:{kind:`yaml`,path:d(`.yarnrc.yml`)}},O=(e,t)=>Object.assign({},e,{bindings:Object.assign({},e.bindings,t)}),je=e=>{let t=[];return e.configAvailableSince!==void 0&&t.push(`available since ${e.configAvailableSince}`),e.defaultSafeSince!==void 0&&t.push(`default safe since ${e.defaultSafeSince}`),e.note!==void 0&&t.push(e.note),t},k=(e,t)=>{if(t===void 0)return e;let n=je(t);return n.length===0?e:`${e} (${n.join(`; `)})`},Me=e=>e.accept?e.accept(e.documentedDefault):e.documentedDefault===e.value,Ne=(e,t,n)=>{if(t!==void 0||e.documentedDefault===void 0||!Me(e))return;let r=e.defaultSatisfiedSeverity??`info`;return r===`off`?{state:`ok`}:{actual:t,expected:e.value,message:n,severity:r,state:`violation`}},Pe=(e,t,n)=>{for(let r of e.extraFix??[]){let e=w(t,r.keyPath);if(e!==r.value)return{actual:e,expected:r.value,message:n,state:`violation`}}},Fe=(e,t,n)=>{let r=t===e.value;if(e.accept&&(r=e.accept(t)),!r)return{actual:t,expected:e.value,message:n,state:`violation`}},Ie=(e,t,n)=>{let r=w(t,e.keyPath),i=Ne(e,r,n);if(i===void 0){let t=Fe(e,r,n);if(t!==void 0)return t}return Pe(e,t,n)??i??{state:`ok`}},Le=(e,t)=>({check(n,r){return t!==void 0&&!t(n)?{state:`na`}:Ie(e,r,k(e.message,e.versionNote))},docs:e.docs,file:e.file,fix(){let t=[{file:e.file,keyPath:e.keyPath,op:`setKey`,value:e.value}];for(let n of e.extraFix??[])t.push({file:e.file,keyPath:n.keyPath,op:`setKey`,value:n.value});return t},fixKind:`auto`,severity:e.severity}),A=e=>{let t={};for(let n of g){let r=e.bindings[n];r!==void 0&&(t[n]=Le(r,e.applies))}return{bindings:t,description:e.description,docs:e.docs,id:e.id,severity:e.severity,title:e.title}},{aubeWorkspace:Re}=D,ze=A({bindings:{aube:{accept:e=>e===`on`||e===`required`,docs:`https://aube.jdx.dev/security.html`,file:Re,keyPath:[`advisoryCheck`],message:"Set `advisoryCheck: on` (or `required`) in aube-workspace.yaml to check dependencies against known-malicious advisories.",value:`on`}},description:`Query the OSV database for known-malicious packages during dependency resolution.`,docs:`https://aube.jdx.dev/security.html`,id:`advisory-check`,severity:`warn`,title:`Enable malicious-package advisory checks`}),{yarnrc:j}=D,Be=k("Set `approvedGitRepositories: []` in .yarnrc.yml to block all git: protocol dependencies (or list approved repository globs).",{configAvailableSince:`yarn 4.14.0`}),Ve={bindings:{yarn:{check(e,t){let n=w(t,[`approvedGitRepositories`]);return Array.isArray(n)?{state:`ok`}:{actual:n,expected:`[]`,message:Be,state:`violation`}},docs:`https://yarnpkg.com/configuration/yarnrc#approvedGitRepositories`,file:j,fix(){return[{file:j,message:"Add `approvedGitRepositories: []` to .yarnrc.yml to block all git deps, or list specific approved repository URL globs.",op:`note`}]},fixKind:`advisory`}},description:`Restrict git: protocol dependencies to an explicit allowlist of approved repository URL patterns.`,docs:`https://yarnpkg.com/configuration/yarnrc#approvedGitRepositories`,id:`approved-git-repos`,severity:`warn`,title:`Approve git repository dependencies`},{yarnrc:M}=D,He=e=>Array.isArray(e)&&e.length>0,Ue=[`npmAuditIgnoreAdvisories`,`npmAuditExcludePackages`],We=e=>{let t=Ue.filter(t=>He(w(e,[t]))),[n]=t;return n===void 0?{state:`ok`}:{actual:w(e,[n]),message:`Review audit suppression in .yarnrc.yml (${t.join(`, `)}). Broad glob patterns can silently hide future vulnerabilities.`,state:`violation`}},Ge={bindings:{yarn:{check(e,t){return We(t)},docs:`https://yarnpkg.com/configuration/yarnrc#npmAuditIgnoreAdvisories`,file:M,fix(){return[{file:M,message:"Review entries in `npmAuditIgnoreAdvisories` and `npmAuditExcludePackages` — remove stale suppressions and overly broad glob patterns.",op:`note`}]},fixKind:`advisory`}},description:`Flag audit advisory suppressions that may silently hide future vulnerabilities via broad glob patterns.`,docs:`https://yarnpkg.com/configuration/yarnrc#npmAuditIgnoreAdvisories`,id:`audit-suppression`,severity:`info`,title:`Review audit suppression entries`},{bunfig:Ke}=D,qe=A({bindings:{bun:{docs:`https://bun.sh/docs/runtime/bunfig#install-auto`,file:Ke,keyPath:[`install`,`auto`],message:'Set `auto = "disable"` under [install] in bunfig.toml to prevent auto-install from bypassing security guards.',value:`disable`}},description:`Disable auto-install so dependencies are only installed through an explicit install step where security guards apply.`,docs:`https://bun.sh/docs/runtime/bunfig#install-auto`,id:`block-auto-install`,severity:`warn`,title:`Block automatic package installation`}),{aubeWorkspace:Je,npmrc:N,pnpmWorkspace:Ye}=D,P=e=>e===`root`||e===`none`,F="Set `allow-git=root` and `allow-remote=root` in .npmrc to block git/tarball URLs in transitive dependencies.",Xe={check(e,t){let n=w(t,[`allow-git`]);if(!P(n))return{actual:n,expected:`root`,message:F,state:`violation`};let r=w(t,[`allow-remote`]);return P(r)?{state:`ok`}:{actual:r,expected:`root`,message:F,state:`violation`}},docs:`https://docs.npmjs.com/cli/v11/using-npm/config#allow-git`,file:N,fix(){return[{file:N,keyPath:[`allow-git`],op:`setKey`,value:`root`},{file:N,keyPath:[`allow-remote`],op:`setKey`,value:`root`}]},fixKind:`auto`},Ze=O(A({bindings:{aube:{docs:`https://aube.jdx.dev/security.html`,documentedDefault:!0,file:Je,keyPath:[`blockExoticSubdeps`],message:"Set `blockExoticSubdeps: true` in aube-workspace.yaml to block git/tarball URLs in transitive dependencies.",value:!0},pnpm:{docs:`https://pnpm.io/settings#blockexoticsubdeps`,documentedDefault:!0,file:Ye,keyPath:[`blockExoticSubdeps`],message:"Set `blockExoticSubdeps: true` in pnpm-workspace.yaml to block git/tarball URLs in transitive dependencies.",value:!0,versionNote:{configAvailableSince:`pnpm 10.26.0`}}},description:`Refuse to install transitive dependencies sourced from git or tarball URLs, which bypass registry integrity checking.`,docs:`https://pnpm.io/settings#blockexoticsubdeps`,id:`block-exotic-subdeps`,severity:`warn`,title:`Block exotic subdependencies`}),{npm:Xe}),{bunfig:I}=D,Qe={configAvailableSince:`bun 1.3.0`},$e={bindings:{bun:{check(e,t){let n=w(t,[`install`,`security`,`scanner`]);return typeof n==`string`&&n.length>0?{state:`ok`}:{actual:n,message:k('Configure `[install.security] scanner = "..."` in bunfig.toml (e.g. `@socketsecurity/bun-security-scanner`) to scan new packages on install.',Qe),state:`violation`}},docs:`https://bun.com/docs/pm/security-scanner-api`,file:I,fix(){return[{file:I,message:'Add `[install.security] scanner = "@socketsecurity/bun-security-scanner"` (or another bun-compatible scanner) to bunfig.toml.',op:`note`}]},fixKind:`advisory`}},description:`Bun supports a Security Scanner API that intercepts new packages at install time (e.g. Socket Firewall).`,docs:`https://github.com/bodadotsh/npm-security-best-practices#preinstall-scanners`,id:`bun-security-scanner`,severity:`info`,title:`Enable a bun install-time security scanner`},{yarnrc:et}=D,tt=A({bindings:{yarn:{docs:`https://yarnpkg.com/configuration/yarnrc#checksumBehavior`,documentedDefault:`throw`,file:et,keyPath:[`checksumBehavior`],message:"Set `checksumBehavior: throw` in .yarnrc.yml to reject packages whose checksum does not match the lockfile.",value:`throw`}},description:`Throw on checksum mismatches so tampered or corrupted packages are never silently installed.`,docs:`https://yarnpkg.com/configuration/yarnrc#checksumBehavior`,id:`checksum-verification`,severity:`warn`,title:`Verify package checksums`}),L={aube:{configs:[D.aubeWorkspace.path],lockfiles:[`aube-lock.yaml`],reusesLockfiles:[`pnpm-lock.yaml`,`package-lock.json`,`npm-shrinkwrap.json`,`yarn.lock`,`bun.lock`,`bun.lockb`,`deno.lock`]},bun:{configs:[D.bunfig.path],lockfiles:[`bun.lock`,`bun.lockb`]},deno:{configs:[D.denoJson.path],lockfiles:[`deno.lock`]},npm:{configs:[],lockfiles:[`package-lock.json`,`npm-shrinkwrap.json`]},pnpm:{configs:[D.pnpmWorkspace.path],lockfiles:[`pnpm-lock.yaml`]},yarn:{configs:[D.yarnrc.path],lockfiles:[`yarn.lock`]}},nt={aube:`https://aube.en.dev/package-manager/lockfiles`,bun:`https://bun.com/docs/install/lockfile`,deno:`https://docs.deno.com/runtime/fundamentals/modules/#integrity-checking-and-lock-files`,npm:`https://docs.npmjs.com/cli/v11/configuring-npm/package-lock-json`,pnpm:`https://pnpm.io/git#lockfiles`,yarn:`https://yarnpkg.com/getting-started/qa#should-lockfiles-be-committed-to-the-repository`},R=e=>{let{lockfiles:t,reusesLockfiles:n}=L[e],[r]=t,i={kind:`fileGlob`,path:d(r)},a=[...t,...n??[]];return{check(e){return a.some(t=>e.exists(d(t)))?{state:`ok`}:{message:`No lockfile found. Generate and commit ${r}.`,state:`violation`}},docs:nt[e],file:i,fix(){return[{file:i,message:`Install dependencies to generate ${r}, then commit it.`,op:`ensureFileTracked`}]},fixKind:`advisory`}},rt={bindings:{aube:R(`aube`),bun:R(`bun`),deno:R(`deno`),npm:R(`npm`),pnpm:R(`pnpm`),yarn:R(`yarn`)},description:"Lockfiles pin the full dependency tree and integrity hashes, enabling reproducible, verifiable installs (e.g. `npm ci`).",docs:`https://github.com/bodadotsh/npm-security-best-practices#2-include-lockfiles`,id:`commit-lockfile`,severity:`error`,title:`Commit a lockfile`},{aubeWorkspace:it,pnpmWorkspace:at}=D,ot=e=>typeof e==`object`&&!!e&&!Array.isArray(e)&&Object.keys(e).length>0,st=(e,t)=>{let n=w(e,[`overrides`]);return ot(n)?{actual:n,message:`Review \`overrides\` in ${t}. Overrides can replace transitive dependencies with arbitrary versions or forks.`,state:`violation`}:{state:`ok`}},z=(e,t,n)=>({check(e,n){return st(n,t)},docs:n,file:e,fix(){return[{file:e,message:"Review each entry in `overrides` — verify pinned versions address a known CVE and are not redirecting to untrusted packages.",op:`note`}]},fixKind:`advisory`}),ct={bindings:{aube:z(it,`aube-workspace.yaml`,`https://aube.jdx.dev/settings.html`),pnpm:z(at,`pnpm-workspace.yaml`,`https://pnpm.io/settings#overrides`)},description:`Flag dependency overrides that can replace transitive packages with arbitrary versions or forks — a supply-chain injection vector.`,docs:`https://pnpm.io/settings#overrides`,id:`dependency-overrides`,severity:`info`,title:`Review dependency overrides`},{npmrc:lt,pnpmWorkspace:B,yarnrc:ut,aubeWorkspace:V,bunfig:dt}=D,ft={configAvailableSince:`pnpm 10.3.0`,defaultSafeSince:`pnpm 11.0.0`},H=e=>k(e,ft),pt={check(e,t){let n=w(t,[`dangerouslyAllowAllBuilds`]);if(n===!0)return{actual:n,expected:!1,manualSteps:["Remove `dangerouslyAllowAllBuilds: true` from pnpm-workspace.yaml (or set it to `false`). Setting `strictDepBuilds: true` alone has no effect while the bypass remains."],message:H("`dangerouslyAllowAllBuilds: true` in pnpm-workspace.yaml bypasses strictDepBuilds — remove it (or set it to false) to restore lifecycle-script gating."),state:`violation`};let r=w(t,[`strictDepBuilds`]);return r===!0?{state:`ok`}:r===void 0?{actual:r,expected:!0,message:H("Set `strictDepBuilds: true` in pnpm-workspace.yaml to pin lifecycle-script gating across versions."),severity:`info`,state:`violation`}:{actual:r,expected:!0,message:H("Set `strictDepBuilds: true` in pnpm-workspace.yaml to block silent skips of un-approved dep builds."),state:`violation`}},docs:`https://pnpm.io/settings#strictdepbuilds`,file:B,fix(){return[{file:B,keyPath:[`strictDepBuilds`],op:`setKey`,value:!0}]},fixKind:`auto`},mt={check(e,t){let n=w(t,[`jailBuilds`]);if(n!==!0)return{actual:n,expected:!0,message:"Set `jailBuilds: true` in aube-workspace.yaml to sandbox build scripts.",state:`violation`};let r=w(t,[`strictDepBuilds`]);return r===!0?{state:`ok`}:{actual:r,expected:!0,message:"Set `strictDepBuilds: true` in aube-workspace.yaml to require explicit allow/deny for lifecycle scripts.",state:`violation`}},docs:`https://aube.jdx.dev/security.html`,file:V,fix(){return[{file:V,keyPath:[`jailBuilds`],op:`setKey`,value:!0},{file:V,keyPath:[`strictDepBuilds`],op:`setKey`,value:!0}]},fixKind:`auto`},ht=k('Set `ignoreScripts = true` under [install] in bunfig.toml — or set `"trustedDependencies": []` in package.json — to opt out of the curated allow-list (postinstall is already blocked for untrusted packages by default).',{configAvailableSince:`bun 1.2.0`}),gt={check(e,t){let n=w(t,[`install`,`ignoreScripts`]);if(n===!0)return{state:`ok`};let r;return e.packageJson&&(r=e.packageJson.trustedDependencies),r!==void 0&&r.length===0?{state:`ok`}:{actual:n,expected:!0,message:ht,state:`violation`}},docs:`https://bun.com/docs/pm/lifecycle`,file:dt,fix(){return[{file:dt,keyPath:[`install`,`ignoreScripts`],op:`setKey`,value:!0}]},fixKind:`auto`,severity:`info`},_t=O(A({bindings:{npm:{docs:`https://docs.npmjs.com/cli/v11/using-npm/config#ignore-scripts`,file:lt,keyPath:[`ignore-scripts`],message:"Set `ignore-scripts=true` in .npmrc to block dependency lifecycle scripts.",value:!0},yarn:{docs:`https://yarnpkg.com/configuration/yarnrc#enableScripts`,documentedDefault:!1,file:ut,keyPath:[`enableScripts`],message:"Set `enableScripts: false` in .yarnrc.yml to pin the policy across versions.",value:!1,versionNote:{configAvailableSince:`yarn 2.0.0`,defaultSafeSince:`yarn 4.14.0`}}},description:`Malicious postinstall scripts are a primary supply-chain attack vector. Prevent automatic execution of dependency lifecycle scripts.`,docs:`https://github.com/bodadotsh/npm-security-best-practices#3-disable-lifecycle-scripts`,id:`disable-lifecycle-scripts`,severity:`error`,title:`Disable dependency lifecycle scripts`}),{aube:mt,bun:gt,pnpm:pt}),{npmrc:vt,yarnrc:yt}=D,bt="Set `enableStrictSsl: true` in .yarnrc.yml to enforce SSL certificate validation for registry connections.",xt={check(e,t){let n=w(t,[`enableStrictSsl`]);if(n===!1)return{actual:!1,expected:!0,message:bt,state:`violation`};let r=w(t,[`unsafeHttpWhitelist`]);return Array.isArray(r)&&r.length>0?{actual:r,expected:``,manualSteps:["Review and remove entries from `unsafeHttpWhitelist` in .yarnrc.yml. Each entry permits unencrypted HTTP to that hostname."],message:"`unsafeHttpWhitelist` in .yarnrc.yml allows unencrypted HTTP connections — remove entries or clear the list to enforce HTTPS-only registry access.",state:`violation`}:n===void 0?{actual:n,expected:!0,message:bt,severity:`info`,state:`violation`}:{state:`ok`}},docs:`https://yarnpkg.com/configuration/yarnrc#enableStrictSsl`,file:yt,fix(){return[{file:yt,keyPath:[`enableStrictSsl`],op:`setKey`,value:!0}]},fixKind:`auto`},St=O(A({bindings:{npm:{docs:`https://docs.npmjs.com/cli/v11/using-npm/config#strict-ssl`,documentedDefault:!0,file:vt,keyPath:[`strict-ssl`],message:"Set `strict-ssl=true` in .npmrc to enforce SSL certificate validation.",value:!0}},description:`Require SSL certificate validation so registry traffic cannot be intercepted or tampered with.`,docs:`https://docs.npmjs.com/cli/v11/using-npm/config#strict-ssl`,id:`enforce-strict-ssl`,severity:`warn`,title:`Enforce SSL for registry connections`}),{yarn:xt}),U=e=>{let t=e.packageJson;return t===void 0?!1:t.private!==!0&&typeof t.name==`string`&&t.name.length>0},{packageJson:Ct,denoJson:wt}=D,W={check(e){if(!U(e))return{state:`na`};let t;return e.packageJson&&({files:t}=e.packageJson),Array.isArray(t)&&t.length>0?{state:`ok`}:{actual:t,message:"Add a `files` allow-list to package.json to limit what gets published.",state:`violation`}},docs:`https://docs.npmjs.com/cli/v11/configuring-npm/package-json#files`,file:Ct,fix(){return[{file:Ct,message:'Add a `files` array to package.json (e.g. ["dist"]) and verify with `npm pack --dry-run`.',op:`note`}]},fixKind:`advisory`},Tt={bindings:{aube:W,bun:W,deno:{check(e,t){if(typeof w(t,[`name`])!=`string`)return{state:`na`};let n=w(t,[`publish`,`include`]);return Array.isArray(n)&&n.length>0?{state:`ok`}:{actual:n,message:"Add `publish.include` to deno.json to limit what gets published.",state:`violation`}},docs:`https://docs.deno.com/runtime/reference/cli/publish/#how-publishing-works`,file:wt,fix(){return[{file:wt,message:"Add `publish.include` (and/or `publish.exclude`) to deno.json.",op:`note`}]},fixKind:`advisory`},npm:W,pnpm:W,yarn:W},description:"An explicit `files` array in package.json restricts what gets published, preventing accidental inclusion of secrets or local files.",docs:`https://github.com/bodadotsh/npm-security-best-practices#11-review-published-files`,id:`files-field`,severity:`info`,title:`Declare a published files allow-list`},{pnpmWorkspace:Et,yarnrc:Dt,bunfig:Ot,denoJson:kt,aubeWorkspace:At}=D,jt={check(e,t){if(w(t,[`lock`,`frozen`])===!0)return{state:`ok`};let n=w(t,[`lock`]),r=n!==void 0&&(typeof n!=`object`||!n||Array.isArray(n)),i={actual:w(t,[`lock`,`frozen`]),expected:!0,message:"Set `lock.frozen: true` in deno.json for reproducible, verified installs.",state:`violation`};return r?Object.assign({},i,{manualSteps:["deno.json `lock` is set to a non-object value; writing `lock.frozen: true` under it would overwrite that value. Set it yourself, or make `lock` an object first."]}):i},docs:`https://docs.deno.com/runtime/fundamentals/configuration/#lock`,file:kt,fix(){return[{file:kt,keyPath:[`lock`,`frozen`],op:`setKey`,value:!0}]},fixKind:`auto`},Mt=O(A({bindings:{aube:{docs:`https://aube.en.dev/settings/`,documentedDefault:!0,file:At,keyPath:[`preferFrozenLockfile`],message:`aube defaults preferFrozenLockfile to true — set it explicitly in aube-workspace.yaml to pin the policy across versions.`,value:!0},bun:{docs:`https://bun.com/docs/runtime/bunfig#install-frozenlockfile`,file:Ot,keyPath:[`install`,`frozenLockfile`],message:"Set `frozenLockfile = true` under [install] in bunfig.toml so installs fail when the lockfile would change.",value:!0,versionNote:{configAvailableSince:`bun 0.6.10`}},pnpm:{docs:`https://pnpm.io/settings#frozenlockfile`,documentedDefault:!0,file:Et,keyPath:[`frozenLockfile`],message:`pnpm defaults frozenLockfile to true in CI — set it explicitly in pnpm-workspace.yaml to pin the policy outside CI too.`,value:!0},yarn:{docs:`https://yarnpkg.com/configuration/yarnrc#enableImmutableInstalls`,documentedDefault:!0,file:Dt,keyPath:[`enableImmutableInstalls`],message:"Set `enableImmutableInstalls: true` in .yarnrc.yml to pin the policy outside CI too.",value:!0,versionNote:{configAvailableSince:`yarn 2.0.0`,defaultSafeSince:`yarn 3.0.0 in CI`}}},description:`Refuse to mutate the lockfile on install so unexpected dependency changes fail loudly.`,docs:`https://github.com/bodadotsh/npm-security-best-practices#2-include-lockfiles`,id:`frozen-lockfile`,severity:`warn`,title:`Freeze the lockfile`}),{deno:jt}),{pnpmWorkspace:G}=D,Nt={bindings:{pnpm:{check(e,t){let n=w(t,[`frozenStore`]);return n===!0?{state:`ok`}:{actual:n,message:"Consider enabling `frozenStore` in pnpm-workspace.yaml. Without it, the content-addressable store can be mutated after initial population, weakening integrity guarantees.",state:`violation`}},docs:`https://pnpm.io/settings#frozenstore`,file:G,fix(){return[{file:G,message:"Set `frozenStore: true` to prevent writes to the store after initial population — especially useful in CI and deployment pipelines.",op:`note`}]},fixKind:`advisory`}},description:`Recommend enabling frozenStore to prevent mutations to the content-addressable store, strengthening supply-chain integrity in CI and deploy environments.`,docs:`https://pnpm.io/settings#frozenstore`,id:`frozen-store`,severity:`info`,title:`Enable frozen store`},{yarnrc:Pt}=D,Ft=A({bindings:{yarn:{docs:`https://yarnpkg.com/configuration/yarnrc#enableHardenedMode`,documentedDefault:!0,file:Pt,keyPath:[`enableHardenedMode`],message:"yarn enables hardened mode automatically only for pull requests on public repositories — set `enableHardenedMode: true` in .yarnrc.yml to extend end-to-end install verification to every install.",value:!0,versionNote:{configAvailableSince:`yarn 4.0.0`,defaultSafeSince:`yarn 4.0.0 (conditional: auto-enabled for PRs on public repositories)`}}},description:`Yarn 4's enableHardenedMode performs end-to-end checksum, lockfile, and version verification at install time.`,docs:`https://yarnpkg.com/configuration/yarnrc#enableHardenedMode`,id:`hardened-mode`,severity:`warn`,title:`Enable hardened mode (yarn)`}),It=1440,K=3*It,Lt=3*86400,q=It,{npmrc:Rt,pnpmWorkspace:zt,yarnrc:Bt,bunfig:Vt,denoJson:Ht,aubeWorkspace:Ut}=D,J=e=>typeof e==`number`&&e>0,Wt=A({bindings:{aube:{accept:J,docs:`https://aube.en.dev/settings/`,documentedDefault:q,file:Ut,keyPath:[`minimumReleaseAge`],message:`Set minimumReleaseAge (~${K} minutes for a 3-day cooldown) in aube-workspace.yaml.`,value:K},bun:{accept:J,docs:`https://bun.com/docs/runtime/bunfig#install-minimumreleaseage`,file:Vt,keyPath:[`install`,`minimumReleaseAge`],message:`Set install.minimumReleaseAge (bun) to ~${Lt} seconds to quarantine brand-new releases.`,value:Lt,versionNote:{configAvailableSince:`bun 1.3.0`}},deno:{accept:e=>typeof e==`number`?e>0:typeof e==`string`?e!==``&&e!==`0`:typeof e==`object`&&!!e,docs:`https://docs.deno.com/runtime/reference/deno_json/`,file:Ht,keyPath:[`minimumDependencyAge`],message:`Set minimumDependencyAge (e.g. "P3D" for a 3-day cooldown) in deno.json.`,value:`P3D`},npm:{accept:J,docs:`https://docs.npmjs.com/cli/v11/using-npm/config#min-release-age`,file:Rt,keyPath:[`min-release-age`],message:`Set min-release-age to ~3 days to quarantine brand-new releases.`,value:3,versionNote:{configAvailableSince:`npm 11.10.0`}},pnpm:{accept:J,docs:`https://pnpm.io/settings#minimumreleaseage`,documentedDefault:q,file:zt,keyPath:[`minimumReleaseAge`],message:`Set minimumReleaseAge (~${K} minutes for a 3-day cooldown) in pnpm-workspace.yaml.`,value:K,versionNote:{configAvailableSince:`pnpm 10.16.0`,defaultSafeSince:`pnpm 11.0.0 (1440 minutes)`}},yarn:{accept:J,docs:`https://yarnpkg.com/configuration/yarnrc#npmMinimalAgeGate`,documentedDefault:q,file:Bt,keyPath:[`npmMinimalAgeGate`],message:`Set npmMinimalAgeGate (~${K} minutes for a 3-day cooldown) in .yarnrc.yml.`,value:K,versionNote:{configAvailableSince:`yarn 4.10.0`,defaultSafeSince:`yarn 4.15.0 (1440 minutes)`}}},description:`Refuse to install releases newer than a cooldown window so freshly published (possibly compromised) versions are skipped.`,docs:`https://github.com/bodadotsh/npm-security-best-practices#set-minimal-release-age`,id:`minimum-release-age`,severity:`warn`,title:`Set a minimum release age`}),{pnpmWorkspace:Gt}=D,Kt=e=>typeof e==`object`&&!!e&&!Array.isArray(e)&&Object.keys(e).length>0,qt={bindings:{pnpm:{check(e,t){let n=w(t,[`namedRegistries`]);return Kt(n)?{actual:n,message:"Review `namedRegistries` in pnpm-workspace.yaml. Custom registry mappings can redirect package resolution away from the public registry, enabling dependency confusion attacks.",state:`violation`}:{state:`ok`}},docs:`https://pnpm.io/settings#namedregistries`,file:Gt,fix(){return[{file:Gt,message:`Verify each named registry points to a trusted source and that scoped packages cannot be hijacked via a public registry of the same name.`,op:`note`}]},fixKind:`advisory`}},description:`Flag named registry mappings that redirect package resolution to custom registries, which may enable dependency confusion attacks.`,docs:`https://pnpm.io/settings#namedregistries`,id:`named-registries`,severity:`info`,title:`Review named registry mappings`},{aubeWorkspace:Jt}=D,Yt=A({bindings:{aube:{docs:`https://aube.jdx.dev/security.html`,file:Jt,keyPath:[`paranoid`],message:"Set `paranoid: true` in aube-workspace.yaml to enable the strict-security bundle (trustPolicy, jailBuilds, minimumReleaseAgeStrict, strictStoreIntegrity, strictDepBuilds, advisoryCheck).",value:!0}},description:`Activate the strict-security setting bundle that forces trustPolicy, jailBuilds, minimumReleaseAgeStrict, strictStoreIntegrity, strictDepBuilds, and advisoryCheck on in one switch.`,docs:`https://aube.jdx.dev/security.html`,id:`paranoid-mode`,severity:`info`,title:`Enable paranoid security mode`}),{pnpmWorkspace:Xt}=D,Zt=e=>typeof e==`object`&&!!e&&!Array.isArray(e)&&Object.keys(e).length>0,Qt={bindings:{pnpm:{check(e,t){let n=w(t,[`patchedDependencies`]);return Zt(n)?{actual:n,message:"Review `patchedDependencies` in pnpm-workspace.yaml. Patches modify dependency source code directly and bypass registry integrity checks.",state:`violation`}:{state:`ok`}},docs:`https://pnpm.io/settings#patcheddependencies`,file:Xt,fix(){return[{file:Xt,message:"Review each entry in `patchedDependencies` — verify patches address a known issue and have not been tampered with.",op:`note`}]},fixKind:`advisory`}},description:`Flag patched dependencies whose source code is modified by local patch files, bypassing registry integrity verification.`,docs:`https://pnpm.io/settings#patcheddependencies`,id:`patched-dependencies`,severity:`info`,title:`Review patched dependencies`},{npmrc:$t,pnpmWorkspace:en,yarnrc:tn,bunfig:nn,denoJson:rn}=D,an=e=>!Array.isArray(e),on=e=>(e.split(`-`,1)[0]??e).split(`.`).some(e=>e===`x`||e===`X`||e===`*`),sn=/[\^~]|>=|<=|<|>|\|\||\s/u,cn=e=>e===``||e===`*`||sn.test(e)||on(e),ln=e=>{if(e.startsWith(`http://`)||e.startsWith(`https://`))return!1;let t=e.lastIndexOf(`@`);return t<=0?!1:cn(e.slice(t+1))},un=e=>{let t=[];for(let[n,r]of Object.entries(e))typeof r==`string`&&ln(r)&&t.push(`${n}=${r}`);return t},dn=e=>{let t=e.slice(0,3).join(`, `),n=``;return e.length>3&&(n=` (and ${e.length-3} more)`),{message:`${e.length} deno imports use semver ranges: ${t}${n}. Use \`deno add --save-exact\` or pin manually.`,state:`violation`}},fn={state:`ok`},pn=e=>{let t=w(e,[`imports`]);if(!(typeof t!=`object`||!t||!an(t)))return t},mn={check(e,t){let n=pn(t);if(!n)return fn;let r=un(n);return r.length===0?fn:dn(r)},docs:`https://docs.deno.com/runtime/reference/cli/add/`,file:rn,fix(){return[{file:rn,message:"Run `deno add --save-exact <pkg>` for each ranged import in deno.json, or rewrite the `imports` entries to use exact versions.",op:`note`}]},fixKind:`advisory`},hn=O(A({bindings:{bun:{docs:`https://bun.com/docs/runtime/bunfig#install-exact`,file:nn,keyPath:[`install`,`exact`],message:"Set `exact = true` under [install] in bunfig.toml to pin exact versions.",value:!0,versionNote:{configAvailableSince:`bun 0.6.10`}},npm:{docs:`https://docs.npmjs.com/cli/v11/using-npm/config#save-exact`,extraFix:[{keyPath:[`save-prefix`],value:``}],file:$t,keyPath:[`save-exact`],message:"Set `save-exact=true` (and `save-prefix=`) in .npmrc to pin exact versions.",value:!0},pnpm:{docs:`https://pnpm.io/settings#saveprefix`,file:en,keyPath:[`savePrefix`],message:"Set `savePrefix: ''` in pnpm-workspace.yaml to pin exact versions.",value:``},yarn:{docs:`https://yarnpkg.com/configuration/yarnrc#defaultSemverRangePrefix`,file:tn,keyPath:[`defaultSemverRangePrefix`],message:"Set `defaultSemverRangePrefix: ''` in .yarnrc.yml to pin exact versions.",value:``,versionNote:{configAvailableSince:`yarn 2.0.0`}}},description:`Semver ranges (^, ~) auto-adopt new releases, including compromised ones. Save exact versions by default.`,docs:`https://github.com/bodadotsh/npm-security-best-practices#1-pin-dependency-versions`,id:`pin-exact-versions`,severity:`error`,title:`Pin exact dependency versions`}),{deno:mn}),{npmrc:gn,yarnrc:_n}=D,Y={file:gn,keyPath:[`provenance`],message:"Set `provenance=true` (and publish from CI) to attest releases.",value:!0},vn=A({applies:U,bindings:{bun:Object.assign({},Y,{docs:`https://github.com/oven-sh/bun/issues/15601`,message:"Set `provenance=true` in .npmrc and publish via `bunx npm publish` from CI — `bun publish` does not emit provenance attestations natively (tracking: oven-sh/bun#15601)."}),npm:Object.assign({},Y,{docs:`https://docs.npmjs.com/cli/v11/using-npm/config#provenance`,versionNote:{configAvailableSince:`npm 9.5.0`}}),pnpm:Object.assign({},Y,{docs:`https://pnpm.io/cli/publish`}),yarn:{docs:`https://yarnpkg.com/configuration/yarnrc#npmPublishProvenance`,file:_n,keyPath:[`npmPublishProvenance`],message:"Set `npmPublishProvenance: true` (and publish from CI) to attest releases.",value:!0}},description:`Provenance statements (via Sigstore) tie a release to its source and build, letting consumers verify it was not tampered with.`,docs:`https://github.com/bodadotsh/npm-security-best-practices#10-generate-provenance-statements`,id:`provenance`,severity:`warn`,title:`Publish with provenance`}),{packageJson:yn}=D,X={check(e){if(!U(e))return{state:`na`};let t;return e.packageJson&&e.packageJson.publishConfig&&({access:t}=e.packageJson.publishConfig),t===`public`||t===`restricted`?{state:`ok`}:{actual:t,message:"Set `publishConfig.access` in package.json to `public` or `restricted` to declare publish scope explicitly.",state:`violation`}},docs:`https://docs.npmjs.com/cli/v11/configuring-npm/package-json#publishconfig`,file:yn,fix(){return[{file:yn,message:'Add `"publishConfig": { "access": "public" }` (or `"restricted"`) to package.json so publishes never default unexpectedly.',op:`note`}]},fixKind:`advisory`},bn={bindings:{aube:X,bun:X,npm:X,pnpm:X,yarn:X},description:"Set `publishConfig.access` so a misconfigured scope or registry never accidentally publishes an internal package publicly.",docs:`https://github.com/bodadotsh/npm-security-best-practices#for-maintainers`,id:`publish-access`,severity:`info`,title:`Declare publish access explicitly`},{pnpmWorkspace:xn}=D,Sn={bindings:{pnpm:{check(e,t){let n=w(t,[`useRunningStoreServer`]);return n===!0?{actual:n,message:"Review `useRunningStoreServer` in pnpm-workspace.yaml. Delegating store operations to an external process introduces a trust boundary — the server process can serve tampered packages.",state:`violation`}:{state:`ok`}},docs:`https://pnpm.io/settings#userunningStoreserver`,file:xn,fix(){return[{file:xn,message:`Verify the store server is running in a trusted environment and that its communication channel is not exposed to untrusted networks.`,op:`note`}]},fixKind:`advisory`}},description:`Flag use of an external store server process, which introduces a trust boundary where tampered packages could be served.`,docs:`https://pnpm.io/settings#userunningStoreserver`,id:`store-server`,severity:`info`,title:`Review store server usage`},{npmrc:Cn}=D,wn=A({bindings:{npm:{docs:`https://docs.npmjs.com/cli/v11/using-npm/config#strict-allow-scripts`,file:Cn,keyPath:[`strict-allow-scripts`],message:"Set `strict-allow-scripts=true` in .npmrc to block packages with unapproved install scripts.",value:!0}},description:`Turn install-script policy warnings into hard errors so unapproved lifecycle scripts block installation.`,docs:`https://docs.npmjs.com/cli/v11/using-npm/config#strict-allow-scripts`,id:`strict-allow-scripts`,severity:`warn`,title:`Require explicit script allow-listing`}),{aubeWorkspace:Tn}=D,En=A({bindings:{aube:{docs:`https://aube.jdx.dev/settings/`,file:Tn,keyPath:[`minimumReleaseAgeStrict`],message:"Set `minimumReleaseAgeStrict: true` in aube-workspace.yaml to make the release age gate a hard block.",value:!0}},description:`Make the minimum release age gate a hard failure instead of falling back to the next-oldest satisfying version.`,docs:`https://aube.jdx.dev/security.html`,id:`strict-release-age`,severity:`info`,title:`Enforce strict release age gate`}),{aubeWorkspace:Dn}=D,On=A({bindings:{aube:{docs:`https://aube.jdx.dev/settings/`,file:Dn,keyPath:[`strictStoreIntegrity`],message:"Set `strictStoreIntegrity: true` in aube-workspace.yaml to refuse tarballs that lack integrity metadata.",value:!0}},description:`Refuse to import tarballs from the registry when the packument lacks a dist.integrity field, preventing silent integrity bypass.`,docs:`https://aube.jdx.dev/security.html`,id:`strict-store-integrity`,severity:`warn`,title:`Require tarball integrity metadata`}),{aubeWorkspace:kn,pnpmWorkspace:An}=D,jn={"advisory-check":ze,"approved-git-repos":Ve,"audit-suppression":Ge,"block-auto-install":qe,"block-exotic-subdeps":Ze,"bun-security-scanner":$e,"checksum-verification":tt,"commit-lockfile":rt,"dependency-overrides":ct,"disable-lifecycle-scripts":_t,"enforce-strict-ssl":St,"files-field":Tt,"frozen-lockfile":Mt,"frozen-store":Nt,"hardened-mode":Ft,"minimum-release-age":Wt,"named-registries":qt,"paranoid-mode":Yt,"patched-dependencies":Qt,"pin-exact-versions":hn,provenance:vn,"publish-access":bn,"store-server":Sn,"strict-allow-scripts":wn,"strict-release-age":En,"strict-store-integrity":On,"trust-policy":A({bindings:{aube:{docs:`https://aube.jdx.dev/security.html`,file:kn,keyPath:[`trustPolicy`],message:"Set `trustPolicy: no-downgrade` in aube-workspace.yaml to block packages whose trust level has decreased.",value:`no-downgrade`},pnpm:{docs:`https://pnpm.io/settings#trustpolicy`,file:An,keyPath:[`trustPolicy`],message:"Set `trustPolicy: no-downgrade` in pnpm-workspace.yaml to block packages whose trust level has decreased.",value:`no-downgrade`,versionNote:{configAvailableSince:`pnpm 10.21.0`}}},description:`Fail installation when a package trust level has decreased compared to previous releases, catching publisher credential downgrades.`,docs:`https://pnpm.io/settings#trustpolicy`,id:`trust-policy`,severity:`warn`,title:`Enforce trust policy on dependency updates`})},Z=Object.values(jn),Mn=(e,t)=>{let n=new Set,r=new Set;for(let i of e)(t.has(i.id)||r.has(i.id))&&n.add(i.id),r.add(i.id);return[...n]},Nn=(e,t)=>{let n=[];for(let r of Object.keys(e))t.has(r)||n.push(r);return n},Pn=(e,t,n=[])=>{let r=new Set(t.map(e=>e.id)),i=e.customRules??[],a=Mn(i,r),o=new Set(r);for(let e of n)o.add(e);for(let e of i)o.add(e.id);return{duplicates:a,unknown:Nn(e.rules??{},o)}},Fn=e=>{let t=``;throw e.length>1&&(t=`s`),new p(`siro.config: unknown rule id${t} in rules: ${e.map(e=>`'${e}'`).join(`, `)}`)},In=(e,t)=>{if(e===void 0)return;let{unknown:n}=Pn(e,Z,(t??[]).map(e=>e.id));n.length>0&&Fn(n)},[Q]=[],Ln=i.fallback(i.optional(i.string()),Q),Rn=i.looseObject({files:i.fallback(i.optional(i.array(i.string())),Q),name:Ln,packageManager:Ln,private:i.optional(i.fallback(i.boolean(),!0)),publishConfig:i.fallback(i.optional(i.looseObject({access:i.fallback(i.optional(i.union([i.literal(`public`),i.literal(`restricted`)])),Q)})),Q),trustedDependencies:i.fallback(i.optional(i.array(i.string())),Q)}),zn=e=>{let t=i.safeParse(Rn,e);if(t.success)return t.output},$={exists(e){try{return a(e,o.F_OK),!0}catch(e){if(x(e)&&e.code===`ENOENT`)return!1;throw e}},readText(e){try{return s(e,`utf8`)}catch(e){if(x(e)&&e.code===`ENOENT`)return;throw e}}},Bn=(e,t)=>u(c.join(e,t)),Vn=e=>{try{return JSON.parse(e)}catch(e){let t=String(e);throw e instanceof Error&&(t=e.message),new p(`package.json: invalid JSON — ${t}`)}},Hn=e=>zn(Vn(e.trim())),Un=(e,t=$)=>{let n=n=>t.readText(Bn(e,n)),r=n=>t.exists(Bn(e,n)),i=n(d(`package.json`)),a;return i!==void 0&&(a=Hn(i)),{exists:r,packageJson:a,readText:n,root:e}},Wn=[`siro.config.ts`,`siro.config.mjs`,`siro.config.js`],Gn=i.strictObject({customRules:i.optional(i.array(i.custom(()=>!0))),pms:i.optional(i.pipe(i.array(i.picklist(g)),i.minLength(1,`must not be empty (omit the key to auto-detect, or list at least one PM)`))),reporters:i.optional(i.array(i.custom(y,`must be a { name, format } reporter`))),rules:i.optional(i.record(i.string(),i.union([i.picklist(_),i.literal(`off`)])))},`unknown config key (check for a typo)`),Kn=e=>e instanceof Error?e.message:String(e),qn=e=>e.map(e=>{let t=(e.path??[]).map(e=>e.key).filter(e=>typeof e==`string`||typeof e==`number`).join(`.`);return t?`${t}: ${e.message}`:e.message}).join(`; `),Jn=(e,t,n)=>e.import(t).catch(e=>{throw new p(`Failed to load ${n}: ${Kn(e)}`)}),Yn=e=>typeof e==`object`&&e&&`default`in e?e.default:e,Xn=(e,t)=>{if(typeof e!=`object`||!e||Array.isArray(e)){let n=typeof e;throw Array.isArray(e)&&(n=`an array`),new p(`${t} must export a config object (got ${n}).`)}return e},Zn=(e,t)=>{let n=i.safeParse(Gn,e);if(!n.success)throw new p(`${t}: ${qn(n.issues)}`);return n.output},Qn=(e,t)=>{let{duplicates:n}=Pn(e,Z);if(n.length>0){let e=``;throw n.length>1&&(e=`s`),new p(`${t}: duplicate customRule id${e}: ${n.map(e=>`'${e}'`).join(`, `)}. Pick a unique id (or use 'rules' to override an existing rule's severity).`)}},$n=(e,t,n)=>Jn(e,t,n).then(e=>{let t=Zn(Xn(Yn(e),n),n);return Qn(t,n),t}),er=(e,t=$)=>{let n=l(e,{moduleCache:!1}),r=Wn.find(n=>t.exists(u(c.join(e,n))));return r===void 0?Promise.resolve(void 0):$n(n,u(c.join(e,r)),r)},tr=(e,t)=>{let n=new Set,r=new Set;for(let i of e)(t.has(i.id)||n.has(i.id))&&r.add(i.id),n.add(i.id);return r},nr=e=>{let t=[...e],n=`rule id collides`;throw t.length>1&&(n=`rule ids collide`),new p(`customRules: ${n} with builtin, config, or another programmatic rule: ${t.map(e=>`'${e}'`).join(`, `)}. Pick a unique id (or use 'rules' to override an existing rule's severity).`)},rr=(e,t,n)=>{if(t===void 0||t.length===0)return e;let r=tr(t,new Set([...e.map(e=>e.id),...(n??[]).map(e=>e.id)]));return r.size>0&&nr(r),[...e,...t]},ir=(e,t,n)=>{for(let r of e){let e=n.get(r);if(e!==void 0)throw TypeError(`detection signal '${r}' is claimed by both '${e}' and '${t}'`);n.set(r,t)}},ar=(()=>{let e=new Map,t=new Map;for(let n of g){let{lockfiles:r,configs:i}=L[n],a=[...r,...i];ir(a,n,t),e.set(n,a)}return e})(),or=(e,t)=>{let n;if(e.packageJson&&(n=e.packageJson.packageManager),typeof n==`string`){let e=ae(n);e!==void 0&&t.add(e)}},sr=e=>{let t=new Set;or(e,t);for(let n of g){let r=ar.get(n);r&&r.some(t=>e.exists(d(t)))&&t.add(n)}return g.filter(e=>t.has(e))},cr=(e,t,n)=>{if(n){if(t.pmOverride)throw new m(`--pm ${t.pmOverride} is not in siro.config.ts pms (${n.join(`, `)}). Adjust the config or the flag.`);let r=`No package manager detected, and siro.config.ts restricts pms to ${n.join(`, `)}. Adjust the config or remove the restriction.`;throw e.length>0&&(r=`Detected PMs (${e.join(`, `)}) do not match siro.config.ts pms (${n.join(`, `)}). Adjust the config or remove the restriction.`),new m(r)}throw new m(`No package manager detected. Pass --pm <${g.join(`|`)}> to be explicit.`)},lr=(e,t)=>{let n=sr(e);t.pmOverride&&(n=[t.pmOverride]);let{allowed:r}=t,i=n;if(r){let e=new Set(r);i=n.filter(t=>e.has(t))}return{detected:n,pms:i}},ur=(e,t)=>{let{detected:n,pms:r}=lr(e,t);if(r.length>0)return r;throw cr(n,t,t.allowed)},dr=(e,t)=>{let n;e&&(n=e.customRules);let r=rr(Z,t,n);return In(e,t),Ae(r,e)},fr=(e,t)=>{let{cwd:n,fs:r,pm:i,customRules:a}=e,o=Un(n,r),s;return t&&(s=t.pms),{ctx:o,pms:ur(o,{allowed:s,pmOverride:i}),ruleSet:dr(t,a),userConfig:t}},pr=e=>er(e.cwd,e.fs).then(t=>fr(e,t)),mr=e=>{let{ctx:t,pms:n,ruleSet:r,parseConfig:i,onViolation:a}=e;for(let e of r)for(let r of n){let n=e.bindings[r];if(n!==void 0){let{parsed:o}=i(t,n.file),s=n.check(t,o);s.state===`violation`&&a({binding:n,pm:r,rule:e,status:s})}}},hr=Object.freeze({parsed:Object.freeze({})}),gr=(e,t,n)=>{let r=e.readText(t.path)??``,i={parsed:h(t.path,()=>n.codecFor(t.kind).parse(r))};return n.cache.set(n.cacheKey,i),i},_r=e=>{let t=new Map;return(n,r)=>{if(r.kind===`fileGlob`)return hr;let i=`${r.kind}:${r.path}`,a=t.get(i);return a===void 0?gr(n,r,{cache:t,cacheKey:i,codecFor:e}):a}},vr=(e,t,n)=>e.severity??t.severity??n.severity,yr=e=>{if(e!==void 0&&e.length>0)return e},br=(e,t)=>{let{rule:n,pm:r,binding:i,status:a}=e,o=vr(a,i,n),s=yr(a.manualSteps),c;i.file.kind!==`fileGlob`&&(c=i.file.path);let l=[];return s===void 0&&(l=i.fix(t)),{actual:a.actual,docs:i.docs??n.docs,expected:a.expected,file:c,fix:l,fixable:i.fixKind===`auto`&&s===void 0,manualSteps:s,message:a.message,pm:r,ruleId:n.id,severity:o}},xr=e=>{let{ctx:t,pms:n,ruleSet:r,codecFor:i}=e,a=[],o={error:0,info:0,warn:0};return mr({ctx:t,onViolation:e=>{let n=br(e,t);a.push(n),o[n.severity]+=1},parseConfig:_r(i),pms:n,ruleSet:r}),{findings:a,summary:o}},Sr=(e,t)=>{if(e===void 0)return t.get(`pretty`);if(typeof e==`string`)return t.get(e);if(!y(e))throw new m(`The 'reporter' option must be a name or a { name, format } object.`);return e},Cr=e=>{for(let t of e??[])if(!y(t))throw new m(`A reporter in 'reporters' needs a string 'name' and a 'format' function.`)},wr=(e,t)=>{let n=Sr(e,t);if(n===void 0)throw new m(`Unknown reporter: ${String(e)} (available: ${t.list().join(`, `)})`);return n},Tr=e=>{let t=xr({codecFor:Te,ctx:e.ctx,pms:e.pms,ruleSet:e.ruleSet}),n=e.options.severity??`info`,r=e.options.severity??`error`;return e.reporter.format(be(t,n),e.io),S(t,r)},Er=(e,t)=>pr({customRules:e.customRules,cwd:e.cwd,fs:e.fs,pm:e.pm}).then(({userConfig:n,ctx:r,pms:i,ruleSet:a})=>{Cr(e.reporters);let o=[];n&&n.reporters&&(o=n.reporters);let s=ve([...e.reporters??[],...o]);return Tr({ctx:r,io:t,options:e,pms:i,reporter:wr(e.reporter,s),ruleSet:a})}),Dr={stderr:e=>process.stderr.write(`${e}\n`),stdout:e=>process.stdout.write(`${e}\n`)};export{d as A,v as C,p as D,m as E,f as O,_ as S,h as T,ue as _,L as a,y as b,A as c,S as d,be as f,he as g,ve as h,$ as i,u as k,D as l,_e as m,Er as n,O as o,x as p,sr as r,k as s,Dr as t,w as u,le as v,re as w,g as x,ce as y};
|
|
4
|
-
//# sourceMappingURL=node-io-BLwef7p6.mjs.map
|