@lockhawk/core 0.2.6 → 0.2.7
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/dist/index.d.ts +7 -11
- package/dist/index.js +46 -191
- package/package.json +12 -2
package/dist/index.d.ts
CHANGED
|
@@ -548,20 +548,16 @@ declare function toSarif(result: ScanResult): SarifLog;
|
|
|
548
548
|
/** Stringified SARIF log. */
|
|
549
549
|
declare function toSarifString(result: ScanResult): string;
|
|
550
550
|
|
|
551
|
+
/** Placeholder the report UI shell embeds so we can inject scan data at scan time. */
|
|
552
|
+
declare const DATA_MARKER = "<!--LOCKHAWK_DATA-->";
|
|
551
553
|
/**
|
|
552
|
-
* Produce a self-contained HTML report
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
* data blob, no external resources (fonts included). Every dynamic value is
|
|
556
|
-
* HTML-escaped, the layout flows within a width-constrained container, and long
|
|
557
|
-
* advisory text lives in a collapsed, contained block — so the report cannot
|
|
558
|
-
* overflow the viewport or render attacker-controlled advisory text as live
|
|
559
|
-
* markup. Copy affordances use CSS `user-select: all` (one click selects a whole
|
|
560
|
-
* command/version) rather than a clipboard script, preserving the no-JS model.
|
|
554
|
+
* Produce a self-contained HTML report. When given the built report-UI `shell`
|
|
555
|
+
* (M6), the scan data is injected into it; otherwise a dependency-free fallback
|
|
556
|
+
* template is rendered so `--format html` always works.
|
|
561
557
|
*/
|
|
562
|
-
declare function toHtml(result: ScanResult): string;
|
|
558
|
+
declare function toHtml(result: ScanResult, shell?: string): string;
|
|
563
559
|
|
|
564
560
|
/** Render a scan result as JUnit XML (one failed test per finding). */
|
|
565
561
|
declare function toJunit(result: ScanResult): string;
|
|
566
562
|
|
|
567
|
-
export { AutoSource, type CvssResult, type DatabaseInfo, type DepNode, type DepScope, type DependencyGraph, type DetectedLockfile, type Finding, type FindingsOptions, LockfileError, OfflineDbMissingError, type OfflineMeta, OfflineSource, type OnlineClientOptions, OnlineSource, type OsvAffected, OsvClient, OsvDatabase, type OsvEvent, type OsvRange, type OsvReference, type OsvSeverity, type OsvVulnerability, type PackageManager, type PackageRef, type PkgKey, type RawGraph, type RawNode, type ResolvedSource, type RootManifest, SEVERITY_RANK, SHARD_BUCKETS, type SarifLog, type ScanOptions, type ScanResult, type ScanStats, type ScanSummary, type Severity, type SeverityInfo, type SourceMode, type SourceOptions, TOOL, type UnscannablePackage, type UpdateResult, type VulnMatch, type VulnSource, buildDependencyGraph, buildFindings, canonicalId, createSource, dedupeVulnerabilities, detectLockfile, isVersionAffected, levelFromLabel, levelFromScore, loadAdvisoriesForPackages, loadDependencyGraph, nearestFix, offlineDbDir, offlineMetaPath, parseLockfile, pkgKey, readOfflineMeta, readRootManifest, resolveCacheDir, resolveSeverity, rootLabel, scan, scoreFromVector, severityAtLeast, shardBucket, shortestPath, shouldFail, toHtml, toJson, toJunit, toSarif, toSarifString, uniqueScannablePackages, updateOfflineDatabase, vulnerabilityAffects };
|
|
563
|
+
export { AutoSource, type CvssResult, DATA_MARKER, type DatabaseInfo, type DepNode, type DepScope, type DependencyGraph, type DetectedLockfile, type Finding, type FindingsOptions, LockfileError, OfflineDbMissingError, type OfflineMeta, OfflineSource, type OnlineClientOptions, OnlineSource, type OsvAffected, OsvClient, OsvDatabase, type OsvEvent, type OsvRange, type OsvReference, type OsvSeverity, type OsvVulnerability, type PackageManager, type PackageRef, type PkgKey, type RawGraph, type RawNode, type ResolvedSource, type RootManifest, SEVERITY_RANK, SHARD_BUCKETS, type SarifLog, type ScanOptions, type ScanResult, type ScanStats, type ScanSummary, type Severity, type SeverityInfo, type SourceMode, type SourceOptions, TOOL, type UnscannablePackage, type UpdateResult, type VulnMatch, type VulnSource, buildDependencyGraph, buildFindings, canonicalId, createSource, dedupeVulnerabilities, detectLockfile, isVersionAffected, levelFromLabel, levelFromScore, loadAdvisoriesForPackages, loadDependencyGraph, nearestFix, offlineDbDir, offlineMetaPath, parseLockfile, pkgKey, readOfflineMeta, readRootManifest, resolveCacheDir, resolveSeverity, rootLabel, scan, scoreFromVector, severityAtLeast, shardBucket, shortestPath, shouldFail, toHtml, toJson, toJunit, toSarif, toSarifString, uniqueScannablePackages, updateOfflineDatabase, vulnerabilityAffects };
|
package/dist/index.js
CHANGED
|
@@ -1,209 +1,64 @@
|
|
|
1
|
-
var $={none:0,unknown:1,low:2,medium:3,high:4,critical:5};function _e(e,n){return $[e]>=$[n]}function S(e,n){return`${e}@${n}`}import{statSync as gr}from"fs";import{resolve as mr}from"path";import{existsSync as qe,readFileSync as Gn,statSync as Bn}from"fs";import{join as Fe}from"path";var zn=[{filename:"pnpm-lock.yaml",manager:"pnpm"},{filename:"yarn.lock",manager:"yarn"},{filename:"package-lock.json",manager:"npm"},{filename:"npm-shrinkwrap.json",manager:"npm"}];function F(e){for(let{filename:n,manager:r}of zn){let s=Fe(e,n);if(qe(s)&&Bn(s).isFile())return{manager:r,path:s,filename:n}}}function C(e){let n=Fe(e,"package.json"),r={};if(qe(n))try{r=JSON.parse(Gn(n,"utf8"))}catch{r={}}let s=t=>t&&typeof t=="object"?t:{};return{name:typeof r.name=="string"?r.name:"project",version:typeof r.version=="string"?r.version:void 0,dependencies:s(r.dependencies),devDependencies:s(r.devDependencies),optionalDependencies:s(r.optionalDependencies),peerDependencies:s(r.peerDependencies)}}function le(e,n){let r=new Set,s=[];for(let t of e)r.has(t)||(r.add(t),s.push(t));for(let t=0;t<s.length;t++){let a=n[s[t]];if(a)for(let i of a.dependencies)r.has(i)||(r.add(i),s.push(i))}return r}function pe(e){let n=le(e.directProd,e.nodes),r=le(e.directOptional,e.nodes),s=le(e.directDev,e.nodes),t=new Set([...e.directProd,...e.directOptional,...e.directDev]),a={};for(let[i,o]of Object.entries(e.nodes)){let d;n.has(i)?d="prod":r.has(i)?d="optional":s.has(i)?d="dev":d="prod",a[i]={key:i,name:o.name,version:o.version,scope:d,direct:t.has(i),dependencies:Yn(o.dependencies),...o.unscannable?{unscannable:o.unscannable}:{}}}return{manager:e.manager,lockfilePath:e.lockfilePath,lockfileVersion:e.lockfileVersion,root:e.root,nodes:a,directKeys:[...t].filter(i=>a[i]!==void 0)}}function Yn(e){return e.length>1?[...new Set(e)]:e}import{readFileSync as Wn}from"fs";import{dirname as Jn}from"path";function Ke(e){let n=JSON.parse(Wn(e,"utf8"));return n.packages?Zn(n,e):Qn(n,e)}function Xn(e){let n="node_modules/",r=e.lastIndexOf(n);return r===-1?e:e.slice(r+n.length)}function Zn(e,n){let r=e.packages??{},s=r[""]??{},t={name:s.name??e.name??"project",version:s.version??e.version},a=Object.create(null),i=[],o=Object.create(null);for(let[f,p]of Object.entries(r)){if(f===""||p.link)continue;let m=p.name??Xn(f),v=p.version;if(!v)continue;let g=S(m,v);if(o[f]=g,!a[g]){let x={key:g,name:m,version:v,dependencies:[]};f.includes("node_modules")||(x.unscannable={reason:"local workspace package"},i.push({name:m,version:v,reason:"local workspace package"})),a[g]=x}}let d=(f,p)=>{let m=f;for(;;){let v=`${m?`${m}/`:""}node_modules/${p}`,g=r[v];if(g)return g.link&&typeof g.resolved=="string"?g.resolved:v;if(!m)return;let x=m.lastIndexOf("/node_modules/");m=x===-1?"":m.slice(0,x)}},c=new Set,l=new Set,u=new Set;for(let[f,p]of Object.entries(r)){if(p.link)continue;let m=f===""?void 0:o[f],v=f===""||!f.includes("node_modules"),g=(x,w)=>{for(let R of x){let O=d(f,R);if(!O)continue;let P=o[O];P&&(m&&m!==P&&a[m].dependencies.push(P),w&&w.add(P))}};g(Object.keys(p.dependencies??{}),v?c:void 0),g(Object.keys(p.optionalDependencies??{}),v?u:void 0),v&&g(Object.keys(p.devDependencies??{}),l)}return{manager:"npm",lockfilePath:n,lockfileVersion:e.lockfileVersion,root:t,nodes:a,directProd:[...c],directDev:[...l],directOptional:[...u],unscannable:i}}var Te=500;function Qn(e,n){let r=C(Jn(n)),s={name:e.name??r.name,version:e.version??r.version},t=Object.create(null),a=p=>{let m=new Map;for(let[v,g]of Object.entries(p??{}))g?.version&&m.set(v,g.version);return m},i=(p,m)=>{for(let v=m.length-1;v>=0;v--){let g=m[v].get(p);if(g)return g}},o=(p,m)=>{if(m.length>Te)throw new Error(`dependency nesting exceeds ${Te} levels (malformed or malicious lockfile)`);let v=a(p),g=[...m,v];for(let[x,w]of Object.entries(p??{})){let R=w?.version;if(!R)continue;let O=S(x,R);t[O]||(t[O]={key:O,name:x,version:R,dependencies:[]});let P=[...g,a(w.dependencies)];for(let M of Object.keys(w.requires??{})){let N=i(M,P);if(N){let D=S(M,N);D!==O&&t[O].dependencies.push(D)}}w.dependencies&&o(w.dependencies,g)}};o(e.dependencies,[]);let d=a(e.dependencies),c=[],l=[],u=[],f=(p,m)=>{for(let v of p){let g=d.get(v);g&&m.push(S(v,g))}};return f(Object.keys(r.dependencies),c),f(Object.keys(r.optionalDependencies),u),f(Object.keys(r.devDependencies),l),{manager:"npm",lockfilePath:n,lockfileVersion:e.lockfileVersion,root:s,nodes:t,directProd:c,directDev:l,directOptional:u,unscannable:[]}}import{readFileSync as er}from"fs";import{dirname as nr}from"path";import{createRequire as rr}from"module";import tr from"js-yaml";var sr=rr(import.meta.url),or=sr("@yarnpkg/lockfile");function Ue(e){let n=er(e,"utf8"),r=C(nr(e)),s=n.includes("__metadata"),{descriptorToVersion:t,entryByVersion:a,lockfileVersion:i}=s?ar(n):ir(n);return cr(e,r,t,a,i,s)}function Ge(e){let n=e.lastIndexOf("@");if(!(n<=0))return{name:e.slice(0,n),range:e.slice(n+1)}}function ir(e){let n=or.parse(e),r=new Map,s=new Map;for(let[t,a]of Object.entries(n.object))if(a.version)for(let i of t.split(/,\s*/)){let o=Ge(i);o&&r.set(`${o.name}@${o.range}`,a.version),o&&s.set(S(o.name,a.version),a)}return{descriptorToVersion:r,entryByVersion:s,lockfileVersion:1}}function ar(e){let n=tr.load(e)??{},r=new Map,s=new Map,t="berry";for(let[a,i]of Object.entries(n)){if(a==="__metadata"){let o=i.version;o!==void 0&&(t=o);continue}if(i.version)for(let o of a.split(/,\s*/)){let d=Ge(o);d&&(r.set(`${d.name}@${d.range}`,i.version),s.set(S(d.name,i.version),i))}}return{descriptorToVersion:r,entryByVersion:s,lockfileVersion:t}}function cr(e,n,r,s,t,a){let i=(c,l)=>{let u=a?[`${c}@${l}`,`${c}@npm:${l}`]:[`${c}@${l}`];for(let f of u){let p=r.get(f);if(p)return p}},o=Object.create(null);for(let[c,l]of s){let u=c.lastIndexOf("@"),f=c.slice(0,u),p=c.slice(u+1);o[c]||(o[c]={key:c,name:f,version:p,dependencies:[]});let m=v=>{for(let[g,x]of Object.entries(v??{})){let w=i(g,x);if(!w)continue;let R=S(g,w);R!==c&&o[c].dependencies.push(R)}};m(l.dependencies),m(l.optionalDependencies)}let d=c=>{let l=[];for(let[u,f]of Object.entries(c)){let p=i(u,f);p&&l.push(S(u,p))}return l};return{manager:"yarn",lockfilePath:e,lockfileVersion:t,root:{name:n.name,version:n.version},nodes:o,directProd:d(n.dependencies),directDev:d(n.devDependencies),directOptional:d(n.optionalDependencies),unscannable:[]}}import{readFileSync as dr}from"fs";import{dirname as lr}from"path";import pr from"js-yaml";function ze(e){let n=pr.load(dr(e,"utf8"))??{},r=C(lr(e)),s=fr(n.lockfileVersion),t=Object.create(null);for(let u of Object.keys(n.packages??{})){let f=Be(u,s);if(!f)continue;let p=S(f.name,f.version);t[p]||(t[p]={key:p,name:f.name,version:f.version,dependencies:[]})}let a=s>=9?n.snapshots??{}:n.packages??{};for(let[u,f]of Object.entries(a)){let p=Be(u,s);if(!p)continue;let m=S(p.name,p.version);t[m]||(t[m]={key:m,name:p.name,version:p.version,dependencies:[]});let v=g=>{for(let[x,w]of Object.entries(g??{})){let R=S(x,Y(w,s));R!==m&&t[R]&&t[m].dependencies.push(R)}};v(f.dependencies),v(f.optionalDependencies)}let i=n.importers??{".":{dependencies:n.dependencies,devDependencies:n.devDependencies,optionalDependencies:n.optionalDependencies}},o=new Set,d=new Set,c=new Set,l=(u,f)=>{for(let[p,m]of Object.entries(u??{})){let v=ur(m,s);if(!v)continue;let g=S(p,v);t[g]&&f.add(g)}};for(let u of Object.values(i))l(u.dependencies,o),l(u.optionalDependencies,c),l(u.devDependencies,d);return{manager:"pnpm",lockfilePath:e,lockfileVersion:n.lockfileVersion,root:{name:r.name,version:r.version},nodes:t,directProd:[...o],directDev:[...d],directOptional:[...c],unscannable:[]}}function fr(e){let n=parseFloat(String(e??"9"));return Number.isNaN(n)?9:Math.floor(n)}function Be(e,n){let r=e.startsWith("/")?e.slice(1):e;if(n<6){let i=r.lastIndexOf("/");if(i<=0)return;let o=r.slice(0,i),d=Y(r.slice(i+1),n);return d?{name:o,version:d}:void 0}let s=r.lastIndexOf("@");if(s<=0)return;let t=r.slice(0,s),a=Y(r.slice(s+1),n);return a?{name:t,version:a}:void 0}function Y(e,n){let r=e;if(n<6){let t=r.indexOf("_");t!==-1&&(r=r.slice(0,t))}let s=r.indexOf("(");return s!==-1&&(r=r.slice(0,s)),r}function ur(e,n){let r;if(typeof e=="string"?r=e:e&&typeof e=="object"&&typeof e.version=="string"&&(r=e.version),!!r&&!(r.startsWith("link:")||r.startsWith("file:")||r.startsWith("workspace:")))return Y(r,n)}var E=class extends Error{constructor(n){super(n),this.name="LockfileError"}};function vr(){let e=Number(process.env.LOCKHAWK_MAX_LOCKFILE_BYTES);return Number.isFinite(e)&&e>0?e:64*1024*1024}function Ye(e){let n=0;try{n=gr(e.path).size}catch{}let r=vr();if(n>r)throw new E(`${e.filename} is ${n} bytes, exceeding the ${r}-byte safety limit. Set LOCKHAWK_MAX_LOCKFILE_BYTES to raise it.`);try{switch(e.manager){case"npm":return Ke(e.path);case"yarn":return Ue(e.path);case"pnpm":return ze(e.path)}}catch(s){throw new E(`Failed to parse ${e.filename}: ${s instanceof Error?s.message:String(s)}`)}}function fe(e){let n=F(mr(e));if(!n)throw new E(`No lockfile found in ${e}. Expected one of: package-lock.json, yarn.lock, pnpm-lock.yaml.`);return pe(Ye(n))}function ue(e){return e.root.version?`${e.root.name}@${e.root.version}`:e.root.name}function ge(e,n){if(!e.nodes[n])return null;let r="\0root",s=new Map,t=new Set([r]),a=[r];for(let i=0;i<a.length;i++){let o=a[i],d=o===r?e.directKeys:e.nodes[o]?.dependencies??[];for(let c of d)if(!t.has(c)){if(t.add(c),s.set(c,o),c===n)return br(e,s,n,r);a.push(c)}}return[ue(e),n]}function br(e,n,r,s){let t=[],a=r;for(;a!==void 0&&a!==s;)t.unshift(a),a=n.get(a);return t.unshift(ue(e)),t}import I from"semver";function We(e,n){if(n.versions?.includes(e))return!0;let r=I.parse(e,{loose:!0});if(!r)return!1;for(let s of n.ranges??[])if(s.type!=="GIT"&&yr(r,s.events))return!0;return!1}function yr(e,n){let r=[];for(let t of n)t.introduced!==void 0?r.push({kind:"introduced",version:t.introduced==="0"?null:me(t.introduced)}):t.fixed!==void 0?r.push({kind:"fixed",version:me(t.fixed)}):t.last_affected!==void 0&&r.push({kind:"last_affected",version:me(t.last_affected)});r.sort((t,a)=>t.version===null?a.version===null?0:-1:a.version===null?1:t.version.compare(a.version));let s=!1;for(let t of r){if(t.version===null){s=!0;continue}let a=e.compare(t.version);switch(t.kind){case"introduced":a>=0&&(s=!0);break;case"fixed":a>=0&&(s=!1);break;case"last_affected":a>0&&(s=!1);break}}return s}function me(e){return I.parse(e,{loose:!0})??I.coerce(e)}function W(e,n,r){if(r.withdrawn)return{affected:!1,fixedVersions:[]};let s=!1,t=new Set;for(let a of r.affected??[]){let i=a.package?.ecosystem;if(!(i&&i!=="npm")&&!(a.package?.name&&a.package.name!==e)&&We(n,a)){s=!0;for(let o of a.ranges??[])for(let d of o.events)d.fixed&&t.add(d.fixed)}}return{affected:s,fixedVersions:[...t]}}function ve(e,n){let r=I.parse(e,{loose:!0});return n.map(t=>I.parse(t,{loose:!0})??I.coerce(t)).filter(t=>t!==null).filter(t=>r?t.compare(r)>0:!0).sort((t,a)=>t.compare(a))[0]?.version}function Je(e){return[e.id,...e.aliases??[]]}function Xe(e){let n=[...e];return n.find(r=>r.startsWith("CVE-"))??n.find(r=>r.startsWith("GHSA-"))??n[0]}function be(e){if(e.length<=1)return e;let n=new Map,r=a=>{let i=a;for(;n.get(i)!==void 0&&n.get(i)!==i;)i=n.get(i);return n.set(a,i),i},s=(a,i)=>{n.set(r(a),r(i))};for(let a of e){let i=Je(a);n.has(i[0])||n.set(i[0],i[0]);for(let o of i.slice(1))n.has(o)||n.set(o,o),s(i[0],o)}let t=new Map;for(let a of e){let i=r(a.id),o=t.get(i);o?o.push(a):t.set(i,[a])}return[...t.values()].map(hr)}function hr(e){if(e.length===1)return e[0];let n=new Set,r=new Map,s=[],t=[],a,i,o;for(let c of e){for(let l of Je(c))n.add(l);for(let l of c.references??[])r.set(l.url,l);for(let l of c.severity??[])s.push(l);for(let l of c.affected??[])t.push(l);a??=c.summary,i??=c.details,o??=c.database_specific}let d=Xe(n);return{id:d,aliases:[...n].filter(c=>c!==d),summary:a,details:i,references:[...r.values()],severity:s,affected:t,database_specific:o}}import hn from"semver";var J={1e5:9.8,100001:9.5,100010:9.4,100011:8.7,100020:9.1,100021:8.1,100100:9.4,100101:8.9,100110:8.6,100111:7.4,100120:7.7,100121:6.4,100200:8.7,100201:7.5,100210:7.4,100211:6.3,100220:6.3,100221:4.9,101e3:9.4,101001:8.9,101010:8.8,101011:7.7,101020:7.6,101021:6.7,101100:8.6,101101:7.6,101110:7.4,101111:5.8,101120:5.9,101121:5,101200:7.2,101201:5.7,101210:5.7,101211:5.2,101220:5.2,101221:2.5,102001:8.3,102011:7,102021:5.4,102101:6.5,102111:5.8,102121:2.6,102201:5.3,102211:2.1,102221:1.3,11e4:9.5,110001:9,110010:8.8,110011:7.6,110020:7.6,110021:7,110100:9,110101:7.7,110110:7.5,110111:6.2,110120:6.1,110121:5.3,110200:7.7,110201:6.6,110210:6.8,110211:5.9,110220:5.2,110221:3,111e3:8.9,111001:7.8,111010:7.6,111011:6.7,111020:6.2,111021:5.8,111100:7.4,111101:5.9,111110:5.7,111111:5.7,111120:4.7,111121:2.3,111200:6.1,111201:5.2,111210:5.7,111211:2.9,111220:2.4,111221:1.6,112001:7.1,112011:5.9,112021:3,112101:5.8,112111:2.6,112121:1.5,112201:2.3,112211:1.3,112221:.6,2e5:9.3,200001:8.7,200010:8.6,200011:7.2,200020:7.5,200021:5.8,200100:8.6,200101:7.4,200110:7.4,200111:6.1,200120:5.6,200121:3.4,200200:7,200201:5.4,200210:5.2,200211:4,200220:4,200221:2.2,201e3:8.5,201001:7.5,201010:7.4,201011:5.5,201020:6.2,201021:5.1,201100:7.2,201101:5.7,201110:5.5,201111:4.1,201120:4.6,201121:1.9,201200:5.3,201201:3.6,201210:3.4,201211:1.9,201220:1.9,201221:.8,202001:6.4,202011:5.1,202021:2,202101:4.7,202111:2.1,202121:1.1,202201:2.4,202211:.9,202221:.4,21e4:8.8,210001:7.5,210010:7.3,210011:5.3,210020:6,210021:5,210100:7.3,210101:5.5,210110:5.9,210111:4,210120:4.1,210121:2,210200:5.4,210201:4.3,210210:4.5,210211:2.2,210220:2,210221:1.1,211e3:7.5,211001:5.5,211010:5.8,211011:4.5,211020:4,211021:2.1,211100:6.1,211101:5.1,211110:4.8,211111:1.8,211120:2,211121:.9,211200:4.6,211201:1.8,211210:1.7,211211:.7,211220:.8,211221:.2,212001:5.3,212011:2.4,212021:1.4,212101:2.4,212111:1.2,212121:.5,212201:1,212211:.3,212221:.1,"000000":10,"000001":9.9,"000010":9.8,"000011":9.5,"000020":9.5,"000021":9.2,"000100":10,"000101":9.6,"000110":9.3,"000111":8.7,"000120":9.1,"000121":8.1,"000200":9.3,"000201":9,"000210":8.9,"000211":8,"000220":8.1,"000221":6.8,"001000":9.8,"001001":9.5,"001010":9.5,"001011":9.2,"001020":9,"001021":8.4,"001100":9.3,"001101":9.2,"001110":8.9,"001111":8.1,"001120":8.1,"001121":6.5,"001200":8.8,"001201":8,"001210":7.8,"001211":7,"001220":6.9,"001221":4.8,"002001":9.2,"002011":8.2,"002021":7.2,"002101":7.9,"002111":6.9,"002121":5,"002201":6.9,"002211":5.5,"002221":2.7,"010000":9.9,"010001":9.7,"010010":9.5,"010011":9.2,"010020":9.2,"010021":8.5,"010100":9.5,"010101":9.1,"010110":9,"010111":8.3,"010120":8.4,"010121":7.1,"010200":9.2,"010201":8.1,"010210":8.2,"010211":7.1,"010220":7.2,"010221":5.3,"011000":9.5,"011001":9.3,"011010":9.2,"011011":8.5,"011020":8.5,"011021":7.3,"011100":9.2,"011101":8.2,"011110":8,"011111":7.2,"011120":7,"011121":5.9,"011200":8.4,"011201":7,"011210":7.1,"011211":5.2,"011220":5,"011221":3,"012001":8.6,"012011":7.5,"012021":5.2,"012101":7.1,"012111":5.2,"012121":2.9,"012201":6.3,"012211":2.9,"012221":1.7},ye={eq1:{0:["AV:N/PR:N/UI:N/"],1:["AV:A/PR:N/UI:N/","AV:N/PR:L/UI:N/","AV:N/PR:N/UI:P/"],2:["AV:P/PR:N/UI:N/","AV:A/PR:L/UI:P/"]},eq2:{0:["AC:L/AT:N/"],1:["AC:H/AT:N/","AC:L/AT:P/"]},eq3:{0:{0:["VC:H/VI:H/VA:H/CR:H/IR:H/AR:H/"],1:["VC:H/VI:H/VA:L/CR:M/IR:M/AR:H/","VC:H/VI:H/VA:H/CR:M/IR:M/AR:M/"]},1:{0:["VC:L/VI:H/VA:H/CR:H/IR:H/AR:H/","VC:H/VI:L/VA:H/CR:H/IR:H/AR:H/"],1:["VC:L/VI:H/VA:L/CR:H/IR:M/AR:H/","VC:L/VI:H/VA:H/CR:H/IR:M/AR:M/","VC:H/VI:L/VA:H/CR:M/IR:H/AR:M/","VC:H/VI:L/VA:L/CR:M/IR:H/AR:H/","VC:L/VI:L/VA:H/CR:H/IR:H/AR:M/"]},2:{1:["VC:L/VI:L/VA:L/CR:H/IR:H/AR:H/"]}},eq4:{0:["SC:H/SI:S/SA:S/"],1:["SC:H/SI:H/SA:H/"],2:["SC:L/SI:L/SA:L/"]},eq5:{0:["E:A/"],1:["E:P/"],2:["E:U/"]}},Ze={eq1:{0:1,1:4,2:5},eq2:{0:1,1:2},eq3eq6:{0:{0:7,1:6},1:{0:8,1:8},2:{1:10}},eq4:{0:6,1:5,2:4},eq5:{0:1,1:1,2:1}};var gn=["AV","AC","AT","PR","UI","VC","VI","VA","SC","SI","SA"],Qe={N:0,A:.1,L:.2,P:.3},en={N:0,L:.1,H:.2},nn={N:0,P:.1,A:.2},rn={L:0,H:.1},tn={N:0,P:.1},sn={H:0,L:.1,N:.2},on={H:0,L:.1,N:.2},an={H:0,L:.1,N:.2},cn={H:.1,L:.2,N:.3},dn={S:0,H:.1,L:.2,N:.3},ln={S:0,H:.1,L:.2,N:.3},pn={H:0,M:.1,L:.2},fn={H:0,M:.1,L:.2},un={H:0,M:.1,L:.2};function mn(e){let n=xr(e);if(!n)return null;let r=kr(n);return J[r]===void 0?null:wr(n,r)}function xr(e){let n=e.split("/");if(n[0]!=="CVSS:4.0")return null;let r={};for(let s of n.slice(1)){let[t,a]=s.split(":");t&&a&&(r[t]=a)}for(let s of gn)if(!(s in r))return null;for(let s of["E","CR","IR","AR"])s in r||(r[s]="X");return r}function b(e,n){let r=e[n];if(n==="E"&&r==="X")return"A";if((n==="CR"||n==="IR"||n==="AR")&&r==="X")return"H";let s=e[`M${n}`];return s!==void 0&&s!=="X"?s:r??""}function kr(e){let n=b(e,"AV"),r=b(e,"PR"),s=b(e,"UI"),t="2";n==="N"&&r==="N"&&s==="N"?t="0":(n==="N"||r==="N"||s==="N")&&n!=="P"&&(t="1");let a=b(e,"AC")==="L"&&b(e,"AT")==="N"?"0":"1",i=b(e,"VC"),o=b(e,"VI"),d=b(e,"VA"),c="2";i==="H"&&o==="H"?c="0":(i==="H"||o==="H"||d==="H")&&(c="1");let l=b(e,"MSI"),u=b(e,"MSA"),f="2";l==="S"||u==="S"?f="0":(b(e,"SC")==="H"||b(e,"SI")==="H"||b(e,"SA")==="H")&&(f="1");let p=b(e,"E"),m=p==="A"?"0":p==="P"?"1":"2",v=b(e,"CR"),g=b(e,"IR"),x=b(e,"AR");return`${t}${a}${c}${f}${m}${v==="H"&&i==="H"||g==="H"&&o==="H"||x==="H"&&d==="H"?"0":"1"}`}function y(e,n){return e[n]??0}function X(e,n){return ye[`eq${n}`][e[n-1]]??[]}function V(e,n){let r=n.indexOf(e)+e.length+1,s=n.slice(r),t=s.indexOf("/");return t>0?s.substring(0,t):s}function wr(e,n){if(gn.slice(5).every(h=>b(e,h)==="N"))return 0;let r=J[n],s=Number(n[0]),t=Number(n[2]),a=Number(n[5]),i=h=>J[h.join("")]??NaN,o=n.split("").map(Number),d=i([o[0]+1,o[1],o[2],o[3],o[4],o[5]]),c=i([o[0],o[1]+1,o[2],o[3],o[4],o[5]]),l;if(t===1&&a===1)l=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]]);else if(t===0&&a===1)l=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]]);else if(t===1&&a===0)l=i([o[0],o[1],o[2],o[3],o[4],o[5]+1]);else if(t===0&&a===0){let h=i([o[0],o[1],o[2],o[3],o[4],o[5]+1]),q=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]]);l=h>q?h:q}else l=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]+1]);let u=i([o[0],o[1],o[2],o[3]+1,o[4],o[5]]),f=i([o[0],o[1],o[2],o[3],o[4]+1,o[5]]),p=X(n,1),m=X(n,2),v=ye.eq3[n[2]][n[5]],g=X(n,4),x=X(n,5),w=[];for(let h of p)for(let q of m)for(let de of v)for(let Kn of g)for(let Un of x)w.push(h+q+de+Kn+Un);let R=0,O=0,P=0,M=0,N=0,D=0,ne=0,re=0,te=0,se=0,oe=0,ie=0,ae=0,ce=0;for(let h of w)if(R=y(Qe,b(e,"AV"))-y(Qe,V("AV",h)),O=y(en,b(e,"PR"))-y(en,V("PR",h)),P=y(nn,b(e,"UI"))-y(nn,V("UI",h)),M=y(rn,b(e,"AC"))-y(rn,V("AC",h)),N=y(tn,b(e,"AT"))-y(tn,V("AT",h)),D=y(sn,b(e,"VC"))-y(sn,V("VC",h)),ne=y(on,b(e,"VI"))-y(on,V("VI",h)),re=y(an,b(e,"VA"))-y(an,V("VA",h)),te=y(cn,b(e,"SC"))-y(cn,V("SC",h)),se=y(dn,b(e,"SI"))-y(dn,V("SI",h)),oe=y(ln,b(e,"SA"))-y(ln,V("SA",h)),ie=y(pn,b(e,"CR"))-y(pn,V("CR",h)),ae=y(fn,b(e,"IR"))-y(fn,V("IR",h)),ce=y(un,b(e,"AR"))-y(un,V("AR",h)),![R,O,P,M,N,D,ne,re,te,se,oe,ie,ae,ce].some(de=>de<0))break;let Dn=R+O+P,En=M+N,In=D+ne+re+ie+ae+ce,Hn=te+se+oe,B=.1,Me=r-d,Ne=r-c,De=r-l,Ee=r-u,Ln=r-f,z=Ze,jn=z.eq1[String(s)]*B,_n=z.eq2[n[1]]*B,qn=z.eq3eq6[String(t)][String(a)]*B,Fn=z.eq4[n[3]]*B,A=0,Ie=0,He=0,Le=0,je=0;Number.isNaN(Me)||(A++,Ie=Me*(Dn/jn)),Number.isNaN(Ne)||(A++,He=Ne*(En/_n)),Number.isNaN(De)||(A++,Le=De*(In/qn)),Number.isNaN(Ee)||(A++,je=Ee*(Hn/Fn)),Number.isNaN(Ln)||A++;let Tn=A===0?0:(Ie+He+Le+je)/A;return r-=Tn,r<0&&(r=0),r>10&&(r=10),Math.round(r*10)/10}var Rr={N:.85,A:.62,L:.55,P:.2},Sr={L:.77,H:.44},Vr={N:.85,R:.62},Or={N:.85,L:.62,H:.27},Pr={N:.85,L:.68,H:.5},he={H:.56,L:.22,N:0};function xe(e){return e<=0?"none":e<4?"low":e<7?"medium":e<9?"high":"critical"}function bn(e){switch(e.trim().toUpperCase()){case"CRITICAL":return"critical";case"HIGH":return"high";case"MODERATE":case"MEDIUM":return"medium";case"LOW":return"low";case"NONE":return"none";default:return"unknown"}}function vn(e){let n=Math.round(e*1e5);return n%1e4===0?n/1e5:(Math.floor(n/1e4)+1)/10}function yn(e){let n=e.split("/"),r=n[0]??"";if(r==="CVSS:4.0"){let g=mn(e);return g===null?null:{score:g,level:xe(g),version:r}}if(!/^CVSS:3\.[01]$/.test(r))return null;let s={};for(let g of n.slice(1)){let[x,w]=g.split(":");x&&w&&(s[x]=w)}let t=Rr[s.AV??""],a=Sr[s.AC??""],i=Vr[s.UI??""],o=s.S==="C",d=(o?Pr:Or)[s.PR??""],c=he[s.C??""],l=he[s.I??""],u=he[s.A??""];if([t,a,i,d,c,l,u].some(g=>g===void 0))return null;let f=1-(1-c)*(1-l)*(1-u),p=o?7.52*(f-.029)-3.25*Math.pow(f-.02,15):6.42*f,m=8.22*t*a*d*i,v;return p<=0?v=0:o?v=vn(Math.min(1.08*(p+m),10)):v=vn(Math.min(p+m,10)),{score:v,level:xe(v),version:r}}function ke(e){let n=[];for(let t of e.severity??[])t.score&&n.push(t.score);for(let t of e.affected??[])for(let a of t.severity??[])a.score&&n.push(a.score);let r=null;for(let t of n){let a=yn(t);a&&(!r||a.score>r.score)&&(r=a)}if(r)return{level:r.level,score:r.score,vector:Ar(r,n),cvssVersion:r.version,source:"cvss"};let s=$r(e);return s?{level:bn(s),source:"database"}:{level:"unknown",source:"unknown"}}function Ar(e,n){return n.find(r=>r.startsWith(e.version))}function $r(e){let n=s=>{let t=s?.severity;return typeof t=="string"?t:void 0},r=n(e.database_specific);if(r)return r;for(let s of e.affected??[]){let t=n(s.database_specific);if(t)return t}}function we(e,n,r={}){let s=[];for(let t of Object.values(e.nodes)){if(t.unscannable||r.prodOnly&&t.scope==="dev")continue;let a=n(t.name);if(a.length===0)continue;let i=new Map,o=a.filter(c=>{let l=W(t.name,t.version,c);return i.set(c,l),l.affected});if(o.length===0)continue;let d=ge(e,t.key);for(let c of be(o)){if(Cr(c,r.ignore))continue;let{fixedVersions:l}=i.get(c)??W(t.name,t.version,c),u=Mr(l),f=ve(t.version,l);s.push({id:c.id,aliases:c.aliases??[],packageName:t.name,version:t.version,scope:t.scope,direct:t.direct,severity:ke(c),summary:c.summary??c.id,details:c.details,references:(c.references??[]).map(p=>p.url),fixedVersions:u,recommendation:Dr(t.name,t.direct,f,e.manager),dependencyPaths:d?[d]:[],source:"osv"})}}return s}function Cr(e,n){return!n||n.size===0?!1:n.has(e.id)?!0:(e.aliases??[]).some(r=>n.has(r))}function Mr(e){return[...new Set(e)].sort((n,r)=>{let s=hn.parse(n,{loose:!0}),t=hn.parse(r,{loose:!0});return s&&t?s.compare(t):n.localeCompare(r)})}var Nr={npm:"npm install",yarn:"yarn add",pnpm:"pnpm add"};function Dr(e,n,r,s){return r?n?`Upgrade to ${e}@${r} or later (e.g. \`${Nr[s]} ${e}@${r}\`).`:`Update the dependency that pulls in ${e} so it resolves to ${e}@${r} or later. If it cannot, force the resolution via an override.`:n?"No fixed version is available yet. Watch the advisory, or evaluate an alternative package.":"No fixed version is available yet. Pressure the parent dependency to update, or override the version."}var Re=class{byName=new Map;ids=new Set;add(n){if(n.withdrawn||this.ids.has(n.id))return;this.ids.add(n.id);let r=new Set;for(let s of n.affected??[]){let t=s.package?.ecosystem;t&&t!=="npm"||s.package?.name&&r.add(s.package.name)}for(let s of r){let t=this.byName.get(s);t?t.push(n):this.byName.set(s,[n])}}addAll(n){for(let r of n)this.add(r)}vulnerabilitiesFor(n){return this.byName.get(n)??[]}get size(){return this.ids.size}};import{resolve as ct}from"path";import{join as Se}from"path";import Er from"env-paths";function Ve(e){return e||(process.env.LOCKHAWK_CACHE?process.env.LOCKHAWK_CACHE:Er("lockhawk",{suffix:""}).cache)}function T(e){return Se(e,"osv-db","npm")}var xn=4096;function Z(e){let n=2166136261;for(let r=0;r<e.length;r++)n^=e.charCodeAt(r),n=Math.imul(n,16777619);return(n>>>0)%xn}function K(e){return Se(T(e),"meta.json")}function Oe(e,n){return Se(e,"query-cache",`${encodeURIComponent(n)}.json`)}import{mkdir as Ir,readFile as Hr,writeFile as Lr}from"fs/promises";import{dirname as jr}from"path";import _r from"p-limit";import qr,{AbortError as Fr}from"p-retry";var Pe="https://api.osv.dev/v1",kn=1e3,U=class{constructor(n){this.opts=n;this.limit=_r(n.concurrency??16)}opts;limit;async fetchAdvisories(n){let r=[...new Set(n.map(o=>o.name))],s=await this.queryBatch(r),t=new Set;for(let o of s.values())for(let d of o)t.add(d);let a=await this.hydrate([...t]),i=new Map;for(let[o,d]of s){let c=[];for(let l of d){let u=a.get(l);u&&c.push(u)}c.length&&i.set(o,c)}return i}async queryBatch(n){let r=new Map;for(let s=0;s<n.length;s+=kn){let t=n.slice(s,s+kn),a={queries:t.map(o=>({package:{name:o,ecosystem:"npm"}}))},i=await this.request(`${Pe}/querybatch`,a);for(let o=0;o<t.length;o++){let d=t[o],c=i.results[o];if(!c)continue;let l=r.get(d)??new Set;for(let f of c.vulns??[])l.add(f.id);let u=c.next_page_token;for(;u;){let f=await this.request(`${Pe}/query`,{package:{name:d,ecosystem:"npm"},page_token:u});for(let p of f.vulns??[])l.add(p.id);u=f.next_page_token}l.size&&r.set(d,l)}}return r}async hydrate(n){let r=new Map;return await Promise.all(n.map(s=>this.limit(async()=>{let t=await this.getVulnerability(s);t&&r.set(s,t)}))),r}async getVulnerability(n){let r=await this.readCache(n);if(r)return r;let s=await this.request(`${Pe}/vulns/${encodeURIComponent(n)}`);return await this.writeCache(n,s),s}async request(n,r){let s=this.opts.retries??3;return qr(async()=>{let t=await fetch(n,{method:r?"POST":"GET",headers:r?{"content-type":"application/json"}:void 0,body:r?JSON.stringify(r):void 0,signal:AbortSignal.timeout(this.opts.timeoutMs??15e3)});if(t.ok)return await t.json();throw t.status===429||t.status>=500?new Error(`OSV ${t.status} for ${n}`):new Fr(`OSV ${t.status} for ${n}`)},{retries:s})}async readCache(n){if(!this.opts.noCache)try{let r=JSON.parse(await Hr(Oe(this.opts.cacheDir,n),"utf8")),s=(this.opts.cacheTtlHours??24)*36e5;return Date.now()-r.fetchedAt>s?void 0:r.vuln}catch{return}}async writeCache(n,r){if(!this.opts.noCache)try{let s=Oe(this.opts.cacheDir,n);await Ir(jr(s),{recursive:!0}),await Lr(s,JSON.stringify({fetchedAt:Date.now(),vuln:r}))}catch{}}};import{createWriteStream as Tr}from"fs";import{mkdir as wn,mkdtemp as Kr,readFile as Rn,rm as Sn,writeFile as Ae}from"fs/promises";import{tmpdir as Ur}from"os";import{join as Q}from"path";import{Readable as Gr}from"stream";import{pipeline as Br}from"stream/promises";import{gunzipSync as zr,gzipSync as Yr}from"zlib";import Wr from"p-limit";import Jr from"yauzl";var Xr="https://storage.googleapis.com/osv-vulnerabilities/npm/all.zip",Zr=32,Qr=8*1024*1024,H=class extends Error{constructor(){super("No offline OSV database found. Run `lockhawk db update` first."),this.name="OfflineDbMissingError"}},et=/^[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/,nt=/^(?:W\/)?"[\x21\x23-\x7e]*"$/;function rt(e){return e&&et.test(e)?e:void 0}function tt(e){return e&&nt.test(e)?e:void 0}async function L(e){try{return JSON.parse(await Rn(K(e),"utf8"))}catch{return}}async function st(e){let{cacheDir:n,nowIso:r,force:s,onProgress:t}=e,a=T(n);await wn(a,{recursive:!0});let i=s?void 0:await L(n),o={},d=tt(i?.etag),c=rt(i?.lastModified);d&&(o["If-None-Match"]=d),c&&(o["If-Modified-Since"]=c),t?.("Fetching npm advisory database from OSV.dev\u2026");let l=await fetch(Xr,{headers:o});if(l.status===304&&i){let g={...i,lastUpdated:r};return await Ae(K(n),JSON.stringify(g,null,2)),{status:"unchanged",meta:g}}if(!l.ok||!l.body)throw new Error(`OSV database download failed: HTTP ${l.status}`);let u=await Kr(Q(Ur(),"lockhawk-osv-")),f=Q(u,"all.zip"),p;try{await Br(Gr.fromWeb(l.body),Tr(f)),t?.("Extracting advisories\u2026"),p=await at(f)}finally{await Sn(u,{recursive:!0,force:!0})}t?.("Building package index\u2026");let m=ot(p);await it(n,m);let v={ecosystem:"npm",source:"osv.dev",lastUpdated:r,recordCount:p.length,packageCount:m.size,etag:l.headers.get("etag")??void 0,lastModified:l.headers.get("last-modified")??void 0};return await Ae(K(n),JSON.stringify(v,null,2)),{status:"updated",meta:v}}function Vn(e){return Q(T(e),"by-name")}function On(e,n){return Q(Vn(e),`${n}.json.gz`)}async function $e(e,n){if(!await L(e))throw new H;let r=new Map;for(let a of new Set(n)){let i=Z(a),o=r.get(i);o?o.push(a):r.set(i,[a])}let s=new Map,t=Wr(Zr);return await Promise.all([...r.entries()].map(([a,i])=>t(async()=>{try{let o=JSON.parse(zr(await Rn(On(e,a))).toString("utf8"));for(let d of i){let c=o[d];c?.length&&s.set(d,c)}}catch{}}))),s}function ot(e){let n=new Map;for(let r of e){if(r.withdrawn)continue;let s=new Set;for(let t of r.affected??[]){let a=t.package?.ecosystem;a&&a!=="npm"||t.package?.name&&s.add(t.package.name)}for(let t of s){let a=n.get(t);a?a.push(r):n.set(t,[r])}}return n}async function it(e,n){let r=Vn(e);await Sn(r,{recursive:!0,force:!0}),await wn(r,{recursive:!0});let s=new Map;for(let[i,o]of n){let d=Z(i),c=s.get(d)??{};c[i]=o,s.set(d,c)}let t=[...s.entries()],a=256;for(let i=0;i<t.length;i+=a)await Promise.all(t.slice(i,i+a).map(([o,d])=>Ae(On(e,o),Yr(Buffer.from(JSON.stringify(d))))))}function at(e){return new Promise((n,r)=>{let s=[];Jr.open(e,{lazyEntries:!0},(t,a)=>{if(t||!a)return r(t??new Error("Failed to open OSV zip"));a.on("entry",i=>{if(!i.fileName.endsWith(".json")||i.uncompressedSize>Qr)return a.readEntry();a.openReadStream(i,(o,d)=>{if(o||!d)return a.readEntry();let c=[];d.on("data",l=>c.push(l)),d.on("error",()=>a.readEntry()),d.on("end",()=>{try{s.push(JSON.parse(Buffer.concat(c).toString("utf8")))}catch{}a.readEntry()})})}),a.on("end",()=>n(s)),a.on("error",r),a.readEntry()})})}var Pn=36e5,j=class{constructor(n){this.opts=n}opts;async prepare(n){let r=await L(this.opts.cacheDir);if(!r)throw new H;let s=await $e(this.opts.cacheDir,n.map(o=>o.name)),t=(Date.now()-Date.parse(r.lastUpdated))/Pn,a=t>(this.opts.staleAfterHours??24),i=a?[`Offline database is ${Math.round(t)}h old. Run \`lockhawk db update\` for current results.`]:[];return{candidatesFor:o=>s.get(o)??[],database:{source:"offline",recordCount:r.recordCount,lastUpdated:r.lastUpdated,ageHours:Math.round(t*10)/10,stale:a,warnings:i}}}},G=class{constructor(n){this.opts=n}opts;async prepare(n){let r=new U({cacheDir:this.opts.cacheDir,concurrency:this.opts.concurrency,noCache:this.opts.noCache,cacheTtlHours:this.opts.cacheTtlHours});try{let s=await r.fetchAdvisories(n);return{candidatesFor:t=>s.get(t)??[],database:{source:"online",stale:!1,warnings:[]}}}catch(s){if(this.opts.strictNetwork)throw s;let t=s instanceof Error?s.message:String(s);return{candidatesFor:()=>[],database:{source:"online",stale:!0,warnings:[`OSV.dev was unreachable (${t}). Scan completed without online data \u2014 results may be incomplete.`]}}}}},ee=class{constructor(n){this.opts=n}opts;async prepare(n){let r=await L(this.opts.cacheDir),s=this.opts.staleAfterHours??24;if(r&&(Date.now()-Date.parse(r.lastUpdated))/Pn<=s)return new j(this.opts).prepare(n);try{return await new G({...this.opts,strictNetwork:!0}).prepare(n)}catch(t){if(r){let i=await new j(this.opts).prepare(n);return i.database.warnings.push("Used the stale offline database because OSV.dev was unreachable."),i}if(this.opts.strictNetwork)throw t;let a=t instanceof Error?t.message:String(t);return{candidatesFor:()=>[],database:{source:"online",stale:!0,warnings:[`No offline database and OSV.dev was unreachable (${a}). Run \`lockhawk db update\`.`]}}}}};function Ce(e,n){switch(e){case"offline":return new j(n);case"online":return new G(n);default:return new ee(n)}}var An={name:"lockhawk",version:"0.1.0"};async function dt(e={},n){let r=Date.now(),s=ct(e.path??"."),t=fe(s),a=F(s),i=$n(t,e.prodOnly),o=Ve(e.cacheDir),d=n??Ce(e.mode??"auto",{cacheDir:o,concurrency:e.concurrency,noCache:e.noCache,cacheTtlHours:e.cacheTtlHours,strictNetwork:e.strictNetwork});e.onProgress?.("Checking dependencies against OSV.dev\u2026");let c=await d.prepare(i),l=we(t,c.candidatesFor,{prodOnly:e.prodOnly,ignore:e.ignore&&e.ignore.length?new Set(e.ignore):void 0});e.severityThreshold&&e.severityThreshold!=="none"&&(l=l.filter(f=>_e(f.severity.level,e.severityThreshold))),l.sort(ft);let u=lt(t);return{schemaVersion:1,tool:{...An},scannedAt:new Date(r).toISOString(),target:{path:s,manager:t.manager,lockfile:a?.filename??"",root:t.root},database:c.database,summary:pt(l),stats:{totalPackages:Object.keys(t.nodes).length,uniquePackages:i.length,directDependencies:t.directKeys.length,unscannable:u.length,durationMs:Date.now()-r},findings:l,unscannable:u}}function $n(e,n){let r=new Set,s=[];for(let t of Object.values(e.nodes))t.unscannable||n&&t.scope==="dev"||r.has(t.key)||(r.add(t.key),s.push({name:t.name,version:t.version}));return s}function lt(e){let n=[];for(let r of Object.values(e.nodes))r.unscannable&&n.push({name:r.name,version:r.version,reason:r.unscannable.reason});return n}function pt(e){let n={total:e.length,critical:0,high:0,medium:0,low:0,unknown:0,none:0,vulnerablePackages:0,fixable:0},r=new Set;for(let s of e)n[s.severity.level]+=1,r.add(`${s.packageName}@${s.version}`),s.fixedVersions.length>0&&(n.fixable+=1);return n.vulnerablePackages=r.size,n}function ft(e,n){let r=$[n.severity.level]-$[e.severity.level];return r!==0?r:e.packageName.localeCompare(n.packageName)}function ut(e,n){return["critical","high","medium","low","unknown"].some(s=>$[s]>=$[n]&&e[s]>0)}function gt(e){return JSON.stringify(e,null,2)}var mt="https://github.com/lockhawk/lockhawk";function vt(e){return e.find(n=>{try{let r=new URL(n);return r.protocol==="http:"||r.protocol==="https:"}catch{return!1}})}function bt(e){if(typeof e.severity.score=="number")return e.severity.score;switch(e.severity.level){case"critical":return 9.5;case"high":return 7.5;case"medium":return 5;case"low":return 2;default:return 0}}function yt(e){return e==="critical"||e==="high"?"error":e==="medium"?"warning":"note"}function Cn(e){let n=e.target.lockfile||"package-lock.json",r=new Map,s=[];for(let t of e.findings){r.has(t.id)||r.set(t.id,{id:t.id,name:`${t.packageName} ${t.id}`,shortDescription:{text:t.summary},fullDescription:t.details?{text:t.details}:void 0,helpUri:vt(t.references),properties:{"security-severity":String(bt(t)),tags:["security","dependency",t.severity.level]}});let a=t.dependencyPaths[0]?.join(" \u203A ")??t.packageName,i=t.fixedVersions[0]?` Fixed in ${t.fixedVersions[0]}.`:"";s.push({ruleId:t.id,level:yt(t.severity.level),message:{text:`${t.severity.level.toUpperCase()}: ${t.packageName}@${t.version} \u2014 ${t.summary}.${i} Path: ${a}.`},locations:[{physicalLocation:{artifactLocation:{uri:n},region:{startLine:1}}}],partialFingerprints:{lockhawkFinding:`${t.packageName}@${t.version}@${t.id}`},properties:{package:t.packageName,version:t.version,scope:t.scope,fixedVersions:t.fixedVersions}})}return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:e.tool.name,version:e.tool.version,informationUri:mt,rules:[...r.values()]}},results:s,properties:{scannedAt:e.scannedAt,databaseSource:e.database.source,databaseAgeHours:e.database.ageHours,databaseStale:e.database.stale}}]}}function ht(e){return JSON.stringify(Cn(e),null,2)}function xt(e){return St(e)}var Mn={critical:"#ff5d67",high:"#ff9f43",medium:"#ffd23f",low:"#5b9dff",unknown:"#8b95a5",none:"#34d399"};function k(e){return e.replace(/[&<>"]/g,n=>({"&":"&","<":"<",">":">",'"':"""})[n])}function kt(e){try{let n=new URL(e);return n.protocol==="http:"||n.protocol==="https:"?e:void 0}catch{return}}var wt=`<svg class="mark" viewBox="0 0 48 48" fill="none" aria-hidden="true">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<p class="fix-text">${k(t)}</p>${a}
|
|
14
|
-
</div>`}function St(e){let{summary:n}=e,r=["critical","high","medium","low","unknown"].map((i,o)=>`
|
|
15
|
-
<div class="card${n[i]===0?" zero":""}" style="--c:${Mn[i]};--i:${o}">
|
|
16
|
-
<div class="count">${n[i]}</div><div class="label">${i}</div>
|
|
17
|
-
</div>`).join(""),s=e.findings.map((i,o)=>{let d=i.dependencyPaths[0]?.join(" \u203A ")??i.packageName,c=i.references.slice(0,3).map(u=>kt(u)).filter(u=>u!==void 0).map(u=>`<a class="ref" href="${k(u)}" target="_blank" rel="noopener">link</a>`).join(""),l=i.fixedVersions[0]?`<span class="fixchip" title="Click to select, then \u2318/Ctrl+C to copy">${k(i.fixedVersions[0])}</span>`:'<span class="nofix">\u2014</span>';return`
|
|
18
|
-
<tr class="main" style="--i:${o}">
|
|
19
|
-
<td><span class="pill" style="--c:${Mn[i.severity.level]}">${k(i.severity.level)}${i.severity.score?` ${i.severity.score}`:""}</span></td>
|
|
20
|
-
<td><span class="pkg">${k(i.packageName)}@${k(i.version)}</span><div class="scope">${k(i.scope)}${i.direct?" \xB7 direct":""}</div></td>
|
|
21
|
-
<td><a class="advid" href="https://osv.dev/${encodeURIComponent(i.id)}" target="_blank" rel="noopener">${k(i.id)}</a></td>
|
|
22
|
-
<td><div class="summary">${k(i.summary)}</div><div class="path">${k(d)}</div></td>
|
|
23
|
-
<td>${l}</td>
|
|
24
|
-
<td class="refs">${c}</td>
|
|
25
|
-
</tr>
|
|
26
|
-
<tr class="detail-row">
|
|
27
|
-
<td colspan="6">
|
|
28
|
-
${i.recommendation?Rt(i.recommendation):""}
|
|
29
|
-
<details>
|
|
30
|
-
<summary>${Nn}<span>Advisory details</span></summary>
|
|
31
|
-
<div class="detail-body">
|
|
32
|
-
<div class="advisory-wrap">
|
|
33
|
-
<div class="advisory-head"><span class="dots"><i></i><i></i><i></i></span><span class="advisory-title">advisory \xB7 ${k(i.id)}</span></div>
|
|
34
|
-
<pre class="advisory">${k(i.details??i.summary)}</pre>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</details>
|
|
38
|
-
</td>
|
|
39
|
-
</tr>`}).join(""),t=e.database.warnings.map(i=>`<div class="warn"><span class="warn-i">\u26A0</span>${k(i)}</div>`).join(""),a=e.findings.length===0?"":`<div class="results-meta">
|
|
40
|
-
<span><b>${n.total}</b> findings</span><span class="sep">\xB7</span>
|
|
41
|
-
<span><b>${n.vulnerablePackages}</b> packages</span><span class="sep">\xB7</span>
|
|
42
|
-
<span><b>${n.fixable}</b> fixable</span>
|
|
43
|
-
</div>`;return`<!doctype html>
|
|
1
|
+
var N={none:0,unknown:1,low:2,medium:3,high:4,critical:5};function qe(e,n){return N[e]>=N[n]}function w(e,n){return`${e}@${n}`}import{statSync as gt}from"fs";import{resolve as mt}from"path";import{existsSync as Fe,readFileSync as Gn,statSync as Bn}from"fs";import{join as Te}from"path";var zn=[{filename:"pnpm-lock.yaml",manager:"pnpm"},{filename:"yarn.lock",manager:"yarn"},{filename:"package-lock.json",manager:"npm"},{filename:"npm-shrinkwrap.json",manager:"npm"}];function F(e){for(let{filename:n,manager:t}of zn){let s=Te(e,n);if(Fe(s)&&Bn(s).isFile())return{manager:t,path:s,filename:n}}}function M(e){let n=Te(e,"package.json"),t={};if(Fe(n))try{t=JSON.parse(Gn(n,"utf8"))}catch{t={}}let s=r=>r&&typeof r=="object"?r:{};return{name:typeof t.name=="string"?t.name:"project",version:typeof t.version=="string"?t.version:void 0,dependencies:s(t.dependencies),devDependencies:s(t.devDependencies),optionalDependencies:s(t.optionalDependencies),peerDependencies:s(t.peerDependencies)}}function fe(e,n){let t=new Set,s=[];for(let r of e)t.has(r)||(t.add(r),s.push(r));for(let r=0;r<s.length;r++){let a=n[s[r]];if(a)for(let i of a.dependencies)t.has(i)||(t.add(i),s.push(i))}return t}function ue(e){let n=fe(e.directProd,e.nodes),t=fe(e.directOptional,e.nodes),s=fe(e.directDev,e.nodes),r=new Set([...e.directProd,...e.directOptional,...e.directDev]),a={};for(let[i,o]of Object.entries(e.nodes)){let d;n.has(i)?d="prod":t.has(i)?d="optional":s.has(i)?d="dev":d="prod",a[i]={key:i,name:o.name,version:o.version,scope:d,direct:r.has(i),dependencies:Yn(o.dependencies),...o.unscannable?{unscannable:o.unscannable}:{}}}return{manager:e.manager,lockfilePath:e.lockfilePath,lockfileVersion:e.lockfileVersion,root:e.root,nodes:a,directKeys:[...r].filter(i=>a[i]!==void 0)}}function Yn(e){return e.length>1?[...new Set(e)]:e}import{readFileSync as Wn}from"fs";import{dirname as Jn}from"path";function Ue(e){let n=JSON.parse(Wn(e,"utf8"));return n.packages?Zn(n,e):Qn(n,e)}function Xn(e){let n="node_modules/",t=e.lastIndexOf(n);return t===-1?e:e.slice(t+n.length)}function Zn(e,n){let t=e.packages??{},s=t[""]??{},r={name:s.name??e.name??"project",version:s.version??e.version},a=Object.create(null),i=[],o=Object.create(null);for(let[l,u]of Object.entries(t)){if(l===""||u.link)continue;let g=u.name??Xn(l),y=u.version;if(!y)continue;let p=w(g,y);if(o[l]=p,!a[p]){let k={key:p,name:g,version:y,dependencies:[]};l.includes("node_modules")||(k.unscannable={reason:"local workspace package"},i.push({name:g,version:y,reason:"local workspace package"})),a[p]=k}}let d=(l,u)=>{let g=l;for(;;){let y=`${g?`${g}/`:""}node_modules/${u}`,p=t[y];if(p)return p.link&&typeof p.resolved=="string"?p.resolved:y;if(!g)return;let k=g.lastIndexOf("/node_modules/");g=k===-1?"":g.slice(0,k)}},c=new Set,f=new Set,m=new Set;for(let[l,u]of Object.entries(t)){if(u.link)continue;let g=l===""?void 0:o[l],y=l===""||!l.includes("node_modules"),p=(k,R)=>{for(let S of k){let O=d(l,S);if(!O)continue;let P=o[O];P&&(g&&g!==P&&a[g].dependencies.push(P),R&&R.add(P))}};p(Object.keys(u.dependencies??{}),y?c:void 0),p(Object.keys(u.optionalDependencies??{}),y?m:void 0),y&&p(Object.keys(u.devDependencies??{}),f)}return{manager:"npm",lockfilePath:n,lockfileVersion:e.lockfileVersion,root:r,nodes:a,directProd:[...c],directDev:[...f],directOptional:[...m],unscannable:i}}var Ke=500;function Qn(e,n){let t=M(Jn(n)),s={name:e.name??t.name,version:e.version??t.version},r=Object.create(null),a=u=>{let g=new Map;for(let[y,p]of Object.entries(u??{}))p?.version&&g.set(y,p.version);return g},i=(u,g)=>{for(let y=g.length-1;y>=0;y--){let p=g[y].get(u);if(p)return p}},o=(u,g)=>{if(g.length>Ke)throw new Error(`dependency nesting exceeds ${Ke} levels (malformed or malicious lockfile)`);let y=a(u),p=[...g,y];for(let[k,R]of Object.entries(u??{})){let S=R?.version;if(!S)continue;let O=w(k,S);r[O]||(r[O]={key:O,name:k,version:S,dependencies:[]});let P=[...p,a(R.dependencies)];for(let C of Object.keys(R.requires??{})){let D=i(C,P);if(D){let $=w(C,D);$!==O&&r[O].dependencies.push($)}}R.dependencies&&o(R.dependencies,p)}};o(e.dependencies,[]);let d=a(e.dependencies),c=[],f=[],m=[],l=(u,g)=>{for(let y of u){let p=d.get(y);p&&g.push(w(y,p))}};return l(Object.keys(t.dependencies),c),l(Object.keys(t.optionalDependencies),m),l(Object.keys(t.devDependencies),f),{manager:"npm",lockfilePath:n,lockfileVersion:e.lockfileVersion,root:s,nodes:r,directProd:c,directDev:f,directOptional:m,unscannable:[]}}import{readFileSync as et}from"fs";import{dirname as nt}from"path";import{createRequire as tt}from"module";import rt from"js-yaml";var st=tt(import.meta.url),ot=st("@yarnpkg/lockfile");function Ge(e){let n=et(e,"utf8"),t=M(nt(e)),s=n.includes("__metadata"),{descriptorToVersion:r,entryByVersion:a,lockfileVersion:i}=s?at(n):it(n);return ct(e,t,r,a,i,s)}function Be(e){let n=e.lastIndexOf("@");if(!(n<=0))return{name:e.slice(0,n),range:e.slice(n+1)}}function it(e){let n=ot.parse(e),t=new Map,s=new Map;for(let[r,a]of Object.entries(n.object))if(a.version)for(let i of r.split(/,\s*/)){let o=Be(i);o&&t.set(`${o.name}@${o.range}`,a.version),o&&s.set(w(o.name,a.version),a)}return{descriptorToVersion:t,entryByVersion:s,lockfileVersion:1}}function at(e){let n=rt.load(e)??{},t=new Map,s=new Map,r="berry";for(let[a,i]of Object.entries(n)){if(a==="__metadata"){let o=i.version;o!==void 0&&(r=o);continue}if(i.version)for(let o of a.split(/,\s*/)){let d=Be(o);d&&(t.set(`${d.name}@${d.range}`,i.version),s.set(w(d.name,i.version),i))}}return{descriptorToVersion:t,entryByVersion:s,lockfileVersion:r}}function ct(e,n,t,s,r,a){let i=(c,f)=>{let m=a?[`${c}@${f}`,`${c}@npm:${f}`]:[`${c}@${f}`];for(let l of m){let u=t.get(l);if(u)return u}},o=Object.create(null);for(let[c,f]of s){let m=c.lastIndexOf("@"),l=c.slice(0,m),u=c.slice(m+1);o[c]||(o[c]={key:c,name:l,version:u,dependencies:[]});let g=y=>{for(let[p,k]of Object.entries(y??{})){let R=i(p,k);if(!R)continue;let S=w(p,R);S!==c&&o[c].dependencies.push(S)}};g(f.dependencies),g(f.optionalDependencies)}let d=c=>{let f=[];for(let[m,l]of Object.entries(c)){let u=i(m,l);u&&f.push(w(m,u))}return f};return{manager:"yarn",lockfilePath:e,lockfileVersion:r,root:{name:n.name,version:n.version},nodes:o,directProd:d(n.dependencies),directDev:d(n.devDependencies),directOptional:d(n.optionalDependencies),unscannable:[]}}import{readFileSync as dt}from"fs";import{dirname as ft}from"path";import ut from"js-yaml";function Ye(e){let n=ut.load(dt(e,"utf8"))??{},t=M(ft(e)),s=lt(n.lockfileVersion),r=Object.create(null);for(let m of Object.keys(n.packages??{})){let l=ze(m,s);if(!l)continue;let u=w(l.name,l.version);r[u]||(r[u]={key:u,name:l.name,version:l.version,dependencies:[]})}let a=s>=9?n.snapshots??{}:n.packages??{};for(let[m,l]of Object.entries(a)){let u=ze(m,s);if(!u)continue;let g=w(u.name,u.version);r[g]||(r[g]={key:g,name:u.name,version:u.version,dependencies:[]});let y=p=>{for(let[k,R]of Object.entries(p??{})){let S=w(k,Y(R,s));S!==g&&r[S]&&r[g].dependencies.push(S)}};y(l.dependencies),y(l.optionalDependencies)}let i=n.importers??{".":{dependencies:n.dependencies,devDependencies:n.devDependencies,optionalDependencies:n.optionalDependencies}},o=new Set,d=new Set,c=new Set,f=(m,l)=>{for(let[u,g]of Object.entries(m??{})){let y=pt(g,s);if(!y)continue;let p=w(u,y);r[p]&&l.add(p)}};for(let m of Object.values(i))f(m.dependencies,o),f(m.optionalDependencies,c),f(m.devDependencies,d);return{manager:"pnpm",lockfilePath:e,lockfileVersion:n.lockfileVersion,root:{name:t.name,version:t.version},nodes:r,directProd:[...o],directDev:[...d],directOptional:[...c],unscannable:[]}}function lt(e){let n=parseFloat(String(e??"9"));return Number.isNaN(n)?9:Math.floor(n)}function ze(e,n){let t=e.startsWith("/")?e.slice(1):e;if(n<6){let i=t.lastIndexOf("/");if(i<=0)return;let o=t.slice(0,i),d=Y(t.slice(i+1),n);return d?{name:o,version:d}:void 0}let s=t.lastIndexOf("@");if(s<=0)return;let r=t.slice(0,s),a=Y(t.slice(s+1),n);return a?{name:r,version:a}:void 0}function Y(e,n){let t=e;if(n<6){let r=t.indexOf("_");r!==-1&&(t=t.slice(0,r))}let s=t.indexOf("(");return s!==-1&&(t=t.slice(0,s)),t}function pt(e,n){let t;if(typeof e=="string"?t=e:e&&typeof e=="object"&&typeof e.version=="string"&&(t=e.version),!!t&&!(t.startsWith("link:")||t.startsWith("file:")||t.startsWith("workspace:")))return Y(t,n)}var E=class extends Error{constructor(n){super(n),this.name="LockfileError"}};function yt(){let e=Number(process.env.LOCKHAWK_MAX_LOCKFILE_BYTES);return Number.isFinite(e)&&e>0?e:64*1024*1024}function We(e){let n=0;try{n=gt(e.path).size}catch{}let t=yt();if(n>t)throw new E(`${e.filename} is ${n} bytes, exceeding the ${t}-byte safety limit. Set LOCKHAWK_MAX_LOCKFILE_BYTES to raise it.`);try{switch(e.manager){case"npm":return Ue(e.path);case"yarn":return Ge(e.path);case"pnpm":return Ye(e.path)}}catch(s){throw new E(`Failed to parse ${e.filename}: ${s instanceof Error?s.message:String(s)}`)}}function le(e){let n=F(mt(e));if(!n)throw new E(`No lockfile found in ${e}. Expected one of: package-lock.json, yarn.lock, pnpm-lock.yaml.`);return ue(We(n))}function pe(e){return e.root.version?`${e.root.name}@${e.root.version}`:e.root.name}function ge(e,n){if(!e.nodes[n])return null;let t="\0root",s=new Map,r=new Set([t]),a=[t];for(let i=0;i<a.length;i++){let o=a[i],d=o===t?e.directKeys:e.nodes[o]?.dependencies??[];for(let c of d)if(!r.has(c)){if(r.add(c),s.set(c,o),c===n)return vt(e,s,n,t);a.push(c)}}return[pe(e),n]}function vt(e,n,t,s){let r=[],a=t;for(;a!==void 0&&a!==s;)r.unshift(a),a=n.get(a);return r.unshift(pe(e)),r}import I from"semver";function Je(e,n){if(n.versions?.includes(e))return!0;let t=I.parse(e,{loose:!0});if(!t)return!1;for(let s of n.ranges??[])if(s.type!=="GIT"&&ht(t,s.events))return!0;return!1}function ht(e,n){let t=[];for(let r of n)r.introduced!==void 0?t.push({kind:"introduced",version:r.introduced==="0"?null:me(r.introduced)}):r.fixed!==void 0?t.push({kind:"fixed",version:me(r.fixed)}):r.last_affected!==void 0&&t.push({kind:"last_affected",version:me(r.last_affected)});t.sort((r,a)=>r.version===null?a.version===null?0:-1:a.version===null?1:r.version.compare(a.version));let s=!1;for(let r of t){if(r.version===null){s=!0;continue}let a=e.compare(r.version);switch(r.kind){case"introduced":a>=0&&(s=!0);break;case"fixed":a>=0&&(s=!1);break;case"last_affected":a>0&&(s=!1);break}}return s}function me(e){return I.parse(e,{loose:!0})??I.coerce(e)}function W(e,n,t){if(t.withdrawn)return{affected:!1,fixedVersions:[]};let s=!1,r=new Set;for(let a of t.affected??[]){let i=a.package?.ecosystem;if(!(i&&i!=="npm")&&!(a.package?.name&&a.package.name!==e)&&Je(n,a)){s=!0;for(let o of a.ranges??[])for(let d of o.events)d.fixed&&r.add(d.fixed)}}return{affected:s,fixedVersions:[...r]}}function ye(e,n){let t=I.parse(e,{loose:!0});return n.map(r=>I.parse(r,{loose:!0})??I.coerce(r)).filter(r=>r!==null).filter(r=>t?r.compare(t)>0:!0).sort((r,a)=>r.compare(a))[0]?.version}function Xe(e){return[e.id,...e.aliases??[]]}function Ze(e){let n=[...e];return n.find(t=>t.startsWith("CVE-"))??n.find(t=>t.startsWith("GHSA-"))??n[0]}function ve(e){if(e.length<=1)return e;let n=new Map,t=a=>{let i=a;for(;n.get(i)!==void 0&&n.get(i)!==i;)i=n.get(i);return n.set(a,i),i},s=(a,i)=>{n.set(t(a),t(i))};for(let a of e){let i=Xe(a);n.has(i[0])||n.set(i[0],i[0]);for(let o of i.slice(1))n.has(o)||n.set(o,o),s(i[0],o)}let r=new Map;for(let a of e){let i=t(a.id),o=r.get(i);o?o.push(a):r.set(i,[a])}return[...r.values()].map(bt)}function bt(e){if(e.length===1)return e[0];let n=new Set,t=new Map,s=[],r=[],a,i,o;for(let c of e){for(let f of Xe(c))n.add(f);for(let f of c.references??[])t.set(f.url,f);for(let f of c.severity??[])s.push(f);for(let f of c.affected??[])r.push(f);a??=c.summary,i??=c.details,o??=c.database_specific}let d=Ze(n);return{id:d,aliases:[...n].filter(c=>c!==d),summary:a,details:i,references:[...t.values()],severity:s,affected:r,database_specific:o}}import kn from"semver";var J={1e5:9.8,100001:9.5,100010:9.4,100011:8.7,100020:9.1,100021:8.1,100100:9.4,100101:8.9,100110:8.6,100111:7.4,100120:7.7,100121:6.4,100200:8.7,100201:7.5,100210:7.4,100211:6.3,100220:6.3,100221:4.9,101e3:9.4,101001:8.9,101010:8.8,101011:7.7,101020:7.6,101021:6.7,101100:8.6,101101:7.6,101110:7.4,101111:5.8,101120:5.9,101121:5,101200:7.2,101201:5.7,101210:5.7,101211:5.2,101220:5.2,101221:2.5,102001:8.3,102011:7,102021:5.4,102101:6.5,102111:5.8,102121:2.6,102201:5.3,102211:2.1,102221:1.3,11e4:9.5,110001:9,110010:8.8,110011:7.6,110020:7.6,110021:7,110100:9,110101:7.7,110110:7.5,110111:6.2,110120:6.1,110121:5.3,110200:7.7,110201:6.6,110210:6.8,110211:5.9,110220:5.2,110221:3,111e3:8.9,111001:7.8,111010:7.6,111011:6.7,111020:6.2,111021:5.8,111100:7.4,111101:5.9,111110:5.7,111111:5.7,111120:4.7,111121:2.3,111200:6.1,111201:5.2,111210:5.7,111211:2.9,111220:2.4,111221:1.6,112001:7.1,112011:5.9,112021:3,112101:5.8,112111:2.6,112121:1.5,112201:2.3,112211:1.3,112221:.6,2e5:9.3,200001:8.7,200010:8.6,200011:7.2,200020:7.5,200021:5.8,200100:8.6,200101:7.4,200110:7.4,200111:6.1,200120:5.6,200121:3.4,200200:7,200201:5.4,200210:5.2,200211:4,200220:4,200221:2.2,201e3:8.5,201001:7.5,201010:7.4,201011:5.5,201020:6.2,201021:5.1,201100:7.2,201101:5.7,201110:5.5,201111:4.1,201120:4.6,201121:1.9,201200:5.3,201201:3.6,201210:3.4,201211:1.9,201220:1.9,201221:.8,202001:6.4,202011:5.1,202021:2,202101:4.7,202111:2.1,202121:1.1,202201:2.4,202211:.9,202221:.4,21e4:8.8,210001:7.5,210010:7.3,210011:5.3,210020:6,210021:5,210100:7.3,210101:5.5,210110:5.9,210111:4,210120:4.1,210121:2,210200:5.4,210201:4.3,210210:4.5,210211:2.2,210220:2,210221:1.1,211e3:7.5,211001:5.5,211010:5.8,211011:4.5,211020:4,211021:2.1,211100:6.1,211101:5.1,211110:4.8,211111:1.8,211120:2,211121:.9,211200:4.6,211201:1.8,211210:1.7,211211:.7,211220:.8,211221:.2,212001:5.3,212011:2.4,212021:1.4,212101:2.4,212111:1.2,212121:.5,212201:1,212211:.3,212221:.1,"000000":10,"000001":9.9,"000010":9.8,"000011":9.5,"000020":9.5,"000021":9.2,"000100":10,"000101":9.6,"000110":9.3,"000111":8.7,"000120":9.1,"000121":8.1,"000200":9.3,"000201":9,"000210":8.9,"000211":8,"000220":8.1,"000221":6.8,"001000":9.8,"001001":9.5,"001010":9.5,"001011":9.2,"001020":9,"001021":8.4,"001100":9.3,"001101":9.2,"001110":8.9,"001111":8.1,"001120":8.1,"001121":6.5,"001200":8.8,"001201":8,"001210":7.8,"001211":7,"001220":6.9,"001221":4.8,"002001":9.2,"002011":8.2,"002021":7.2,"002101":7.9,"002111":6.9,"002121":5,"002201":6.9,"002211":5.5,"002221":2.7,"010000":9.9,"010001":9.7,"010010":9.5,"010011":9.2,"010020":9.2,"010021":8.5,"010100":9.5,"010101":9.1,"010110":9,"010111":8.3,"010120":8.4,"010121":7.1,"010200":9.2,"010201":8.1,"010210":8.2,"010211":7.1,"010220":7.2,"010221":5.3,"011000":9.5,"011001":9.3,"011010":9.2,"011011":8.5,"011020":8.5,"011021":7.3,"011100":9.2,"011101":8.2,"011110":8,"011111":7.2,"011120":7,"011121":5.9,"011200":8.4,"011201":7,"011210":7.1,"011211":5.2,"011220":5,"011221":3,"012001":8.6,"012011":7.5,"012021":5.2,"012101":7.1,"012111":5.2,"012121":2.9,"012201":6.3,"012211":2.9,"012221":1.7},he={eq1:{0:["AV:N/PR:N/UI:N/"],1:["AV:A/PR:N/UI:N/","AV:N/PR:L/UI:N/","AV:N/PR:N/UI:P/"],2:["AV:P/PR:N/UI:N/","AV:A/PR:L/UI:P/"]},eq2:{0:["AC:L/AT:N/"],1:["AC:H/AT:N/","AC:L/AT:P/"]},eq3:{0:{0:["VC:H/VI:H/VA:H/CR:H/IR:H/AR:H/"],1:["VC:H/VI:H/VA:L/CR:M/IR:M/AR:H/","VC:H/VI:H/VA:H/CR:M/IR:M/AR:M/"]},1:{0:["VC:L/VI:H/VA:H/CR:H/IR:H/AR:H/","VC:H/VI:L/VA:H/CR:H/IR:H/AR:H/"],1:["VC:L/VI:H/VA:L/CR:H/IR:M/AR:H/","VC:L/VI:H/VA:H/CR:H/IR:M/AR:M/","VC:H/VI:L/VA:H/CR:M/IR:H/AR:M/","VC:H/VI:L/VA:L/CR:M/IR:H/AR:H/","VC:L/VI:L/VA:H/CR:H/IR:H/AR:M/"]},2:{1:["VC:L/VI:L/VA:L/CR:H/IR:H/AR:H/"]}},eq4:{0:["SC:H/SI:S/SA:S/"],1:["SC:H/SI:H/SA:H/"],2:["SC:L/SI:L/SA:L/"]},eq5:{0:["E:A/"],1:["E:P/"],2:["E:U/"]}},Qe={eq1:{0:1,1:4,2:5},eq2:{0:1,1:2},eq3eq6:{0:{0:7,1:6},1:{0:8,1:8},2:{1:10}},eq4:{0:6,1:5,2:4},eq5:{0:1,1:1,2:1}};var mn=["AV","AC","AT","PR","UI","VC","VI","VA","SC","SI","SA"],en={N:0,A:.1,L:.2,P:.3},nn={N:0,L:.1,H:.2},tn={N:0,P:.1,A:.2},rn={L:0,H:.1},sn={N:0,P:.1},on={H:0,L:.1,N:.2},an={H:0,L:.1,N:.2},cn={H:0,L:.1,N:.2},dn={H:.1,L:.2,N:.3},fn={S:0,H:.1,L:.2,N:.3},un={S:0,H:.1,L:.2,N:.3},ln={H:0,M:.1,L:.2},pn={H:0,M:.1,L:.2},gn={H:0,M:.1,L:.2};function yn(e){let n=kt(e);if(!n)return null;let t=Rt(n);return J[t]===void 0?null:St(n,t)}function kt(e){let n=e.split("/");if(n[0]!=="CVSS:4.0")return null;let t={};for(let s of n.slice(1)){let[r,a]=s.split(":");r&&a&&(t[r]=a)}for(let s of mn)if(!(s in t))return null;for(let s of["E","CR","IR","AR"])s in t||(t[s]="X");return t}function v(e,n){let t=e[n];if(n==="E"&&t==="X")return"A";if((n==="CR"||n==="IR"||n==="AR")&&t==="X")return"H";let s=e[`M${n}`];return s!==void 0&&s!=="X"?s:t??""}function Rt(e){let n=v(e,"AV"),t=v(e,"PR"),s=v(e,"UI"),r="2";n==="N"&&t==="N"&&s==="N"?r="0":(n==="N"||t==="N"||s==="N")&&n!=="P"&&(r="1");let a=v(e,"AC")==="L"&&v(e,"AT")==="N"?"0":"1",i=v(e,"VC"),o=v(e,"VI"),d=v(e,"VA"),c="2";i==="H"&&o==="H"?c="0":(i==="H"||o==="H"||d==="H")&&(c="1");let f=v(e,"MSI"),m=v(e,"MSA"),l="2";f==="S"||m==="S"?l="0":(v(e,"SC")==="H"||v(e,"SI")==="H"||v(e,"SA")==="H")&&(l="1");let u=v(e,"E"),g=u==="A"?"0":u==="P"?"1":"2",y=v(e,"CR"),p=v(e,"IR"),k=v(e,"AR");return`${r}${a}${c}${l}${g}${y==="H"&&i==="H"||p==="H"&&o==="H"||k==="H"&&d==="H"?"0":"1"}`}function h(e,n){return e[n]??0}function X(e,n){return he[`eq${n}`][e[n-1]]??[]}function V(e,n){let t=n.indexOf(e)+e.length+1,s=n.slice(t),r=s.indexOf("/");return r>0?s.substring(0,r):s}function St(e,n){if(mn.slice(5).every(b=>v(e,b)==="N"))return 0;let t=J[n],s=Number(n[0]),r=Number(n[2]),a=Number(n[5]),i=b=>J[b.join("")]??NaN,o=n.split("").map(Number),d=i([o[0]+1,o[1],o[2],o[3],o[4],o[5]]),c=i([o[0],o[1]+1,o[2],o[3],o[4],o[5]]),f;if(r===1&&a===1)f=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]]);else if(r===0&&a===1)f=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]]);else if(r===1&&a===0)f=i([o[0],o[1],o[2],o[3],o[4],o[5]+1]);else if(r===0&&a===0){let b=i([o[0],o[1],o[2],o[3],o[4],o[5]+1]),q=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]]);f=b>q?b:q}else f=i([o[0],o[1],o[2]+1,o[3],o[4],o[5]+1]);let m=i([o[0],o[1],o[2],o[3]+1,o[4],o[5]]),l=i([o[0],o[1],o[2],o[3],o[4]+1,o[5]]),u=X(n,1),g=X(n,2),y=he.eq3[n[2]][n[5]],p=X(n,4),k=X(n,5),R=[];for(let b of u)for(let q of g)for(let de of y)for(let Kn of p)for(let Un of k)R.push(b+q+de+Kn+Un);let S=0,O=0,P=0,C=0,D=0,$=0,ne=0,te=0,re=0,se=0,oe=0,ie=0,ae=0,ce=0;for(let b of R)if(S=h(en,v(e,"AV"))-h(en,V("AV",b)),O=h(nn,v(e,"PR"))-h(nn,V("PR",b)),P=h(tn,v(e,"UI"))-h(tn,V("UI",b)),C=h(rn,v(e,"AC"))-h(rn,V("AC",b)),D=h(sn,v(e,"AT"))-h(sn,V("AT",b)),$=h(on,v(e,"VC"))-h(on,V("VC",b)),ne=h(an,v(e,"VI"))-h(an,V("VI",b)),te=h(cn,v(e,"VA"))-h(cn,V("VA",b)),re=h(dn,v(e,"SC"))-h(dn,V("SC",b)),se=h(fn,v(e,"SI"))-h(fn,V("SI",b)),oe=h(un,v(e,"SA"))-h(un,V("SA",b)),ie=h(ln,v(e,"CR"))-h(ln,V("CR",b)),ae=h(pn,v(e,"IR"))-h(pn,V("IR",b)),ce=h(gn,v(e,"AR"))-h(gn,V("AR",b)),![S,O,P,C,D,$,ne,te,re,se,oe,ie,ae,ce].some(de=>de<0))break;let $n=S+O+P,En=C+D,In=$+ne+te+ie+ae+ce,Hn=re+se+oe,B=.1,De=t-d,$e=t-c,Ee=t-f,Ie=t-m,Ln=t-l,z=Qe,jn=z.eq1[String(s)]*B,_n=z.eq2[n[1]]*B,qn=z.eq3eq6[String(r)][String(a)]*B,Fn=z.eq4[n[3]]*B,A=0,He=0,Le=0,je=0,_e=0;Number.isNaN(De)||(A++,He=De*($n/jn)),Number.isNaN($e)||(A++,Le=$e*(En/_n)),Number.isNaN(Ee)||(A++,je=Ee*(In/qn)),Number.isNaN(Ie)||(A++,_e=Ie*(Hn/Fn)),Number.isNaN(Ln)||A++;let Tn=A===0?0:(He+Le+je+_e)/A;return t-=Tn,t<0&&(t=0),t>10&&(t=10),Math.round(t*10)/10}var xt={N:.85,A:.62,L:.55,P:.2},wt={L:.77,H:.44},Vt={N:.85,R:.62},Ot={N:.85,L:.62,H:.27},Pt={N:.85,L:.68,H:.5},be={H:.56,L:.22,N:0};function ke(e){return e<=0?"none":e<4?"low":e<7?"medium":e<9?"high":"critical"}function hn(e){switch(e.trim().toUpperCase()){case"CRITICAL":return"critical";case"HIGH":return"high";case"MODERATE":case"MEDIUM":return"medium";case"LOW":return"low";case"NONE":return"none";default:return"unknown"}}function vn(e){let n=Math.round(e*1e5);return n%1e4===0?n/1e5:(Math.floor(n/1e4)+1)/10}function bn(e){let n=e.split("/"),t=n[0]??"";if(t==="CVSS:4.0"){let p=yn(e);return p===null?null:{score:p,level:ke(p),version:t}}if(!/^CVSS:3\.[01]$/.test(t))return null;let s={};for(let p of n.slice(1)){let[k,R]=p.split(":");k&&R&&(s[k]=R)}let r=xt[s.AV??""],a=wt[s.AC??""],i=Vt[s.UI??""],o=s.S==="C",d=(o?Pt:Ot)[s.PR??""],c=be[s.C??""],f=be[s.I??""],m=be[s.A??""];if([r,a,i,d,c,f,m].some(p=>p===void 0))return null;let l=1-(1-c)*(1-f)*(1-m),u=o?7.52*(l-.029)-3.25*Math.pow(l-.02,15):6.42*l,g=8.22*r*a*d*i,y;return u<=0?y=0:o?y=vn(Math.min(1.08*(u+g),10)):y=vn(Math.min(u+g,10)),{score:y,level:ke(y),version:t}}function Re(e){let n=[];for(let r of e.severity??[])r.score&&n.push(r.score);for(let r of e.affected??[])for(let a of r.severity??[])a.score&&n.push(a.score);let t=null;for(let r of n){let a=bn(r);a&&(!t||a.score>t.score)&&(t=a)}if(t)return{level:t.level,score:t.score,vector:At(t,n),cvssVersion:t.version,source:"cvss"};let s=Nt(e);return s?{level:hn(s),source:"database"}:{level:"unknown",source:"unknown"}}function At(e,n){return n.find(t=>t.startsWith(e.version))}function Nt(e){let n=s=>{let r=s?.severity;return typeof r=="string"?r:void 0},t=n(e.database_specific);if(t)return t;for(let s of e.affected??[]){let r=n(s.database_specific);if(r)return r}}function Se(e,n,t={}){let s=[];for(let r of Object.values(e.nodes)){if(r.unscannable||t.prodOnly&&r.scope==="dev")continue;let a=n(r.name);if(a.length===0)continue;let i=new Map,o=a.filter(c=>{let f=W(r.name,r.version,c);return i.set(c,f),f.affected});if(o.length===0)continue;let d=ge(e,r.key);for(let c of ve(o)){if(Mt(c,t.ignore))continue;let{fixedVersions:f}=i.get(c)??W(r.name,r.version,c),m=Ct(f),l=ye(r.version,f);s.push({id:c.id,aliases:c.aliases??[],packageName:r.name,version:r.version,scope:r.scope,direct:r.direct,severity:Re(c),summary:c.summary??c.id,details:c.details,references:(c.references??[]).map(u=>u.url),fixedVersions:m,recommendation:$t(r.name,r.direct,l,e.manager),dependencyPaths:d?[d]:[],source:"osv"})}}return s}function Mt(e,n){return!n||n.size===0?!1:n.has(e.id)?!0:(e.aliases??[]).some(t=>n.has(t))}function Ct(e){return[...new Set(e)].sort((n,t)=>{let s=kn.parse(n,{loose:!0}),r=kn.parse(t,{loose:!0});return s&&r?s.compare(r):n.localeCompare(t)})}var Dt={npm:"npm install",yarn:"yarn add",pnpm:"pnpm add"};function $t(e,n,t,s){return t?n?`Upgrade to ${e}@${t} or later (e.g. \`${Dt[s]} ${e}@${t}\`).`:`Update the dependency that pulls in ${e} so it resolves to ${e}@${t} or later. If it cannot, force the resolution via an override.`:n?"No fixed version is available yet. Watch the advisory, or evaluate an alternative package.":"No fixed version is available yet. Pressure the parent dependency to update, or override the version."}var xe=class{byName=new Map;ids=new Set;add(n){if(n.withdrawn||this.ids.has(n.id))return;this.ids.add(n.id);let t=new Set;for(let s of n.affected??[]){let r=s.package?.ecosystem;r&&r!=="npm"||s.package?.name&&t.add(s.package.name)}for(let s of t){let r=this.byName.get(s);r?r.push(n):this.byName.set(s,[n])}}addAll(n){for(let t of n)this.add(t)}vulnerabilitiesFor(n){return this.byName.get(n)??[]}get size(){return this.ids.size}};import{resolve as cr}from"path";import{join as we}from"path";import Et from"env-paths";function Ve(e){return e||(process.env.LOCKHAWK_CACHE?process.env.LOCKHAWK_CACHE:Et("lockhawk",{suffix:""}).cache)}function T(e){return we(e,"osv-db","npm")}var Rn=4096;function Z(e){let n=2166136261;for(let t=0;t<e.length;t++)n^=e.charCodeAt(t),n=Math.imul(n,16777619);return(n>>>0)%Rn}function K(e){return we(T(e),"meta.json")}function Oe(e,n){return we(e,"query-cache",`${encodeURIComponent(n)}.json`)}import{mkdir as It,readFile as Ht,writeFile as Lt}from"fs/promises";import{dirname as jt}from"path";import _t from"p-limit";import qt,{AbortError as Ft}from"p-retry";var Pe="https://api.osv.dev/v1",Sn=1e3,U=class{constructor(n){this.opts=n;this.limit=_t(n.concurrency??16)}opts;limit;async fetchAdvisories(n){let t=[...new Set(n.map(o=>o.name))],s=await this.queryBatch(t),r=new Set;for(let o of s.values())for(let d of o)r.add(d);let a=await this.hydrate([...r]),i=new Map;for(let[o,d]of s){let c=[];for(let f of d){let m=a.get(f);m&&c.push(m)}c.length&&i.set(o,c)}return i}async queryBatch(n){let t=new Map;for(let s=0;s<n.length;s+=Sn){let r=n.slice(s,s+Sn),a={queries:r.map(o=>({package:{name:o,ecosystem:"npm"}}))},i=await this.request(`${Pe}/querybatch`,a);for(let o=0;o<r.length;o++){let d=r[o],c=i.results[o];if(!c)continue;let f=t.get(d)??new Set;for(let l of c.vulns??[])f.add(l.id);let m=c.next_page_token;for(;m;){let l=await this.request(`${Pe}/query`,{package:{name:d,ecosystem:"npm"},page_token:m});for(let u of l.vulns??[])f.add(u.id);m=l.next_page_token}f.size&&t.set(d,f)}}return t}async hydrate(n){let t=new Map;return await Promise.all(n.map(s=>this.limit(async()=>{let r=await this.getVulnerability(s);r&&t.set(s,r)}))),t}async getVulnerability(n){let t=await this.readCache(n);if(t)return t;let s=await this.request(`${Pe}/vulns/${encodeURIComponent(n)}`);return await this.writeCache(n,s),s}async request(n,t){let s=this.opts.retries??3;return qt(async()=>{let r=await fetch(n,{method:t?"POST":"GET",headers:t?{"content-type":"application/json"}:void 0,body:t?JSON.stringify(t):void 0,signal:AbortSignal.timeout(this.opts.timeoutMs??15e3)});if(r.ok)return await r.json();throw r.status===429||r.status>=500?new Error(`OSV ${r.status} for ${n}`):new Ft(`OSV ${r.status} for ${n}`)},{retries:s})}async readCache(n){if(!this.opts.noCache)try{let t=JSON.parse(await Ht(Oe(this.opts.cacheDir,n),"utf8")),s=(this.opts.cacheTtlHours??24)*36e5;return Date.now()-t.fetchedAt>s?void 0:t.vuln}catch{return}}async writeCache(n,t){if(!this.opts.noCache)try{let s=Oe(this.opts.cacheDir,n);await It(jt(s),{recursive:!0}),await Lt(s,JSON.stringify({fetchedAt:Date.now(),vuln:t}))}catch{}}};import{createWriteStream as Tt}from"fs";import{mkdir as xn,mkdtemp as Kt,readFile as wn,rm as Vn,writeFile as Ae}from"fs/promises";import{tmpdir as Ut}from"os";import{join as Q}from"path";import{Readable as Gt}from"stream";import{pipeline as Bt}from"stream/promises";import{gunzipSync as zt,gzipSync as Yt}from"zlib";import Wt from"p-limit";import Jt from"yauzl";var Xt="https://storage.googleapis.com/osv-vulnerabilities/npm/all.zip",Zt=32,Qt=8*1024*1024,H=class extends Error{constructor(){super("No offline OSV database found. Run `lockhawk db update` first."),this.name="OfflineDbMissingError"}},er=/^[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/,nr=/^(?:W\/)?"[\x21\x23-\x7e]*"$/;function tr(e){return e&&er.test(e)?e:void 0}function rr(e){return e&&nr.test(e)?e:void 0}async function L(e){try{return JSON.parse(await wn(K(e),"utf8"))}catch{return}}async function sr(e){let{cacheDir:n,nowIso:t,force:s,onProgress:r}=e,a=T(n);await xn(a,{recursive:!0});let i=s?void 0:await L(n),o={},d=rr(i?.etag),c=tr(i?.lastModified);d&&(o["If-None-Match"]=d),c&&(o["If-Modified-Since"]=c),r?.("Fetching npm advisory database from OSV.dev\u2026");let f=await fetch(Xt,{headers:o});if(f.status===304&&i){let p={...i,lastUpdated:t};return await Ae(K(n),JSON.stringify(p,null,2)),{status:"unchanged",meta:p}}if(!f.ok||!f.body)throw new Error(`OSV database download failed: HTTP ${f.status}`);let m=await Kt(Q(Ut(),"lockhawk-osv-")),l=Q(m,"all.zip"),u;try{await Bt(Gt.fromWeb(f.body),Tt(l)),r?.("Extracting advisories\u2026"),u=await ar(l)}finally{await Vn(m,{recursive:!0,force:!0})}r?.("Building package index\u2026");let g=or(u);await ir(n,g);let y={ecosystem:"npm",source:"osv.dev",lastUpdated:t,recordCount:u.length,packageCount:g.size,etag:f.headers.get("etag")??void 0,lastModified:f.headers.get("last-modified")??void 0};return await Ae(K(n),JSON.stringify(y,null,2)),{status:"updated",meta:y}}function On(e){return Q(T(e),"by-name")}function Pn(e,n){return Q(On(e),`${n}.json.gz`)}async function Ne(e,n){if(!await L(e))throw new H;let t=new Map;for(let a of new Set(n)){let i=Z(a),o=t.get(i);o?o.push(a):t.set(i,[a])}let s=new Map,r=Wt(Zt);return await Promise.all([...t.entries()].map(([a,i])=>r(async()=>{try{let o=JSON.parse(zt(await wn(Pn(e,a))).toString("utf8"));for(let d of i){let c=o[d];c?.length&&s.set(d,c)}}catch{}}))),s}function or(e){let n=new Map;for(let t of e){if(t.withdrawn)continue;let s=new Set;for(let r of t.affected??[]){let a=r.package?.ecosystem;a&&a!=="npm"||r.package?.name&&s.add(r.package.name)}for(let r of s){let a=n.get(r);a?a.push(t):n.set(r,[t])}}return n}async function ir(e,n){let t=On(e);await Vn(t,{recursive:!0,force:!0}),await xn(t,{recursive:!0});let s=new Map;for(let[i,o]of n){let d=Z(i),c=s.get(d)??{};c[i]=o,s.set(d,c)}let r=[...s.entries()],a=256;for(let i=0;i<r.length;i+=a)await Promise.all(r.slice(i,i+a).map(([o,d])=>Ae(Pn(e,o),Yt(Buffer.from(JSON.stringify(d))))))}function ar(e){return new Promise((n,t)=>{let s=[];Jt.open(e,{lazyEntries:!0},(r,a)=>{if(r||!a)return t(r??new Error("Failed to open OSV zip"));a.on("entry",i=>{if(!i.fileName.endsWith(".json")||i.uncompressedSize>Qt)return a.readEntry();a.openReadStream(i,(o,d)=>{if(o||!d)return a.readEntry();let c=[];d.on("data",f=>c.push(f)),d.on("error",()=>a.readEntry()),d.on("end",()=>{try{s.push(JSON.parse(Buffer.concat(c).toString("utf8")))}catch{}a.readEntry()})})}),a.on("end",()=>n(s)),a.on("error",t),a.readEntry()})})}var An=36e5,j=class{constructor(n){this.opts=n}opts;async prepare(n){let t=await L(this.opts.cacheDir);if(!t)throw new H;let s=await Ne(this.opts.cacheDir,n.map(o=>o.name)),r=(Date.now()-Date.parse(t.lastUpdated))/An,a=r>(this.opts.staleAfterHours??24),i=a?[`Offline database is ${Math.round(r)}h old. Run \`lockhawk db update\` for current results.`]:[];return{candidatesFor:o=>s.get(o)??[],database:{source:"offline",recordCount:t.recordCount,lastUpdated:t.lastUpdated,ageHours:Math.round(r*10)/10,stale:a,warnings:i}}}},G=class{constructor(n){this.opts=n}opts;async prepare(n){let t=new U({cacheDir:this.opts.cacheDir,concurrency:this.opts.concurrency,noCache:this.opts.noCache,cacheTtlHours:this.opts.cacheTtlHours});try{let s=await t.fetchAdvisories(n);return{candidatesFor:r=>s.get(r)??[],database:{source:"online",stale:!1,warnings:[]}}}catch(s){if(this.opts.strictNetwork)throw s;let r=s instanceof Error?s.message:String(s);return{candidatesFor:()=>[],database:{source:"online",stale:!0,warnings:[`OSV.dev was unreachable (${r}). Scan completed without online data \u2014 results may be incomplete.`]}}}}},ee=class{constructor(n){this.opts=n}opts;async prepare(n){let t=await L(this.opts.cacheDir),s=this.opts.staleAfterHours??24;if(t&&(Date.now()-Date.parse(t.lastUpdated))/An<=s)return new j(this.opts).prepare(n);try{return await new G({...this.opts,strictNetwork:!0}).prepare(n)}catch(r){if(t){let i=await new j(this.opts).prepare(n);return i.database.warnings.push("Used the stale offline database because OSV.dev was unreachable."),i}if(this.opts.strictNetwork)throw r;let a=r instanceof Error?r.message:String(r);return{candidatesFor:()=>[],database:{source:"online",stale:!0,warnings:[`No offline database and OSV.dev was unreachable (${a}). Run \`lockhawk db update\`.`]}}}}};function Me(e,n){switch(e){case"offline":return new j(n);case"online":return new G(n);default:return new ee(n)}}var Nn={name:"lockhawk",version:"0.1.0"};async function dr(e={},n){let t=Date.now(),s=cr(e.path??"."),r=le(s),a=F(s),i=Mn(r,e.prodOnly),o=Ve(e.cacheDir),d=n??Me(e.mode??"auto",{cacheDir:o,concurrency:e.concurrency,noCache:e.noCache,cacheTtlHours:e.cacheTtlHours,strictNetwork:e.strictNetwork});e.onProgress?.("Checking dependencies against OSV.dev\u2026");let c=await d.prepare(i),f=Se(r,c.candidatesFor,{prodOnly:e.prodOnly,ignore:e.ignore&&e.ignore.length?new Set(e.ignore):void 0});e.severityThreshold&&e.severityThreshold!=="none"&&(f=f.filter(l=>qe(l.severity.level,e.severityThreshold))),f.sort(lr);let m=fr(r);return{schemaVersion:1,tool:{...Nn},scannedAt:new Date(t).toISOString(),target:{path:s,manager:r.manager,lockfile:a?.filename??"",root:r.root},database:c.database,summary:ur(f),stats:{totalPackages:Object.keys(r.nodes).length,uniquePackages:i.length,directDependencies:r.directKeys.length,unscannable:m.length,durationMs:Date.now()-t},findings:f,unscannable:m}}function Mn(e,n){let t=new Set,s=[];for(let r of Object.values(e.nodes))r.unscannable||n&&r.scope==="dev"||t.has(r.key)||(t.add(r.key),s.push({name:r.name,version:r.version}));return s}function fr(e){let n=[];for(let t of Object.values(e.nodes))t.unscannable&&n.push({name:t.name,version:t.version,reason:t.unscannable.reason});return n}function ur(e){let n={total:e.length,critical:0,high:0,medium:0,low:0,unknown:0,none:0,vulnerablePackages:0,fixable:0},t=new Set;for(let s of e)n[s.severity.level]+=1,t.add(`${s.packageName}@${s.version}`),s.fixedVersions.length>0&&(n.fixable+=1);return n.vulnerablePackages=t.size,n}function lr(e,n){let t=N[n.severity.level]-N[e.severity.level];return t!==0?t:e.packageName.localeCompare(n.packageName)}function pr(e,n){return["critical","high","medium","low","unknown"].some(s=>N[s]>=N[n]&&e[s]>0)}function gr(e){return JSON.stringify(e,null,2)}var mr="https://github.com/lockhawk/lockhawk";function yr(e){return e.find(n=>{try{let t=new URL(n);return t.protocol==="http:"||t.protocol==="https:"}catch{return!1}})}function vr(e){if(typeof e.severity.score=="number")return e.severity.score;switch(e.severity.level){case"critical":return 9.5;case"high":return 7.5;case"medium":return 5;case"low":return 2;default:return 0}}function hr(e){return e==="critical"||e==="high"?"error":e==="medium"?"warning":"note"}function Cn(e){let n=e.target.lockfile||"package-lock.json",t=new Map,s=[];for(let r of e.findings){t.has(r.id)||t.set(r.id,{id:r.id,name:`${r.packageName} ${r.id}`,shortDescription:{text:r.summary},fullDescription:r.details?{text:r.details}:void 0,helpUri:yr(r.references),properties:{"security-severity":String(vr(r)),tags:["security","dependency",r.severity.level]}});let a=r.dependencyPaths[0]?.join(" \u203A ")??r.packageName,i=r.fixedVersions[0]?` Fixed in ${r.fixedVersions[0]}.`:"";s.push({ruleId:r.id,level:hr(r.severity.level),message:{text:`${r.severity.level.toUpperCase()}: ${r.packageName}@${r.version} \u2014 ${r.summary}.${i} Path: ${a}.`},locations:[{physicalLocation:{artifactLocation:{uri:n},region:{startLine:1}}}],partialFingerprints:{lockhawkFinding:`${r.packageName}@${r.version}@${r.id}`},properties:{package:r.packageName,version:r.version,scope:r.scope,fixedVersions:r.fixedVersions}})}return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:e.tool.name,version:e.tool.version,informationUri:mr,rules:[...t.values()]}},results:s,properties:{scannedAt:e.scannedAt,databaseSource:e.database.source,databaseAgeHours:e.database.ageHours,databaseStale:e.database.stale}}]}}function br(e){return JSON.stringify(Cn(e),null,2)}var Ce="<!--LOCKHAWK_DATA-->";function kr(e){return`<script>window.__SCAN_RESULT__ = ${JSON.stringify(e).replace(/</g,"\\u003c").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")};</script>`}function Rr(e,n){let t=kr(e);return n?n.includes(Ce)?n.replace(Ce,()=>t):n.replace("</head>",()=>`${t}</head>`):xr(e,t)}var Dn={critical:"#b4232c",high:"#d9822b",medium:"#c9a227",low:"#3a7bd5",unknown:"#7a7a7a",none:"#3a9e54"};function x(e){return e.replace(/[&<>"]/g,n=>({"&":"&","<":"<",">":">",'"':"""})[n])}function Sr(e){try{let n=new URL(e);return n.protocol==="http:"||n.protocol==="https:"?e:void 0}catch{return}}function xr(e,n){let{summary:t}=e,s=["critical","high","medium","low","unknown"].map(i=>`
|
|
2
|
+
<div class="card" style="border-top:4px solid ${Dn[i]}">
|
|
3
|
+
<div class="count">${t[i]}</div><div class="label">${i}</div>
|
|
4
|
+
</div>`).join(""),r=e.findings.map(i=>{let o=i.dependencyPaths[0]?.join(" \u203A ")??i.packageName,d=i.references.slice(0,3).map(c=>Sr(c)).filter(c=>c!==void 0).map(c=>`<a href="${x(c)}" target="_blank" rel="noopener">link</a>`).join(" ");return`
|
|
5
|
+
<tr>
|
|
6
|
+
<td><span class="pill" style="background:${Dn[i.severity.level]}">${i.severity.level}${i.severity.score?` ${i.severity.score}`:""}</span></td>
|
|
7
|
+
<td><code>${x(i.packageName)}@${x(i.version)}</code><div class="scope">${i.scope}${i.direct?" \xB7 direct":""}</div></td>
|
|
8
|
+
<td><a href="https://osv.dev/${x(i.id)}" target="_blank" rel="noopener">${x(i.id)}</a></td>
|
|
9
|
+
<td>${x(i.summary)}<div class="path">${x(o)}</div></td>
|
|
10
|
+
<td>${i.fixedVersions[0]?x(i.fixedVersions[0]):"\u2014"}</td>
|
|
11
|
+
<td>${d}</td>
|
|
12
|
+
</tr>`}).join(""),a=e.database.warnings.map(i=>`<div class="warn">\u26A0 ${x(i)}</div>`).join("");return`<!doctype html>
|
|
44
13
|
<html lang="en">
|
|
45
14
|
<head>
|
|
46
15
|
<meta charset="utf-8">
|
|
47
16
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
48
|
-
<title>lockhawk report \u2014 ${
|
|
17
|
+
<title>lockhawk report \u2014 ${x(e.target.root.name)}</title>
|
|
18
|
+
${n}
|
|
49
19
|
<style>
|
|
50
|
-
:root {
|
|
51
|
-
--bg:#070a12; --panel:#11151e; --panel-2:#141926; --elev:#171d2b;
|
|
52
|
-
--border:#222a3a; --border-soft:#1b2230;
|
|
53
|
-
--text:#f1f4fa; --muted:#aeb8cc; --faint:#808b9f;
|
|
54
|
-
--accent:#ffc266; --fix:#48e0a6;
|
|
55
|
-
--glass: rgba(255,255,255,.055); --glass-2: rgba(255,255,255,.09);
|
|
56
|
-
--glass-line: rgba(255,255,255,.14); --glass-hi: inset 0 1px 0 rgba(255,255,255,.10);
|
|
57
|
-
--glass-dark: rgba(8,11,18,.42);
|
|
58
|
-
--blur: blur(18px) saturate(160%);
|
|
59
|
-
--mono: ui-monospace,'SF Mono','JetBrains Mono','Cascadia Code','Fira Code',Menlo,Consolas,monospace;
|
|
60
|
-
--sans: -apple-system,BlinkMacSystemFont,'SF Pro Text','Segoe UI',system-ui,Roboto,sans-serif;
|
|
61
|
-
color-scheme: dark;
|
|
62
|
-
}
|
|
20
|
+
:root { color-scheme: light dark; }
|
|
63
21
|
* { box-sizing: border-box; }
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
background-image: linear-gradient(rgba(255,255,255,.025) 1px,transparent 1px), linear-gradient(90deg,rgba(255,255,255,.025) 1px,transparent 1px);
|
|
84
|
-
background-size: 34px 34px; -webkit-mask: linear-gradient(180deg,#000,transparent); mask: linear-gradient(180deg,#000,transparent); }
|
|
85
|
-
header > * { position: relative; }
|
|
86
|
-
.brand { display:flex; align-items:center; gap:14px; }
|
|
87
|
-
.mark { width:38px; height:38px; flex:none; filter: drop-shadow(0 3px 12px rgba(255,93,103,.3)); }
|
|
88
|
-
.wordmark { font-family: var(--mono); font-size: clamp(22px,4vw,27px); letter-spacing:-.02em; display:flex; align-items:center; }
|
|
89
|
-
.wordmark .lock { color: var(--muted); font-weight:500; }
|
|
90
|
-
.wordmark .hawk { color: var(--text); font-weight:700; }
|
|
91
|
-
.caret { display:inline-block; width:9px; height:1em; margin-left:5px; background: var(--accent); border-radius:1px; box-shadow:0 0 12px var(--accent); animation: blink 1.2s steps(1) infinite; }
|
|
92
|
-
.meta { margin-top:12px; color: var(--muted); font:12.5px/1.7 var(--mono); letter-spacing:.01em; overflow-wrap:anywhere; }
|
|
93
|
-
.meta b { color: var(--text); font-weight:600; }
|
|
94
|
-
|
|
95
|
-
/* ---- Layout ----------------------------------------------------------- */
|
|
96
|
-
main { max-width: 1120px; margin: 0 auto; padding: 26px clamp(16px,4vw,32px) 48px; }
|
|
97
|
-
.cards { display:grid; grid-template-columns: repeat(auto-fit, minmax(116px,1fr)); gap:14px; }
|
|
98
|
-
.card { position:relative; overflow:hidden; background: linear-gradient(180deg, var(--glass-2), var(--glass)); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); border:1px solid var(--glass-line); border-radius:16px; padding:16px 18px; box-shadow: 0 12px 36px -18px rgba(0,0,0,.75), var(--glass-hi); animation: rise .55s both cubic-bezier(.2,.7,.2,1); animation-delay: calc(var(--i)*55ms); }
|
|
99
|
-
.card::before { content:''; position:absolute; top:0; left:0; right:0; height:3px; background:var(--c); box-shadow:0 0 16px var(--c); }
|
|
100
|
-
.card .count { font:700 32px/1 var(--mono); font-variant-numeric: tabular-nums; letter-spacing:-.03em; color:var(--c); text-shadow: 0 0 24px color-mix(in srgb, var(--c) 45%, transparent); }
|
|
101
|
-
.card .label { margin-top:7px; color:var(--muted); font-size:11.5px; text-transform:uppercase; letter-spacing:.09em; }
|
|
102
|
-
.card.zero .count { color: var(--faint); text-shadow:none; }
|
|
103
|
-
.card.zero::before { box-shadow:none; opacity:.45; }
|
|
104
|
-
|
|
105
|
-
.warn { display:flex; gap:9px; align-items:flex-start; background: rgba(255,210,63,.1); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border:1px solid rgba(255,210,63,.32); color:#ffe6a6; padding:10px 14px; border-radius:12px; margin-top:14px; font-size:13px; overflow-wrap:anywhere; }
|
|
106
|
-
.warn-i { color: var(--accent); }
|
|
107
|
-
|
|
108
|
-
.results-meta { display:flex; gap:14px; flex-wrap:wrap; align-items:center; margin:26px 2px 0; color:var(--muted); font:12px/1 var(--mono); letter-spacing:.04em; }
|
|
109
|
-
.results-meta b { color:var(--text); font-weight:600; }
|
|
110
|
-
.results-meta .sep { color:var(--faint); }
|
|
111
|
-
|
|
112
|
-
/* ---- Table ------------------------------------------------------------ */
|
|
113
|
-
.table-wrap { margin-top:14px; border:1px solid var(--glass-line); border-radius:18px; overflow:hidden; background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.025)); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); box-shadow: 0 22px 60px -32px rgba(0,0,0,.85), var(--glass-hi); }
|
|
114
|
-
.scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }
|
|
115
|
-
table { width:100%; border-collapse:collapse; }
|
|
116
|
-
thead th { text-align:left; background: rgba(255,255,255,.05); color:var(--muted); font:600 11px/1 var(--mono); text-transform:uppercase; letter-spacing:.08em; padding:14px 16px; border-bottom:1px solid var(--glass-line); white-space:nowrap; }
|
|
117
|
-
tbody td { padding:15px 16px; border-bottom:1px solid rgba(255,255,255,.07); vertical-align:top; font-size:13.5px; overflow-wrap:anywhere; word-break:break-word; }
|
|
118
|
-
tbody tr.main { animation: rise .5s both ease; animation-delay: calc(var(--i)*30ms + 120ms); }
|
|
119
|
-
tbody tr.main:hover { background: rgba(255,255,255,.05); }
|
|
120
|
-
|
|
121
|
-
.pill { display:inline-flex; align-items:center; gap:7px; font:600 11.5px/1 var(--mono); padding:6px 10px; border-radius:8px; white-space:nowrap; text-transform:capitalize; color:var(--c); background: color-mix(in srgb, var(--c) 15%, transparent); border:1px solid color-mix(in srgb, var(--c) 34%, transparent); }
|
|
122
|
-
.pill::before { content:''; width:7px; height:7px; border-radius:50%; background:var(--c); box-shadow:0 0 9px var(--c); flex:none; }
|
|
123
|
-
|
|
124
|
-
.pkg { display:inline-block; font-family:var(--mono); font-size:12.5px; color:var(--text); background: rgba(255,255,255,.07); border:1px solid var(--glass-line); padding:4px 9px; border-radius:8px; }
|
|
125
|
-
.scope { margin-top:7px; color:var(--faint); font:11px/1 var(--mono); text-transform:uppercase; letter-spacing:.06em; }
|
|
126
|
-
.advid { font-family:var(--mono); font-size:12.5px; color:var(--accent); text-decoration:none; border-bottom:1px solid transparent; }
|
|
127
|
-
.advid:hover { border-bottom-color: color-mix(in srgb, var(--accent) 60%, transparent); }
|
|
128
|
-
.summary { color:var(--text); }
|
|
129
|
-
.path { margin-top:7px; color:var(--faint); font:11.5px/1.5 var(--mono); }
|
|
130
|
-
|
|
131
|
-
.fixchip { display:inline-block; font-family:var(--mono); font-size:12.5px; color:var(--fix); background: color-mix(in srgb, var(--fix) 13%, transparent); border:1px solid color-mix(in srgb, var(--fix) 30%, transparent); padding:4px 10px; border-radius:8px; cursor:copy; white-space:nowrap; user-select:all; -webkit-user-select:all; transition: background .15s ease; }
|
|
132
|
-
.fixchip:hover { background: color-mix(in srgb, var(--fix) 22%, transparent); }
|
|
133
|
-
.nofix { color:var(--faint); }
|
|
134
|
-
.refs { white-space:nowrap; }
|
|
135
|
-
.ref { display:inline-block; font:11.5px/1 var(--mono); color:var(--muted); text-decoration:none; padding:4px 8px; border:1px solid var(--glass-line); border-radius:7px; margin:0 4px 4px 0; background: rgba(255,255,255,.04); transition: color .15s, border-color .15s, background .15s; }
|
|
136
|
-
.ref:hover { color:var(--text); border-color: rgba(255,255,255,.28); background: rgba(255,255,255,.08); }
|
|
137
|
-
|
|
138
|
-
/* ---- Disclosure / advisory ------------------------------------------- */
|
|
139
|
-
.detail-row td { padding:0 16px 4px; background: rgba(0,0,0,.16); border-bottom:1px solid rgba(255,255,255,.07); }
|
|
140
|
-
details { margin:0; }
|
|
141
|
-
summary { list-style:none; cursor:pointer; display:inline-flex; align-items:center; gap:9px; color:var(--muted); font:600 11px/1 var(--mono); letter-spacing:.08em; text-transform:uppercase; padding:13px 0; user-select:none; transition: color .15s ease; }
|
|
142
|
-
summary::-webkit-details-marker { display:none; }
|
|
143
|
-
summary:hover { color:var(--text); }
|
|
144
|
-
summary .chev { color:var(--accent); transition: transform .25s cubic-bezier(.2,.7,.2,1); flex:none; }
|
|
145
|
-
details[open] summary .chev { transform: rotate(90deg); }
|
|
146
|
-
.detail-body { padding-bottom:18px; animation: fade .3s both ease; }
|
|
147
|
-
|
|
148
|
-
.fix { border:1px solid color-mix(in srgb, var(--fix) 28%, transparent); background: linear-gradient(180deg, color-mix(in srgb, var(--fix) 12%, transparent), rgba(255,255,255,.02)); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border-radius:13px; padding:13px 15px; margin:12px 0; box-shadow: var(--glass-hi); }
|
|
149
|
-
.fix-label { display:flex; align-items:center; gap:7px; font:700 10px/1 var(--mono); letter-spacing:.12em; text-transform:uppercase; color:var(--fix); margin-bottom:9px; }
|
|
150
|
-
.fix-label .spark { color:var(--fix); }
|
|
151
|
-
.fix-pending { border-color: color-mix(in srgb, var(--accent) 32%, transparent); background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 11%, transparent), rgba(255,255,255,.02)); }
|
|
152
|
-
.fix-pending .fix-label, .fix-pending .spark { color: var(--accent); }
|
|
153
|
-
.fix-text { margin:0; color:var(--text); font-size:13px; }
|
|
154
|
-
.fix-text + .cmdbar { margin-top:11px; }
|
|
155
|
-
.cmdbar { display:flex; align-items:center; gap:14px; flex-wrap:wrap; background: rgba(4,7,12,.55); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); border:1px solid var(--glass-line); border-radius:11px; padding:11px 14px; transition: border-color .18s ease, background .18s ease; }
|
|
156
|
-
.cmdbar:hover { border-color: color-mix(in srgb, var(--fix) 55%, transparent); background: rgba(4,7,12,.68); }
|
|
157
|
-
.cmdbar .cmd { font-family:var(--mono); font-size:13px; color:#d7f7e8; cursor:copy; white-space:pre-wrap; word-break:break-all; user-select:all; -webkit-user-select:all; }
|
|
158
|
-
.cmdbar .cmd::before { content:'$\\00a0\\00a0'; color:var(--fix); user-select:none; -webkit-user-select:none; }
|
|
159
|
-
.cmdbar .hint { margin-left:auto; font:11px/1 var(--sans); color:var(--muted); white-space:nowrap; }
|
|
160
|
-
|
|
161
|
-
.advisory-wrap { border:1px solid var(--glass-line); border-radius:13px; overflow:hidden; background: var(--glass-dark); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: var(--glass-hi); }
|
|
162
|
-
.advisory-head { display:flex; align-items:center; gap:11px; padding:10px 15px; background: rgba(255,255,255,.05); border-bottom:1px solid var(--glass-line); font:600 10.5px/1 var(--mono); letter-spacing:.07em; text-transform:uppercase; color:var(--muted); }
|
|
163
|
-
.dots { display:flex; gap:6px; }
|
|
164
|
-
.dots i { width:9px; height:9px; border-radius:50%; background: rgba(255,255,255,.18); }
|
|
165
|
-
pre.advisory { margin:0; padding:15px 16px; white-space:pre-wrap; word-break:break-word; overflow-x:auto; max-width:100%; font-family:var(--mono); font-size:12.5px; line-height:1.65; color:#d2dae8; }
|
|
166
|
-
|
|
167
|
-
.empty { background: linear-gradient(180deg, var(--glass-2), var(--glass)); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); border:1px solid var(--glass-line); border-radius:18px; padding:48px; text-align:center; color:var(--fix); font:600 19px/1.4 var(--sans); margin-top:16px; box-shadow: var(--glass-hi); }
|
|
168
|
-
footer { max-width:1120px; margin:0 auto; padding:18px clamp(16px,4vw,32px) 44px; color:var(--faint); font:12px/1.6 var(--mono); letter-spacing:.02em; }
|
|
169
|
-
|
|
170
|
-
/* Entrance animates position only \u2014 content stays fully opaque/visible throughout. */
|
|
171
|
-
@keyframes rise { from { transform: translateY(12px); } to { transform: none; } }
|
|
172
|
-
@keyframes fade { from { opacity:0; } to { opacity:1; } }
|
|
173
|
-
@keyframes blink { 50% { opacity:0; } }
|
|
174
|
-
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation:none !important; transition:none !important; } }
|
|
175
|
-
|
|
176
|
-
@media print {
|
|
177
|
-
body { background:#fff; color:#111; }
|
|
178
|
-
header, .card, .table-wrap, .advisory-wrap, .cmdbar, .fix, thead th, .warn, .empty {
|
|
179
|
-
background:#fff !important; -webkit-backdrop-filter:none !important; backdrop-filter:none !important;
|
|
180
|
-
box-shadow:none !important; border-color:#ddd !important; color:#111;
|
|
181
|
-
}
|
|
182
|
-
.caret { display:none; }
|
|
183
|
-
pre.advisory, .fix-text, .summary, .pkg { color:#111 !important; }
|
|
184
|
-
}
|
|
22
|
+
body { font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 0; background: #f6f7f9; color: #1c1e21; }
|
|
23
|
+
header { background: #11161d; color: #fff; padding: 24px 32px; }
|
|
24
|
+
header h1 { margin: 0 0 4px; font-size: 20px; }
|
|
25
|
+
header .meta { color: #9aa4b2; font-size: 13px; }
|
|
26
|
+
main { max-width: 1100px; margin: 0 auto; padding: 24px 32px; }
|
|
27
|
+
.cards { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; margin-bottom: 8px; }
|
|
28
|
+
.card { background: #fff; border-radius: 8px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(0,0,0,.08); }
|
|
29
|
+
.card .count { font-size: 28px; font-weight: 700; }
|
|
30
|
+
.card .label { text-transform: capitalize; color: #6b7280; font-size: 13px; }
|
|
31
|
+
.warn { background: #fff7e6; border: 1px solid #ffd591; color: #874d00; padding: 8px 12px; border-radius: 6px; margin: 12px 0; font-size: 13px; }
|
|
32
|
+
table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,.08); margin-top: 16px; }
|
|
33
|
+
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid #eef0f3; vertical-align: top; font-size: 13px; }
|
|
34
|
+
th { background: #f0f2f5; font-size: 12px; text-transform: uppercase; letter-spacing: .03em; color: #6b7280; }
|
|
35
|
+
.pill { color: #fff; padding: 2px 8px; border-radius: 999px; font-size: 12px; text-transform: capitalize; white-space: nowrap; }
|
|
36
|
+
.scope, .path { color: #6b7280; font-size: 12px; }
|
|
37
|
+
.path { margin-top: 4px; }
|
|
38
|
+
code { background: #eef0f3; padding: 1px 5px; border-radius: 4px; }
|
|
39
|
+
.empty { background: #fff; border-radius: 8px; padding: 40px; text-align: center; color: #3a9e54; font-size: 18px; box-shadow: 0 1px 3px rgba(0,0,0,.08); }
|
|
40
|
+
footer { max-width: 1100px; margin: 0 auto; padding: 16px 32px 40px; color: #9aa4b2; font-size: 12px; }
|
|
185
41
|
</style>
|
|
186
42
|
</head>
|
|
187
43
|
<body>
|
|
188
44
|
<header>
|
|
189
|
-
<
|
|
190
|
-
<div class="meta"
|
|
45
|
+
<h1>lockhawk report</h1>
|
|
46
|
+
<div class="meta">${x(e.target.root.name)}${e.target.root.version?`@${x(e.target.root.version)}`:""} \xB7 ${x(e.target.manager)} \xB7 ${e.stats.totalPackages} packages \xB7 scanned ${x(e.scannedAt)}</div>
|
|
191
47
|
</header>
|
|
192
48
|
<main>
|
|
193
|
-
<div class="cards">${
|
|
194
|
-
${
|
|
195
|
-
${e.findings.length===0?'<div class="empty">\u2713 No known vulnerabilities found.</div>'
|
|
196
|
-
<div class="table-wrap"><div class="scroll"><table>
|
|
49
|
+
<div class="cards">${s}</div>
|
|
50
|
+
${a}
|
|
51
|
+
${e.findings.length===0?'<div class="empty">\u2713 No known vulnerabilities found.</div>':`<table>
|
|
197
52
|
<thead><tr><th>Severity</th><th>Package</th><th>Advisory</th><th>Summary & path</th><th>Fixed in</th><th>Refs</th></tr></thead>
|
|
198
|
-
<tbody>${
|
|
199
|
-
</table
|
|
53
|
+
<tbody>${r}</tbody>
|
|
54
|
+
</table>`}
|
|
200
55
|
</main>
|
|
201
|
-
<footer>Generated by lockhawk v${
|
|
56
|
+
<footer>Generated by lockhawk v${x(e.tool.version)} \xB7 data from OSV.dev \xB7 database: ${x(e.database.source)}</footer>
|
|
202
57
|
</body>
|
|
203
|
-
</html>`}function _(e){return e.replace(/[&<>"']/g,n=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[n])}function
|
|
58
|
+
</html>`}function _(e){return e.replace(/[&<>"']/g,n=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[n])}function wr(e){return[`Package: ${e.packageName}@${e.version} (${e.scope}${e.direct?", direct":", transitive"})`,`Severity: ${e.severity.level}${e.severity.score!==void 0?`, CVSS ${e.severity.score}`:""}${e.severity.vector?` (${e.severity.vector})`:""}`,e.fixedVersions.length?`Fixed in: ${e.fixedVersions.join(", ")}`:"Fix: none available yet",e.dependencyPaths[0]?`Dependency path: ${e.dependencyPaths[0].join(" > ")}`:"",e.recommendation?`Recommendation: ${e.recommendation}`:"",e.references.length?`References:
|
|
204
59
|
${e.references.join(`
|
|
205
60
|
`)}`:""].filter(Boolean).join(`
|
|
206
|
-
`)}function
|
|
207
|
-
`)});return['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="lockhawk" tests="${s}" failures="${
|
|
208
|
-
`)}export{ee as AutoSource,E as LockfileError,H as OfflineDbMissingError,j as OfflineSource,G as OnlineSource,U as OsvClient,
|
|
61
|
+
`)}function Vr(e){let{findings:n}=e,t=n.length,s=n.length===0?1:n.length,r=((e.stats.durationMs??0)/1e3).toFixed(3),a=`${e.target.root.name} npm dependency vulnerabilities`,i=n.length===0?[' <testcase name="No known vulnerabilities" classname="lockhawk" time="0" />']:n.map(o=>{let d=`${o.packageName}@${o.version}: ${o.id} (${o.severity.level})`,c=`${o.severity.level.toUpperCase()}${o.severity.score!==void 0?` ${o.severity.score}`:""} ${o.id}: ${o.summary}`;return[` <testcase name="${_(d)}" classname="lockhawk.${_(o.severity.level)}" time="0">`,` <failure message="${_(c)}" type="${_(o.severity.level)}">${_(wr(o))}</failure>`," </testcase>"].join(`
|
|
62
|
+
`)});return['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="lockhawk" tests="${s}" failures="${t}" errors="0" time="${r}">`,` <testsuite name="${_(a)}" tests="${s}" failures="${t}" errors="0" time="${r}">`,...i," </testsuite>","</testsuites>",""].join(`
|
|
63
|
+
`)}export{ee as AutoSource,Ce as DATA_MARKER,E as LockfileError,H as OfflineDbMissingError,j as OfflineSource,G as OnlineSource,U as OsvClient,xe as OsvDatabase,N as SEVERITY_RANK,Rn as SHARD_BUCKETS,Nn as TOOL,ue as buildDependencyGraph,Se as buildFindings,Ze as canonicalId,Me as createSource,ve as dedupeVulnerabilities,F as detectLockfile,Je as isVersionAffected,hn as levelFromLabel,ke as levelFromScore,Ne as loadAdvisoriesForPackages,le as loadDependencyGraph,ye as nearestFix,T as offlineDbDir,K as offlineMetaPath,We as parseLockfile,w as pkgKey,L as readOfflineMeta,M as readRootManifest,Ve as resolveCacheDir,Re as resolveSeverity,pe as rootLabel,dr as scan,bn as scoreFromVector,qe as severityAtLeast,Z as shardBucket,ge as shortestPath,pr as shouldFail,Rr as toHtml,gr as toJson,Vr as toJunit,Cn as toSarif,br as toSarifString,Mn as uniqueScannablePackages,sr as updateOfflineDatabase,W as vulnerabilityAffects};
|
|
209
64
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lockhawk/core",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Scanning engine for lockhawk: lockfile parsing, OSV matching, CVSS scoring, reports",
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"description": "Scanning engine for lockhawk: lockfile parsing, OSV.dev vulnerability matching, CVSS v3/v4 scoring, and SARIF/JUnit/HTML reports",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -18,13 +18,23 @@
|
|
|
18
18
|
"security",
|
|
19
19
|
"vulnerability",
|
|
20
20
|
"vulnerability-scanner",
|
|
21
|
+
"vulnerability-scanning",
|
|
22
|
+
"security-scanner",
|
|
23
|
+
"cve-scanner",
|
|
24
|
+
"dependency-scanner",
|
|
21
25
|
"sca",
|
|
22
26
|
"software-composition-analysis",
|
|
27
|
+
"supply-chain-security",
|
|
28
|
+
"devsecops",
|
|
29
|
+
"audit",
|
|
30
|
+
"scanner",
|
|
23
31
|
"osv",
|
|
32
|
+
"osv-scanner",
|
|
24
33
|
"cve",
|
|
25
34
|
"ghsa",
|
|
26
35
|
"cvss",
|
|
27
36
|
"sarif",
|
|
37
|
+
"junit",
|
|
28
38
|
"dependencies",
|
|
29
39
|
"lockfile",
|
|
30
40
|
"npm",
|