Skip to content

Commit 1f891d2

Browse files
authored
Merge pull request CERTCC#62 from CERTCC/ssvc-calc-registry
2 parents a17f0af + db0078a commit 1f891d2

2 files changed

Lines changed: 75 additions & 78 deletions

File tree

docs/ssvc-calc/findex.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ <h4>Decision Tree Usage:</h4>
425425
<div class="row justify-content-center position-absolute exportdiv">
426426
<div class="export-header">
427427
<h3> Export Calculated SSVC Score</h3>
428-
<button type="button" aria-label="Close" class="close h3" onclick="$(this).parent().parent().remove()">
428+
<button type="button" aria-label="Close" class="close h3">
429429
<span aria-hidden="true">×</span>
430430
</button>
431431
</div>
@@ -434,8 +434,11 @@ <h3> Export Calculated SSVC Score</h3>
434434
<tbody>
435435
<tr>
436436
<td>
437-
<input type="text" placeholder="timestamp UTC like 2025-01-01T12:00:00Z" name="timestamp"
438-
class="form-control timestamp" pattern="^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))$">
437+
<label>
438+
Timestamp: <input type="text" placeholder="timestamp UTC like 2025-01-01T12:00:00Z" name="timestamp"
439+
class="form-control timestamp" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?Z" style="width:auto; display:inline-block;min-width:24ch;">
440+
</label>
441+
<br>
439442
<label> <input type="checkbox" name="export-optional" onclick="export_optional(this.checked)"> Provide extra information</label>
440443
</td>
441444
</tr>

docs/ssvc-explorer/simple.js

