| |
| function cloneJSON(obj) { |
| // basic type deep copy |
| if (obj === null || obj === undefined || typeof obj !== 'object' || obj === "") { |
| return obj |
| } |
| // array deep copy |
| if (obj instanceof Array) { |
| var cloneA = []; |
| for (var i = 0; i < obj.length; ++i) { |
| cloneA[i] = cloneJSON(obj[i]); |
| } |
| if(cloneA.length > 0) { |
| return cloneA; |
| } else { |
| return null; |
| } |
| } |
| // object deep copy |
| var cloneO = {}; |
| for (var i in obj) { |
| var c = cloneJSON(obj[i]); |
| if(c !== null && c !== "") { |
| cloneO[i] = c; |
| } |
| } |
| return cloneO; |
| }; |
| |
| var textUtil = { |
| jsonView: function(obj) { |
| if (obj instanceof Array) { |
| var ret = '<table>'; |
| for(var k in obj) { |
| ret = ret + '<tr><td>' + this.jsonView(obj)+ '</td></tr>'; |
| } |
| return(ret + '</table>'); |
| } else if (obj instanceof Object) { |
| var ret = '<div>'; |
| for (var k in obj){ |
| if (obj.hasOwnProperty(k)){ |
| ret = ret + '<div><b>' + k + '</b>: ' + this.jsonView(obj[k]) + '</div>'; |
| } |
| } |
| return ret + '</div>' |
| } else { |
| return obj; |
| }; |
| }, |
| reduceJSON: function (cve) { |
| //todo: this is to create a duplcate object |
| // needs cleaner implementation |
| var c = cloneJSON(cve); |
| delete c.CNA_private; |
| if (c.description && c.description.description_data) { |
| var merged = {}; |
| var d; |
| for (d of c.description.description_data) { |
| if (d && d.lang) { |
| if (!merged[d.lang]) { |
| merged[d.lang] = []; |
| } |
| merged[d.lang].push(d.value); |
| } |
| } |
| var new_d = []; |
| for (var m in merged) { |
| new_d.push({ |
| lang: m, |
| value: merged[m].join("\n") |
| }); |
| } |
| c.description.description_data = new_d; |
| } |
| if(c.impact && c.impact.cvss && c.impact.cvss.baseScore === 0) { |
| delete c.impact; |
| } |
| // ASF |
| if(c.who) { delete c.who; } |
| if(c.containers && c.containers.cna && c.containers.cna.title) { |
| if (!(c.containers.cna.title.substring(0,40).includes("Apache"))) { |
| c.containers.cna.title = getProductListNoVendor(c) + ": " + c.containers.cna.title |
| } |
| } |
| // END ASF |
| return(c); |
| }, |
| |
| getMITREJSON: function(cve) { |
| return JSON.stringify(cve, null, " "); |
| }, |
| getPR: function(cve) { |
| var matches = []; |
| var re = /PRs?[ \t]+((or|and|[0-9\t\ \,])+)/igm; |
| var m; |
| while((m = re.exec(cve.solution)) !== null) { |
| var prs = m[1].trim().split(/[ \t,andor]{1,}/).filter(x => x); |
| matches = matches.concat(prs); |
| } |
| return matches; |
| }, |
| |
| getAffectedProductString: function (cve) { |
| var status={}; |
| var lines = []; |
| for (var vendor of cve.affects.vendor.vendor_data) { |
| var vendor_name = vendor.vendor_name; |
| for(var product of vendor.product.product_data) { |
| for(var version of product.version.version_data) { |
| var vv = version.version_value; |
| var cat = "affected"; |
| if(version.version_affected) { |
| if(version.version_affected.startsWith('?')) { |
| cat = "unknown"; |
| } else if (version.version_affected.startsWith('!')) { |
| cat = "unaffected"; |
| } |
| var prefix = product.product_name + " "; |
| if(version.version_name && version.version_name != "") { |
| prefix += version.version_name + " "; |
| } |
| switch (version.version_affected) { |
| case "!": |
| case "?": |
| case "=": |
| vv = version.version_value; |
| break; |
| case "<": |
| case "!<": |
| case "?<": |
| vv = prefix + "versions earlier than " + version.version_value; |
| break; |
| case ">": |
| case "?>": |
| vv = prefix + "versions later than " + version.version_value; |
| break; |
| case "<=": |
| case "!<=": |
| case "?<=": |
| vv = product.product_name + " " + version.version_value + " and earlier versions"; |
| break; |
| case ">=": |
| case "!>=": |
| case "?>=": |
| vv = product.product_name + " " + version.version_value + " and later versions"; |
| break; |
| default: |
| vv = version.version_value; |
| } |
| } |
| if (version.platform) { |
| vv = vv + " on " + version.platform; |
| } |
| |
| if (!status[cat]) { |
| status[cat] = {}; |
| } |
| // ASF |
| if(!status[cat][product.product_name]) { |
| status[cat][product.product_name] = []; |
| } |
| status[cat][product.product_name].push(vv); |
| // END ASF |
| } |
| } |
| } |
| var stringifyArray = function(ob) { |
| var ret = []; |
| for(var p in ob) { |
| ret.push(p + "\n" + ob[p].join(';\n') + "."); |
| } |
| return ret.join('\n'); |
| } |
| var ret = []; |
| if (status.affected) { |
| ret.push('This issue affects:\n' + stringifyArray(status.affected)); |
| } |
| if (status.unaffected) { |
| ret.push('This issue does not affect:\n' + stringifyArray(status.unaffected)); |
| } |
| if (status.unknown) { |
| ret.push('It is not known whether this issue affects:\n' + stringifyArray(status.unknown)); |
| } |
| return ret.join('\n\n'); |
| }, |
| affectedTable: function (cve) { |
| var status={}; |
| for (var vendor of cve.affects.vendor.vendor_data) { |
| var vendor_name = vendor.vendor_name; |
| if(!status[vendor_name]) { |
| status[vendor_name] = {}; |
| } |
| for(var product of vendor.product.product_data) { |
| var product_name = product.product_name; |
| if(!status[vendor_name][product_name]) { |
| status[vendor_name][product_name] = {}; |
| } |
| for(var version of product.version.version_data) { |
| var vv = version.version_value; |
| var cat = "affected"; |
| var prefix = vn = ""; |
| if(version.version_name && version.version_name != "") { |
| vn = version.version_name; |
| } |
| if(version.version_affected) { |
| if(version.version_affected.startsWith('?')) { |
| cat = "unknown"; |
| } else if (version.version_affected.startsWith('!')) { |
| cat = "unaffected"; |
| } |
| switch (version.version_affected) { |
| case "!": |
| case "?": |
| case "=": |
| vv = version.version_value; |
| break; |
| case "<": |
| case "!<": |
| case "?<": |
| vv = "< " + version.version_value; |
| break; |
| case ">": |
| case "?>": |
| vv = "> " + version.version_value; |
| break; |
| case "<=": |
| case "!<=": |
| case "?<=": |
| vv = "<= " + version.version_value; |
| break; |
| case ">=": |
| case "!>=": |
| case "?>=": |
| vv = ">= " + version.version_value; |
| break; |
| default: |
| vv = version.version_value; |
| } |
| } |
| if(version.platform && version.platform != "") { |
| vv += ' on ' + version.platform; |
| } |
| if(!status[vendor_name][product_name][vn]) { |
| status[vendor_name][product_name][vn] = {}; |
| } |
| if (!status[vendor_name][product_name][vn][cat]) { |
| status[vendor_name][product_name][vn][cat] = []; |
| } |
| status[vendor_name][product_name][vn][cat].push(vv); |
| } |
| } |
| } |
| return status; |
| }, |
| appliesTo: function(affects){ |
| var ret = []; |
| for (var vendor of affects.vendor.vendor_data) { |
| var vendor_name = vendor.vendor_name; |
| for(var product of vendor.product.product_data) { |
| var product_name = product.product_name; |
| for(var version of product.version.version_data) { |
| var vv = version.version_value; |
| var prefix = vn = ""; |
| if(version.version_name && version.version_name != "") { |
| vn = version.version_name; |
| } |
| if(version.version_affected) { |
| if(version.version_affected.startsWith('?')) { |
| cat = "unknown"; |
| } else if (version.version_affected.startsWith('!')) { |
| cat = "no"; |
| } |
| switch (version.version_affected) { |
| case "=": |
| case "<": |
| case ">": |
| case "<=": |
| case ">=": |
| ret.push(product_name + ' ' + vn); |
| break; |
| } |
| } |
| } |
| } |
| } |
| return ret; |
| }, |
| affectedYesNo: function(affects){ |
| var status={yes:[],no:[],unknown:[]}; |
| for (var vendor of affects.vendor.vendor_data) { |
| var vendor_name = vendor.vendor_name; |
| for(var product of vendor.product.product_data) { |
| var product_name = product.product_name; |
| for(var version of product.version.version_data) { |
| var vv = version.version_value; |
| var cat = "yes"; |
| var prefix = vn = ""; |
| if(version.version_name && version.version_name != "") { |
| vn = version.version_name; |
| } |
| if(version.version_affected) { |
| if(version.version_affected.startsWith('?')) { |
| cat = "unknown"; |
| } else if (version.version_affected.startsWith('!')) { |
| cat = "no"; |
| } |
| switch (version.version_affected) { |
| case "!": |
| case "?": |
| case "=": |
| vv = version.version_value; |
| break; |
| case "<": |
| case "!<": |
| case "?<": |
| vv = "< " + version.version_value; |
| break; |
| case ">": |
| case "?>": |
| vv = "> " + version.version_value; |
| break; |
| case "<=": |
| case "!<=": |
| case "?<=": |
| vv = "<= " + version.version_value; |
| break; |
| case ">=": |
| case "!>=": |
| case "?>=": |
| vv = ">= " + version.version_value; |
| break; |
| default: |
| vv = version.version_value; |
| } |
| if(version.platform && version.platform != "") { |
| vv += ' on ' + version.platform; |
| } |
| } |
| var ph = status[cat][product_name]; |
| if(ph == undefined) { |
| ph = status[cat][product_name] = {}; |
| } |
| vns = ph.version_names; |
| if(vns == undefined) { |
| vns = ph.version_names = [] |
| } |
| if(vns.indexOf(vn)<0) { |
| vns.push(vn); |
| } |
| vvs = ph.version_values; |
| if(vvs == undefined) { |
| vvs = ph.version_values = [] |
| } |
| if(vvs.indexOf(vv)<0) { |
| vvs.push(vv); |
| } |
| } |
| } |
| } |
| var rstatus = {yes:[],no:[],unknown:[]}; |
| for (var cat of ['yes','no','unknown']){ |
| for(var p in status[cat]) { |
| rstatus[cat].push({product:p, version_names:status[cat][p].version_names, version_values: status[cat][p].version_values}) |
| } |
| } |
| return rstatus; |
| }, |
| /** |
| * Takes an ISO date-time string and renders it in a user-friendly format |
| * based on its recency. |
| * |
| * - If the date is today, shows the local time (e.g., "12:43 PM"). |
| * - If the date is this year (but not today), shows "MMM DD" (e.g., "Nov 14"). |
| * - If the date is a previous year, shows "YYYY MMM DD" (e.g., "2024 Nov 14"). |
| * |
| * @param {string} isoString A string representing a date in ISO format. |
| * @returns {string} A formatted, user-friendly date string. |
| */ |
| formatFriendlyDate: function (isoString) { |
| if (isoString === "" || isoString === null || isoString === 0) { |
| return "-"; |
| } |
| |
| const date = new Date(isoString); |
| |
| if (Number.isNaN(date.getTime())) { |
| return "-"; |
| } |
| |
| const now = new Date(); |
| |
| // Create date objects for comparison, stripping out the time part. |
| const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); |
| const inputDateOnly = new Date(date.getFullYear(), date.getMonth(), date.getDate()); |
| |
| // Case 1: The date is today |
| if (inputDateOnly.getTime() === today.getTime()) { |
| return date.toLocaleTimeString(undefined, { |
| hour: 'numeric', |
| minute: '2-digit', |
| }).toLocaleLowerCase(); |
| } |
| |
| // Case 2: The date is this year (but not today) |
| if (date.getFullYear() === now.getFullYear()) { |
| return date.toLocaleDateString(undefined, { |
| month: 'short', |
| day: 'numeric', |
| }); |
| } |
| |
| // Case 3: The date is from a previous year |
| return date.toLocaleDateString(undefined, { |
| year: 'numeric', |
| month: 'short', |
| day: 'numeric', |
| }); |
| }, |
| //determine next bundle date |
| nextPatchDay : function (dateString, weekday) { |
| const n = 2; //2nd Wednesday |
| var date = new Date(dateString); |
| var monthstogo = (12-date.getMonth()) % 3; |
| |
| var count = 0, |
| idate = new Date(date.getFullYear(), date.getMonth()+ monthstogo, 1); |
| |
| while (true) { |
| if (idate.getDay() === weekday) { |
| if (++count == n) { |
| break; |
| } |
| } |
| idate.setDate(idate.getDate() + 1); |
| } |
| if (idate < date) { |
| return this.nextPatchDay(date.setMonth(date.getMonth()+1), weekday); |
| } else { |
| return idate; |
| } |
| }, |
| deep_value: function(obj, path) { |
| var ret = obj; |
| for (var i=0, path=path.split('.'), len=path.length; i<len; i++){ |
| ret = ret[path[i]]; |
| if (ret === undefined) { |
| break; |
| } |
| }; |
| return ret; |
| }, |
| getDocuments: async function(schemaName, ids, paths) { |
| const res = await fetch('/' + schemaName + '/json/', { |
| method: 'POST', |
| credentials: 'include', |
| headers: { |
| 'Accept': 'application/json, text/plain, */*', |
| 'Content-Type': 'application/json', |
| 'CSRF-Token': csrfToken |
| }, |
| redirect: 'error', |
| body: JSON.stringify({ids:ids,fields:paths}) |
| }); |
| const docs = await res.json(); |
| return docs; |
| }, |
| |
| diffline: function(line1, line2) { |
| var ret1 = []; |
| var ret2 = []; |
| var s = 0; |
| var m = line1.length - 1; |
| var n = line2.length - 1; |
| while (s <= m && s <= n && (line1.charAt(s) == line2.charAt(s))) { |
| s++; |
| } |
| |
| while (s <= m && s <= n && (line1.charAt(m) == line2.charAt(n))) { |
| m--; |
| n--; |
| } |
| |
| // deleted |
| if (s <= m) { |
| ret1.push({t:0, str: line1.substring(0, s)}); |
| //StringBuilder sb = new StringBuilder(); |
| //sb.append(Util.htmlize(line1.substring(0, s))); |
| ret1.push({t:1, str: line1.substring(s, m + 1)}); |
| ret1.push({t:0, str: line1.substring(m+1, line1.length)}); |
| //sb.append(Util.htmlize(line1.substring(m + 1, line1.length()))); |
| //ret1.push({t:0, str: line1.substring(0, s)}) |
| // = sb.toString(); |
| } else { |
| ret1.push({t:0, str: line1}); |
| //ret[0] = Util.htmlize(line1.toString()); // no change |
| } |
| |
| // added |
| if (s <= n) { |
| ret2.push({t:0,str:line2.substring(0, s)}); |
| ret2.push({t:1,str:line2.substring(s, n + 1)}); |
| ret2.push({t:0,str:line2.substring(n + 1, line2.length)}); |
| //StringBuilder sb = new StringBuilder(); |
| //sb.append(Util.htmlize(line2.substring(0, s))); |
| //sb.append(HtmlConsts.SPAN_A); |
| //sb.append(Util.htmlize(line2.substring(s, n + 1))); |
| //sb.append(HtmlConsts.ZSPAN); |
| //sb.append(Util.htmlize(line2.substring(n + 1, line2.length()))); |
| //ret[1] = sb.toString(); |
| } else { |
| ret2.push({t:0,str: line2}); |
| //ret[1] = Util.htmlize(line2.toString()); // no change |
| } |
| |
| return {lhs: ret1, rhs: ret2}; |
| }, |
| fileSize : function(a,b,c,d,e){ |
| return (b=Math,c=b.log,d=1024,e=c(a)/c(d)|0,a/b.pow(d,e)).toFixed(2) |
| +' '+(e?'KMGTPEZY'[--e]+'B':'Bytes') |
| }, |
| diffEdgeClass: function(op, side) { |
| if (op == 'add') return side == 'next' ? 'diff-add' : ''; |
| if (op == 'remove') return side == 'current' ? 'diff-del' : ''; |
| if (op == 'replace') return side == 'current' ? 'diff-chg-l' : 'diff-chg-r'; |
| return ''; |
| }, |
| cvssIcons: { |
| attackVector: { NETWORK: 'cvss-net', ADJACENT_NETWORK: 'cvss-adj', ADJACENT: 'cvss-adj', LOCAL: 'cvss-user', PHYSICAL: 'cvss-physical' }, |
| attackComplexity: { HIGH: 'rocket', LOW: 'paper-plane' }, |
| attackRequirements: { PRESENT: 'cvss-required', NONE: 'cvss-direct' }, |
| privilegesRequired: { HIGH: 'king', LOW: 'pawn', NONE: 'thief' }, |
| userInteraction: { REQUIRED: 'cvss-ui', ACTIVE: 'alert', PASSIVE: 'eye-close', NONE: 'cvss-direct' }, |
| scope: { UNCHANGED: 'cvss-direct', CHANGED: 'cvss-scope-change' }, |
| confidentialityImpact: { HIGH: 'eye', LOW: 'eye-half', NONE: 'eye-close' }, |
| integrityImpact: { HIGH: 'box-high', LOW: 'box-low', NONE: 'box' }, |
| availabilityImpact: { HIGH: 'signal-1', LOW: 'signal-2', NONE: 'signal' }, |
| vulnConfidentialityImpact: { HIGH: 'eye', LOW: 'eye-half', NONE: 'eye-close' }, |
| subConfidentialityImpact: { HIGH: 'eye', LOW: 'eye-half', NONE: 'eye-close' }, |
| vulnIntegrityImpact: { HIGH: 'box-high', LOW: 'box-low', NONE: 'box' }, |
| subIntegrityImpact: { HIGH: 'box-high', LOW: 'box-low', NONE: 'box' }, |
| vulnAvailabilityImpact: { HIGH: 'signal-1', LOW: 'signal-2', NONE: 'signal' }, |
| subAvailabilityImpact: { HIGH: 'signal-1', LOW: 'signal-2', NONE: 'signal' } |
| } |
| }; |
| if(typeof module !== 'undefined') { |
| module.exports = textUtil; |
| } |
| var cvssjs = { |
| vectorMap4: { |
| "attackVector": "AV", |
| "attackComplexity": "AC", |
| "attackRequirements": "AT", |
| "privilegesRequired": "PR", |
| "userInteraction": "UI", |
| "vulnConfidentialityImpact": "VC", |
| "vulnIntegrityImpact": "VI", |
| "vulnAvailabilityImpact": "VA", |
| "subConfidentialityImpact": "SC", |
| "subIntegrityImpact": "SI", |
| "subAvailabilityImpact": "SA", |
| "exploitMaturity": "E", |
| "Safety": "S", |
| "Automatable": "AU", |
| "Recovery": "R", |
| "valueDensity": "V", |
| "vulnerabilityResponseEffort": "RE", |
| "providerUrgency": "U" |
| }, |
| metricMap4: { |
| "AV": "attackVector", |
| "AC": "attackComplexity", |
| "AT": "attackRequirements", |
| "PR": "privilegesRequired", |
| "UI": "userInteraction", |
| "VC": "vulnConfidentialityImpact", |
| "VI": "vulnIntegrityImpact", |
| "VA": "vulnAvailabilityImpact", |
| "SC": "subConfidentialityImpact", |
| "SI": "subIntegrityImpact", |
| "SA": "subAvailabilityImpact", |
| "E": "exploitMaturity", |
| "S": "Safety", |
| "AU": "Automatable", |
| "R": "Recovery", |
| "V": "valueDensity", |
| "RE": "vulnerabilityResponseEffort", |
| "U": "providerUrgency", |
| }, |
| vectorMap3: { |
| "attackVector": "AV", |
| "attackComplexity": "AC", |
| "privilegesRequired": "PR", |
| "userInteraction": "UI", |
| "scope": "S", |
| "confidentialityImpact": "C", |
| "integrityImpact": "I", |
| "availabilityImpact": "A" |
| }, |
| vectorMap2: { |
| "accessVector": "AV", |
| "accessComplexity": "AC", |
| "authentication": "Au", |
| "confidentialityImpact": "C", |
| "integrityImpact": "I", |
| "availabilityImpact": "A" |
| }, |
| |
| // Define associative arrays mapping each metric value to the constant used in the CVSS scoring formula. |
| Weight: { |
| attackVector: { |
| NETWORK: 0.85, |
| ADJACENT_NETWORK: 0.62, |
| LOCAL: 0.55, |
| PHYSICAL: 0.2 |
| }, |
| attackComplexity: { |
| HIGH: 0.44, |
| LOW: 0.77 |
| }, |
| privilegesRequired: { |
| UNCHANGED: { |
| NONE: 0.85, |
| LOW: 0.62, |
| HIGH: 0.27 |
| }, |
| // These values are used if Scope is Unchanged |
| CHANGED: { |
| NONE: 0.85, |
| LOW: 0.68, |
| HIGH: 0.5 |
| } |
| }, |
| // These values are used if Scope is Changed |
| userInteraction: { |
| NONE: 0.85, |
| REQUIRED: 0.62 |
| }, |
| scope: { |
| UNCHANGED: 6.42, |
| CHANGED: 7.52 |
| }, |
| confidentialityImpact: { |
| NONE: 0, |
| LOW: 0.22, |
| HIGH: 0.56 |
| }, |
| integrityImpact: { |
| NONE: 0, |
| LOW: 0.22, |
| HIGH: 0.56 |
| }, |
| availabilityImpact: { |
| NONE: 0, |
| LOW: 0.22, |
| HIGH: 0.56 |
| } |
| // C, I and A have the same weights |
| }, |
| valueMap: { |
| "GREEN": "Green", |
| "RED": "Red", |
| "CLEAR": "Clear", |
| "AMBER": "Amber" |
| }, |
| cvss: {}, |
| m: function(m) { |
| var metric = this.metricMap4[m]; |
| if (metric && this.cvss[metric]) { |
| //console.log(["M:", m, this.valueMap[this.cvss[metric]] || this.cvss[metric].charAt(0)]); |
| return (this.valueMap[this.cvss[metric]] || this.cvss[metric].charAt(0)); |
| } else { |
| //console.log("M:", m, "X!"); |
| return "X"; |
| } |
| }, |
| vector4: function (cvss) { |
| var sep = "/"; |
| var r = "CVSS:4.0"; |
| for (var m in this.vectorMap4) { |
| if (this.vectorMap4[m] && cvss[m] && cvss[m] != 'NOT_DEFINED') { |
| r += sep + this.vectorMap4[m] + ':' + (this.valueMap[cvss[m]] || cvss[m].charAt(0)); |
| } |
| } |
| return r; |
| }, |
| vector3: function (cvss) { |
| var sep = "/"; |
| var r = "CVSS:3.1"; |
| for (var m in cvss) { |
| if (this.vectorMap3[m] && cvss[m]) { |
| r += sep + this.vectorMap3[m] + ':' + cvss[m].charAt(0); |
| } |
| } |
| return r; |
| |
| }, |
| vector2: function (cvss) { |
| var sep = "/"; |
| var r = []; |
| for (var m in cvss) { |
| if (this.vectorMap2[m] && cvss[m]) { |
| r.push(this.vectorMap2[m] + ':' + cvss[m].charAt(0)); |
| } |
| } |
| return r.join('/'); |
| }, |
| CVSSseveritys: [{ |
| name: "NONE", |
| bottom: 0.0, |
| top: 0.0 |
| }, { |
| name: "LOW", |
| bottom: 0.1, |
| top: 3.9 |
| }, { |
| name: "MEDIUM", |
| bottom: 4.0, |
| top: 6.9 |
| }, { |
| name: "HIGH", |
| bottom: 7.0, |
| top: 8.9 |
| }, { |
| name: "CRITICAL", |
| bottom: 9.0, |
| top: 10.0 |
| }], |
| severityLevel: function (score) { |
| if (score == 0) { |
| return 'NONE' |
| } else if (score <= 3.9) { |
| return 'LOW' |
| } else if (score <= 6.9) { |
| return 'MEDIUM' |
| } else if (score <= 8.9) { |
| return 'HIGH' |
| } else { |
| return 'CRITICAL' |
| } |
| }, |
| severity: function (score) { |
| var i; |
| var severityRatingLength = this.CVSSseveritys.length; |
| for (i = 0; i < severityRatingLength; i++) { |
| if (score >= this.CVSSseveritys[i].bottom && score <= this.CVSSseveritys[i].top) { |
| return this.CVSSseveritys[i]; |
| } |
| } |
| return { |
| name: "?", |
| bottom: 'Not', |
| top: 'defined' |
| }; |
| }, |
| roundUp1: function Roundup(input) { |
| var int_input = Math.round(input * 100000); |
| if (int_input % 10000 === 0) { |
| return int_input / 100000 |
| } else { |
| return (Math.floor(int_input / 10000) + 1) / 10 |
| } |
| }, |
| calculate3: function (cvss) { |
| var cvssVersion = "3.1"; |
| var exploitabilityCoefficient = 8.22; |
| var scopeCoefficient = 1.08; |
| var p; |
| var val = {}, |
| metricWeight = {}; |
| try { |
| for (p in this.Weight) { |
| val[p] = cvss[p]; |
| if (typeof val[p] === "undefined" || val[p] === '' || val[p] == null) { |
| return "?"; |
| } |
| metricWeight[p] = this.Weight[p][val[p]]; |
| } |
| } catch (err) { |
| return err; // TODO: need to catch and return sensible error value & do a better job of specifying *which* parm is at fault. |
| } |
| metricWeight.privilegesRequired = this.Weight.privilegesRequired[val.scope][val.privilegesRequired]; |
| // |
| // CALCULATE THE CVSS BASE SCORE |
| // |
| try { |
| var baseScore, impactSubScore, impact, exploitability; |
| var impactSubScoreMultiplier = (1 - ((1 - metricWeight.confidentialityImpact) * (1 - metricWeight.integrityImpact) * (1 - metricWeight.availabilityImpact))); |
| if (val.scope === 'UNCHANGED') { |
| impactSubScore = metricWeight.scope * impactSubScoreMultiplier; |
| } else { |
| impactSubScore = metricWeight.scope * (impactSubScoreMultiplier - 0.029) - 3.25 * Math.pow(impactSubScoreMultiplier - 0.02, 15); |
| } |
| exploitability = exploitabilityCoefficient * metricWeight.attackVector * metricWeight.attackComplexity * metricWeight.privilegesRequired * metricWeight.userInteraction; |
| if (impactSubScore <= 0) { |
| baseScore = 0; |
| } else { |
| if (val.scope === 'UNCHANGED') { |
| baseScore = this.roundUp1(Math.min((exploitability + impactSubScore), 10)); |
| } else { |
| baseScore = this.roundUp1(Math.min((exploitability + impactSubScore) * scopeCoefficient, 10)); |
| } |
| } |
| return baseScore.toFixed(1); |
| } catch (err) { |
| return err; |
| } |
| }, |
| w2: { |
| accessComplexity: { |
| HIGH: 0.35, |
| MEDIUM: 0.61, |
| LOW: 0.71 |
| }, |
| authentication: { |
| NONE: 0.704, |
| SINGLE: 0.56, |
| MULTIPLE: 0.45 |
| }, |
| accessVector: { |
| LOCAL: 0.395, ADJACENT_NETWORK: 0.646, |
| NETWORK: 1 |
| }, |
| confidentialityImpact: { |
| NONE: 0, |
| PARTIAL: 0.275, |
| COMPLETE: 0.660 |
| }, |
| integrityImpact: { |
| NONE: 0, |
| PARTIAL: 0.275, |
| COMPLETE: 0.660 |
| }, |
| availabilityImpact: { |
| NONE: 0, |
| PARTIAL: 0.275, |
| COMPLETE: 0.660 |
| } |
| }, |
| calculate2: function(cvss) { |
| var w2 = this.w2; |
| var impact = 10.41 * (1 - |
| (1-w2.confidentialityImpact[cvss.confidentialityImpact]) |
| *(1-w2.integrityImpact[cvss.integrityImpact]) |
| *(1-w2.availabilityImpact[cvss.availabilityImpact])); |
| if (impact == 0) { |
| return 0; |
| } |
| var exploitability = 20.0 |
| * w2.accessComplexity[cvss.accessComplexity] |
| * w2.authentication[cvss.authentication] |
| * w2.accessVector[cvss.accessVector]; |
| |
| return ((0.6*impact + 0.4*exploitability - 1.5)*1.176).toFixed(1); |
| } |
| } |
| |
| function copyToClipboard(text){ |
| navigator.clipboard.writeText(text).catch(err => { |
| console.error('Failed to copy text: ', err); |
| }); |
| return false; |
| } |