Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions site/assets/theme.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ <h2 id="competition-results-title">Competition results</h2>
<h3>Kaspersky{CTF} 2026</h3>
<p>Division: North America, South America, Caribbean</p>
<dl class="archive-meta">
<div><dt>Placement</dt><dd>3 / 361</dd></div>
<div><dt>Placement</dt><dd>3 / 361<span class="placement-extra">1 / 52 (Europe)</span></dd></div>
<div><dt>Solved</dt><dd>30 / 30</dd></div>
<div><dt>Score</dt><dd>4,413</dd></div>
</dl>
Expand Down Expand Up @@ -82,7 +82,7 @@ <h2 id="team-title">Team</h2>
<div class="roster-identity">
<h3>SHOOTTHEMESSENGER/Moo</h3>
<p class="roster-handle">spookymoo</p>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li></ul>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li><li>Cyber Apocalypse 2026 (1337_PwnSp4c3)</li><li>BushBash CTF 2026 (1337_PwnSp4c3)</li></ul>
</div>
</li>
<li class="roster-member">
Expand All @@ -98,7 +98,7 @@ <h3>Hoxed</h3>
<div class="roster-identity">
<h3>romil0xsec</h3>
<p class="roster-handle">romil1998</p>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li></ul>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li><li>Cyber Apocalypse 2026 (1337_PwnSp4c3)</li><li>BushBash CTF 2026 (1337_PwnSp4c3)</li></ul>
</div>
</li>
<li class="roster-member">
Expand All @@ -114,7 +114,7 @@ <h3>Deva_RP</h3>
<div class="roster-identity">
<h3>Antsy</h3>
<p class="roster-handle">antsyy_</p>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li></ul>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li><li>Cyber Apocalypse 2026 (1337_PwnSp4c3)</li><li>BushBash CTF 2026 (1337_PwnSp4c3)</li></ul>
</div>
</li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion site/results/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<p class="path-label">RESULT 2026 / team result</p>
<h2 id="kaspersky-ctf-title">Kaspersky{CTF} 2026</h2>
<p>Division: North America, South America, Caribbean</p>
<dl class="archive-meta"><div><dt>Placement</dt><dd>3 / 361</dd></div><div><dt>Solved</dt><dd>30 / 30</dd></div><div><dt>Score</dt><dd>4,413</dd></div></dl>
<dl class="archive-meta"><div><dt>Placement</dt><dd>3 / 361<span class="placement-extra">1 / 52 (Europe)</span></dd></div><div><dt>Solved</dt><dd>30 / 30</dd></div><div><dt>Score</dt><dd>4,413</dd></div></dl>
</article>
</section>
<section class="archive-section" aria-labelledby="cyber-apocalypse-title">
Expand Down
6 changes: 3 additions & 3 deletions site/team/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h2 id="roster-title">Roster</h2>
<div class="roster-identity">
<h3>SHOOTTHEMESSENGER/Moo</h3>
<p class="roster-handle">spookymoo</p>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li></ul>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li><li>Cyber Apocalypse 2026 (1337_PwnSp4c3)</li><li>BushBash CTF 2026 (1337_PwnSp4c3)</li></ul>
</div>
</li>
<li class="roster-member">
Expand All @@ -54,7 +54,7 @@ <h3>Hoxed</h3>
<div class="roster-identity">
<h3>romil0xsec</h3>
<p class="roster-handle">romil1998</p>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li></ul>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li><li>Cyber Apocalypse 2026 (1337_PwnSp4c3)</li><li>BushBash CTF 2026 (1337_PwnSp4c3)</li></ul>
</div>
</li>
<li class="roster-member">
Expand All @@ -70,7 +70,7 @@ <h3>Deva_RP</h3>
<div class="roster-identity">
<h3>Antsy</h3>
<p class="roster-handle">antsyy_</p>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li></ul>
<ul class="roster-events"><li>Kaspersky{CTF} 2026</li><li>Cyber Apocalypse 2026 (1337_PwnSp4c3)</li><li>BushBash CTF 2026 (1337_PwnSp4c3)</li></ul>
</div>
</li>
</ul>
Expand Down
28 changes: 21 additions & 7 deletions tests/site-contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ test("the homepage removes the redundant path and publishes every verified resul
assert.match(home, /5,997/);
assert.match(home, /Kaspersky\{CTF\} 2026/);
assert.match(home, /3 \/ 361/);
assert.match(home, /<span class="placement-extra">1 \/ 52 \(Europe\)<\/span>/);
assert.match(home, /Division: North America, South America, Caribbean/);
assert.match(home, /30 \/ 30/);
assert.match(home, /4,413/);
assert.equal((home.match(/RESULT 2026 \/ team result/g) ?? []).length, 1);
assert.equal((home.match(/RESULT 2026 \/ member result with a prior team/g) ?? []).length, 2);
assert.doesNotMatch(home, /KK \/ RESULT 2026/);
assert.equal((home.match(/1337_PwnSp4c3/g) ?? []).length, 2);
assert.equal((home.match(/Credited to <strong>1337_PwnSp4c3<\/strong>/g) ?? []).length, 2);
assert.ok(
home.indexOf("Competition results") < home.indexOf("Recruitment"),
"results must appear before Recruitment",
Expand All @@ -109,6 +110,7 @@ test("the Results page contains the complete verified competition record", () =>
assert.match(results, /Open - International/);
assert.match(results, /Kaspersky\{CTF\} 2026/);
assert.match(results, /3 \/ 361/);
assert.match(results, /<span class="placement-extra">1 \/ 52 \(Europe\)<\/span>/);
assert.match(results, /Division: North America, South America, Caribbean/);
assert.match(results, /30 \/ 30/);
assert.match(results, /4,413/);
Expand Down Expand Up @@ -200,20 +202,32 @@ test("the application client has no modal controls", () => {
});

test("the roster is published on the homepage and the team page", () => {
const kaspersky = "Kaspersky{CTF} 2026";
const apocalypse = "Cyber Apocalypse 2026 (1337_PwnSp4c3)";
const bushbash = "BushBash CTF 2026 (1337_PwnSp4c3)";
const members = [
["spookymoo", "SHOOTTHEMESSENGER/Moo"],
["hoxed", "Hoxed"],
["romil1998", "romil0xsec"],
["deva_rp", "Deva_RP"],
["antsyy_", "Antsy"],
["spookymoo", "SHOOTTHEMESSENGER/Moo", [kaspersky, apocalypse, bushbash]],
["hoxed", "Hoxed", [kaspersky]],
["romil1998", "romil0xsec", [kaspersky, apocalypse, bushbash]],
["deva_rp", "Deva_RP", [kaspersky]],
["antsyy_", "Antsy", [kaspersky, apocalypse, bushbash]],
];
for (const page of ["index.html", "team/index.html"]) {
const html = read(page);
assert.equal((html.match(/class="roster-member"/g) ?? []).length, members.length, `${page} needs one card per player`);
for (const [file, name] of members) {
const cards = [...html.matchAll(/<li class="roster-member">[\s\S]*?<\/li>\s*<\/ul>/g)].map((m) => m[0]);
assert.equal(cards.length, members.length, `${page} needs one parsed card per player`);
for (const [index, [file, name, played]] of members.entries()) {
assert.ok(html.includes(`src="/brand/team/${file}.png"`), `${page} is missing the ${file} avatar`);
assert.ok(html.includes(`<h3>${name}</h3>`), `${page} is missing the name ${name}`);
assert.ok(html.includes(`<p class="roster-handle">${file}</p>`), `${page} is missing the handle ${file}`);
const card = cards[index];
assert.ok(card.includes(file), `${page} card order drifted at ${file}`);
assert.deepEqual(
[...card.matchAll(/<li>([^<]+)<\/li>/g)].map((m) => m[1]),
played,
`${page} lists the wrong competitions for ${file}`,
);
}
for (const match of html.matchAll(/<img class="roster-avatar"[^>]*>/g)) {
assert.match(match[0], /alt=""/, "avatars are decorative next to the name");
Expand Down