public/js/util.js (753 lines of code) (raw):

function orderKeys(obj) { var keys = Object.keys(obj).sort(function keyOrder(k1, k2) { if (k1 < k2) return -1; else if (k1 > k2) return +1; else return 0; }); var i, after = {}; for (i = 0; i < keys.length; i++) { after[keys[i]] = obj[keys[i]]; delete obj[keys[i]]; } for (i = 0; i < keys.length; i++) { obj[keys[i]] = after[keys[i]]; //recurse if (obj[keys[i]] instanceof Object) { obj[keys[i]] = orderKeys(obj[keys[i]]); } } return obj; } 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; }; //for inserting images as data URLs in wysihytml5 widget function loadimg(e) { var sibs = this.parentNode.parentNode.childNodes; var f = sibs[1]; var ok = sibs[4]; e.preventDefault(); var file = this.files[0]; if(file.size > 528385) { alert('Image size should less than 500k'); return false; }; if(file.type.indexOf("image")==-1){ alert("Not an image!"); return false; } var reader = new FileReader(); reader.onload = function (event) { f.value = event.target.result; ok.click(); }; reader.readAsDataURL(file); return false; }; 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(orderKeys(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; }, mergeJSON : function (target, add) { function isObject(obj) { if (typeof obj == "object") { for (var key in obj) { if (obj.hasOwnProperty(key)) { return true; } } } return false; } for (var key in add) { if (key === "__proto__" || key === "constructor") continue; if (add.hasOwnProperty(key)) { if (target[key] && isObject(target[key]) && isObject(add[key])) { this.mergeJSON(target[key], add[key]); } else { target[key] = add[key]; } } } return target; }, timeSince: function(date) { var seconds = Math.floor((new Date() - date) / 1000); var interval = Math.floor(seconds / 31536000); if (interval > 1) { return interval + " years"; } interval = Math.floor(seconds / 2592000); if (interval > 1) { return interval + " months"; } interval = Math.floor(seconds / 86400); if (interval > 1) { return interval + " days"; } interval = Math.floor(seconds / 3600); if (interval > 1) { return interval + " hours"; } interval = Math.floor(seconds / 60); if (interval > 1) { return interval + " minutes"; } return Math.floor(seconds) + " seconds"; }, //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' }, 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') } }; 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", "Safety": "S", "Automatable": "AU", "Recovery": "R", "valueDensity": "V", "vulnerabilityResponseEffort": "RE", "providerUrgency": "U", "exploitMaturity": "E" }, metricMap4: { "AV": "attackVector", "AC": "attackComplexity", "AT": "attackRequirements", "PR": "privilegesRequired", "UI": "userInteraction", "VC": "vulnConfidentialityImpact", "VI": "vulnIntegrityImpact", "VA": "vulnAvailabilityImpact", "SC": "subConfidentialityImpact", "SI": "subIntegrityImpact", "SA": "subAvailabilityImpact", "S": "Safety", "AU": "Automatable", "R": "Recovery", "V": "valueDensity", "RE": "vulnerabilityResponseEffort", "U": "providerUrgency", "E": "exploitMaturity" }, 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); } }