Skip to content
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
9 changes: 6 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1319,8 +1319,9 @@ <h2>AI Security Standards Crosswalk</h2>
DATA.forEach(function(e) {
e.mappings.forEach(function(m) { FW_SET.add(m.framework); });
});
// Also include registry-only frameworks (e.g. CoSAI, EU AI Act CoP)
FW_REGISTRY.forEach(function(fw) { FW_SET.add(fw.name); });
// A framework counts once a mapping row cites it. A registry nothing maps yet
// (transcribed ahead of its rows) stays out of every count and selector, so
// the headline matches the README's "frameworks mapped".
FRAMEWORKS = Array.from(FW_SET).sort();

var ENTRY_MAP = {};
Expand Down Expand Up @@ -1807,7 +1808,9 @@ <h2>AI Security Standards Crosswalk</h2>


// Stats
var totalRegControls = FW_REGISTRY.reduce(function(s, fw) { return s + (fw.controls || []).length; }, 0);
var totalRegControls = FW_REGISTRY.reduce(function(s, fw) {
return FW_SET.has(fw.name) ? s + (fw.controls || []).length : s;
}, 0);
var stats = [
[FRAMEWORKS.length, 'Frameworks'],
[totalMappings.toLocaleString(), 'Mappings'],
Expand Down
18 changes: 18 additions & 0 deletions scripts/webapp.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,24 @@ test('an unknown route falls back to a page, not a blank screen', () => {
assert.ok(app.children.length > 0, 'the fallback rendered an empty page');
});

// A registry can land before any row maps it (NIST AI 600-1 in #187). The home
// page must not count it as a framework, or add its controls to the headline.
test('the home page counts mapped frameworks only', () => {
const { window } = dom;
const stats = JSON.parse(readFileSync(path.join(ROOT, 'data', 'stats.json'), 'utf8'));
const mapped = new Set(window.CROSSWALK_DATA.flatMap((e) => e.mappings.map((m) => m.framework)));
const controls = window.CROSSWALK_FRAMEWORKS
.filter((fw) => mapped.has(fw.name))
.reduce((s, fw) => s + (fw.controls || []).length, 0);

const app = visit('#/');
const cards = Object.fromEntries([...app.querySelectorAll('.stat-card')].map((c) => [
c.querySelector('.stat-label').textContent, c.querySelector('.stat-num').textContent,
]));
assert.equal(cards.Frameworks, String(stats.frameworks.mapped));
assert.equal(cards['Registry Controls'], String(controls));
});

// The About page is the one place in the webapp allowed to carry the creator
// credit, and this file is not — so the test checks that the section renders
// with content, never the name itself.
Expand Down
Loading