Skip to content
This repository was archived by the owner on Nov 7, 2024. It is now read-only.

Commit f509cf5

Browse files
committed
Add a script to check that cells are under the right headers.
1 parent ded6a51 commit f509cf5

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

index.bs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,75 @@ details[open] {
8585
text-align: left;
8686
}
8787
</style>
88+
<script type="module">
89+
/** Takes a threat model table header, which should be written in Bikeshed
90+
* like
91+
*
92+
* <th><div><div>[[#goal-transfer-userid]]</div></div></th>
93+
*
94+
* and extracts the "transfer-userid" part from the generated <a> tag.
95+
*/
96+
function findGoalFromColumnHeader(header) {
97+
const firstDiv = header.firstElementChild;
98+
const secondDiv = firstDiv?.firstElementChild;
99+
const link = secondDiv?.firstElementChild;
100+
if (header.colSpan != 1 ||
101+
link == null ||
102+
firstDiv.tagName !== "DIV" ||
103+
secondDiv.tagName !== "DIV" ||
104+
link.tagName !== "A" ||
105+
link.href === "") {
106+
console.error("Expected header cell to be written like <th><div><div><a href=...>...\n",
107+
header);
108+
return "error";
109+
}
110+
const target = new URL(link.href);
111+
if (!target.hash.startsWith("#goal-")) {
112+
console.error("Column header expected to have a fragment starting with '#goal-': ",
113+
link);
114+
}
115+
return target.hash.slice(6);
116+
}
117+
118+
for (const table of document.getElementsByClassName("threatmodel")) {
119+
// Make sure that the checks and Xes in the source are meant to apply to the
120+
// attacker goal described by the heading they appear under.
121+
const goalOrder = [];
122+
let goalsStarted = false;
123+
for (const header of table.rows[0].cells) {
124+
if (header.textContent.trim() === "" && !goalsStarted) {
125+
for (const _ of Array(header.colSpan)) {
126+
goalOrder.push(undefined);
127+
}
128+
continue;
129+
}
130+
goalsStarted = true;
131+
goalOrder.push(findGoalFromColumnHeader(header));
132+
}
133+
134+
// Check the body rows.
135+
for (const row of Array.prototype.slice.call(table.rows, 1)) {
136+
let column = 0;
137+
for (const cell of row.cells) {
138+
let expectedGoal = goalOrder[column];
139+
column += cell.colSpan;
140+
if (expectedGoal === undefined && "goal" in cell.dataset) {
141+
console.error("Cell without visible header has a data-goal attribute.\n",
142+
cell);
143+
} else if (expectedGoal === "error") {
144+
// Don't be spammy if the header's broken.
145+
continue;
146+
} else if (cell.dataset.goal !== expectedGoal) {
147+
console.error("Cell expected to have goal '" + expectedGoal +
148+
"' but actually claims '" + cell.dataset.goal + "'.\n",
149+
cell);
150+
} else {
151+
// The cell is in the right column.
152+
}
153+
}
154+
}
155+
}
156+
</script>
88157
89158
Advisement: This document is at a very early stage. Many things in it are wrong
90159
and/or incomplete. Please take it as a rough shape for how we might document the

0 commit comments

Comments
 (0)