103 lines
4.1 KiB
Markdown
103 lines
4.1 KiB
Markdown
# CertPack
|
|
|
|
Generates a NIST 800-53 control document (a System Security Plan section with
|
|
a remediation plan) from automated verification checks, with the evidence for
|
|
each check attached. It is used in Hitt Hosting products.
|
|
|
|
## What it does and why
|
|
|
|
An accreditation package that lists results without the scan output behind
|
|
them is a set of claims. **CertPack attaches the evidence to each check and
|
|
labels any check without evidence as a claim.** It does not drop the check or
|
|
leave the cell blank, because a blank cell reads as "nothing to report" and a
|
|
missing check is worse than a weak one.
|
|
|
|
You pass a control (document number, title, control family, the gap, the fix)
|
|
and a list of checks, each with a status and optional evidence in the
|
|
`@extant2000/evidence-record` format. `renderControlDoc()` returns HTML and
|
|
markdown built from one list of seven sections, so the two never drift apart:
|
|
|
|
1. Document information
|
|
2. Purpose and scope, including the gap and the deployments in scope
|
|
3. Current implementation status: a table of checks with their evidence, a
|
|
summary line, and a count of checks with no attached evidence
|
|
4. Remediation plan with a target completion time (30 days by default)
|
|
5. Evidence: the full citation for every check, or a statement that the
|
|
result is a claim and not a verified finding
|
|
6. References (NIST SP 800-53 Rev. 5, SP 800-53A and the FedRAMP SSP template
|
|
by default)
|
|
7. Approval and sign-off table
|
|
|
|
The rules that keep the document from claiming more than it knows:
|
|
|
|
- A status other than `pass`, `fail` or `warn` is counted as unrecognised and
|
|
shown in the summary, never folded into another status or dropped.
|
|
- A control with no checks says that an unassessed control is not an
|
|
implemented control, instead of printing "0 failing".
|
|
- Missing fields print "Not specified", never `undefined` or `null`.
|
|
- The date is a parameter, not read from the clock, so the same input always
|
|
produces byte-identical output. A document that changes when regenerated is
|
|
not reproducible evidence.
|
|
|
|
Every value interpolated into the HTML goes through `escapeHtml()`, including
|
|
titles, table cells and evidence, with no "trusted field" exception. The
|
|
markdown output decodes entities because markdown is not an HTML context; if
|
|
you inject that markdown into a page as HTML, escape it there.
|
|
|
|
`toReport()` and `formatControl()` give the same assessment as an
|
|
evidence-record report, so a control's status can be rolled up without
|
|
parsing HTML. `pass` passes, `fail` and `warn` fail, and an unrecognised
|
|
status is not assessed.
|
|
|
|
## Usage
|
|
|
|
```ts
|
|
import { renderControlDoc, formatControl, type ControlDocInput } from '@extant2000/cert-pack'
|
|
import { evidence } from '@extant2000/evidence-record'
|
|
|
|
const input: ControlDocInput = {
|
|
docNumber: 'SSP-AC-01',
|
|
title: 'Access Control',
|
|
family: 'AC',
|
|
familyName: 'Access Control',
|
|
gap: 'Tenant isolation is not documented',
|
|
fix: 'Document the row-level security policy',
|
|
date: '2026-09-28',
|
|
systemName: 'Example App',
|
|
checks: [
|
|
{ check: 'Row-level security on every tenant table', status: 'pass',
|
|
evidence: [evidence.command('psql -c "select relname, relrowsecurity from pg_class ..."', 0, '42 of 42 tables')] },
|
|
{ check: 'Least privilege enforced', status: 'pass' },
|
|
{ check: 'MFA for admins', status: 'skipped' },
|
|
],
|
|
}
|
|
|
|
const { html, markdown, sections } = renderControlDoc(input)
|
|
console.log(formatControl(input))
|
|
```
|
|
|
|
In the generated document, section 3 ends with "2 passing, 0 failing, 0
|
|
warnings, 1 unrecognised out of 3 checks" and "2 of 3 check(s) carry no
|
|
attached evidence". The two checks without evidence show NOT ATTACHED.
|
|
`formatControl()` prints:
|
|
|
|
```
|
|
✗ HIGH MFA for admins [NOT ASSESSED]
|
|
Status "skipped" is not recognised. An unrecognised status is not a passing one.
|
|
◦ info Row-level security on every tenant table [MET]
|
|
◦ info Least privilege enforced [MET]
|
|
```
|
|
|
|
`controlDocSections()`, `renderDocument()`, `table()`, `stripTags()`,
|
|
`escapeHtml()` and `tally()` are exported for building other documents.
|
|
|
|
## Install
|
|
|
|
```
|
|
npm install @extant2000/cert-pack
|
|
```
|
|
|
|
## License
|
|
|
|
MIT. See [LICENSE](LICENSE).
|