@conterra/vuln-scan 1.0.15 → 1.0.16

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.
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var Ee=Object.create;var re=Object.defineProperty;var Ne=Object.getOwnPropertyDescriptor;var Le=Object.getOwnPropertyNames;var Je=Object.getPrototypeOf,Be=Object.prototype.hasOwnProperty;var Oe=(o,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Le(e))!Be.call(o,n)&&n!==r&&re(o,n,{get:()=>e[n],enumerable:!(t=Ne(e,n))||t.enumerable});return o};var De=(o,e,r)=>(r=o!=null?Ee(Je(o)):{},Oe(e||!o||!o.__esModule?re(r,"default",{value:o,enumerable:!0}):r,o));var Q=require("path"),Te=require("zx");var be=require("fs/promises");var I={UNKNOWN:0,LOW:1,MEDIUM:2,HIGH:3,CRITICAL:4},y=["UNKNOWN","LOW","MEDIUM","HIGH","CRITICAL"];function k(o,e){return I[o]-I[e]}function E(o,e){return I[o]<I[e]}function j(...o){return{type:"doc",version:1,content:o}}function C(o,e){return{type:"heading",attrs:{level:o},content:[{type:"text",text:e}]}}function N(o,e="text"){return{type:"codeBlock",attrs:{language:e},content:[{type:"text",text:o}]}}function ne(o){return{type:"bulletList",content:o.map(e=>({type:"listItem",content:[{type:"paragraph",content:[{type:"text",text:e}]}]}))}}function oe(o){return{type:"bulletList",content:o.map(e=>({type:"listItem",content:[{type:"paragraph",content:[{type:"text",text:e,marks:[{type:"link",attrs:{href:e}}]}]}]}))}}function L(o,...e){return{type:"expand",attrs:{title:o},content:e}}function h(o){return{type:"paragraph",content:[{type:"text",text:o,marks:[{type:"em"}]}]}}var m=require("fs/promises");async function se(o){try{return await Ue(o)?JSON.parse(await(0,m.readFile)(o,{encoding:"utf-8"})):void 0}catch(e){throw new Error(`Error reading JSON file ${o}: ${e}`,{cause:e})}}async function Ue(o){try{return await(0,m.access)(o,m.constants.F_OK),!0}catch{return!1}}function ie(o,e){return`Basic ${Buffer.from(`${o}:${e}`).toString("base64")}`}var J=class{authHeader;baseUrl;constructor(e,r,t){this.baseUrl=e.replace(/\/$/,""),this.authHeader=ie(r,t)}get headers(){return{Authorization:this.authHeader,"Content-Type":"application/json",Accept:"application/json"}}async safeJson(e,r){let t=await e.text();try{return JSON.parse(t)}catch{let n=t.slice(0,500).replace(/\s+/g," ").trim();throw new Error(`${r}: expected JSON but got (HTTP ${e.status}): ${n}`)}}async findExistingIssue(e,r){let t=e.replace(/"/g,'\\"'),n=r.replace(/"/g,'\\"'),s=`project = "${t}" AND summary ~ "\\"${n}\\""`,i=`${this.baseUrl}/rest/api/3/search/jql?jql=${encodeURIComponent(s)}&maxResults=1&fields=summary,labels,status`,c=await fetch(i,{method:"GET",headers:this.headers});if(!c.ok){let d=await c.text();throw new Error(`Jira search failed (${c.status}): ${d}`)}let l=(await this.safeJson(c,"Jira search")).issues[0];return l?{key:l.key,summary:l.fields?.summary??"",labels:l.fields?.labels??[],statusCategory:l.fields?.status?.statusCategory?.key??"new"}:null}async createIssue(e){let r={project:{key:e.projectKey},issuetype:{name:e.issueType},summary:e.summary,description:e.description};e.labels&&e.labels.length>0&&(r.labels=e.labels),e.sprintId!=null&&(r.customfield_10020=e.sprintId);let t={fields:r},n=await fetch(`${this.baseUrl}/rest/api/3/issue`,{method:"POST",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let i=await n.text();throw new Error(`Jira create issue failed (${n.status}): ${i}`)}return(await this.safeJson(n,"Jira create issue")).key}async addLabels(e,r){if(r.length===0)return;let t={update:{labels:r.map(s=>({add:s}))}},n=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}`,{method:"PUT",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let s=await n.text();throw new Error(`Jira add labels failed (${n.status}): ${s}`)}}async updateIssueSummary(e,r){let t={fields:{summary:r}},n=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}`,{method:"PUT",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let s=await n.text();throw new Error(`Jira update summary failed (${n.status}): ${s}`)}}async setSprint(e,r){let t={fields:{customfield_10020:r}},n=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}`,{method:"PUT",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let s=await n.text();throw new Error(`Jira set sprint failed (${n.status}): ${s}`)}}async addComment(e,r){let t=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}/comment`,{method:"POST",headers:this.headers,body:JSON.stringify({body:r})});if(!t.ok){let n=await t.text();throw new Error(`Jira add comment failed (${t.status}): ${n}`)}}async getTransitions(e){let r=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}/transitions`,{method:"GET",headers:this.headers});if(!r.ok){let n=await r.text();throw new Error(`Jira get transitions failed (${r.status}): ${n}`)}return(await this.safeJson(r,"Jira transitions lookup")).transitions.map(n=>({id:n.id,name:n.name,toStatusName:n.to?.name??"",toStatusCategory:n.to?.statusCategory?.key??""}))}async transitionIssue(e,r){let t=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}/transitions`,{method:"POST",headers:this.headers,body:JSON.stringify({transition:{id:r}})});if(!t.ok){let n=await t.text();throw new Error(`Jira transition issue failed (${t.status}): ${n}`)}}async attachMarkdown(e,r,t){let n=`${this.baseUrl}/rest/api/3/issue/${e}/attachments`,s=new FormData;s.append("file",new Blob([t],{type:"text/markdown"}),r);let i=await fetch(n,{method:"POST",headers:{Authorization:this.authHeader,"X-Atlassian-Token":"no-check"},body:s});if(!i.ok){let c=await i.text();throw new Error(`Jira attach file failed (${i.status}): ${c}`)}}async getActiveSprintId(e){let r=`${this.baseUrl}/rest/agile/1.0/board/${e}/sprint?state=active&maxResults=1`,t=await fetch(r,{method:"GET",headers:this.headers});if(!t.ok){let s=await t.text();throw new Error(`Jira agile sprint lookup failed (${t.status}): ${s}`)}return(await this.safeJson(t,"Jira sprint lookup")).values[0]?.id??null}};function Me(o){return o.replace(/[.+?^${}()|[\]\\]/g,"\\$&")}function Fe(o,e){let r=e.split("*").map(Me).join(".*");return new RegExp(`^${r}$`).test(o)}function B(o,e){return e.some(r=>Fe(o,r))}function ae(o,e=""){return ce(o,e,new Set,new Set)}function ce(o,e="",r,t){let n=`${e}- ${o.purl}`;if(r.has(o.purl))return n+=" (cycle detected)",n;if(t.has(o.purl))return n+=" (deduplicated)",n;t.add(o.purl),r.add(o.purl);let s=r.size===1;for(let i of o.usedBy){let c=ce(i,e+(s?" ":" "),r,t);c!==""&&(n+=`
3
- `,n+=c)}return r.delete(o.purl),n}function le(o){let e=o.split(/\r?\n/),r=[],t=0;for(;t<e.length;){let n=e[t],s=n.match(/^```(\w*)$/);if(s){let a=s[1]??"text",l=[];for(t++;t<e.length&&!e[t].startsWith("```");)l.push(e[t]),t++;t++,r.push(N(l.join(`
4
- `),a));continue}let i=n.match(/^(#{1,3})\s+(.+)$/);if(i){let a=Math.min(i[1].length,3);r.push({type:"heading",attrs:{level:a},content:[{type:"text",text:i[2].trim()}]}),t++;continue}if(/^[-*]\s/.test(n)){let a=[];for(;t<e.length&&/^[-*]\s/.test(e[t]);){let l=e[t].replace(/^[-*]\s+/,"");a.push({type:"listItem",content:[{type:"paragraph",content:_(l)}]}),t++}r.push({type:"bulletList",content:a});continue}if(/^\d+\.\s/.test(n)){let a=[];for(;t<e.length&&/^\d+\.\s/.test(e[t]);){let l=e[t].replace(/^\d+\.\s+/,"");a.push({type:"listItem",content:[{type:"paragraph",content:_(l)}]}),t++}r.push({type:"orderedList",content:a});continue}if(n.trim()===""){t++;continue}let c=[];for(;t<e.length&&e[t].trim()!==""&&!/^(#{1,3}\s|[-*]\s|\d+\.\s|```)/.test(e[t]);)c.push(e[t]),t++;if(c.length>0){let a=[];for(let l=0;l<c.length;l++)a.push(..._(c[l])),l<c.length-1&&a.push({type:"hardBreak"});r.push({type:"paragraph",content:a})}}return r}function _(o){let e=[],r=/(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|_(.+?)_)/g,t=0,n;for(;(n=r.exec(o))!==null;)n.index>t&&e.push({type:"text",text:o.slice(t,n.index)}),n[2]!==void 0?e.push({type:"text",text:n[2],marks:[{type:"strong"}]}):n[3]!==void 0?e.push({type:"text",text:n[3],marks:[{type:"em"}]}):n[4]!==void 0?e.push({type:"text",text:n[4],marks:[{type:"code"}]}):n[5]!==void 0&&e.push({type:"text",text:n[5],marks:[{type:"em"}]}),t=n.index+n[0].length;return t<o.length&&e.push({type:"text",text:o.slice(t)}),e.length>0?e:[{type:"text",text:o}]}var Ve=2e4;function z(o){let e=o.title?`: ${o.title}`:"";return`[${o.severity}] ${o.id}${e}`}function de(o){let e=o.match(/^\[([A-Z]+)\]\s+(\S+?)(?::\s*(.*))?$/);if(!e)return;let[,r,t,n]=e;if(!(!r||!t)&&y.includes(r))return{severity:r,vulnId:t,title:n??""}}function ue(o,e,r=new Map){let t=[];if(t.push(C(1,"Informationen zur Schwachstelle")),t.push(h("Diese Informationen wurden automatisch generiert.")),o.refs&&o.refs.length>0&&t.push(oe(o.refs)),o.description){let n=le(o.description);t.push(L("Beschreibung",...n))}return t.push(C(2,"Betroffene Artefakte")),o.components&&o.components.length>0?t.push(ne(o.components)):t.push(h("Keine Komponenten bekannt.")),t.push(C(2,"Gemeldet f\xFCr")),ge(t,e,r),t.push(C(1,"Einwertung")),t.push(h("Einwertung des CVE in Bezug auf die gemeldeten Projekte/Produkte. Dies kann eine Bewertung der Schwere, der Auswirkungen und der Wahrscheinlichkeit eines Angriffs umfassen.")),t.push(C(1,"Betroffen sind")),t.push(h("Abschlie\xDFende Darstellung, welche Projekte/Produkte tats\xE4chlich betroffen oder nicht betroffen sind.")),t.push(C(1,"Ma\xDFnahmen")),t.push(h("\xDCbersicht der zu ergreifenden Ma\xDFnahmen, um den CVE zu beheben oder zu mitigieren. Dies kann Patches, Updates oder Konfigurations\xE4nderungen umfassen.")),j(...t)}function pe(o,e=new Map){let r=[];return r.push(h("Diese Projekte sind ebenfalls von dieser Schwachstelle betroffen und wurden automatisch erg\xE4nzt.")),r.push(C(2,"Zus\xE4tzlich gemeldet f\xFCr")),ge(r,o,e),j(...r)}function T(o){return o.slice().sort((e,r)=>e.projectId.localeCompare(r.projectId)).map(e=>` - ${e.projectId} (Schwelle ${e.threshold})`).join(`
5
- `)}function fe(o,e){let r=[`Automatisch geschlossen: Schwere ${o} liegt unterhalb der konfigurierten Auto-Close-Schwelle.`];return e.length>0&&(r.push("Auto-akzeptiert f\xFCr:"),r.push(T(e))),j(h(r.join(`
6
- `)))}function H(o,e,r){let t=[`Teilweise auto-akzeptiert. Schwere ${o}.`];return e.length>0&&(t.push("Auto-akzeptiert f\xFCr:"),t.push(T(e))),r.length>0&&(t.push("Manuelle Bewertung erforderlich f\xFCr:"),t.push(T(r))),j(h(t.join(`
7
- `)))}function he(o,e){let r=[];return o.length>0&&(r.push("Auto-akzeptiert f\xFCr:"),r.push(T(o))),e.length>0&&(r.push("Manuelle Bewertung erforderlich f\xFCr:"),r.push(T(e))),r.join(`
8
- `)}function ge(o,e,r){let t=0;for(let n of e.slice().sort()){o.push({type:"paragraph",content:[{type:"text",text:n,marks:[{type:"strong"}]}]});let s=r.get(n);if(s&&s.length>0){let i=ye(s);i&&(t+=i.length,t>Ve?o.push(L("Verwendungsbaum",h("Gesamtgr\xF6\xDFe der Verwendungsb\xE4ume \xFCberschreitet Limit. Siehe Issue-Attachments f\xFCr vollst\xE4ndige B\xE4ume."))):o.push(L("Verwendungsbaum",N(i))))}}}function me(o,e){if(e.size===0)return"";let r=[`# Verwendungsb\xE4ume f\xFCr ${o}`,""];for(let[t,n]of[...e.entries()].sort(([s],[i])=>s.localeCompare(i))){r.push(`## ${t}`,"");let s=ye(n);s&&r.push("```",s,"```"),r.push("")}return r.join(`
9
- `)}function ye(o){return!o||o.length===0?"":o.map(e=>ae(e,"")).join(`
2
+ "use strict";var Je=Object.create;var ne=Object.defineProperty;var Oe=Object.getOwnPropertyDescriptor;var De=Object.getOwnPropertyNames;var Ue=Object.getPrototypeOf,Me=Object.prototype.hasOwnProperty;var Fe=(o,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of De(e))!Me.call(o,n)&&n!==r&&ne(o,n,{get:()=>e[n],enumerable:!(t=Oe(e,n))||t.enumerable});return o};var Ve=(o,e,r)=>(r=o!=null?Je(Ue(o)):{},Fe(e||!o||!o.__esModule?ne(r,"default",{value:o,enumerable:!0}):r,o));var ee=require("path"),ke=require("zx");var Re=require("fs/promises");var k={UNKNOWN:0,LOW:1,MEDIUM:2,HIGH:3,CRITICAL:4},w=["UNKNOWN","LOW","MEDIUM","HIGH","CRITICAL"];function E(o,e){return k[o]-k[e]}function N(o,e){return k[o]<k[e]}function v(...o){return{type:"doc",version:1,content:o}}function P(o,e){return{type:"heading",attrs:{level:o},content:[{type:"text",text:e}]}}function L(o,e="text"){return{type:"codeBlock",attrs:{language:e},content:[{type:"text",text:o}]}}function oe(o){return{type:"bulletList",content:o.map(e=>({type:"listItem",content:[{type:"paragraph",content:[{type:"text",text:e}]}]}))}}function se(o){return{type:"bulletList",content:o.map(e=>({type:"listItem",content:[{type:"paragraph",content:[{type:"text",text:e,marks:[{type:"link",attrs:{href:e}}]}]}]}))}}function B(o,...e){return{type:"expand",attrs:{title:o},content:e}}function h(o){return{type:"paragraph",content:[{type:"text",text:o,marks:[{type:"em"}]}]}}var m=require("fs/promises");async function ie(o){try{return await _e(o)?JSON.parse(await(0,m.readFile)(o,{encoding:"utf-8"})):void 0}catch(e){throw new Error(`Error reading JSON file ${o}: ${e}`,{cause:e})}}async function _e(o){try{return await(0,m.access)(o,m.constants.F_OK),!0}catch{return!1}}function ae(o,e){return`Basic ${Buffer.from(`${o}:${e}`).toString("base64")}`}var J=class{authHeader;baseUrl;constructor(e,r,t){this.baseUrl=e.replace(/\/$/,""),this.authHeader=ae(r,t)}get headers(){return{Authorization:this.authHeader,"Content-Type":"application/json",Accept:"application/json"}}async safeJson(e,r){let t=await e.text();try{return JSON.parse(t)}catch{let n=t.slice(0,500).replace(/\s+/g," ").trim();throw new Error(`${r}: expected JSON but got (HTTP ${e.status}): ${n}`)}}async findExistingIssue(e,r){let t=e.replace(/"/g,'\\"'),n=r.replace(/"/g,'\\"'),s=`project = "${t}" AND summary ~ "\\"${n}\\""`,i=`${this.baseUrl}/rest/api/3/search/jql?jql=${encodeURIComponent(s)}&maxResults=1&fields=summary,labels,status`,c=await fetch(i,{method:"GET",headers:this.headers});if(!c.ok){let l=await c.text();throw new Error(`Jira search failed (${c.status}): ${l}`)}let d=(await this.safeJson(c,"Jira search")).issues[0];return d?{key:d.key,summary:d.fields?.summary??"",labels:d.fields?.labels??[],statusCategory:d.fields?.status?.statusCategory?.key??"new"}:null}async createIssue(e){let r={project:{key:e.projectKey},issuetype:{name:e.issueType},summary:e.summary,description:e.description};e.labels&&e.labels.length>0&&(r.labels=e.labels),e.sprintId!=null&&(r.customfield_10020=e.sprintId);let t={fields:r},n=await fetch(`${this.baseUrl}/rest/api/3/issue`,{method:"POST",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let i=await n.text();throw new Error(`Jira create issue failed (${n.status}): ${i}`)}return(await this.safeJson(n,"Jira create issue")).key}async addLabels(e,r){if(r.length===0)return;let t={update:{labels:r.map(s=>({add:s}))}},n=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}`,{method:"PUT",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let s=await n.text();throw new Error(`Jira add labels failed (${n.status}): ${s}`)}}async updateIssueSummary(e,r){let t={fields:{summary:r}},n=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}`,{method:"PUT",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let s=await n.text();throw new Error(`Jira update summary failed (${n.status}): ${s}`)}}async setSprint(e,r){let t={fields:{customfield_10020:r}},n=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}`,{method:"PUT",headers:this.headers,body:JSON.stringify(t)});if(!n.ok){let s=await n.text();throw new Error(`Jira set sprint failed (${n.status}): ${s}`)}}async addComment(e,r){let t=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}/comment`,{method:"POST",headers:this.headers,body:JSON.stringify({body:r})});if(!t.ok){let n=await t.text();throw new Error(`Jira add comment failed (${t.status}): ${n}`)}}async getTransitions(e){let r=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}/transitions`,{method:"GET",headers:this.headers});if(!r.ok){let n=await r.text();throw new Error(`Jira get transitions failed (${r.status}): ${n}`)}return(await this.safeJson(r,"Jira transitions lookup")).transitions.map(n=>({id:n.id,name:n.name,toStatusName:n.to?.name??"",toStatusCategory:n.to?.statusCategory?.key??""}))}async transitionIssue(e,r){let t=await fetch(`${this.baseUrl}/rest/api/3/issue/${e}/transitions`,{method:"POST",headers:this.headers,body:JSON.stringify({transition:{id:r}})});if(!t.ok){let n=await t.text();throw new Error(`Jira transition issue failed (${t.status}): ${n}`)}}async attachMarkdown(e,r,t){let n=`${this.baseUrl}/rest/api/3/issue/${e}/attachments`,s=new FormData;s.append("file",new Blob([t],{type:"text/markdown"}),r);let i=await fetch(n,{method:"POST",headers:{Authorization:this.authHeader,"X-Atlassian-Token":"no-check"},body:s});if(!i.ok){let c=await i.text();throw new Error(`Jira attach file failed (${i.status}): ${c}`)}}async getActiveSprintId(e){let r=`${this.baseUrl}/rest/agile/1.0/board/${e}/sprint?state=active&maxResults=1`,t=await fetch(r,{method:"GET",headers:this.headers});if(!t.ok){let s=await t.text();throw new Error(`Jira agile sprint lookup failed (${t.status}): ${s}`)}return(await this.safeJson(t,"Jira sprint lookup")).values[0]?.id??null}};function ze(o){return o.replace(/[.+?^${}()|[\]\\]/g,"\\$&")}function He(o,e){let r=e.split("*").map(ze).join(".*");return new RegExp(`^${r}$`).test(o)}function O(o,e){return e.some(r=>He(o,r))}function ce(o,e=""){return de(o,e,new Set,new Set)}function de(o,e="",r,t){let n=`${e}- ${o.purl}`;if(r.has(o.purl))return n+=" (cycle detected)",n;if(t.has(o.purl))return n+=" (deduplicated)",n;t.add(o.purl),r.add(o.purl);let s=r.size===1;for(let i of o.usedBy){let c=de(i,e+(s?" ":" "),r,t);c!==""&&(n+=`
3
+ `,n+=c)}return r.delete(o.purl),n}function le(o){let e=o.split(/\r?\n/),r=[],t=0;for(;t<e.length;){let n=e[t],s=n.match(/^```(\w*)$/);if(s){let a=s[1]??"text",d=[];for(t++;t<e.length&&!e[t].startsWith("```");)d.push(e[t]),t++;t++,r.push(L(d.join(`
4
+ `),a));continue}let i=n.match(/^(#{1,3})\s+(.+)$/);if(i){let a=Math.min(i[1].length,3);r.push({type:"heading",attrs:{level:a},content:[{type:"text",text:i[2].trim()}]}),t++;continue}if(/^[-*]\s/.test(n)){let a=[];for(;t<e.length&&/^[-*]\s/.test(e[t]);){let d=e[t].replace(/^[-*]\s+/,"");a.push({type:"listItem",content:[{type:"paragraph",content:z(d)}]}),t++}r.push({type:"bulletList",content:a});continue}if(/^\d+\.\s/.test(n)){let a=[];for(;t<e.length&&/^\d+\.\s/.test(e[t]);){let d=e[t].replace(/^\d+\.\s+/,"");a.push({type:"listItem",content:[{type:"paragraph",content:z(d)}]}),t++}r.push({type:"orderedList",content:a});continue}if(n.trim()===""){t++;continue}let c=[];for(;t<e.length&&e[t].trim()!==""&&!/^(#{1,3}\s|[-*]\s|\d+\.\s|```)/.test(e[t]);)c.push(e[t]),t++;if(c.length>0){let a=[];for(let d=0;d<c.length;d++)a.push(...z(c[d])),d<c.length-1&&a.push({type:"hardBreak"});r.push({type:"paragraph",content:a})}}return r}function z(o){let e=[],r=/(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|_(.+?)_)/g,t=0,n;for(;(n=r.exec(o))!==null;)n.index>t&&e.push({type:"text",text:o.slice(t,n.index)}),n[2]!==void 0?e.push({type:"text",text:n[2],marks:[{type:"strong"}]}):n[3]!==void 0?e.push({type:"text",text:n[3],marks:[{type:"em"}]}):n[4]!==void 0?e.push({type:"text",text:n[4],marks:[{type:"code"}]}):n[5]!==void 0&&e.push({type:"text",text:n[5],marks:[{type:"em"}]}),t=n.index+n[0].length;return t<o.length&&e.push({type:"text",text:o.slice(t)}),e.length>0?e:[{type:"text",text:o}]}var We=2e4;function H(o){let e=o.title?`: ${o.title}`:"";return`[${o.severity}] ${o.id}${e}`}function ue(o){let e=o.match(/^\[([A-Z]+)\]\s+(\S+?)(?::\s*(.*))?$/);if(!e)return;let[,r,t,n]=e;if(!(!r||!t)&&w.includes(r))return{severity:r,vulnId:t,title:n??""}}function pe(o,e,r=new Map){let t=[];if(t.push(P(1,"Informationen zur Schwachstelle")),t.push(h("Diese Informationen wurden automatisch generiert.")),o.refs&&o.refs.length>0&&t.push(se(o.refs)),o.description){let n=le(o.description);t.push(B("Beschreibung",...n))}return t.push(P(2,"Betroffene Artefakte")),o.components&&o.components.length>0?t.push(oe(o.components)):t.push(h("Keine Komponenten bekannt.")),t.push(P(2,"Gemeldet f\xFCr")),we(t,e,r),t.push(P(1,"Einwertung")),t.push(h("Einwertung des CVE in Bezug auf die gemeldeten Projekte/Produkte. Dies kann eine Bewertung der Schwere, der Auswirkungen und der Wahrscheinlichkeit eines Angriffs umfassen.")),t.push(P(1,"Betroffen sind")),t.push(h("Abschlie\xDFende Darstellung, welche Projekte/Produkte tats\xE4chlich betroffen oder nicht betroffen sind.")),t.push(P(1,"Ma\xDFnahmen")),t.push(h("\xDCbersicht der zu ergreifenden Ma\xDFnahmen, um den CVE zu beheben oder zu mitigieren. Dies kann Patches, Updates oder Konfigurations\xE4nderungen umfassen.")),v(...t)}function fe(o,e=new Map){let r=[];return r.push(h("Diese Projekte sind ebenfalls von dieser Schwachstelle betroffen und wurden automatisch erg\xE4nzt.")),r.push(P(2,"Zus\xE4tzlich gemeldet f\xFCr")),we(r,o,e),v(...r)}function I(o){return o.slice().sort((e,r)=>e.projectId.localeCompare(r.projectId)).map(e=>` - ${e.projectId} (Schwelle ${e.threshold})`).join(`
5
+ `)}function he(o,e){let r=[`Automatisch geschlossen: Schwere ${o} liegt unterhalb der konfigurierten Auto-Close-Schwelle.`];return e.length>0&&(r.push("Auto-akzeptiert f\xFCr:"),r.push(I(e))),v(h(r.join(`
6
+ `)))}function ge(o,e,r){let t=[`Teilweise auto-akzeptiert. Schwere ${o}.`];return e.length>0&&(t.push("Auto-akzeptiert f\xFCr:"),t.push(I(e))),r.length>0&&(t.push("Manuelle Bewertung erforderlich f\xFCr:"),t.push(I(r))),v(h(t.join(`
7
+ `)))}function me(o,e,r){let t=r.slice().sort(),n=`Neue betroffene Projekte gefunden, aber Issue bleibt geschlossen: Schweregrad ${o} liegt unterhalb der Auto-Close-Schwelle ${e}. Betroffene Projekte: ${t.join(", ")}.`;return v(h(n))}function ye(o,e){let r=[];return o.length>0&&(r.push("Auto-akzeptiert f\xFCr:"),r.push(I(o))),e.length>0&&(r.push("Manuelle Bewertung erforderlich f\xFCr:"),r.push(I(e))),r.join(`
8
+ `)}function we(o,e,r){let t=0;for(let n of e.slice().sort()){o.push({type:"paragraph",content:[{type:"text",text:n,marks:[{type:"strong"}]}]});let s=r.get(n);if(s&&s.length>0){let i=ve(s);i&&(t+=i.length,t>We?o.push(B("Verwendungsbaum",h("Gesamtgr\xF6\xDFe der Verwendungsb\xE4ume \xFCberschreitet Limit. Siehe Issue-Attachments f\xFCr vollst\xE4ndige B\xE4ume."))):o.push(B("Verwendungsbaum",L(i))))}}}function W(o,e){if(e.size===0)return"";let r=[`# Verwendungsb\xE4ume f\xFCr ${o}`,""];for(let[t,n]of[...e.entries()].sort(([s],[i])=>s.localeCompare(i))){r.push(`## ${t}`,"");let s=ve(n);s&&r.push("```",s,"```"),r.push("")}return r.join(`
9
+ `)}function ve(o){return!o||o.length===0?"":o.map(e=>ce(e,"")).join(`
10
10
 
11
- `)}var W="proj:";function G(o){let e=o.replace(/@/g,"_v").replace(/\s+/g,"_");return`${W}${e}`}function _e(o){if(!o.startsWith(W))return null;let e=o.slice(W.length),r=e.lastIndexOf("_v");return r<0?e:e.slice(0,r)+"@"+e.slice(r+2)}function we(o){let e=new Set;if(!o)return e;for(let r of o){let t=_e(r);t!==null&&e.add(t)}return e}function ve(o){let e=o.severityChange,r=o.newProjects;return e&&r&&r.length>0?`Issue wieder ge\xF6ffnet, da sich der Schweregrad von ${e.from} auf ${e.to} ge\xE4ndert hat und zus\xE4tzlich neue betroffene Projekte gefunden wurden: ${r.join(", ")}.`:e?`Issue wieder ge\xF6ffnet: Schweregrad hat sich von ${e.from} auf ${e.to} ge\xE4ndert. Eine ggf. bestehende automatische VEX-Ausnahme ist nicht mehr g\xFCltig und sollte erneut bewertet werden.`:r&&r.length>0?`Issue wieder ge\xF6ffnet, da die Schwachstelle nun zus\xE4tzlich folgende Projekte betrifft: ${r.join(", ")}. Bitte erneut bewerten, ob die urspr\xFCngliche L\xF6sung/Mitigation weiterhin ausreicht.`:"Issue wieder ge\xF6ffnet."}function Se(o){let e=[];return o.severityChange&&e.push(`severity ${o.severityChange.from}->${o.severityChange.to}`),o.newProjects&&o.newProjects.length>0&&e.push(`new projects: ${o.newProjects.join(", ")}`),e.join(" + ")||"unspecified"}function w(o){return o.length===0?"-":o.map(e=>e.projectId).slice().sort((e,r)=>e.localeCompare(r)).join(", ")}function ze(o){let e=[];return o.autoAcknowledged.length>0&&e.push(`auto-ack: ${w(o.autoAcknowledged)}`),o.manualEvaluation.length>0&&e.push(`manual: ${w(o.manualEvaluation)}`),e.join("; ")}var je=10,Ce=" ".repeat(je);function q(o){return`[${o}]`.padEnd(je," ")}function $e(o){return o==="warn"?console.warn:o==="error"?console.error:console.log}function He(o,e,r=[]){let t=$e(o);t(`${q(o)}${e}`);for(let n of r)t(`${Ce}${n}`)}function We(o){let e="\u2500".repeat(Math.max(0,62-o.length-4));console.log(""),console.log(`\u2500\u2500 ${o} ${e}`)}var K=class{vulnId;jiraProject;severity;issueKey;kind="skip";header="";details=[];constructor(e,r,t){this.vulnId=e,this.jiraProject=r,this.severity=t}setOutcome(e,r){this.kind=e,this.header=this.composeHeader(r)}detail(e,r){this.details.push(`${e.padEnd(9," ")}: ${r}`)}note(e){this.details.push(e)}warn(e,r){console.warn(`${q("warn")}${e}`),this.detail("warn",e),r.warnings.push(e)}flush(){let e=$e(this.kind);e(`${q(this.kind)}${this.header}`);for(let r of this.details)e(`${Ce}${r}`)}composeHeader(e){let r=this.issueKey??"<new>",t=e?` ${e}`:"",n=r.padEnd(12," ");return`${this.vulnId} -> ${n} in ${this.jiraProject}, severity=${this.severity}${t}`}},O=class{constructor(e,r,t){this.config=e;this.client=new J(e.jiraUrl,r,t)}config;client;activeSprintCache=new Map;async report(e,r){let t={created:[],updated:[],skipped:[],errors:[],warnings:[],noMatchingRule:[],reopened:[],autoClosed:[],keptClosed:[]};We("Issue processing");for(let[n,s]of Object.entries(e.vulnerabilities)){let i=e.vulnerabilityToProject[n]??[],c=!1;for(let a of this.config.issues){let l=i.filter(u=>B(u,a.reportProjects));if(l.length===0)continue;c=!0;let d=new K(n,a.jiraProject,s.severity);try{let u=await this.client.findExistingIssue(a.jiraProject,n);u?(d.issueKey=u.key,await this.handleExistingIssue(e,s,u,a,l,r,t,d)):await this.handleNewIssue(e,n,s,a,l,r,t,d)}catch(u){let p=`Failed to process ${n} for project ${a.jiraProject}: ${String(u)}`;t.errors.push(p),d.setOutcome("error"),d.note(`error : ${String(u)}`)}d.flush()}c||(t.noMatchingRule.push(n),He("skip",`${n} (no matching jira-project rule, severity=${s.severity})`))}return t}async handleNewIssue(e,r,t,n,s,i,c,a){let l=z(t),d=this.bucketProjects(s,t.severity,n),u=d.shouldAutoClose,p=!u&&d.autoAcknowledged.length>0&&d.manualEvaluation.length>0;if(i){a.setOutcome("dry-run"),a.detail("would",`create issue with title "${l}"`),a.detail("projects",s.join(", "));let f=`${n.jiraProject}::${r}`;u?(a.detail("would",`auto-close (severity ${t.severity} below threshold ${d.strictestThreshold}; auto-ack: ${w(d.autoAcknowledged)})`),c.autoClosed.push(f)):p&&a.detail("would",`post partial-ack comment (auto: ${w(d.autoAcknowledged)}; manual: ${w(d.manualEvaluation)})`),c.created.push(f);return}let{activeSprintId:v,allLabels:S}=await this.resolveBoardRouting(n,s,a,c),P=this.collectProjectComponentTrees(e,s,t.components),R=ue(t,s,P),A=[...S,...s.map(G)],g=await this.client.createIssue({projectKey:n.jiraProject,issueType:n.issueType,summary:l,description:R,labels:A.length>0?A:void 0,sprintId:v});if(a.issueKey=g,a.setOutcome("create"),a.detail("title",l),a.detail("projects",s.join(", ")),c.created.push(g),P.size>0){let f=me(r,P);if(f){let x=`${r}-dependency-trees.md`;await this.client.attachMarkdown(g,x,f),a.detail("attach",x)}}u?await this.autoCloseIssue(g,t.severity,d,a,c):p&&await this.postPartialAckComment(g,t.severity,d,a,c)}async handleExistingIssue(e,r,t,n,s,i,c,a){let l=we(t.labels),d=s.filter(b=>!l.has(b)),p=de(t.summary)?.severity,v=p!==void 0&&k(r.severity,p)>0;if(d.length===0&&!v){a.setOutcome("skip"),a.detail("reason",`already covers all matching projects (${s.join(", ")})`),c.skipped.push(t.key);return}let S=t.statusCategory==="done",P=this.bucketProjects(s,r.severity,n),R=P.strictestThreshold,A=P.shouldAutoClose,g=S&&!A,f=this.bucketProjects(d,r.severity,n),x=g?he(f.autoAcknowledged,f.manualEvaluation):"",ee=!g&&d.length>0&&f.autoAcknowledged.length>0&&f.manualEvaluation.length>0,M=g?{severityChange:v?{from:p,to:r.severity}:void 0,newProjects:d.length>0?d:void 0}:void 0;if(i){if(a.setOutcome("dry-run"),v&&a.detail("would",`retitle severity ${p} -> ${r.severity}`),d.length>0&&a.detail("would",`add project tag(s) ${d.join(", ")}`),g){a.note("would re-open + re-add to active sprint");let b=x?`${Se(M)}; ${ze(f)}`:Se(M);a.detail("would",`post reopen-reason comment (${b})`),c.reopened.push(t.key)}else S&&(a.note(`would keep closed (severity ${r.severity} below auto-close threshold ${R})`),c.keptClosed.push(t.key));ee&&a.detail("would",`post partial-ack comment (auto: ${w(f.autoAcknowledged)}; manual: ${w(f.manualEvaluation)})`),c.updated.push(t.key);return}let{activeSprintId:Re,allLabels:Ie}=await this.resolveBoardRouting(n,s,a,c);if(v){let b=z(r);try{await this.client.updateIssueSummary(t.key,b),a.detail("retitle",`${p} -> ${r.severity}`)}catch(F){a.warn(`Could not update summary of ${t.key}: ${String(F)}`,c)}g||await this.postSeverityChangeComment(t.key,p,r.severity,a,c)}if(d.length>0){let b=this.collectProjectComponentTrees(e,d,r.components),F=[...Ie,...d.map(G)],ke=new Set(t.labels),te=F.filter(V=>!ke.has(V));if(te.length>0&&await this.client.addLabels(t.key,te),!g){let V=ee?H(r.severity,f.autoAcknowledged,f.manualEvaluation):pe(d,b);await this.client.addComment(t.key,V)}a.setOutcome("update"),a.detail("projects",`added ${d.join(", ")}`)}else a.setOutcome("update"),a.detail("reason","severity changed (no new projects)");S&&A?(a.note(`kept closed: severity ${r.severity} below auto-close threshold ${R}`),c.keptClosed.push(t.key)):S&&await this.reopenAndReassignSprint(t.key,Re,M,x,a,c),c.updated.push(t.key)}async postSeverityChangeComment(e,r,t,n,s){try{let i=j(h(`Schweregrad ge\xE4ndert von ${r} auf ${t}. Falls eine automatisch erstellte VEX-Ausnahme bestand, ist sie nicht mehr g\xFCltig.`));await this.client.addComment(e,i)}catch(i){n.warn(`Could not post severity-change comment on ${e}: ${String(i)}`,s)}}async reopenAndReassignSprint(e,r,t,n,s,i){let c;try{c=await this.client.getTransitions(e)}catch(l){s.warn(`Could not list transitions for ${e}: ${String(l)}`,i);return}let a=c.find(l=>l.toStatusCategory==="new");if(!a){s.warn(`${e} is closed but no transition leading to a non-done status is available; leaving status unchanged`,i);return}try{await this.client.transitionIssue(e,a.id),s.detail("reopen",`via "${a.name}"`),i.reopened.push(e)}catch(l){s.warn(`Could not transition ${e} to a non-done status: ${String(l)}`,i);return}if(await this.postReopenReasonComment(e,t,n,s,i),r!=null)try{await this.client.setSprint(e,r),s.detail("sprint",`added to active sprint ${r}`)}catch(l){s.warn(`Could not set sprint for ${e}: ${String(l)}`,i)}}async postReopenReasonComment(e,r,t,n,s){try{let i=t?`${ve(r)}
12
- ${t}`:ve(r),c=j(h(i));await this.client.addComment(e,c),n.detail("comment","reopen-reason posted")}catch(i){n.warn(`Could not post reopen-reason comment on ${e}: ${String(i)}`,s)}}resolveAutoCloseThreshold(e){return e.autoCloseBelowSeverity??this.config.autoCloseBelowSeverity}effectiveProjectThreshold(e,r){let t;for(let n of r.projectOverrides??[])B(e,n.projects)&&(t==null||k(n.autoCloseBelowSeverity,t)<0)&&(t=n.autoCloseBelowSeverity);return t??this.resolveAutoCloseThreshold(r)}bucketProjects(e,r,t){if(e.length===0){let l=this.resolveAutoCloseThreshold(t);return{strictestThreshold:l,autoAcknowledged:[],manualEvaluation:[],shouldAutoClose:l!=null&&E(r,l)}}let n=[],s=[],i,c=!1;for(let l of e){let d=this.effectiveProjectThreshold(l,t);if(d==null){c=!0;continue}(i==null||k(d,i)<0)&&(i=d);let u={projectId:l,threshold:d};E(r,d)?n.push(u):s.push(u)}let a=!c&&s.length===0&&n.length>0&&i!=null&&E(r,i);return{strictestThreshold:i,autoAcknowledged:n,manualEvaluation:s,shouldAutoClose:a}}async autoCloseIssue(e,r,t,n,s){let i=t.strictestThreshold,c;try{c=await this.client.getTransitions(e)}catch(d){n.warn(`Could not list transitions for ${e} (auto-close): ${String(d)}`,s);return}let a=c.filter(d=>d.toStatusCategory==="done"),l=a.find(d=>d.toStatusName.toLowerCase()==="closed")??a.find(d=>{let u=d.name.toLowerCase();return u==="close"||u==="closed"})??a[0];if(!l){n.warn(`${e} has no transition leading to a done status; cannot auto-close`,s);return}try{await this.client.transitionIssue(e,l.id),n.detail("close",`auto-closed (severity ${r} below ${i}) via "${l.name}"`),s.autoClosed.push(e)}catch(d){n.warn(`Could not transition ${e} to a done status: ${String(d)}`,s);return}try{let d=fe(r,t.autoAcknowledged);await this.client.addComment(e,d)}catch(d){n.warn(`Could not post auto-close comment on ${e}: ${String(d)}`,s)}}async postPartialAckComment(e,r,t,n,s){try{let i=H(r,t.autoAcknowledged,t.manualEvaluation);await this.client.addComment(e,i),n.detail("comment",`partial-ack posted (auto: ${w(t.autoAcknowledged)}; manual: ${w(t.manualEvaluation)})`)}catch(i){n.warn(`Could not post partial-ack comment on ${e}: ${String(i)}`,s)}}async resolveBoardRouting(e,r,t,n){let s,i=[],c=!1;for(let l of e.addToBoard??[]){let d=l.matchProjects??["*"];if(r.some(p=>B(p,d)))if(l.sprintBoardId!=null){if(!c){c=!0;let p=l.sprintBoardId;if(!this.activeSprintCache.has(p)){let v=await this.client.getActiveSprintId(p).catch(S=>(t.warn(`Could not resolve active sprint for boardId ${p}: ${String(S)}`,n),null));this.activeSprintCache.set(p,v)}s=this.activeSprintCache.get(p)??void 0,l.issueLabels&&i.push(...l.issueLabels)}}else l.issueLabels&&i.push(...l.issueLabels)}let a=[...e.issueLabels??[],...i];return{activeSprintId:s,allLabels:a}}collectProjectComponentTrees(e,r,t){let n=new Map;if(!e.projectsToComponents)return n;for(let s of r){let i=e.projectsToComponents[s]??{},c=[];for(let a of t){let l=i[a];l&&c.push(l)}c.length>0&&n.set(s,c)}return n}};var $=De(require("path")),X=require("fs/promises");var Y=class o{constructor(e){this.workingDir=e}workingDir;static DEFAULT_MAVEN_REPO="https://repository.conterra.de/repository/maven-mirror-ct";static DEFAULT_VEX_SUBDIR="input/vex";static DEFAULT_OUT_SUBDIR="output";static DEFAULT_CACHE_SUBDIR="cache";static DEFAULT_SCANNERS=["grype","trivy","ossindex"];normalize(e,r){let t=e.findNoLongerDetectedVulnerabilitiesOnlyForSnapshots??!1;return{projectConfigFileLocation:r,onNewVulnerabilities:e.onNewVulnerabilities??(e.failOnNewVulnerabilities?"fail":"warn"),onNoLongerDetectedVulnerabilities:e.onNoLongerDetectedVulnerabilities??"ignore",reportNoLongerDetectedVulnerabilities:e.reportNoLongerDetectedVulnerabilities??!0,consoleReport:e.consoleReport??"BY_PROJECT_DETAILED",createSubDirsForProject:e.createSubDirsForProject??!0,mavenRepo:e.mavenRepo??o.DEFAULT_MAVEN_REPO,vexUrls:e.vexUrls??[],vexDir:this.resolvePath(e.vexDir)??(0,$.resolve)(this.workingDir,o.DEFAULT_VEX_SUBDIR),outDir:this.resolvePath(e.outDir)??(0,$.resolve)(this.workingDir,o.DEFAULT_OUT_SUBDIR),cacheDir:this.resolvePath(e.cacheDir)??(0,$.resolve)(this.workingDir,o.DEFAULT_CACHE_SUBDIR),scanners:this.normalizeScanners(e.scanners,r),projects:this.normalizeProjects(e.projects??[],t)}}normalizeScanners(e,r){return(e??o.DEFAULT_SCANNERS).map(n=>{if(typeof n!="string")throw new Error(`Invalid scanner name '${String(n)}' in configuration file '${r}'`);let s=n.split("@");if(s.length>2)throw new Error(`Invalid scanner name '${n}' in configuration file '${r}'`);return{name:s[0],version:s[1]??""}})}normalizeProjects(e,r){let t=e.map(n=>{let s=n;if(!s.name)throw new Error("Project name is missing on entry: "+JSON.stringify(s));if(!s.version)throw new Error("Project version is missing on entry: "+JSON.stringify(s));if(!s.purl)throw new Error("Project purl is missing on entry: "+JSON.stringify(s));let i={...s,purlAliases:s.purlAliases??[],enabled:s.enabled??!0,silent:s.silent??!1,minimumSeverity:s.minimumSeverity??"UNKNOWN"};return i.sbomFile?{...i,sbomFile:this.resolvePath(i.sbomFile)??i.sbomFile}:{...i,detectNoLongerUsedVulnerabilities:r?i.purl.endsWith("-SNAPSHOT"):!0}});return this.assertUniqueProjectIds(t),this.assertUniqueProjectPurls(t),t}assertUniqueProjectIds(e){let r=new Set;for(let t of e){let n=`${t.name}@${t.version}`;if(r.has(n))throw new Error(`Duplicate project id '${n}' in vuln-scan-conf.json: each 'name@version' pair must be unique across the projects array.`);r.add(n)}}assertUniqueProjectPurls(e){let r=new Map;for(let t of e){let n=`${t.name}@${t.version}`,s=r.get(t.purl);if(s!==void 0)throw new Error(`Duplicate project purl '${t.purl}' in vuln-scan-conf.json: used by '${s}' and '${n}'. Each project must have a unique purl; use 'purlAliases' when two projects should be matched by the same VEX statement.`);r.set(t.purl,n)}}resolvePath(e){if(e)return $.default.isAbsolute(e)?e:(0,$.resolve)(this.workingDir,e)}},D=class{constructor(e){this.workingDir=e;this.normalizer=new Y(e)}workingDir;normalizer;projectConfigCache;async loadProjectConfig(){return this.projectConfigCache??=this.doLoadProjectConfig(),this.projectConfigCache}async doLoadProjectConfig(){let e=(0,$.resolve)(this.workingDir,"vuln-scan-conf.json"),r=await se(e);if(!r)throw new Error(`Missing project configuration file '${e}'`);return this.normalizer.normalize(r,e)}async loadJiraConfig(e){let r=await(0,X.readFile)(e,"utf8"),t=JSON.parse(r);if(!t.issues&&t.issue&&(t.issues=t.issue),!t.jiraUrl||typeof t.jiraUrl!="string")throw new Error("jira-conf.json: missing or invalid 'jiraUrl' field");if(!Array.isArray(t.issues))throw new Error("jira-conf.json: missing or invalid 'issues' array");this.validateAutoCloseSeverity(t.autoCloseBelowSeverity,"jira-conf.json: invalid 'autoCloseBelowSeverity'");for(let[n,s]of t.issues.entries())if(s&&typeof s=="object"){let i=s;this.validateAutoCloseSeverity(i.autoCloseBelowSeverity,`jira-conf.json: invalid 'issues[${n}].autoCloseBelowSeverity'`),this.validateAutoCloseProjectOverrides(i.projectOverrides,`jira-conf.json: invalid 'issues[${n}].projectOverrides'`)}return t}validateAutoCloseSeverity(e,r){if(e!==void 0&&!y.includes(e))throw new Error(`${r} value '${String(e)}'. Must be one of: ${y.join(", ")}`)}validateAutoCloseProjectOverrides(e,r){if(e!==void 0){if(!Array.isArray(e))throw new Error(`${r}: must be an array`);for(let t=0;t<e.length;t++){let n=e[t],s=`${r}[${t}]`;if(n==null||typeof n!="object")throw new Error(`${s}: must be an object`);let i=n,c=i.projects;if(!Array.isArray(c)||c.length===0)throw new Error(`${s}.projects: must be a non-empty array of strings`);for(let a=0;a<c.length;a++){let l=c[a];if(typeof l!="string"||l.length===0)throw new Error(`${s}.projects[${a}]: must be a non-empty string`)}if(i.autoCloseBelowSeverity===void 0)throw new Error(`${s}.autoCloseBelowSeverity: is required`);if(this.validateAutoCloseSeverity(i.autoCloseBelowSeverity,`${s}.autoCloseBelowSeverity`),i.comment!==void 0&&typeof i.comment!="string")throw new Error(`${s}.comment: must be a string`)}}}async loadAutoVexConfig(e){let r=await(0,X.readFile)(e,"utf8"),t=JSON.parse(r);if(!t.impactStatement||typeof t.impactStatement!="string")throw new Error("auto-vex-conf.json: missing or invalid 'impactStatement' field (must be a non-empty string)");if(t.maxSeverity!==void 0&&!y.includes(t.maxSeverity))throw new Error(`auto-vex-conf.json: invalid 'maxSeverity' value '${String(t.maxSeverity)}'. Must be one of: ${y.join(", ")}`);if(t.projectOverrides!==void 0){if(!Array.isArray(t.projectOverrides))throw new Error("auto-vex-conf.json: 'projectOverrides' must be an array");for(let[n,s]of t.projectOverrides.entries()){if(!s||typeof s!="object")throw new Error(`auto-vex-conf.json: 'projectOverrides[${n}]' must be an object`);let i=s;if(!Array.isArray(i.projects)||i.projects.length===0||!i.projects.every(c=>typeof c=="string"&&c.length>0))throw new Error(`auto-vex-conf.json: 'projectOverrides[${n}].projects' must be a non-empty array of non-empty strings`);if(!y.includes(i.maxSeverity))throw new Error(`auto-vex-conf.json: invalid 'projectOverrides[${n}].maxSeverity' value '${String(i.maxSeverity)}'. Must be one of: ${y.join(", ")}`)}}return t}};async function Pe(o){let{scanSummaryPath:e,jiraConfigPath:r,jiraUser:t,jiraApiToken:n,dryRun:s=!1}=o,i=new D(process.cwd()),[c,a]=await Promise.all([Ge(e),i.loadJiraConfig(r)]);return new O(a,t,n).report(c,s)}function Ae(o){return o.created.length===0&&o.updated.length===0&&o.skipped.length===0&&o.noMatchingRule.length===0&&o.autoClosed.length===0&&o.reopened.length===0&&o.keptClosed.length===0&&o.warnings.length===0&&o.errors.length===0}async function Ge(o){let e=await(0,be.readFile)(o,"utf8");return JSON.parse(e)}var Z={flag:"--dry-run",description:"Print what would happen without making changes"},U=class{specs;booleanSpecs;commandName;helpLines;constructor(e,r,t=[],n=[]){this.commandName=e,this.specs=r;let s=n.some(i=>i.flag===Z.flag);this.booleanSpecs=s?n:[...n,Z],this.helpLines=t}parse(e){let r=e.slice(2),t=new Map(this.specs.map(s=>[s.flag,s.defaultValue])),n=new Map(this.booleanSpecs.map(s=>[s.flag,!1]));for(let s=0;s<r.length;s++){let i=r[s];(i==="--help"||i==="-h")&&(this.printHelp(),process.exit(0));let c=this.booleanSpecs.find(l=>l.flag===i||l.alias===i);if(c){n.set(c.flag,!0);continue}let a=this.specs.find(l=>l.flag===i||l.alias===i);a&&r[s+1]?t.set(a.flag,r[++s]):(console.error(`Unknown argument: ${i}`),this.printHelp(),process.exit(1))}return{values:t,dryRun:n.get(Z.flag)===!0,booleans:n}}printHelp(){let e=this.specs.map(t=>` ${(t.alias?`${t.alias}, ${t.flag} <path>`:` ${t.flag} <path>`).padEnd(38)}${t.description}
11
+ `)}var K="proj:";function G(o){let e=o.replace(/@/g,"_v").replace(/\s+/g,"_");return`${K}${e}`}function Ke(o){if(!o.startsWith(K))return null;let e=o.slice(K.length),r=e.lastIndexOf("_v");return r<0?e:e.slice(0,r)+"@"+e.slice(r+2)}function Se(o){let e=new Set;if(!o)return e;for(let r of o){let t=Ke(r);t!==null&&e.add(t)}return e}function Ce(o){let e=o.severityChange,r=o.newProjects;return e&&r&&r.length>0?`Issue wieder ge\xF6ffnet, da sich der Schweregrad von ${e.from} auf ${e.to} ge\xE4ndert hat und zus\xE4tzlich neue betroffene Projekte gefunden wurden: ${r.join(", ")}.`:e?`Issue wieder ge\xF6ffnet: Schweregrad hat sich von ${e.from} auf ${e.to} ge\xE4ndert. Eine ggf. bestehende automatische VEX-Ausnahme ist nicht mehr g\xFCltig und sollte erneut bewertet werden.`:r&&r.length>0?`Issue wieder ge\xF6ffnet, da die Schwachstelle nun zus\xE4tzlich folgende Projekte betrifft: ${r.join(", ")}. Bitte erneut bewerten, ob die urspr\xFCngliche L\xF6sung/Mitigation weiterhin ausreicht.`:"Issue wieder ge\xF6ffnet."}function je(o){let e=[];return o.severityChange&&e.push(`severity ${o.severityChange.from}->${o.severityChange.to}`),o.newProjects&&o.newProjects.length>0&&e.push(`new projects: ${o.newProjects.join(", ")}`),e.join(" + ")||"unspecified"}function S(o){return o.length===0?"-":o.map(e=>e.projectId).slice().sort((e,r)=>e.localeCompare(r)).join(", ")}function $e(){return new Date().toISOString().slice(0,10)}function Ge(o){let e=[];return o.autoAcknowledged.length>0&&e.push(`auto-ack: ${S(o.autoAcknowledged)}`),o.manualEvaluation.length>0&&e.push(`manual: ${S(o.manualEvaluation)}`),e.join("; ")}var Pe=10,be=" ".repeat(Pe);function q(o){return`[${o}]`.padEnd(Pe," ")}function Ae(o){return o==="warn"?console.warn:o==="error"?console.error:console.log}function qe(o,e,r=[]){let t=Ae(o);t(`${q(o)}${e}`);for(let n of r)t(`${be}${n}`)}function Xe(o){let e="\u2500".repeat(Math.max(0,62-o.length-4));console.log(""),console.log(`\u2500\u2500 ${o} ${e}`)}var X=class{vulnId;jiraProject;severity;issueKey;kind="skip";header="";details=[];constructor(e,r,t){this.vulnId=e,this.jiraProject=r,this.severity=t}setOutcome(e,r){this.kind=e,this.header=this.composeHeader(r)}detail(e,r){this.details.push(`${e.padEnd(9," ")}: ${r}`)}note(e){this.details.push(e)}warn(e,r){console.warn(`${q("warn")}${e}`),this.detail("warn",e),r.warnings.push(e)}flush(){let e=Ae(this.kind);e(`${q(this.kind)}${this.header}`);for(let r of this.details)e(`${be}${r}`)}composeHeader(e){let r=this.issueKey??"<new>",t=e?` ${e}`:"",n=r.padEnd(12," ");return`${this.vulnId} -> ${n} in ${this.jiraProject}, severity=${this.severity}${t}`}},D=class{constructor(e,r,t){this.config=e;this.client=new J(e.jiraUrl,r,t)}config;client;activeSprintCache=new Map;async report(e,r){let t={created:[],updated:[],skipped:[],errors:[],warnings:[],noMatchingRule:[],reopened:[],autoClosed:[],keptClosed:[]};Xe("Issue processing");for(let[n,s]of Object.entries(e.vulnerabilities)){let i=e.vulnerabilityToProject[n]??[],c=!1;for(let a of this.config.issues){let d=i.filter(u=>O(u,a.reportProjects));if(d.length===0)continue;c=!0;let l=new X(n,a.jiraProject,s.severity);try{let u=await this.client.findExistingIssue(a.jiraProject,n);u?(l.issueKey=u.key,await this.handleExistingIssue(e,s,u,a,d,r,t,l)):await this.handleNewIssue(e,n,s,a,d,r,t,l)}catch(u){let p=`Failed to process ${n} for project ${a.jiraProject}: ${String(u)}`;t.errors.push(p),l.setOutcome("error"),l.note(`error : ${String(u)}`)}l.flush()}c||(t.noMatchingRule.push(n),qe("skip",`${n} (no matching jira-project rule, severity=${s.severity})`))}return t}async handleNewIssue(e,r,t,n,s,i,c,a){let d=H(t),l=this.bucketProjects(s,t.severity,n),u=l.shouldAutoClose,p=!u&&l.autoAcknowledged.length>0&&l.manualEvaluation.length>0;if(i){a.setOutcome("dry-run"),a.detail("would",`create issue with title "${d}"`),a.detail("projects",s.join(", "));let f=`${n.jiraProject}::${r}`;u?(a.detail("would",`auto-close (severity ${t.severity} below threshold ${l.strictestThreshold}; auto-ack: ${S(l.autoAcknowledged)})`),c.autoClosed.push(f)):p&&a.detail("would",`post partial-ack comment (auto: ${S(l.autoAcknowledged)}; manual: ${S(l.manualEvaluation)})`),c.created.push(f);return}let{activeSprintId:C,allLabels:j}=await this.resolveBoardRouting(n,s,a,c),R=this.collectProjectComponentTrees(e,s,t.components),A=pe(t,s,R),T=[...j,...s.map(G)],g=await this.client.createIssue({projectKey:n.jiraProject,issueType:n.issueType,summary:d,description:A,labels:T.length>0?T:void 0,sprintId:C});if(a.issueKey=g,a.setOutcome("create"),a.detail("title",d),a.detail("projects",s.join(", ")),c.created.push(g),R.size>0){let f=W(r,R);if(f){let x=`${r}-dependency-trees.md`;await this.client.attachMarkdown(g,x,f),a.detail("attach",x)}}u?await this.autoCloseIssue(g,t.severity,l,a,c):p&&await this.postPartialAckComment(g,t.severity,l,a,c)}async handleExistingIssue(e,r,t,n,s,i,c,a){let d=Se(t.labels),l=s.filter(y=>!d.has(y)),p=ue(t.summary)?.severity,C=p!==void 0&&E(r.severity,p)>0;if(l.length===0&&!C){a.setOutcome("skip"),a.detail("reason",`already covers all matching projects (${s.join(", ")})`),c.skipped.push(t.key);return}let j=t.statusCategory==="done",R=this.bucketProjects(s,r.severity,n),A=R.strictestThreshold,T=R.shouldAutoClose,g=j&&!T,f=this.bucketProjects(l,r.severity,n),x=g?ye(f.autoAcknowledged,f.manualEvaluation):"",te=!g&&l.length>0&&f.autoAcknowledged.length>0&&f.manualEvaluation.length>0,F=g?{severityChange:C?{from:p,to:r.severity}:void 0,newProjects:l.length>0?l:void 0}:void 0;if(i){if(a.setOutcome("dry-run"),C&&a.detail("would",`retitle severity ${p} -> ${r.severity}`),l.length>0&&(a.detail("would",`add project tag(s) ${l.join(", ")}`),a.detail("would","post additional-projects comment (with dependency trees)"),a.detail("would",`attach ${r.id}-dependency-trees-${$e()}.md`)),g){a.note("would re-open + re-add to active sprint");let y=x?`${je(F)}; ${Ge(f)}`:je(F);a.detail("would",`post reopen-reason comment (${y})`),c.reopened.push(t.key)}else j&&(a.note(`would keep closed (severity ${r.severity} below auto-close threshold ${A})`),l.length>0&&a.detail("would",`post kept-closed reason comment (severity ${r.severity} below ${A})`),c.keptClosed.push(t.key));te&&a.detail("would",`post partial-ack comment (auto: ${S(f.autoAcknowledged)}; manual: ${S(f.manualEvaluation)})`),c.updated.push(t.key);return}let{activeSprintId:Ee,allLabels:Ne}=await this.resolveBoardRouting(n,s,a,c);if(C){let y=H(r);try{await this.client.updateIssueSummary(t.key,y),a.detail("retitle",`${p} -> ${r.severity}`)}catch(V){a.warn(`Could not update summary of ${t.key}: ${String(V)}`,c)}g||await this.postSeverityChangeComment(t.key,p,r.severity,a,c)}if(l.length>0){let y=this.collectProjectComponentTrees(e,l,r.components),V=[...Ne,...l.map(G)],Le=new Set(t.labels),re=V.filter($=>!Le.has($));re.length>0&&await this.client.addLabels(t.key,re);try{let $=fe(l,y);await this.client.addComment(t.key,$),a.detail("comment","additional-projects posted")}catch($){a.warn(`Could not post additional-projects comment on ${t.key}: ${String($)}`,c)}if(y.size>0){let $=W(r.id,y);if($){let _=`${r.id}-dependency-trees-${$e()}.md`;try{await this.client.attachMarkdown(t.key,_,$),a.detail("attach",_)}catch(Be){a.warn(`Could not attach ${_} to ${t.key}: ${String(Be)}`,c)}}}te&&await this.postPartialAckComment(t.key,r.severity,f,a,c),a.setOutcome("update"),a.detail("projects",`added ${l.join(", ")}`)}else a.setOutcome("update"),a.detail("reason","severity changed (no new projects)");j&&T?(a.note(`kept closed: severity ${r.severity} below auto-close threshold ${A}`),l.length>0&&A!=null&&await this.postKeptClosedReasonComment(t.key,r.severity,A,l,a,c),c.keptClosed.push(t.key)):j&&await this.reopenAndReassignSprint(t.key,Ee,F,x,a,c),c.updated.push(t.key)}async postKeptClosedReasonComment(e,r,t,n,s,i){try{let c=me(r,t,n);await this.client.addComment(e,c),s.detail("comment","kept-closed reason posted")}catch(c){s.warn(`Could not post kept-closed reason comment on ${e}: ${String(c)}`,i)}}async postSeverityChangeComment(e,r,t,n,s){try{let i=v(h(`Schweregrad ge\xE4ndert von ${r} auf ${t}. Falls eine automatisch erstellte VEX-Ausnahme bestand, ist sie nicht mehr g\xFCltig.`));await this.client.addComment(e,i)}catch(i){n.warn(`Could not post severity-change comment on ${e}: ${String(i)}`,s)}}async reopenAndReassignSprint(e,r,t,n,s,i){let c;try{c=await this.client.getTransitions(e)}catch(d){s.warn(`Could not list transitions for ${e}: ${String(d)}`,i);return}let a=c.find(d=>d.toStatusCategory==="new");if(!a){s.warn(`${e} is closed but no transition leading to a non-done status is available; leaving status unchanged`,i);return}try{await this.client.transitionIssue(e,a.id),s.detail("reopen",`via "${a.name}"`),i.reopened.push(e)}catch(d){s.warn(`Could not transition ${e} to a non-done status: ${String(d)}`,i);return}if(await this.postReopenReasonComment(e,t,n,s,i),r!=null)try{await this.client.setSprint(e,r),s.detail("sprint",`added to active sprint ${r}`)}catch(d){s.warn(`Could not set sprint for ${e}: ${String(d)}`,i)}}async postReopenReasonComment(e,r,t,n,s){try{let i=t?`${Ce(r)}
12
+ ${t}`:Ce(r),c=v(h(i));await this.client.addComment(e,c),n.detail("comment","reopen-reason posted")}catch(i){n.warn(`Could not post reopen-reason comment on ${e}: ${String(i)}`,s)}}resolveAutoCloseThreshold(e){return e.autoCloseBelowSeverity??this.config.autoCloseBelowSeverity}effectiveProjectThreshold(e,r){let t;for(let n of r.projectOverrides??[])O(e,n.projects)&&(t==null||E(n.autoCloseBelowSeverity,t)<0)&&(t=n.autoCloseBelowSeverity);return t??this.resolveAutoCloseThreshold(r)}bucketProjects(e,r,t){if(e.length===0){let d=this.resolveAutoCloseThreshold(t);return{strictestThreshold:d,autoAcknowledged:[],manualEvaluation:[],shouldAutoClose:d!=null&&N(r,d)}}let n=[],s=[],i,c=!1;for(let d of e){let l=this.effectiveProjectThreshold(d,t);if(l==null){c=!0;continue}(i==null||E(l,i)<0)&&(i=l);let u={projectId:d,threshold:l};N(r,l)?n.push(u):s.push(u)}let a=!c&&s.length===0&&n.length>0&&i!=null&&N(r,i);return{strictestThreshold:i,autoAcknowledged:n,manualEvaluation:s,shouldAutoClose:a}}async autoCloseIssue(e,r,t,n,s){let i=t.strictestThreshold,c;try{c=await this.client.getTransitions(e)}catch(l){n.warn(`Could not list transitions for ${e} (auto-close): ${String(l)}`,s);return}let a=c.filter(l=>l.toStatusCategory==="done"),d=a.find(l=>l.toStatusName.toLowerCase()==="closed")??a.find(l=>{let u=l.name.toLowerCase();return u==="close"||u==="closed"})??a[0];if(!d){n.warn(`${e} has no transition leading to a done status; cannot auto-close`,s);return}try{await this.client.transitionIssue(e,d.id),n.detail("close",`auto-closed (severity ${r} below ${i}) via "${d.name}"`),s.autoClosed.push(e)}catch(l){n.warn(`Could not transition ${e} to a done status: ${String(l)}`,s);return}try{let l=he(r,t.autoAcknowledged);await this.client.addComment(e,l)}catch(l){n.warn(`Could not post auto-close comment on ${e}: ${String(l)}`,s)}}async postPartialAckComment(e,r,t,n,s){try{let i=ge(r,t.autoAcknowledged,t.manualEvaluation);await this.client.addComment(e,i),n.detail("comment",`partial-ack posted (auto: ${S(t.autoAcknowledged)}; manual: ${S(t.manualEvaluation)})`)}catch(i){n.warn(`Could not post partial-ack comment on ${e}: ${String(i)}`,s)}}async resolveBoardRouting(e,r,t,n){let s,i=[],c=!1;for(let d of e.addToBoard??[]){let l=d.matchProjects??["*"];if(r.some(p=>O(p,l)))if(d.sprintBoardId!=null){if(!c){c=!0;let p=d.sprintBoardId;if(!this.activeSprintCache.has(p)){let C=await this.client.getActiveSprintId(p).catch(j=>(t.warn(`Could not resolve active sprint for boardId ${p}: ${String(j)}`,n),null));this.activeSprintCache.set(p,C)}s=this.activeSprintCache.get(p)??void 0,d.issueLabels&&i.push(...d.issueLabels)}}else d.issueLabels&&i.push(...d.issueLabels)}let a=[...e.issueLabels??[],...i];return{activeSprintId:s,allLabels:a}}collectProjectComponentTrees(e,r,t){let n=new Map;if(!e.projectsToComponents)return n;for(let s of r){let i=e.projectsToComponents[s]??{},c=[];for(let a of t){let d=i[a];d&&c.push(d)}c.length>0&&n.set(s,c)}return n}};var b=Ve(require("path")),Y=require("fs/promises");var Z=class o{constructor(e){this.workingDir=e}workingDir;static DEFAULT_MAVEN_REPO="https://repository.conterra.de/repository/maven-mirror-ct";static DEFAULT_VEX_SUBDIR="input/vex";static DEFAULT_OUT_SUBDIR="output";static DEFAULT_CACHE_SUBDIR="cache";static DEFAULT_SCANNERS=["grype","trivy","ossindex"];normalize(e,r){let t=e.findNoLongerDetectedVulnerabilitiesOnlyForSnapshots??!1;return{projectConfigFileLocation:r,onNewVulnerabilities:e.onNewVulnerabilities??(e.failOnNewVulnerabilities?"fail":"warn"),onNoLongerDetectedVulnerabilities:e.onNoLongerDetectedVulnerabilities??"ignore",reportNoLongerDetectedVulnerabilities:e.reportNoLongerDetectedVulnerabilities??!0,consoleReport:e.consoleReport??"BY_PROJECT_DETAILED",createSubDirsForProject:e.createSubDirsForProject??!0,mavenRepo:e.mavenRepo??o.DEFAULT_MAVEN_REPO,vexUrls:e.vexUrls??[],vexDir:this.resolvePath(e.vexDir)??(0,b.resolve)(this.workingDir,o.DEFAULT_VEX_SUBDIR),outDir:this.resolvePath(e.outDir)??(0,b.resolve)(this.workingDir,o.DEFAULT_OUT_SUBDIR),cacheDir:this.resolvePath(e.cacheDir)??(0,b.resolve)(this.workingDir,o.DEFAULT_CACHE_SUBDIR),scanners:this.normalizeScanners(e.scanners,r),projects:this.normalizeProjects(e.projects??[],t)}}normalizeScanners(e,r){return(e??o.DEFAULT_SCANNERS).map(n=>{if(typeof n!="string")throw new Error(`Invalid scanner name '${String(n)}' in configuration file '${r}'`);let s=n.split("@");if(s.length>2)throw new Error(`Invalid scanner name '${n}' in configuration file '${r}'`);return{name:s[0],version:s[1]??""}})}normalizeProjects(e,r){let t=e.map(n=>{let s=n;if(!s.name)throw new Error("Project name is missing on entry: "+JSON.stringify(s));if(!s.version)throw new Error("Project version is missing on entry: "+JSON.stringify(s));if(!s.purl)throw new Error("Project purl is missing on entry: "+JSON.stringify(s));let i={...s,purlAliases:s.purlAliases??[],enabled:s.enabled??!0,silent:s.silent??!1,minimumSeverity:s.minimumSeverity??"UNKNOWN"};return i.sbomFile?{...i,sbomFile:this.resolvePath(i.sbomFile)??i.sbomFile}:{...i,detectNoLongerUsedVulnerabilities:r?i.purl.endsWith("-SNAPSHOT"):!0}});return this.assertUniqueProjectIds(t),this.assertUniqueProjectPurls(t),t}assertUniqueProjectIds(e){let r=new Set;for(let t of e){let n=`${t.name}@${t.version}`;if(r.has(n))throw new Error(`Duplicate project id '${n}' in vuln-scan-conf.json: each 'name@version' pair must be unique across the projects array.`);r.add(n)}}assertUniqueProjectPurls(e){let r=new Map;for(let t of e){let n=`${t.name}@${t.version}`,s=r.get(t.purl);if(s!==void 0)throw new Error(`Duplicate project purl '${t.purl}' in vuln-scan-conf.json: used by '${s}' and '${n}'. Each project must have a unique purl; use 'purlAliases' when two projects should be matched by the same VEX statement.`);r.set(t.purl,n)}}resolvePath(e){if(e)return b.default.isAbsolute(e)?e:(0,b.resolve)(this.workingDir,e)}},U=class{constructor(e){this.workingDir=e;this.normalizer=new Z(e)}workingDir;normalizer;projectConfigCache;async loadProjectConfig(){return this.projectConfigCache??=this.doLoadProjectConfig(),this.projectConfigCache}async doLoadProjectConfig(){let e=(0,b.resolve)(this.workingDir,"vuln-scan-conf.json"),r=await ie(e);if(!r)throw new Error(`Missing project configuration file '${e}'`);return this.normalizer.normalize(r,e)}async loadJiraConfig(e){let r=await(0,Y.readFile)(e,"utf8"),t=JSON.parse(r);if(!t.issues&&t.issue&&(t.issues=t.issue),!t.jiraUrl||typeof t.jiraUrl!="string")throw new Error("jira-conf.json: missing or invalid 'jiraUrl' field");if(!Array.isArray(t.issues))throw new Error("jira-conf.json: missing or invalid 'issues' array");this.validateAutoCloseSeverity(t.autoCloseBelowSeverity,"jira-conf.json: invalid 'autoCloseBelowSeverity'");for(let[n,s]of t.issues.entries())if(s&&typeof s=="object"){let i=s;this.validateAutoCloseSeverity(i.autoCloseBelowSeverity,`jira-conf.json: invalid 'issues[${n}].autoCloseBelowSeverity'`),this.validateAutoCloseProjectOverrides(i.projectOverrides,`jira-conf.json: invalid 'issues[${n}].projectOverrides'`)}return t}validateAutoCloseSeverity(e,r){if(e!==void 0&&!w.includes(e))throw new Error(`${r} value '${String(e)}'. Must be one of: ${w.join(", ")}`)}validateAutoCloseProjectOverrides(e,r){if(e!==void 0){if(!Array.isArray(e))throw new Error(`${r}: must be an array`);for(let t=0;t<e.length;t++){let n=e[t],s=`${r}[${t}]`;if(n==null||typeof n!="object")throw new Error(`${s}: must be an object`);let i=n,c=i.projects;if(!Array.isArray(c)||c.length===0)throw new Error(`${s}.projects: must be a non-empty array of strings`);for(let a=0;a<c.length;a++){let d=c[a];if(typeof d!="string"||d.length===0)throw new Error(`${s}.projects[${a}]: must be a non-empty string`)}if(i.autoCloseBelowSeverity===void 0)throw new Error(`${s}.autoCloseBelowSeverity: is required`);if(this.validateAutoCloseSeverity(i.autoCloseBelowSeverity,`${s}.autoCloseBelowSeverity`),i.comment!==void 0&&typeof i.comment!="string")throw new Error(`${s}.comment: must be a string`)}}}async loadAutoVexConfig(e){let r=await(0,Y.readFile)(e,"utf8"),t=JSON.parse(r);if(!t.impactStatement||typeof t.impactStatement!="string")throw new Error("auto-vex-conf.json: missing or invalid 'impactStatement' field (must be a non-empty string)");if(t.maxSeverity!==void 0&&!w.includes(t.maxSeverity))throw new Error(`auto-vex-conf.json: invalid 'maxSeverity' value '${String(t.maxSeverity)}'. Must be one of: ${w.join(", ")}`);if(t.projectOverrides!==void 0){if(!Array.isArray(t.projectOverrides))throw new Error("auto-vex-conf.json: 'projectOverrides' must be an array");for(let[n,s]of t.projectOverrides.entries()){if(!s||typeof s!="object")throw new Error(`auto-vex-conf.json: 'projectOverrides[${n}]' must be an object`);let i=s;if(!Array.isArray(i.projects)||i.projects.length===0||!i.projects.every(c=>typeof c=="string"&&c.length>0))throw new Error(`auto-vex-conf.json: 'projectOverrides[${n}].projects' must be a non-empty array of non-empty strings`);if(!w.includes(i.maxSeverity))throw new Error(`auto-vex-conf.json: invalid 'projectOverrides[${n}].maxSeverity' value '${String(i.maxSeverity)}'. Must be one of: ${w.join(", ")}`)}}return t}};async function Te(o){let{scanSummaryPath:e,jiraConfigPath:r,jiraUser:t,jiraApiToken:n,dryRun:s=!1}=o,i=new U(process.cwd()),[c,a]=await Promise.all([Ye(e),i.loadJiraConfig(r)]);return new D(a,t,n).report(c,s)}function xe(o){return o.created.length===0&&o.updated.length===0&&o.skipped.length===0&&o.noMatchingRule.length===0&&o.autoClosed.length===0&&o.reopened.length===0&&o.keptClosed.length===0&&o.warnings.length===0&&o.errors.length===0}async function Ye(o){let e=await(0,Re.readFile)(o,"utf8");return JSON.parse(e)}var Q={flag:"--dry-run",description:"Print what would happen without making changes"},M=class{specs;booleanSpecs;commandName;helpLines;constructor(e,r,t=[],n=[]){this.commandName=e,this.specs=r;let s=n.some(i=>i.flag===Q.flag);this.booleanSpecs=s?n:[...n,Q],this.helpLines=t}parse(e){let r=e.slice(2),t=new Map(this.specs.map(s=>[s.flag,s.defaultValue])),n=new Map(this.booleanSpecs.map(s=>[s.flag,!1]));for(let s=0;s<r.length;s++){let i=r[s];(i==="--help"||i==="-h")&&(this.printHelp(),process.exit(0));let c=this.booleanSpecs.find(d=>d.flag===i||d.alias===i);if(c){n.set(c.flag,!0);continue}let a=this.specs.find(d=>d.flag===i||d.alias===i);a&&r[s+1]?t.set(a.flag,r[++s]):(console.error(`Unknown argument: ${i}`),this.printHelp(),process.exit(1))}return{values:t,dryRun:n.get(Q.flag)===!0,booleans:n}}printHelp(){let e=this.specs.map(t=>` ${(t.alias?`${t.alias}, ${t.flag} <path>`:` ${t.flag} <path>`).padEnd(38)}${t.description}
13
13
  ${"".padEnd(42)}(default: ${t.defaultValue})`),r=this.booleanSpecs.map(t=>` ${(t.alias?`${t.alias}, ${t.flag}`:` ${t.flag}`).padEnd(38)}${t.description}`);console.log([`Usage: ${this.commandName} [options]`,"","Options:",...e,...r," -h, --help"+" ".repeat(32)+"Show this help message",...this.helpLines].join(`
14
- `))}};Te.dotenv.config();var xe=process.env,qe=new U("ct-vuln-jira-report",[{flag:"--jira-config",alias:"-c",description:"Path to jira-conf.json configuration file",defaultValue:(0,Q.join)(process.cwd(),"jira-conf.json")},{flag:"--summary",alias:"-s",description:"Path to scan-summary.json produced by ct-vuln-scan",defaultValue:(0,Q.join)(process.cwd(),"output","scan-summary.json")}],["","Required environment variables:"," JIRA_USER Jira account e-mail address"," JIRA_API_TOKEN Jira API token (see https://id.atlassian.com/manage-profile/security/api-tokens)"]);(async()=>{let{values:o,dryRun:e}=qe.parse(process.argv),r=o.get("--jira-config"),t=o.get("--summary"),n=xe.JIRA_USER,s=xe.JIRA_API_TOKEN;(!n||!s)&&(console.error("Error: JIRA_USER and JIRA_API_TOKEN environment variables must be set."),process.exit(1)),console.log(`Jira reporter${e?" [dry-run]":""}`);try{let c=await Pe({scanSummaryPath:t,jiraConfigPath:r,jiraUser:n,jiraApiToken:s,dryRun:e});Ae(c)&&console.log("[info] nothing to do"),e&&(console.log(""),console.log("No issues were created or modified (dry-run). Re-run without --dry-run to apply.")),c.errors.length>0&&(process.exitCode=1)}catch(c){console.error(`Fatal error: ${String(c)}`),process.exit(1)}})();
14
+ `))}};ke.dotenv.config();var Ie=process.env,Ze=new M("ct-vuln-jira-report",[{flag:"--jira-config",alias:"-c",description:"Path to jira-conf.json configuration file",defaultValue:(0,ee.join)(process.cwd(),"jira-conf.json")},{flag:"--summary",alias:"-s",description:"Path to scan-summary.json produced by ct-vuln-scan",defaultValue:(0,ee.join)(process.cwd(),"output","scan-summary.json")}],["","Required environment variables:"," JIRA_USER Jira account e-mail address"," JIRA_API_TOKEN Jira API token (see https://id.atlassian.com/manage-profile/security/api-tokens)"]);(async()=>{let{values:o,dryRun:e}=Ze.parse(process.argv),r=o.get("--jira-config"),t=o.get("--summary"),n=Ie.JIRA_USER,s=Ie.JIRA_API_TOKEN;(!n||!s)&&(console.error("Error: JIRA_USER and JIRA_API_TOKEN environment variables must be set."),process.exit(1)),console.log(`Jira reporter${e?" [dry-run]":""}`);try{let c=await Te({scanSummaryPath:t,jiraConfigPath:r,jiraUser:n,jiraApiToken:s,dryRun:e});xe(c)&&console.log("[info] nothing to do"),e&&(console.log(""),console.log("No issues were created or modified (dry-run). Re-run without --dry-run to apply.")),c.errors.length>0&&(process.exitCode=1)}catch(c){console.error(`Fatal error: ${String(c)}`),process.exit(1)}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conterra/vuln-scan",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "con terra vulnerability scan process",
5
5
  "author": "con terra GmbH",
6
6
  "license": "Apache-2.0",