@conterra/vuln-scan 0.1.4 → 0.1.6
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/vuln-scan.js +1 -1
- package/package.json +1 -1
package/dist/vuln-scan.js
CHANGED
|
@@ -617,7 +617,7 @@ Usage tree:
|
|
|
617
617
|
${e.componentTree?.map(s=>gV(s)).join(`
|
|
618
618
|
`)}
|
|
619
619
|
\`\`\`
|
|
620
|
-
`}var xV=require("fs/promises");async function vBe(e,t,r){if("sbomFile"in e)return await(0,xV.copyFile)(e.sbomFile,t),e.sbomFile=t,e;let n=await r.downloadArtifact(e.sbomMavenCoordinates);return await(0,xV.writeFile)(t,n),{...e,sbomFile:t}}var vV=class{constructor(t,r){this.repo=t;this.authn=r}async downloadArtifact(t){let r=this.toArtifactInfo(t);if(this.isSnapshotVersion(r.version)&&!r.timestamp){let s=await this.fetchLatestSnapshotTimestamp(r);r={...r,timestamp:s}}let n=this.toArtifactUrl(r),a=await EI(n,this.authn);return Buffer.from(await a.arrayBuffer())}async fetchLatestSnapshotTimestamp(t){let r=`${this.toArtifactBaseUrl(t)}/maven-metadata.xml`,a=await(await EI(r,this.authn)).text();return Ogr(a,t)}toArtifactUrl(t){let r=this.toArtifactBaseUrl(t),n=this.isSnapshotVersion(t.version)&&t.timestamp?t.version.replace(/SNAPSHOT$/,t.timestamp):t.version;return`${r}/${t.artifactId}-${n}-${t.classifier}.${t.extension}`}toArtifactBaseUrl(t){return`${this.repo}/${t.groupId.replace(/\./g,"/")}/${t.artifactId}/${t.version}`}isSnapshotVersion(t){return t.endsWith("-SNAPSHOT")}toArtifactInfo(t){let r=t.split(":");return{groupId:r[0],artifactId:r[1],version:r[2],classifier:r[3],extension:"json"}}};function Ogr(e,t){if(t.classifier&&t.extension){let a=e.match(new RegExp(`<snapshotVersion>\\s*<classifier>${t.classifier}</classifier>\\s*<extension>${t.extension}</extension>\\s*<value>([^<]+)</value>\\s*<updated>[^<]+</updated>\\s*</snapshotVersion>`)),s=t.version.replace(/SNAPSHOT$/,"");if(a?.[1])return a[1].replace(s,"")}let r=e.match(/<timestamp>([^<]+)<\/timestamp>/),n=e.match(/<buildNumber>([^<]+)<\/buildNumber>/);if(!r||!n)throw new Error(`Failed to get timestamp and build number from ${e} for artifact '${t.artifactId}'`);return`${r[1]}-${n[1]}`}var bV=require("fs/promises"),Hle=require("path");var Kle=class{constructor(t){this.location=t}async read(){let t=await uf(this.location);if(!t)throw new Error("Invalid VEX file");return t}async write(t){await Ale(this.location,t)}async delete(){await(0,bV.unlink)(this.location)}};async function DV(e,t){for(let r of await(0,bV.readdir)(e,{withFileTypes:!0}))r.name.endsWith(".json")?await t(new Kle((0,Hle.resolve)(r.parentPath,r.name))):r.isDirectory()&&await DV((0,Hle.resolve)(r.parentPath,r.name),t)}function Xle(e){let t=e.vulnerability.name;return new Set([t,...e.vulnerability.aliases??[]])}var o5=class e{statements=[];filteredToProducts=[];static async fromUrl(t,r,n=new e){let s=await(await EI(t,r)).json();return n.add(s),n}static async fromFiles(t,r=new e){return await DV(t,async n=>{let a=await n.read();r.add(a)}),r}add(t){Mgr(t);let r=t.timestamp;for(let n of t.statements){n.timestamp=n.timestamp??r;let a=Date.parse(n.timestamp),s=n.vulnerability.name,u=Xle(n),c=new Set(n.products?.map(d=>d["@id"]).filter(d=>!!d)??[]),l=new Gle(a,s,u,c,n);this.statements.push(l)}this.sortStatementsByTimestampNewestFirst()}filterByProduct(t){if(this.assertCorrectProductId(t),this.filteredToProducts.length)return this;let r=new e;return r.filteredToProducts=t.slice(0),r.statements=this.statements.filter(n=>n.matchesAtLeastOneProduct(t)),r}findNotMatchingVulnerabilities(t,r){let n=this.filterByProduct(r??this.filteredToProducts),a=new Set;for(let s of n.statements)a.add(s.vulnName);for(let s of t)a.delete(s);return Array.from(a)}newestVulnerabilityStatement(t,r){let n=this.filterByProduct(r??this.filteredToProducts);for(let a of n.statements)if(a.matchesVulnerability(t))return this.reduceToProductInformation(a,n.filteredToProducts[0])}toVexFileForProduct(t){let r=this.filterByProduct(t??this.filteredToProducts),n=r.statements.map(a=>this.reduceToProductInformation(a,r.filteredToProducts[0]));return this.toVexFile(n)}toVexFile(t){let r=t.filter(a=>a.status!=="under_investigation");if(r.length===0)return;this.sortVexStatementsByTimestampNewestFirst(r);let n=r[0].timestamp;return{"@context":"https://openvex.dev/ns/v0.2.0","@id":"https://openvex.dev/docs/public/vex-fc763e6eb63bfbcda20b7cbbddd4e9f8feaaa317fe8d8a6f51a5663a1180343e",author:"conterra",timestamp:n,last_updated:n,version:1,statements:r}}assertCorrectProductId(t){if(!t||t.length===0)throw new Error("Product ID must be provided");if(this.filteredToProducts.length&&!Lgr(this.filteredToProducts,t))throw new Error("Cannot filter to a different product")}reduceToProductInformation(t,r){let n={timestamp:new Date(t.timestamp).toISOString(),...t.statement};if(!r)return n;let a=n.products?.filter(s=>s["@id"]===r);return a?.length||(a=[{"@id":r}]),{...n,products:a}}sortStatementsByTimestampNewestFirst(){this.statements.sort((t,r)=>r.timestamp-t.timestamp)}sortVexStatementsByTimestampNewestFirst(t){t.sort((r,n)=>Date.parse(n.timestamp)-Date.parse(r.timestamp))}};function Lgr(e,t){return e.length===t.length&&e.every((r,n)=>r===t[n])}var Gle=class{constructor(t,r,n,a,s){this.timestamp=t;this.vulnName=r;this.vulnAliases=n;this.products=a;this.statement=s}matchesProduct(t){return this.products.has(t)}matchesAtLeastOneProduct(t){return t.some(this.matchesProduct.bind(this))}matchesVulnerability(t){return this.vulnAliases.has(t)}};function Mgr(e){if(e?.["@context"]!=="https://openvex.dev/ns/v0.2.0")throw new Error("Invalid VEX file format")}var AV=require("path"),xBe=require("fs/promises");var EV=class{constructor(t,r,n){this.project=t;this.outputDir=r;this.useSubDirForProject=n}async lookupOutputFileForWriting(t){let r=this.useSubDirForProject?`${this.project.name}-${this.project.version}`:"",n=`${this.project.name}-${this.project.version}-`,a=this.getFileSuffix(t),s=(0,AV.resolve)(this.outputDir,r,`${n}${a}`),u=(0,AV.dirname)(s);return await cT(u)||await(0,xBe.mkdir)(u,{recursive:!0}),s}getFileSuffix(t){switch(t){case"grype-results":return"scan-grype-results.json";case"trivy-results":return"scan-trivy-results.json";case"ossindex-results":return"scan-ossindex-results.json";case"aggregate-results":return"scan-aggregate-results.json";case"aggregated-sarif-results":return"scan-aggregated-results.sarif.json";case"project-cyclonedx-sbom-file":return"sbom.cdx.json";case"project-openvex-file":return"openvex.vex.json";default:throw new Error(`Unknown output file key: ${t}`)}}};var Yle=require("zx");var CV=require("path"),bBe="anchore/grype";async function DBe(e,t,r){r&&console.log(`Initializing Grype DB in cache directory: ${t}`);let n=`${bBe}:${e||"latest"}`,a=await Yle.$`docker run --rm -v '${t}:/tmp/.cache' -e GRYPE_DB_CACHE_DIR=/tmp/.cache -e TMPDIR=/tmp ${n} db update`.nothrow();if(a.stdout&&console.log(` Grype DB Update: ${a.stdout}`),a.stderr&&console.log(` Grype DB Update error: ${a.stderr}`),a.exitCode!==0)throw new Error(`Grype DB update failed with exit code ${a.exitCode}: ${a.stderr}`)}async function EBe(e,t,r,n,a,s,u){let c=(0,CV.dirname)(r),l=(0,CV.basename)(r),d=[];s||d.push("--quiet");let _=`${bBe}:${e||"latest"}`,f=`${t}:/tmp/.cache`,v=`${c}:/tmp/output`,b=`${n.sbomFile}:/tmp/input.json`,D=`/tmp/output/${l}`,S=await Yle.$`docker run --rm -v ${f} -v ${v} -v ${b} -e GRYPE_DB_CACHE_DIR=/tmp/.cache -e TMPDIR=/tmp -e GRYPE_DB_AUTO_UPDATE=false ${_} sbom:/tmp/input.json -o json --file ${D} --by-cve ${d}`.nothrow();if(S.stdout&&u(` Scan log: ${S.stdout}`),S.stderr&&u(` Scan error: ${S.stderr}`),S.exitCode!==0)throw new Error(`Grype scan failed with exit code ${S.exitCode}: ${S.stderr}`);u(` Grype output file: ${r}`);try{let L=await uf(r);Rgr(a,L)}catch(L){throw new Error(`Grype scan results could not be read from '${r}': ${L}`,{cause:L})}}function Rgr(e,t){if(t)for(let r of t.matches){let n=jgr(r);e.addVulnerability(n,"grype")}}function jgr(e){let t=e.vulnerability,r=t.id,n=Z1(e.artifact.purl),a=Z1(t.dataSource),s=qgr(t.severity);return{id:r,severity:s,scannerSeverity:{grype:s},title:"",description:t.description,components:[n],detectedBy:["grype"],refs:[a]}}function qgr(e){switch(e){case"Negligible":return"LOW";case"Low":return"LOW";case"Medium":return"MEDIUM";case"High":return"HIGH";case"Critical":return"CRITICAL";default:return"UNKNOWN"}}var ABe=require("fs/promises");async function CBe(e,t,r,n,a,s){try{let u={},c="";r.user&&r.token&&(u.Authorization=`Basic ${Buffer.from(`${r.user}:${r.token}`).toString("base64")}`,c="/authorized");let l=`https://ossindex.sonatype.org/api/v3${c}/component-report`,d=await Ugr(t.sbomFile),_=new Set(d.map(b=>b.purl.replace(/\?.*$/,"")));Hgr(_);let f=Array.from(_),v=[];for(let b=0;b<f.length;b+=128){let D=JSON.stringify({coordinates:f.slice(b,b+128)});a&&s(` Sonatype OssIndex request ${l} with body: ${D}`);let L=await(await fetch(l,{method:"POST",headers:{...u,"Content-Type":"application/vnd.ossindex.component-report-request.v1+json"},body:D})).json();Array.isArray(L)?v.push(...L.filter(q=>q.vulnerabilities.length>0)):s(` Sonatype OssIndex Error: ${L.code} - ${L.message}`)}await(0,ABe.writeFile)(e,JSON.stringify(v,null,2)),s(` Sonatype OssIndex output file: ${e}`),Jgr(n,v)}catch(u){throw new Error(`Sonatype OSS Index scan failed: ${u}`,{cause:u})}}async function Ugr(e){return(await uf(e)).components.filter(r=>$gr(r)&&!Vgr(r))}function $gr(e){return!!e.purl}function Vgr(e){return e.group?e.group.startsWith("de.conterra")||e.group.startsWith("org.n52.security"):!1}function Jgr(e,t){if(t)for(let r of t)for(let n of r.vulnerabilities){let a=zgr(n,r.coordinates);e.addVulnerability(a,"ossindex")}}function zgr(e,t){let r=e.cve||e.id,n=Z1(t),a=Z1(e.reference),s=Wgr(e.cvssScore);return{id:r,severity:s,scannerSeverity:{ossindex:s},title:e.title,description:e.description,components:[n],detectedBy:["ossindex"],refs:a?[a]:[]}}function Wgr(e){return e===0?"UNKNOWN":e<4?"LOW":e<7?"MEDIUM":e<9?"HIGH":"CRITICAL"}function Hgr(e){let t=Array.from(e);for(let r of t){let n=r.replace(/(@[^-]+)-.*$/g,"$1");e.add(n)}}var Qle=require("zx");var TV=require("path"),TBe="aquasec/trivy";async function SBe(e,t,r,n){let a=[];r&&(a.push("-e"),a.push(`GITHUB_TOKEN=${r}`));let s=[];s.push("--db-repository"),s.push("public.ecr.aws/aquasecurity/trivy-db,aquasec/trivy-db,ghcr.io/aquasecurity/trivy-db"),n||s.push("--quiet");let u=`${TBe}:${e||"0.69.3"}`,c=await Qle.$`docker run --rm -v '${t}:/.cache' ${a} ${u} ${s} --cache-dir '/.cache' sbom --download-db-only`.nothrow();if(!c.stdout&&!c.stderr&&console.log(" Trivy DB Update: finished"),c.stdout&&console.log(` Trivy DB Update: ${c.stdout}`),c.stderr&&console.log(` Trivy DB Update error: ${c.stderr}`),c.exitCode!==0)throw new Error(`Trivy DB update failed with exit code ${c.exitCode}: ${c.stderr}`)}async function wBe(e,t,r,n,a,s,u){let c=(0,TV.dirname)(r),l=(0,TV.basename)(r),d=[];s||d.push("--quiet");let _=`${TBe}:${e||"latest"}`,f=`${t}:/.cache`,v=`${c}:/tmp/output`,b=`${n.sbomFile}:/tmp/input.json`,D=`/tmp/output/${l}`,S=await Qle.$`docker run --rm -v ${f} -v ${v} -v ${b} ${_} sbom --cache-dir '/.cache' --format json --output ${D} ${d} --skip-db-update /tmp/input.json`.nothrow();if(S.stdout&&u(` Scan log: ${S.stdout}`),S.stderr&&u(` Scan error: ${S.stderr}`),S.exitCode!==0)throw new Error(`Trivy scan failed with exit code ${S.exitCode}: ${S.stderr}`);u(` Trivy output file: ${r}`);try{let L=await uf(r);Kgr(a,L)}catch(L){throw new Error(`Trivy scan results could not be read from '${r}': ${L}`,{cause:L})}}function Kgr(e,t){if(t){for(let r of t.Results??[])if(r.Vulnerabilities)for(let n of r.Vulnerabilities){let a=Ggr(n);e.addVulnerability(a,"trivy")}}}function Ggr(e){let t=e.VulnerabilityID,r=Z1(e.PkgIdentifier.PURL),n=Z1(e.PrimaryURL),a=Xgr(e.Severity);return{id:t,severity:a,scannerSeverity:{trivy:a},title:e.Title,description:e.Description,components:[r],detectedBy:["trivy"],refs:[n]}}function Xgr(e){switch(e){case"UNKNOWN":case"LOW":case"MEDIUM":case"HIGH":case"CRITICAL":return e;default:return"UNKNOWN"}}var SV=class{constructor(t,r,n){this.scanners=t;this.cacheDir=r;this.options=n}async init(t){let r=[];for(let n of this.scanners)switch(n.name){case"grype":r.push(DBe(n.version,this.cacheDir,t));break;case"trivy":r.push(SBe(n.version,this.cacheDir,this.options.trivyGithubToken,t));break;case"ossindex":continue}await Promise.all(r)}async scanProject(t,r,n,a,s){let u=[];for(let c of this.scanners)u.push(this.scanProjectBy(c,t,r,this.cacheDir,n,this.options.ossIndexApiUser,this.options.ossIndexApiToken,a,s));await Promise.all(u)}async scanProjectBy(t,r,n,a,s,u,c,l,d){switch(t.name){case"grype":return EBe(t.version,a,await n.lookupOutputFileForWriting("grype-results"),r,s,l,d);case"trivy":return wBe(t.version,a,await n.lookupOutputFileForWriting("trivy-results"),r,s,l,d);case"ossindex":return CBe(await n.lookupOutputFileForWriting("ossindex-results"),r,{user:u,token:c},s,l,d)}}};async function kBe(e,t,r){await DV(e,async n=>{let a=await n.read(),s=!1,u=a.statements,c=[];for(let l of u){if(r&&!Xle(l).has(r)||!l.products)continue;let d=l.products.findIndex(_=>_["@id"]===t);d!==-1&&(l.products.splice(d,1),s=!0,l.products.length===0&&c.push(l))}if(s){for(let l of c){let d=u.indexOf(l);u.splice(d,1)}if(u.length===0){console.info(`Remove ${t} from ${n.location} and delete file.`),await n.delete();return}console.info(`Remove ${t} from ${n.location}`),await n.write(a)}})}var NI=require("fs/promises"),Zle=require("path");async function PBe(e,t){let{orderBy:r,reportNoLongerDetectedVulnerabilities:n,reportCveDetails:a,outputDirectory:s}={orderBy:"CVE",reportNoLongerDetectedVulnerabilities:!0,reportCveDetails:!1,...t},u=!1,c=!1,l=!1,d=new Map,_=new Map,f=new Map,v=new Map,b=new Map,D=[],S=new Map;for(let C of e.values()){let k=C.project,T=`${k.name}@${k.version}`;if(C.error&&(u=!0,D.push({project:T,error:C.error})),k.silent)continue;let O=Vd[k.minimumSeverity],M=C.newVulns.filter($=>O<=Vd[$.severity]);for(let $ of M){if(c=!0,!d.has($.id))d.set($.id,{id:$.id,title:$.title,severity:$.severity,description:$.description,components:$.components,refs:$.refs});else{let X=d.get($.id);t7r(X,$)}_.has($.id)||_.set($.id,[]),v.has(T)||v.set(T,[]),S.has(T)||S.set(T,new Map),v.get(T).push($.id),_.get($.id).push(T);for(let X of $.componentTree)X.purl&&(S.get(T).has(X.purl)||S.get(T).set(X.purl,X))}for(let $ of C.noLongerDetectedVulns)l=!0,f.has($)||f.set($,[]),b.has(T)||b.set(T,[]),b.get(T).push($),f.get($).push(T)}let L;s&&(L=(0,Zle.join)(s,"scan-summary.txt"),await(0,NI.writeFile)(L,"","utf8"));let q=async C=>{console.log(C),L&&await(0,NI.appendFile)(L,C+`
|
|
620
|
+
`}var xV=require("fs/promises");async function vBe(e,t,r){if("sbomFile"in e)return await(0,xV.copyFile)(e.sbomFile,t),e.sbomFile=t,e;let n=await r.downloadArtifact(e.sbomMavenCoordinates);return await(0,xV.writeFile)(t,n),{...e,sbomFile:t}}var vV=class{constructor(t,r){this.repo=t;this.authn=r}async downloadArtifact(t){let r=this.toArtifactInfo(t);if(this.isSnapshotVersion(r.version)&&!r.timestamp){let s=await this.fetchLatestSnapshotTimestamp(r);r={...r,timestamp:s}}let n=this.toArtifactUrl(r),a=await EI(n,this.authn);return Buffer.from(await a.arrayBuffer())}async fetchLatestSnapshotTimestamp(t){let r=`${this.toArtifactBaseUrl(t)}/maven-metadata.xml`,a=await(await EI(r,this.authn)).text();return Ogr(a,t)}toArtifactUrl(t){let r=this.toArtifactBaseUrl(t),n=this.isSnapshotVersion(t.version)&&t.timestamp?t.version.replace(/SNAPSHOT$/,t.timestamp):t.version;return`${r}/${t.artifactId}-${n}-${t.classifier}.${t.extension}`}toArtifactBaseUrl(t){return`${this.repo}/${t.groupId.replace(/\./g,"/")}/${t.artifactId}/${t.version}`}isSnapshotVersion(t){return t.endsWith("-SNAPSHOT")}toArtifactInfo(t){let r=t.split(":");return{groupId:r[0],artifactId:r[1],version:r[2],classifier:r[3],extension:"json"}}};function Ogr(e,t){if(t.classifier&&t.extension){let a=e.match(new RegExp(`<snapshotVersion>\\s*<classifier>${t.classifier}</classifier>\\s*<extension>${t.extension}</extension>\\s*<value>([^<]+)</value>\\s*<updated>[^<]+</updated>\\s*</snapshotVersion>`)),s=t.version.replace(/SNAPSHOT$/,"");if(a?.[1])return a[1].replace(s,"")}let r=e.match(/<timestamp>([^<]+)<\/timestamp>/),n=e.match(/<buildNumber>([^<]+)<\/buildNumber>/);if(!r||!n)throw new Error(`Failed to get timestamp and build number from ${e} for artifact '${t.artifactId}'`);return`${r[1]}-${n[1]}`}var bV=require("fs/promises"),Hle=require("path");var Kle=class{constructor(t){this.location=t}async read(){let t=await uf(this.location);if(!t)throw new Error("Invalid VEX file");return t}async write(t){await Ale(this.location,t)}async delete(){await(0,bV.unlink)(this.location)}};async function DV(e,t){for(let r of await(0,bV.readdir)(e,{withFileTypes:!0}))r.name.endsWith(".json")?await t(new Kle((0,Hle.resolve)(r.parentPath,r.name))):r.isDirectory()&&await DV((0,Hle.resolve)(r.parentPath,r.name),t)}function Xle(e){let t=e.vulnerability.name;return new Set([t,...e.vulnerability.aliases??[]])}var o5=class e{statements=[];filteredToProducts=[];static async fromUrl(t,r,n=new e){let s=await(await EI(t,r)).json();return n.add(s),n}static async fromFiles(t,r=new e){return await DV(t,async n=>{let a=await n.read();r.add(a)}),r}add(t){Mgr(t);let r=t.timestamp;for(let n of t.statements){n.timestamp=n.timestamp??r;let a=Date.parse(n.timestamp),s=n.vulnerability.name,u=Xle(n),c=new Set(n.products?.map(d=>d["@id"]).filter(d=>!!d)??[]),l=new Gle(a,s,u,c,n);this.statements.push(l)}this.sortStatementsByTimestampNewestFirst()}filterByProduct(t){if(this.assertCorrectProductId(t),this.filteredToProducts.length)return this;let r=new e;return r.filteredToProducts=t.slice(0),r.statements=this.statements.filter(n=>n.matchesAtLeastOneProduct(t)),r}findNotMatchingVulnerabilities(t,r){let n=this.filterByProduct(r??this.filteredToProducts),a=new Set;for(let s of n.statements)a.add(s.vulnName);for(let s of t)a.delete(s);return Array.from(a)}newestVulnerabilityStatement(t,r){let n=this.filterByProduct(r??this.filteredToProducts);for(let a of n.statements)if(a.matchesVulnerability(t))return this.reduceToProductInformation(a,n.filteredToProducts[0])}toVexFileForProduct(t){let r=this.filterByProduct(t??this.filteredToProducts),n=r.statements.map(a=>this.reduceToProductInformation(a,r.filteredToProducts[0]));return this.toVexFile(n)}toVexFile(t){let r=t.filter(a=>a.status!=="under_investigation");if(r.length===0)return;this.sortVexStatementsByTimestampNewestFirst(r);let n=r[0].timestamp;return{"@context":"https://openvex.dev/ns/v0.2.0","@id":"https://openvex.dev/docs/public/vex-fc763e6eb63bfbcda20b7cbbddd4e9f8feaaa317fe8d8a6f51a5663a1180343e",author:"conterra",timestamp:n,last_updated:n,version:1,statements:r}}assertCorrectProductId(t){if(!t||t.length===0)throw new Error("Product ID must be provided");if(this.filteredToProducts.length&&!Lgr(this.filteredToProducts,t))throw new Error("Cannot filter to a different product")}reduceToProductInformation(t,r){let n={timestamp:new Date(t.timestamp).toISOString(),...t.statement};if(!r)return n;let a=n.products?.filter(s=>s["@id"]===r);return a?.length||(a=[{"@id":r}]),{...n,products:a}}sortStatementsByTimestampNewestFirst(){this.statements.sort((t,r)=>r.timestamp-t.timestamp)}sortVexStatementsByTimestampNewestFirst(t){t.sort((r,n)=>Date.parse(n.timestamp)-Date.parse(r.timestamp))}};function Lgr(e,t){return e.length===t.length&&e.every((r,n)=>r===t[n])}var Gle=class{constructor(t,r,n,a,s){this.timestamp=t;this.vulnName=r;this.vulnAliases=n;this.products=a;this.statement=s}matchesProduct(t){return this.products.has(t)}matchesAtLeastOneProduct(t){return t.some(this.matchesProduct.bind(this))}matchesVulnerability(t){return this.vulnAliases.has(t)}};function Mgr(e){if(e?.["@context"]!=="https://openvex.dev/ns/v0.2.0")throw new Error("Invalid VEX file format")}var AV=require("path"),xBe=require("fs/promises");var EV=class{constructor(t,r,n){this.project=t;this.outputDir=r;this.useSubDirForProject=n}async lookupOutputFileForWriting(t){let r=this.useSubDirForProject?`${this.project.name}-${this.project.version}`:"",n=`${this.project.name}-${this.project.version}-`,a=this.getFileSuffix(t),s=(0,AV.resolve)(this.outputDir,r,`${n}${a}`),u=(0,AV.dirname)(s);return await cT(u)||await(0,xBe.mkdir)(u,{recursive:!0}),s}getFileSuffix(t){switch(t){case"grype-results":return"scan-grype-results.json";case"trivy-results":return"scan-trivy-results.json";case"ossindex-results":return"scan-ossindex-results.json";case"aggregate-results":return"scan-aggregate-results.json";case"aggregated-sarif-results":return"scan-aggregated-results.sarif.json";case"project-cyclonedx-sbom-file":return"sbom.cdx.json";case"project-openvex-file":return"openvex.vex.json";default:throw new Error(`Unknown output file key: ${t}`)}}};var Yle=require("zx");var CV=require("path"),bBe="anchore/grype";async function DBe(e,t,r){r&&console.log(`Initializing Grype DB in cache directory: ${t}`);let n=`${bBe}:${e||"latest"}`,a=await Yle.$`docker run --rm -v '${t}:/tmp/.cache' -e GRYPE_DB_CACHE_DIR=/tmp/.cache -e TMPDIR=/tmp ${n} db update`.nothrow();if(a.stdout&&console.log(` Grype DB Update: ${a.stdout}`),a.stderr&&console.log(` Grype DB Update error: ${a.stderr}`),a.exitCode!==0)throw new Error(`Grype DB update failed with exit code ${a.exitCode}: ${a.stderr}`)}async function EBe(e,t,r,n,a,s,u){let c=(0,CV.dirname)(r),l=(0,CV.basename)(r),d=[];s||d.push("--quiet");let _=`${bBe}:${e||"latest"}`,f=`${t}:/tmp/.cache`,v=`${c}:/tmp/output`,b=`${n.sbomFile}:/tmp/input.json`,D=`/tmp/output/${l}`,S=await Yle.$`docker run --rm -v ${f} -v ${v} -v ${b} -e GRYPE_DB_CACHE_DIR=/tmp/.cache -e TMPDIR=/tmp -e GRYPE_DB_AUTO_UPDATE=false ${_} sbom:/tmp/input.json -o json --file ${D} --by-cve ${d}`.nothrow();if(S.stdout&&u(` Scan log: ${S.stdout}`),S.stderr&&u(` Scan error: ${S.stderr}`),S.exitCode!==0)throw new Error(`Grype scan failed with exit code ${S.exitCode}: ${S.stderr}`);u(` Grype output file: ${r}`);try{let L=await uf(r);Rgr(a,L)}catch(L){throw new Error(`Grype scan results could not be read from '${r}': ${L}`,{cause:L})}}function Rgr(e,t){if(t)for(let r of t.matches){let n=jgr(r);e.addVulnerability(n,"grype")}}function jgr(e){let t=e.vulnerability,r=t.id,n=Z1(e.artifact.purl),a=Z1(t.dataSource),s=qgr(t.severity);return{id:r,severity:s,scannerSeverity:{grype:s},title:"",description:t.description,components:[n],detectedBy:["grype"],refs:[a]}}function qgr(e){switch(e){case"Negligible":return"LOW";case"Low":return"LOW";case"Medium":return"MEDIUM";case"High":return"HIGH";case"Critical":return"CRITICAL";default:return"UNKNOWN"}}var ABe=require("fs/promises");async function CBe(e,t,r,n,a,s){try{let u={},c="";r.user&&r.token&&(u.Authorization=`Basic ${Buffer.from(`${r.user}:${r.token}`).toString("base64")}`,c="/authorized");let l=`https://ossindex.sonatype.org/api/v3${c}/component-report`,d=await Ugr(t.sbomFile),_=new Set(d.map(b=>b.purl.replace(/\?.*$/,"")));Hgr(_);let f=Array.from(_),v=[];for(let b=0;b<f.length;b+=128){let D=JSON.stringify({coordinates:f.slice(b,b+128)});a&&s(` Sonatype OssIndex request ${l} with body: ${D}`);let L=await(await fetch(l,{method:"POST",headers:{...u,"Content-Type":"application/vnd.ossindex.component-report-request.v1+json"},body:D})).json();Array.isArray(L)?v.push(...L.filter(q=>q.vulnerabilities.length>0)):s(` Sonatype OssIndex Error: ${L.code} - ${L.message}`)}await(0,ABe.writeFile)(e,JSON.stringify(v,null,2)),s(` Sonatype OssIndex output file: ${e}`),Jgr(n,v)}catch(u){throw new Error(`Sonatype OSS Index scan failed: ${u}`,{cause:u})}}async function Ugr(e){return(await uf(e)).components.filter(r=>$gr(r)&&!Vgr(r))}function $gr(e){return!!e.purl}function Vgr(e){return e.group?e.group.startsWith("de.conterra")||e.group.startsWith("org.n52.security"):!1}function Jgr(e,t){if(t)for(let r of t)for(let n of r.vulnerabilities){let a=zgr(n,r.coordinates);e.addVulnerability(a,"ossindex")}}function zgr(e,t){let r=e.cve||e.id,n=Z1(t),a=Z1(e.reference),s=Wgr(e.cvssScore);return{id:r,severity:s,scannerSeverity:{ossindex:s},title:e.title,description:e.description,components:[n],detectedBy:["ossindex"],refs:a?[a]:[]}}function Wgr(e){return e===0?"UNKNOWN":e<4?"LOW":e<7?"MEDIUM":e<9?"HIGH":"CRITICAL"}function Hgr(e){let t=Array.from(e);for(let r of t){let n=r.replace(/(@[^-]+)-.*$/g,"$1");e.add(n)}}var Qle=require("zx");var TV=require("path"),TBe="aquasec/trivy";async function SBe(e,t,r,n){let a=[];r&&(a.push("-e"),a.push(`GITHUB_TOKEN=${r}`));let s=[];s.push("--db-repository"),s.push("public.ecr.aws/aquasecurity/trivy-db,aquasec/trivy-db,ghcr.io/aquasecurity/trivy-db"),n||s.push("--quiet");let u=`${TBe}:${e||"0.69.3"}`,c=await Qle.$`docker run --rm -v '${t}:/.cache' ${a} ${u} ${s} --cache-dir '/.cache' sbom --download-db-only`.nothrow();if(!c.stdout&&!c.stderr&&console.log(" Trivy DB Update: finished"),c.stdout&&console.log(` Trivy DB Update: ${c.stdout}`),c.stderr&&console.log(` Trivy DB Update error: ${c.stderr}`),c.exitCode!==0)throw new Error(`Trivy DB update failed with exit code ${c.exitCode}: ${c.stderr}`)}async function wBe(e,t,r,n,a,s,u){let c=(0,TV.dirname)(r),l=(0,TV.basename)(r),d=[];s||d.push("--quiet");let _=`${TBe}:${e||"0.69.3"}`,f=`${t}:/.cache`,v=`${c}:/tmp/output`,b=`${n.sbomFile}:/tmp/input.json`,D=`/tmp/output/${l}`,S=await Qle.$`docker run --rm -v ${f} -v ${v} -v ${b} ${_} sbom --cache-dir '/.cache' --format json --output ${D} ${d} --skip-db-update /tmp/input.json`.nothrow();if(S.stdout&&u(` Scan log: ${S.stdout}`),S.stderr&&u(` Scan error: ${S.stderr}`),S.exitCode!==0)throw new Error(`Trivy scan failed with exit code ${S.exitCode}: ${S.stderr}`);u(` Trivy output file: ${r}`);try{let L=await uf(r);Kgr(a,L)}catch(L){throw new Error(`Trivy scan results could not be read from '${r}': ${L}`,{cause:L})}}function Kgr(e,t){if(t){for(let r of t.Results??[])if(r.Vulnerabilities)for(let n of r.Vulnerabilities){let a=Ggr(n);e.addVulnerability(a,"trivy")}}}function Ggr(e){let t=e.VulnerabilityID,r=Z1(e.PkgIdentifier.PURL),n=Z1(e.PrimaryURL),a=Xgr(e.Severity);return{id:t,severity:a,scannerSeverity:{trivy:a},title:e.Title,description:e.Description,components:[r],detectedBy:["trivy"],refs:[n]}}function Xgr(e){switch(e){case"UNKNOWN":case"LOW":case"MEDIUM":case"HIGH":case"CRITICAL":return e;default:return"UNKNOWN"}}var SV=class{constructor(t,r,n){this.scanners=t;this.cacheDir=r;this.options=n}async init(t){let r=[];for(let n of this.scanners)switch(n.name){case"grype":r.push(DBe(n.version,this.cacheDir,t));break;case"trivy":r.push(SBe(n.version,this.cacheDir,this.options.trivyGithubToken,t));break;case"ossindex":continue}await Promise.all(r)}async scanProject(t,r,n,a,s){let u=[];for(let c of this.scanners)u.push(this.scanProjectBy(c,t,r,this.cacheDir,n,this.options.ossIndexApiUser,this.options.ossIndexApiToken,a,s));await Promise.all(u)}async scanProjectBy(t,r,n,a,s,u,c,l,d){switch(t.name){case"grype":return EBe(t.version,a,await n.lookupOutputFileForWriting("grype-results"),r,s,l,d);case"trivy":return wBe(t.version,a,await n.lookupOutputFileForWriting("trivy-results"),r,s,l,d);case"ossindex":return CBe(await n.lookupOutputFileForWriting("ossindex-results"),r,{user:u,token:c},s,l,d)}}};async function kBe(e,t,r){await DV(e,async n=>{let a=await n.read(),s=!1,u=a.statements,c=[];for(let l of u){if(r&&!Xle(l).has(r)||!l.products)continue;let d=l.products.findIndex(_=>_["@id"]===t);d!==-1&&(l.products.splice(d,1),s=!0,l.products.length===0&&c.push(l))}if(s){for(let l of c){let d=u.indexOf(l);u.splice(d,1)}if(u.length===0){console.info(`Remove ${t} from ${n.location} and delete file.`),await n.delete();return}console.info(`Remove ${t} from ${n.location}`),await n.write(a)}})}var NI=require("fs/promises"),Zle=require("path");async function PBe(e,t){let{orderBy:r,reportNoLongerDetectedVulnerabilities:n,reportCveDetails:a,outputDirectory:s}={orderBy:"CVE",reportNoLongerDetectedVulnerabilities:!0,reportCveDetails:!1,...t},u=!1,c=!1,l=!1,d=new Map,_=new Map,f=new Map,v=new Map,b=new Map,D=[],S=new Map;for(let C of e.values()){let k=C.project,T=`${k.name}@${k.version}`;if(C.error&&(u=!0,D.push({project:T,error:C.error})),k.silent)continue;let O=Vd[k.minimumSeverity],M=C.newVulns.filter($=>O<=Vd[$.severity]);for(let $ of M){if(c=!0,!d.has($.id))d.set($.id,{id:$.id,title:$.title,severity:$.severity,description:$.description,components:$.components,refs:$.refs});else{let X=d.get($.id);t7r(X,$)}_.has($.id)||_.set($.id,[]),v.has(T)||v.set(T,[]),S.has(T)||S.set(T,new Map),v.get(T).push($.id),_.get($.id).push(T);for(let X of $.componentTree)X.purl&&(S.get(T).has(X.purl)||S.get(T).set(X.purl,X))}for(let $ of C.noLongerDetectedVulns)l=!0,f.has($)||f.set($,[]),b.has(T)||b.set(T,[]),b.get(T).push($),f.get($).push(T)}let L;s&&(L=(0,Zle.join)(s,"scan-summary.txt"),await(0,NI.writeFile)(L,"","utf8"));let q=async C=>{console.log(C),L&&await(0,NI.appendFile)(L,C+`
|
|
621
621
|
`,"utf8")};switch(await q("=== Scan summary ==="),D.length&&await Ygr(q,D),a&&await e7r(q,d,S),r){case"CVE":{await Zgr(q,_,d,n?f:new Map);break}case"PROJECT":{await Qgr(q,v,n?b:new Map,d);break}}!u&&!c&&(!l||!n)&&await q(" No issues found");let F={errors:D,vulnerabilities:Object.fromEntries(d),vulnerabilityToProject:Object.fromEntries(_.entries()),projectToVulnerability:Object.fromEntries(v.entries()),projectsToComponents:Object.fromEntries(Array.from(S.entries()).map(([C,k])=>[C,Object.fromEntries(k.entries())])),noLongerDetectedVulnerabilities:Object.fromEntries(f)};if(s){let C=(0,Zle.join)(s,"scan-summary.json");await(0,NI.writeFile)(C,JSON.stringify(F,null,2),"utf8")}return{hasError:u,hasNewVuln:c,hasNoLongerDetectedVuln:l}}async function Ygr(e,t){await e(""),await e("- Errors during scan:");for(let{project:r,error:n}of t)await e(` - ${r}:`),await e(` Error: ${n}`)}async function Qgr(e,t,r,n){let a=new Set([...Array.from(t.keys()),...Array.from(r.keys())]),s=Array.from(a.keys()).sort();if(s.length){await e(""),await e("- Vulnerabilities by project:");for(let u of s){await e(` - ${u}:`);let c=t.get(u)??[];for(let d of c.slice().sort(e0e(_=>n.get(_).severity))){let{severity:_}=n.get(d);await e(` - [${_}] ${d}`)}let l=(r.get(u)??[]).slice().sort();for(let d of l)await e(` - ${d} (no longer detected)`)}}}async function Zgr(e,t,r,n){let a=Array.from(t.keys()).sort(e0e(u=>r.get(u).severity));if(a.length){await e(""),await e("- New vulnerabilities:");for(let u of a){let c=t.get(u),{severity:l}=r.get(u);await e(` - [${l}] ${u}`);for(let d of c.slice().sort())await e(` - ${d}`)}}let s=Array.from(n.keys()).sort();if(s.length){await e(""),await e("- Declared but no longer detected vulnerabilities:");for(let u of s){let c=n.get(u);await e(` - ${u}`);for(let l of c.slice().sort())await e(` - ${l}`)}}}async function e7r(e,t,r){if(t.size===0)return;await e(""),await e("- Vulnerability details:");let n=Array.from(t.keys()).sort(e0e(a=>t.get(a).severity));for(let a of n){let s=t.get(a);if(await e(` - [${s.severity}] ${s.id} - ${s.title||""}:`),s.description){await e(" Description:");let u=s.description.split(/\r?\n/);for(let c of u)await e(` ${c}`)}if(s.components.length){await e(" Components:");for(let u of s.components)await e(" - "+u);if(r){for(let u of s.components)for(let[c,l]of r.entries())if(l.has(u)){await e(` Usage Tree of ${u} by ${c} (as example):`),await e(gV(l.get(u)," "));break}}}if(s.refs.length){await e(" Links:");for(let u of s.refs)await e(` - ${u}`)}}}function t7r(e,t){e.title=e.title||t.title,e.severity=Vd[e.severity]>Vd[t.severity]?e.severity:t.severity,e.description=e.description||t.description,e.components=FBe(e.components,t.components),e.refs=FBe(e.refs,t.refs)}function FBe(e,t){let r=new Set([...e,...t]);return Array.from(r)}function e0e(e){return(t,r)=>{let n=e(t),a=e(r),s=Vd[n],u=Vd[a];return s!==u?s>u?-1:1:t.localeCompare(r)}}wV.os.type().startsWith("Windows")&&(0,wV.usePowerShell)();async function NBe(e){let t=await Sle(e),r=t.cacheDir;await cT(r)||await(0,_T.mkdir)(r);let n=t.outDir;await cT(n)&&await Cle(n),await(0,_T.mkdir)(n);let a=await a7r(t,{user:e.vexUrlsUser,pw:e.vexUrlsPw}),s=new vV(t.mavenRepo,{user:e.mavenRepoUser,pw:e.mavenRepoPw}),u=s7r(t,e,r),c=Date.now();console.log("Initializing scanner engines ..."),await u.init(e.verbose),console.log(`Scanner engines initialization took ${Date.now()-c}ms
|
|
622
622
|
`);let l=o7r(e),d=t.projects,_=new Map,f=Date.now();console.log("Scanning projects ...");let v=d.filter(l).map(D=>{let S=new EV(D,n,t.createSubDirsForProject),L=[],q=C=>{L.push(C)};return{scan(){return r7r(D,S,s,u,a,q,_,e,L)}}});await c7r(v),console.log(`Scanning took ${Date.now()-f}ms`),e.autoRemoveUnusedStatements&&await u7r(_,t.vexDir);let b=await PBe(_,{orderBy:t.consoleReport.startsWith("BY_CVE")?"CVE":"PROJECT",reportCveDetails:t.consoleReport.endsWith("_DETAILED"),reportNoLongerDetectedVulnerabilities:t.reportNoLongerDetectedVulnerabilities,outputDirectory:n});if(b.hasError)return 1;if(b.hasNewVuln){if(t.onNewVulnerabilities==="fail")return 1;t.onNewVulnerabilities==="warn"&&(console.log("##vso[task.logissue type=warning;]NEW_VULNERABILITIES_DETECTED"),console.log("##vso[task.complete result=SucceededWithIssues;]"))}if(b.hasNoLongerDetectedVuln){if(t.onNoLongerDetectedVulnerabilities==="fail")return 1;t.onNoLongerDetectedVulnerabilities==="warn"&&(console.log("##vso[task.logissue type=warning;]NO_LONGER_DETECTED_VULNERABILITIES"),console.log("##vso[task.complete result=SucceededWithIssues;]"))}return 0}async function r7r(e,t,r,n,a,s,u,c,l){try{let{unmatchedVulnerabilities:d,noLongerDetectedVulnerabilities:_}=await n7r(e,t,r,n,a,c.verbose,s);u.set(e.purl,{project:e,newVulns:d,noLongerDetectedVulns:_})}catch(d){let _=d;u.set(e.purl,{project:e,error:`${_}`,newVulns:[],noLongerDetectedVulns:[]})}if(c.verbose)for(let d of l)console.log(d)}async function n7r(e,t,r,n,a,s,u){let c=await vBe(e,await t.lookupOutputFileForWriting("project-cyclonedx-sbom-file"),r);u(`Analyzing project: ${c.name} - ${c.version} - ${c.sbomFile}`);let l=await uf(c.sbomFile);if(!l)throw new Error(`Could not read SBOM file ${c.sbomFile} for project ${c.name}`);if(!l.components||l.components.length===0)throw new Error(`SBOM file ${c.sbomFile} for project ${c.name} does not contain any components`);let d=new JR(yV.createForSbom(l));await n.scanProject(c,t,d,s,u);let _=a.filterByProduct([c.purl,...c.purlAliases]),{unmatchedVulnerabilities:f}=d.enhanceWithVexData(_),v=c.detectNoLongerUsedVulnerabilities?_.findNotMatchingVulnerabilities(d.getAllVulnIds()):[];return await i7r(t,c,d,_,u),{unmatchedVulnerabilities:f,noLongerDetectedVulnerabilities:v}}async function i7r(e,t,r,n,a){let s=r.getAllVulnerabilitiesSortedBySeverity(),u=await e.lookupOutputFileForWriting("aggregate-results");await(0,_T.writeFile)(u,JSON.stringify(s,null,2)),a(` Vulnerabilities output file: ${u}`);let c=await e.lookupOutputFileForWriting("aggregated-sarif-results"),l=gBe(s,t);await(0,_T.writeFile)(c,JSON.stringify(l,null,2)),a(` Vuln Sarif output file: ${c}`);let d=n.toVexFile(s.filter(f=>f.vexStatement).map(f=>{let v=f.vexStatement;for(let b of f.components??[])v.products.push({"@id":b});return v}));if(!d)return;let _=await e.lookupOutputFileForWriting("project-openvex-file");await(0,_T.writeFile)(_,JSON.stringify(d,null,2)),a(` OpenVex file: ${_}`)}async function a7r(e,t){let r=e.vexDir,n;await cT(r)?n=await o5.fromFiles(r):n=new o5;for(let a of e.vexUrls)await o5.fromUrl(a,t,n);return n}function s7r(e,t,r){if(t.scannerEngine)return t.scannerEngine;if(e.scanners.length===0)throw new Error("No scanners configured");return new SV(e.scanners,r,t)}function o7r(e){let{reduceToProjectName:t,reduceToProjectVersion:r}=e;return n=>!(!n.enabled||t&&(n.name!==t||r&&n.version!==r))}async function u7r(e,t){for(let r of e.values())if(!r.project.silent)for(let n of r.noLongerDetectedVulns)await kBe(t,r.project.purl,n)}async function c7r(e,t=5){let r=0,n=0,a=0,s=e.length,u,c=new Promise(d=>{u=d});function l(){for(;r<t&&a<s;){let d=e[a++];r++,d.scan().finally(()=>{r--,n++,n>=s?u():l()})}}l(),await c}var IBe=require("zx");IBe.dotenv.config();var BD=process.env;(async()=>{let e=await NBe({workingDir:process.cwd(),reduceToProjectName:process.argv[2],reduceToProjectVersion:process.argv[3],ossIndexApiUser:BD.OSS_INDEX_API_USER,ossIndexApiToken:BD.OSS_INDEX_API_TOKEN,mavenRepoUser:BD.MAVEN_REPO_USER,mavenRepoPw:BD.MAVEN_REPO_PW,trivyGithubToken:BD.TRIVY_GITHUB_TOKEN,vexUrlsUser:BD.VEX_URLS_USER,vexUrlsPw:BD.VEX_URLS_PW,verbose:BD.VERBOSE==="true",autoRemoveUnusedStatements:BD.AUTO_REMOVE_UNUSED_STATEMENTS==="true"});process.exitCode=e})();
|
|
623
623
|
/*! Bundled license information:
|