First public release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { evidence, overall, validateReport } from '@extant2000/evidence-record'
|
||||
import {
|
||||
controlDocSections,
|
||||
toReport,
|
||||
formatControl,
|
||||
escapeHtml,
|
||||
renderControlDoc,
|
||||
renderDocument,
|
||||
stripTags,
|
||||
table,
|
||||
tally,
|
||||
} from '../src/index.js'
|
||||
|
||||
const base = { docNumber: 'SSP-AC-01', title: 'Access Control', date: '2026-07-28' }
|
||||
|
||||
describe('escapeHtml', () => {
|
||||
it('escapes every dangerous character', () => {
|
||||
expect(escapeHtml(`<script>"x"&'y'`))
|
||||
.toBe('<script>"x"&'y'')
|
||||
})
|
||||
|
||||
it('escapes ampersands first so entities are not double-broken', () => {
|
||||
expect(escapeHtml('<')).toBe('&lt;')
|
||||
})
|
||||
|
||||
it('renders null and undefined as empty, not "null"', () => {
|
||||
expect(escapeHtml(null)).toBe('')
|
||||
expect(escapeHtml(undefined)).toBe('')
|
||||
})
|
||||
|
||||
it('coerces non-strings', () => {
|
||||
expect(escapeHtml(42)).toBe('42')
|
||||
})
|
||||
})
|
||||
|
||||
describe('table', () => {
|
||||
it('escapes both headers and cells', () => {
|
||||
const html = table(['<h>'], [['<script>alert(1)</script>']])
|
||||
expect(html).toContain('<h>')
|
||||
expect(html).toContain('<script>')
|
||||
expect(html).not.toContain('<script>')
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderDocument', () => {
|
||||
it('escapes section titles', () => {
|
||||
const { html } = renderDocument([{ title: '<img onerror=x>', content: '<p>ok</p>' }])
|
||||
expect(html).toContain('<img onerror=x>')
|
||||
expect(html).not.toContain('<img')
|
||||
})
|
||||
|
||||
it('produces html and markdown from one section list', () => {
|
||||
const r = renderDocument([{ title: 'One', content: '<p>Body text</p>' }])
|
||||
expect(r.html).toContain('<section><h2>One</h2>')
|
||||
expect(r.markdown).toContain('## One')
|
||||
expect(r.markdown).toContain('Body text')
|
||||
expect(r.sections).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('stripTags', () => {
|
||||
it('decodes entities — markdown is not an HTML context', () => {
|
||||
// Documented and deliberate: it undoes escapeHtml so a reader sees `<`.
|
||||
// Safe only because the consumer re-escapes at the render boundary.
|
||||
expect(stripTags('<p><tag></p>').trim()).toBe('<tag>')
|
||||
})
|
||||
|
||||
it('turns list items into markdown bullets', () => {
|
||||
expect(stripTags('<ul><li>a</li><li>b</li></ul>')).toContain('- a')
|
||||
})
|
||||
|
||||
it('collapses runs of blank lines', () => {
|
||||
expect(stripTags('<p>a</p><p></p><p></p><p></p><p>b</p>')).not.toMatch(/\n{3,}/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('tally', () => {
|
||||
it('counts each status', () => {
|
||||
expect(tally([
|
||||
{ check: 'a', status: 'pass' },
|
||||
{ check: 'b', status: 'fail' },
|
||||
{ check: 'c', status: 'warn' },
|
||||
])).toEqual({ pass: 1, fail: 1, warn: 1, unknown: 0, total: 3 })
|
||||
})
|
||||
|
||||
it('counts an unrecognised status as unknown rather than dropping it', () => {
|
||||
// A status the tool does not recognise is not a passing status, and an
|
||||
// accreditation package must not quietly lose checks.
|
||||
const t = tally([{ check: 'a', status: 'skipped' }, { check: 'b', status: 'pass' }])
|
||||
expect(t.unknown).toBe(1)
|
||||
expect(t.total).toBe(2)
|
||||
expect(t.pass).toBe(1)
|
||||
})
|
||||
|
||||
it('handles an empty set', () => {
|
||||
expect(tally([])).toEqual({ pass: 0, fail: 0, warn: 0, unknown: 0, total: 0 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('controlDocSections', () => {
|
||||
it('emits the seven standard sections in order', () => {
|
||||
const titles = controlDocSections(base).map(s => s.title)
|
||||
expect(titles).toEqual([
|
||||
'1. Document Information',
|
||||
'2. Purpose and Scope',
|
||||
'3. Current Implementation Status',
|
||||
'4. Remediation Plan',
|
||||
'5. Evidence',
|
||||
'6. References',
|
||||
'7. Approval and Sign-Off',
|
||||
])
|
||||
})
|
||||
|
||||
it('escapes hostile input in every field it interpolates', () => {
|
||||
const { html } = renderControlDoc({
|
||||
...base,
|
||||
title: '<script>alert(1)</script>',
|
||||
family: '<img src=x onerror=1>',
|
||||
gap: '"><script>bad()</script>',
|
||||
fix: `'; DROP TABLE--`,
|
||||
checks: [{ check: '<b>evil</b>', status: '<i>pass</i>' }],
|
||||
})
|
||||
expect(html).not.toContain('<script>')
|
||||
expect(html).not.toContain('<img src=x')
|
||||
expect(html).not.toContain('<b>evil</b>')
|
||||
expect(html).toContain('<script>')
|
||||
})
|
||||
|
||||
it('does NOT report an unassessed control as clean', () => {
|
||||
// With no checks, "0 failing" would read as a pass. Say so explicitly.
|
||||
const s = controlDocSections({ ...base, checks: [] })
|
||||
const status = s.find(x => x.title.startsWith('3.'))!
|
||||
expect(status.content).toContain('An unassessed control is not an implemented control')
|
||||
})
|
||||
|
||||
it('surfaces unrecognised statuses in the summary line', () => {
|
||||
const s = controlDocSections({
|
||||
...base,
|
||||
checks: [{ check: 'a', status: 'weird' }, { check: 'b', status: 'pass' }],
|
||||
})
|
||||
expect(s.find(x => x.title.startsWith('3.'))!.content).toContain('1 unrecognised')
|
||||
})
|
||||
|
||||
it('omits the unrecognised clause when there are none', () => {
|
||||
const s = controlDocSections({ ...base, checks: [{ check: 'a', status: 'pass' }] })
|
||||
expect(s.find(x => x.title.startsWith('3.'))!.content).not.toContain('unrecognised')
|
||||
})
|
||||
|
||||
it('is reproducible — same input, byte-identical output', () => {
|
||||
// The date is a parameter, not new Date(). An accreditation artifact that
|
||||
// changes when regenerated is not reproducible evidence.
|
||||
const a = renderControlDoc({ ...base, gap: 'g', fix: 'f' })
|
||||
const b = renderControlDoc({ ...base, gap: 'g', fix: 'f' })
|
||||
expect(a.html).toBe(b.html)
|
||||
expect(a.markdown).toBe(b.markdown)
|
||||
})
|
||||
|
||||
it('falls back to "Not specified" rather than printing undefined', () => {
|
||||
const { markdown } = renderControlDoc(base)
|
||||
expect(markdown).toContain('Not specified')
|
||||
expect(markdown).not.toContain('undefined')
|
||||
expect(markdown).not.toContain('null')
|
||||
})
|
||||
|
||||
it('accepts custom references, approvers, deployments and timeline', () => {
|
||||
const { html } = renderControlDoc({
|
||||
...base,
|
||||
references: ['ISO 27001'],
|
||||
approvers: ['CISO'],
|
||||
deployments: ['Air-gapped'],
|
||||
remediationDays: 14,
|
||||
systemName: 'Extant 2000',
|
||||
})
|
||||
expect(html).toContain('ISO 27001')
|
||||
expect(html).toContain('CISO')
|
||||
expect(html).toContain('Air-gapped')
|
||||
expect(html).toContain('within 14 days')
|
||||
expect(html).toContain('Extant 2000')
|
||||
})
|
||||
|
||||
it('omits the scope list entirely when no deployments are given', () => {
|
||||
expect(renderControlDoc(base).html).not.toContain('<h3>Scope</h3>')
|
||||
})
|
||||
})
|
||||
|
||||
describe('evidence attached, not asserted', () => {
|
||||
const base = { docNumber: 'SSP-AC-01', title: 'Access Control', family: 'AC', date: '2026-08-01' }
|
||||
|
||||
it('renders the evidence behind each check, not just its status', () => {
|
||||
// The gap this closes: CertPack rendered a document asserting results
|
||||
// without attaching the scan output that produced them.
|
||||
const doc = renderControlDoc({
|
||||
...base,
|
||||
checks: [{
|
||||
check: 'RLS enabled on every tenant table',
|
||||
status: 'pass',
|
||||
evidence: [evidence.command('psql -c "select relrowsecurity..."', 0, '42 of 42 tables')],
|
||||
}],
|
||||
})
|
||||
expect(doc.html).toContain('42 of 42 tables')
|
||||
expect(doc.html).toContain('5. Evidence')
|
||||
})
|
||||
|
||||
it('labels an unevidenced check as a claim rather than leaving it blank', () => {
|
||||
// A blank cell in an accreditation package reads as "nothing to report".
|
||||
const doc = renderControlDoc({
|
||||
...base,
|
||||
checks: [{ check: 'Least privilege enforced', status: 'pass' }],
|
||||
})
|
||||
expect(doc.html).toContain('NOT ATTACHED')
|
||||
expect(doc.html).toContain('carry no attached evidence')
|
||||
expect(doc.html).toContain('This result is a claim, not a verified finding')
|
||||
})
|
||||
|
||||
it('says so when there are no checks at all', () => {
|
||||
const doc = renderControlDoc(base)
|
||||
expect(doc.html).toContain('An unassessed control is not an implemented control')
|
||||
expect(doc.html).toContain('no evidence exists to attach')
|
||||
})
|
||||
|
||||
it('an unrecognised status is not-assessed, never a pass', () => {
|
||||
const r = toReport({ ...base, checks: [{ check: 'x', status: 'skipped' }] })
|
||||
expect(r.findings[0]!.determination).toBe('not-assessed')
|
||||
expect(overall(r)).toBe('not-assessed')
|
||||
})
|
||||
|
||||
it('a zero-check control reports NOT ASSESSED', () => {
|
||||
expect(overall(toReport(base))).toBe('not-assessed')
|
||||
expect(formatControl(base)).toContain('NOT ASSESSED')
|
||||
})
|
||||
|
||||
it('every conclusion carries a citation', () => {
|
||||
const r = toReport({
|
||||
...base,
|
||||
checks: [{ check: 'RLS enabled', status: 'pass', evidence: [evidence.command('psql', 0)] }],
|
||||
})
|
||||
expect(validateReport(r)).toEqual([])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user