default/cve5/portal.js (684 lines of code) (raw):
//CVE Services Client and Portal GUI
var csClient = undefined;
var csCache = {
portalType: 'production',
url: 'https://cveawg.mitre.org/api',
org: null,
user: null,
orgInfo: null
}
async function initCsClient() {
if ('serviceWorker' in navigator) {
try {
if (window.localStorage.getItem('cveApi')) {
csCache = JSON.parse(window.localStorage.getItem('cveApi'));
}
if (!csCache.url) {
csCache.url = 'https://cveawg.mitre.org/api';
}
csClient = new CveServices(csCache.url, "./static/cve5sw.js");
listenforLogins();
listenforLogouts();
if (!csCache.user) {
showPortalLogin();
return;
}
await showPortalView();
} catch (e) {
portalErrorHandler(e);
}
} else {
document.getElementById('port').innerHTML = '<h2 class="pad2 tred">Browser does not support Service Workers feature required for this tab.</h2><i class="indent pad2">Are you using Firefox in Private mode? Try normal mode.</i>';
// console.log("Browser does not support Service Workers. Are you using Firefox in Private mode?")
//cveShowError('Browser not supported!');
}
}
function showPortalLogin(message) {
let prevPortalType = window.localStorage.getItem('portalType');
let prevPortalUrl = window.localStorage.getItem('portalUrl');
if (!prevPortalType || !prevPortalUrl) {
// ensure consistency if either value is missing from localStorage by setting both to default
prevPortalType = 'production';
prevPortalUrl = 'https://cveawg.mitre.org/api';
}
csCache = {
portalType: prevPortalType,
url: prevPortalUrl,
org: null,
user: null,
orgInfo: null
}
window.localStorage.removeItem('cveApi');
if (document.getElementById('port'))
document.getElementById('port').innerHTML = cveRender({
ctemplate: 'cveLoginBox',
message: message,
prevPortal: prevPortalType,
prevOrg: window.localStorage.getItem('shortName')
})
}
async function portalLogout(message) {
if (csClient != null) {
await csClient.logout();
}
showPortalLogin(message);
}
async function showPortalView(orgInfo, userInfo) {
try {
if (!orgInfo) {
orgInfo = await csClient.getOrgInfo();
}
if (!userInfo) {
userInfo = await csClient.getOrgUser(csCache.user);
}
if (document.getElementById('port'))
document.getElementById('port').innerHTML = cveRender({
portalType: csCache.portalType,
portalURL: csCache.url,
ctemplate: 'portal',
userInfo: userInfo,
org: orgInfo
});
var button1 = document.getElementById('post1');
if(button1) {
if(csCache.portalType == 'test') {
button1.innerText = 'Post to Test Portal'
} else {
button1.innerText = 'Post to CVE.org'
}
}
var button2 = document.getElementById("post2")
if(button2) {
if(csCache.portalType == 'test') {
button2.innerText = 'Post to Test Portal'
} else {
button2.innerText = 'Post to CVE.org';
}
}
return await cveGetList();
} catch (e) {
portalErrorHandler(e);
}
}
var loginChannel = new BroadcastChannel("login");
var logoutChannel = new BroadcastChannel("logout");
function listenforLogins() {
loginChannel.onmessage = function (a) {
initCsClient();
}
}
function listenforLogouts() {
logoutChannel.onmessage = function (a) {
showPortalLogin(a.message);
}
}
async function portalLogin(elem, credForm) {
try {
if (!'serviceWorker' in navigator) {
cveShowError('Browser is missing required features. Try a different browser or the normal mode.')
return (false);
}
if (!credForm.checkValidity()) {
return (false);
}
elem.preventDefault();
var url = credForm.portal.value;
var portalType = credForm.portal.options[credForm.portal.selectedIndex].text;
if (csClient && csCache.url != url) {
csClient = new CveServices(url, "./static/cve5sw.js");
}
var ret = await csClient.login(
credForm.user.value,
credForm.org.value,
credForm.key.value);
var orgInfo = await csClient.getOrgInfo();
var userInfo = await csClient.getOrgUser(credForm.user.value);
csCache.user = credForm.user.value;
csCache.org = credForm.org.value;
csCache.url = url;
csCache.portalType = portalType;
csCache.orgInfo = orgInfo;
window.localStorage.setItem('cveApi', JSON.stringify(csCache));
window.localStorage.setItem('portalType', portalType);
window.localStorage.setItem('portalUrl', url);
window.localStorage.setItem('shortName', credForm.org.value);
if (ret == 'ok' || ret.data == "ok") {
csCache.keyUrl = ret.keyUrl;
await showPortalView(orgInfo, userInfo);
/* Add one hour session timeout in addition to timeout in serviceWorker */
setTimeout(portalLogout, defaultTimeout);
//announce to others that a login happened.
loginChannel.postMessage({ message: 'The user has logged in' });
} else {
document.getElementById("loginErr").innerText = 'Failed to login: Possibly invalid credentials!';
}
} catch (e) {
portalErrorHandler(e);
}
}
function resetPortalLoginErr() {
//console.log('changed form');
document.getElementById("loginErr").innerText = '';
}
function portalErrorHandler(e) {
if (e.error && (e.error == 'NO_SESSION' || e.error == 'UNAUTHORIZED' || e.error.message == 'Failed to fetch')) {
if (e.error == 'UNAUTHORIZED') {
e.message = "Valid credentials required",
mainTabGroup.focus(3);
}
if (e.error.message == 'Failed to fetch') {
e.message = "Error connecting to service";
}
if (document.getElementById("loginErr")) {
// Login screen exists
document.getElementById("loginErr").innerText = e && e.message ? e.message : 'Valid credentials required!';
} else {
showPortalLogin(e.message);
}
} else {
cveShowError(e);
}
}
async function userlistUpdate(elem, event) {
if (elem.open) {
document.getElementById("userStatsPopup").open = false;
try {
var ret = await csClient.getOrgUsers();
var userlist = document.getElementById('userlist');
if (userlist) {
userlist.innerHTML = cveRender({
ctemplate: 'listUsers',
users: ret.users
})
}
} catch (e) {
portalErrorHandler(e);
}
}
}
async function cveUserKeyReset(elem, confirm) {
var u = elem.form.u.value;
var temp1 = document.getElementById("alertOk");
if (confirm) {
temp1.setAttribute("onclick", "document.getElementById('alertDialog').close();");
elem.removeAttribute('id');
document.getElementById('alertDialog').close();
} else {
showAlert("Are you sure?", "A new API key will be generated for user " + u + "! The old API key will no longer work!", undefined, true);
let randid = Math.random().toString(32).substring(2);
elem.setAttribute('id', randid);
temp1.setAttribute('u', u);
temp1.setAttribute('onclick', 'cveUserKeyReset(document.getElementById("' + randid + '"),true)');
return;
}
try {
var ret = await csClient.resetOrgUserApiKey(u);
if (ret["API-secret"]) {
var msg = "API Key was reset for " + u + "!";
if (csCache.user == u) {
msg += " You will need to login again with the new key!";
portalLogout();
}
document.getElementById("userMessage").innerText = msg;
document.getElementById("secretDialogForm").pass.value = ret["API-secret"];
document.getElementById("secretDialogForm").pass.type = "password";
document.getElementById("secretDialog").showModal();
}
} catch (e) {
portalErrorHandler(e);
}
}
async function cveUpdateUser(f) {
try {
params = {
"name.first": f.first.value,
"name.last": f.last.value
};
if (f.u.value != f.new_username.value) {
params.new_username = f.new_username.value
}
if (csCache.user != f.u.value) {
params.active = f.active.checked;
if (f.admin.checked) {
params["active_roles.add"] = 'ADMIN'
} else {
params["active_roles.remove"] = 'ADMIN'
}
}
var ret = await csClient.updateOrgUser(f.u.value, params);
if (ret.updated) {
document.getElementById("userEditDialog").close();
if (document.getElementById("userListPopup")) {
userlistUpdate(document.getElementById("userListPopup"));
}
//the current user is updating self
if ((csCache.user == f.u.value) && document.getElementById("cveUser")) {
if (csCache.user != ret.updated.username) {
cveShowError({ error: 'Username changed!', message: 'Username successfully changed to ' + ret.updated.username + '! You will need to login again!' });
portalLogout();
return;
}
if (document.getElementById('cveUser'))
document.getElementById("cveUser").innerHTML =
cveRender({
ctemplate: 'userstats',
userInfo: ret.updated,
org: await csClient.getOrgInfo()
})
}
}
} catch (e) {
cveShowError(e);
}
}
function removeErrors() {
Array.from(document.getElementsByClassName('formError')).forEach(x => x.remove());
}
function addError(el) {
var div = document.createElement('div');
div.classList.add('formError');
div.innerHTML = "Please provide a valid data";
div.setAttribute('onclick', 'this.remove(); return false;');
el.setAttribute('onfocus', 'removeErrors()');
el.after(div);
}
function validateForm(f) {
let isvalid = true;
f.elements.forEach(x => {
if (!isvalid)
return;
/* Needed is an alias for required to avoid showing
red boxes unless a submit event is initiated. */
if (x.getAttribute("needed"))
x.setAttribute("required", "required");
if ('validity' in x) {
if ('valid' in x.validity) {
isvalid = x.validity.valid;
if (!isvalid)
addError(x);
}
}
});
return isvalid;
}
async function cveUserEdit(elem) {
f = document.getElementById('userEditForm');
f.u.value = elem.getAttribute('u');
f.new_username.value = elem.getAttribute('u');
f.first.value = elem.getAttribute('f');
f.last.value = elem.getAttribute('l');
f.admin.checked = elem.getAttribute('ad') ? true : false;
f.active.checked = elem.getAttribute('ac') ? true : false;
if (csCache.user == f.u.value) {
if (!elem.getAttribute('ad')) {
f.new_username.disabled = true;
}
f.admin.parentElement.setAttribute('class', 'hid');
f.admin.setAttribute('disabled', true);
f.active.setAttribute('disabled', true);
} else {
f.admin.parentElement.removeAttribute('class');
f.admin.removeAttribute('disabled');
f.active.removeAttribute('disabled');
}
document.getElementById('userEditDialog').showModal();
}
async function cveAddUser(f) {
if (validateForm(f)) {
try {
const userFields = {
"username": f.new_username.value,
"name": {
"first": f.first.value,
"last": f.last.value
},
"authority": {
"active_roles": []
}
}
if (f.admin.checked) {
userFields.authority.active_roles.push("ADMIN")
}
var ret = await csClient.createOrgUser(userFields);
if (ret.created && ret.created.secret) {
document.getElementById('userAddDialog').close();
document.getElementById("secretDialogForm").pass.value = ret.created.secret;
document.getElementById("secretDialogForm").pass.type = "password";
document.getElementById("secretDialog").showModal();
document.getElementById("userMessage").innerText = ret.message;
f.reset()
userlistUpdate({ open: true });
}
} catch (e) {
portalErrorHandler(e);
}
} else {
cveShowError('Please provide valid information!');
}
}
async function cveOrgUpdate() {
cveShowError('To be done');
}
async function cveRenderList(l, refreshEditor) {
if (l && document.getElementById('cveList')) {
document.getElementById('cveList').innerHTML = cveRender({
ctemplate: 'listIds',
cveIds: l,
editable: true//(csCache.portalType == 'test')
})
if (l.length > 0) {
new Tablesort(document.getElementById('cveListTable'));
}
if (refreshEditor) {
docSchema.definitions.cveId.examples = l.map(i => i.cve_id);
editorSetCveDatalist(l);
}
var editableList = document.getElementById('editablelist');
if (editableList) {
editableList.innerHTML = cveRender({
ctemplate: 'editables',
cveIds: l
})
}
}
}
async function editorSetCveDatalist(l) {
document.getElementById('root.cveMetadata.cveId-datalist').innerHTML = cveRender({
ctemplate: 'reserveds',
cveIds: l
})
}
function paginate(a) {
let el = document.getElementById('cvePage');
if (!el) {
//console.log("Error cannot find template ");
//console.log(a);
return false;
}
let cp = parseInt(el.getAttribute('data-page'));
if (isNaN(cp)) {
//console.log("The data-page element is not pareable ");
//console.log(cp);
return galse;
}
let np = cp + parseInt(a);
var cveForm = document.getElementById("cvePortalFilter");
cveForm.page = np;
cveGetList();
return false;
}
//var collator = new Intl.Collator(undefined, {numeric: true});
async function pageShow(ret) {
let el = document.getElementById('cvePage');
if (!el) {
//console.log("Error cannot find template ");
//console.log(ret);
return;
}
el.style.display = 'block';
el.setAttribute('data-page', ret.currentPage);
let start = (ret.currentPage - 1) * ret.itemsPerPage + 1;
let end = start + ret.itemsPerPage - 1;
let total = ret.totalCount;
if (end > total)
end = total;
document.getElementById('cvePageInfo').innerHTML = "Showing " +
String(start) + " to " + String(end) + " of " +
String(total) + " records "
document.getElementById('currentPage').innerHTML = ret.currentPage
if (ret.prevPage)
document.getElementById('prevPage').style.display = 'block';
else
document.getElementById('prevPage').style.display = 'none';
if (ret.nextPage)
document.getElementById('nextPage').style.display = 'block';
else
document.getElementById('nextPage').style.display = 'none';
}
async function cveShowError(err) {
if ((err.error == 'UNAUTHORIZED' || err.error == 'NO_SESSION' || csClient == null || await csClient._middleware.worker == null)) {
err.message = 'Login required';
showPortalLogin();
mainTabGroup.focus(3);
}
document.getElementById('cveErrors').innerHTML = cveRender({
ctemplate: 'cveErrors',
err: err
})
document.getElementById('cveErrorsModal').showModal();
}
async function cveGetList() {
var currentReserved = true;
var filter = {
state: 'RESERVED',
cve_id_year: currentYear
}
var cveForm = document.getElementById("cvePortalFilter");
if (cveForm) {
if (cveForm.fstate) {
if (cveForm.fstate.value) {
filter.state = cveForm.fstate.value + '';
if (filter.state != 'RESERVED') {
currentReserved = false;
}
} else {
delete filter.state;
}
}
if (cveForm.y) {
filter.cve_id_year = cveForm.y.value + '';
if (filter.cve_id_year != currentYear) {
currentReserved = false;
}
}
if (cveForm.page) {
filter.page = cveForm.page;
}
}
if (document.getElementById('cveList')) {
document.getElementById('cveList').innerHTML = '<center><div class="spinner"></div></center>';
}
try {
var ret = await csClient.getCveIds(filter);
if (ret.error) {
cveShowError(ret);
} else {
var idList = [];
var idState = {};
if (ret && ret.cve_ids) {
idList = ret.cve_ids;
idList = idList.sort((b, a) => (a.reserved > b.reserved) ? 1 : ((b.reserved > a.reserved) ? -1 : 0));
for (var i = 0; i < idList.length; i++) {
idState[idList[i].cve_id] = idList[i].state;
}
}
cveRenderList(idList, currentReserved);
if (ret && (ret.nextPage || ret.prevPage)) {
pageShow(ret);
} else {
let el = document.getElementById('cvePage');
if (el) {
el.removeAttribute('data-page');
el.style.display = 'none';
}
if (cveForm)
cveForm.page = 0;
}
return idList;
}
} catch (e) {
cveShowError(e);
cveRenderList([]);
return ([]);
}
}
async function cveReserve(yearOffset, number) {
var year = currentYear + (yearOffset ? yearOffset : 0);
try {
var args = {
amount: number > 0 && number <= 50 ? number : 1,
// Request only one at this time to get four digits! Requesting more at time gives the 5 digit ids.
// batch_type: 'nonsequential',
cve_year: year,
short_name: csCache.org
};
if (number > 1) {
args.batch_type = 'sequential';
}
var json = await csClient.reserveCveIds(args);
return json.cve_ids;
} catch (e) {
cveShowError(e.message);
}
}
async function cveSelectLoad(event) {
event.preventDefault();
try {
cveLoad(event.target.elements.id.value)
} catch (e) {
portalErrorHandler(e);
cveShowError('Please login to CVE Portal. Your session may have expired!');
}
return false;
}
async function cveLoad(cveId) {
try {
var res = await csClient.getCve(cveId);
if (res.cveMetadata) {
if (res.containers) {
res = cveFixForVulnogram(res);
} else {
console.log('no containers');
}
var edOpts = (res.cveMetadata.state == 'REJECTED') ? rejectEditorOption : publicEditorOption;
loadJSON(res, cveId, "Loaded " + cveId + " from CVE.org!", edOpts);
mainTabGroup.change(0);
return res;
} else {
errMsg.textContent = "Failed to load valid CVE Record";
infoMsg.textContent = "";
}
} catch (e) {
if (e == '404' || e.error == 'CVE_RECORD_DNE') {
var skeleton = {
"cveMetadata": {
"cveId": cveId,
"assigner": csCache.orgInfo ? csCache.orgInfo.UUID : "",
}
};
try {
var res = await csClient.getCveId(cveId);
var edOpts = publicEditorOption;
if (res.state == 'RESERVED') {
skeleton.cveMetadata.state = "PUBLISHED";
} else if (res.state == 'REJECTED') {
skeleton.cveMetadata.state = "REJECTED";
edOpts = rejectEditorOption;
} else {
return {};
}
loadJSON(skeleton, cveId, "Loaded " + cveId, edOpts);
mainTabGroup.change(0);
return skeleton;
} catch (e2) {
if (e2 == '404') {
showAlert('CVE Not found!');
}
}
} else {
//console.log(e);
portalErrorHandler(e);
}
}
}
async function cveReject(elem, event) {
var id = elem.getAttribute('data');
if (window.confirm('Do you want to reject ' + id + '? It cannot be undone!')) {
try {
var ret = await csClient.updateCveId(id, 'REJECTED', csCache.org);
if (ret.updated && ret.updated.state == 'REJECTED') {
var m = document.getElementById("cveStatusMessage");
m.innerText = "Rejected " + id;
await cveGetList();
}
} catch (e) {
portalErrorHandler(e);
}
}
}
function transatePath(p) {
if(p) {
p = p.replace("/cnaContainer", "root.containers.cna");
p = p.replaceAll('/', '.');
}
return p;
}
async function cvePost() {
// ASF
var j = await mainTabGroup.getValue();
res = await fetch('/publishcve', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken,
},
redirect: 'error',
body: JSON.stringify({cve:j.cveMetadata.cveId})
});
const out = await res.json();
cveShowError(out.body);
return;
// END ASF
if (docEditor.validation_results && docEditor.validation_results.length == 0) {
/*if (save != undefined) {
await save();
}*/
try {
//if (csCache.portalType === 'test') {
//console.log('uploading...');
var j = await mainTabGroup.getValue();
var j = textUtil.reduceJSON(j);
/*var pts = j.containers.cna.problemTypes;
if(pts && pts.length == 1 && pts[0].descriptions && pts[0].descriptions[0].description == undefined) {
delete j.containers.cna.problemTypes;
}
var ims = j.containers.cna.impacts;
if(ims && ims.length == 1 && ims[0].descriptions && ims[0].descriptions[0].value == undefined) {
delete j.containers.cna.impacts;
}*/
var ret = null;
try {
var latestId = await csClient.getCveId(j.cveMetadata.cveId);
if (latestId.state == 'RESERVED') {
//console.log('Creating');
if (j.cveMetadata.state == 'PUBLISHED') {
ret = await csClient.createCve(j.cveMetadata.cveId, { cnaContainer: j.containers.cna });
} else if (j.cveMetadata.state == 'REJECTED') {
ret = await csClient.createRejectedCve(j.cveMetadata.cveId, { cnaContainer: j.containers.cna });
}
} else {
//console.log('uploading');
if (j.cveMetadata.state == 'PUBLISHED') {
ret = await csClient.updateCve(j.cveMetadata.cveId, { cnaContainer: j.containers.cna });
} else if (j.cveMetadata.state == 'REJECTED') {
ret = await csClient.updateRejectedCve(j.cveMetadata.cveId, { cnaContainer: j.containers.cna });
}
}
} catch (e) {
//console.log('Got error');
//console.log(e);
if (e.error) {
infoMsg.innerText = "";
var alertMessage = "";
if (e.details && e.details.errors) {
showJSONerrors(e.details.errors.map(
a => {
alertMessage = alertMessage + ', ' + a.message;
return ({
path: transatePath(a.instancePath),
message: a.message
});
}
));
} else if (e.error == 'UNAUTHORIZED') {
cveShowError(e);
}
} else {
showAlert('Error publishing! Got error ' + e)
}
}
//console.log(ret);
if (ret != null && ret.message) {
showAlert("CVE Record is Published", ret.message, 10000);
var a = document.createElement('a');
a.setAttribute('href', (csCache.portalType == 'test'? 'https://test.cve.org/cverecord?id=' : 'https://www.cve.org/cverecord?id=')+j.cveMetadata.cveId);
a.setAttribute('target', '_blank');
a.innerText = ret.message;
infoMsg.innerText = '';
infoMsg.appendChild(a);
hideJSONerrors();
}
//} else {
// showAlert('CVE posting is not currently supported by production CVE services! Try Logging to Test Portal instance');
//}
} catch (e) {
portalErrorHandler(e);
}
} else {
showAlert('Please fix errors before posting');
}
}
async function cveReserveAndRender(yearOffset, number) {
try {
var r = await cveReserve(yearOffset, number);
var m = document.getElementById("cveStatusMessage");
if (m && r && r.length > 0) {
m.innerText = "Got " + r.map(x => x.cve_id).join(', ');
} else {
m.innerText = "Failed to get a CVE ID";
}
cvePortalFilter.reset();
await cveGetList();
return r;
} catch (e) {
portalErrorHandler(e);
}
}