Lines changed: 69 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const __version__ = "1.0.8";
1+
const __version__ = "1.0.9";
22
const SSVC = {
33
"outcomes": [],
44
"results": {},
@@ -398,7 +398,7 @@ function createSSVC(csv, uploaded) {
398398
/* Dynamically build the name map per Tree. Assumption is there
399399
are NO two decision points with the same name */
400400
if(dp.name in SSVC.dpMap)
401-
topalert("danger", "Duplicate Names found in Decision Table can ause confusion", 0);
401+
topalert("danger", "Duplicate Names found in Decision Table can cause confusion", 0);
402402
SSVC.dpMap[dp.name] = {name: dp.name, version: dp.version,
403403
namespace: dp.namespace, data: dp};
404404
if(k != jsonTree.outcome) {
@@ -485,7 +485,7 @@ function createSSVC(csv, uploaded) {
485485
if(dp.description)
486486
dpInput.parentElement.parentElement.setAttribute("data-help",JSON.stringify(dp));
487487
}
488-
}
488+
}
489489
if(dp.values) {
490490
for(let i=0; i<dp.values.length; i++) {
491491
if(dp.values[i].name.toLowerCase() == dpInput.value.toLowerCase()) {
@@ -1465,70 +1465,49 @@ function deepGet(inputs) {
14651465
return fdp.obj;
14661466
return {};
14671467
}
1468-
function enumerate_dps(obj) {
1469-
const keys = Object.keys(obj);
1470-
const values = Object.values(obj);
1471-
1472-
return values.reduce(function(acc, curr) {
1473-
const result = [];
1474-
acc.forEach(function(row) {
1475-
curr.forEach(function(value) {
1476-
result.push(row.concat(value));
1477-
});
1478-
});
1479-
return result;
1480-
}, [[]]).map(function(combination) {
1481-
return combination.reduce(function (acc, value, index) {
1482-
acc[keys[index]] = value;
1483-
return acc;
1484-
}, {});
1485-
});
1486-
}
1487-
function makeTree(rows, outcomeName, name) {
1488-
const heads = Object.keys(rows[0]);
1489-
let datacsv = heads.join(",");
1490-
let endcsv = "\n";
1491-
if(heads.includes(outcomeName)) {
1492-
datacsv = datacsv + endcsv;
1493-
} else {
1494-
/* If final field is not provided add it with unknown as the outcome */
1495-
datacsv = datacsv + "," + outcomeName + "\n";
1496-
endcsv = ",Unknown\n";
1497-
}
1498-
rows.forEach(function(row) {
1499-
datacsv += heads.map(function(head) { return row[head]}).join(",") + endcsv;
1500-
});
1468+
function makeTree(jsonTree) {
1469+
const clbutton = SSVC.form.parentElement.querySelector("[data-clear]");
1470+
jsonTree.mapping = [];
1471+
jsonTree.mapping = enumerateCombinations(jsonTree);
15011472
SSVC.form.innerHTML = "";
1502-
createSSVC(datacsv, false);
1503-
if(name)
1504-
selectCustom(name, datacsv, -1);
1505-
else
1506-
customize({innerHTML: "Customize"});
1473+
jsonTree.key = uniq_key(jsonTree, SSVC.decision_trees.map(x => x.data),"DT_", 2);
1474+
createSSVC(jsonTree, false);
1475+
customize({innerHTML: "Customize"});
1476+
clbutton.setAttribute("data-changed","1");
1477+
topalert();
1478+
topalert("New Decision Tree has Outcomes that are evenly laid out! Please update these " +
1479+
"as appropriate for your Decision Model, before Saving it!","warn",0);
15071480
}
15081481
async function deleteDP(el) {
15091482
const dpName = el.parentElement.childNodes[0].textContent;
15101483
const confirm = await popupConfirm('Do you want to delete Decision Point "' + dpName + '"');
15111484
if(confirm == "Yes") {
1512-
const delDP = el.parentElement.getAttribute("data-dp");
1513-
const alldps = Array.from(SSVC.form.querySelectorAll("main [data-dp]")).map(function(el) { return el.getAttribute("data-dp")});
1514-
if(alldps.length < 3) {
1515-
topalert("Minimum two decision points are needed", "danger");
1516-
return false;
1517-
}
1518-
const dpCombo = alldps.reduce(function(combo,dp) { combo[dp] = []; return combo; }, {});
1519-
delete dpCombo[delDP];
1520-
SSVC.decision_table.forEach(function(row) {
1521-
delete row[delDP];
1522-
Object.keys(row).forEach(function(dp) {
1523-
/* Ignore the dp which is actually result/priority */
1524-
if(!dpCombo[dp])
1525-
return;
1526-
if(!dpCombo[dp].includes(row[dp]))
1527-
dpCombo[dp].push(row[dp]);
1485+
try {
1486+
const delDP = el.parentElement.getAttribute("data-dp");
1487+
const alldps = Array.from(SSVC.form.querySelectorAll("main [data-dp]")).map(function(el) { return el.getAttribute("data-dp")});
1488+
if(alldps.length < 3) {
1489+
topalert("Minimum two decision points are needed", "danger");
1490+
return false;
1491+
}
1492+
const removedp = JSON.parse(el.parentElement.parentElement.getAttribute("data-help"));
1493+
console.log(removedp);
1494+
const clbutton = SSVC.form.parentElement.querySelector("[data-clear]");
1495+
const jsonTree = JSON.parse(clbutton.getAttribute("data-json"));
1496+
const matched = Object.keys(jsonTree.decision_points).some(function(dpkey) {
1497+
const dp = jsonTree.decision_points[dpkey];
1498+
if(match_name_ns_vers({data: dp},removedp)) {
1499+
delete jsonTree.decision_points[dpkey];
1500+
return true;
1501+
}
1502+
return false;
15281503
});
1529-
});
1530-
const rows = enumerate_dps(dpCombo);
1531-
makeTree(rows, "Priority", null);
1504+
if(matched)
1505+
makeTree(jsonTree);
1506+
else
1507+
throw "Could not delete decision point";
1508+
} catch(err) {
1509+
topalert(err,"danger");
1510+
}
15321511
}
15331512
}
15341513
function find_key(dp,dt) {
@@ -1726,7 +1705,9 @@ function updateTree() {
17261705
/* This is to replace a decision point */
17271706
const oldvalues = simpleCopy(jsonTree.decision_points[oldKey].values);
17281707
delete jsonTree.decision_points[oldKey];
1729-
const newKey = dp.namespace.substr(0,3) + ":" + dp.key + ":" + dp.version;
1708+
let newKey = dp.namespace + ":" + dp.key + ":" + dp.version;
1709+
if(dp.namespace.indexOf("x_") > -1)
1710+
newKey = dp.namespace.substr(0,3) + ":" + dp.key + ":" + dp.version;
17301711
jsonTree.decision_points[newKey] = dp;
17311712
if(dpOutcome) {
17321713
jsonTree.outcome = newKey;
@@ -1755,23 +1736,16 @@ function updateTree() {
17551736
}
17561737
}
17571738
} else if(updatebtn.innerText == "Add") {
1739+
if(!dp.version)
1740+
dp.version = "1.0.0";
17581741
const newKey = dp.namespace + ":" + dp.key + ":" + dp.version;
17591742
jsonTree.decision_points[newKey] = dp;
17601743
} else {
17611744
topalert("Error: Unable to find if this is a new Decision Point or a replacement",
17621745
"danger");
17631746
}
17641747
/* Somethings have majorly changed, we have to update the decision tree mappings */
1765-
jsonTree.mapping = [];
1766-
jsonTree.mapping = enumerateCombinations(jsonTree);
1767-
SSVC.form.innerHTML = "";
1768-
jsonTree.key = uniq_key(jsonTree, SSVC.decision_trees.map(x => x.data),"DT_", 2);
1769-
createSSVC(jsonTree, false);
1770-
customize({innerHTML: "Customize"});
1771-
clbutton.setAttribute("data-changed","1");
1772-
topalert();
1773-
topalert("New Decision Tree has Outcomes that are evenly laid out! Please update these " +
1774-
"as appropriate for your Decision Model, before Saving it!","warn",0);
1748+
makeTree(jsonTree);
17751749
}
17761750
function schemaTransform(dtnew) {
17771751
const dtobj = simpleCopy(dtnew);
@@ -1826,9 +1800,29 @@ function import_json(json, name) {
18261800
name = "Custom Uploaded ";
18271801
SSVC.form.innerHTML = "";
18281802
createSSVC(json, false);
1829-
if(name)
1830-
selectCustom(name, json, -1);
1831-
return;
1803+
/* Insert a new element in the array*/
1804+
let fIndex = SSVC.decision_trees.findIndex(function(dtobj) {
1805+
return match_name_ns_vers(dtobj,json);
1806+
});
1807+
if(fIndex < 0) {
1808+
const newname = name_version(json);
1809+
Object.values(json.decision_points).forEach(function(newdp) {
1810+
if(!(SSVC.decision_points.some(function(dtobj) {
1811+
return match_name_ns_vers(dtobj,newdp);
1812+
}))) {
1813+
if(!newdp.version)
1814+
newdp.version = "1.0.0";
1815+
SSVC.decision_points.push({data: newdp});
1816+
}
1817+
});
1818+
selectCustom(newname, json, -1*(SSVC.decision_trees.length));
1819+
SSVC.decision_trees.push({data:json,displayname: newname});
1820+
} else {
1821+
const select = SSVC.form.parentElement.querySelector("[id='sampletrees']");
1822+
select.value = fIndex;
1823+
select.dispatchEvent(new Event('change'));
1824+
topalert("Imported JSON is already in the registry","warn");
1825+
}
18321826
}
18331827
} else {
18341828
topalert("Unknown JSON file format","danger");
@@ -1906,7 +1900,7 @@ function readFile(input) {
19061900
if(header.indexOf(":") > -1)
19071901
head = header.split(":")[1];
19081902
let dpkey = uniq_key({name:head},Object.values(json.decision_points));
1909-
keymap[i] = json.namespace.substr(0,3) + ":" + dpkey
1903+
keymap[i] = json.namespace.substr(0,3) + ":" + dpkey + ":1.0.0";
19101904
json.decision_points[keymap[i]] = {name: head, namespace: json.namespace,
19111905
description: head,
19121906
key: dpkey};

0 commit comments

Comments
 (0)