First public release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
# @extant2000/evidence-record
|
||||
|
||||
A small vocabulary for checks that have to show their work: four-way
|
||||
determinations, cited evidence, and reports that derive their own verdict.
|
||||
|
||||
Zero runtime dependencies. Pure functions. Ships ESM and CommonJS.
|
||||
|
||||
## Why it exists
|
||||
|
||||
Automated checks tend to fail their readers in the same few ways. This package
|
||||
gives every check one shared way to avoid them.
|
||||
|
||||
1. **A boolean hides the case that matters most.** `passed: true` can mean "we
|
||||
looked and it held" or "there was nothing to look at". Those need to stay
|
||||
apart, so a determination has four values, and three of them are not a
|
||||
pass.
|
||||
2. Findings that do not say where they came from ask the reader to take them
|
||||
on trust. Every finding here carries `Evidence`: a file and line, a database
|
||||
row, a command, a measurement, or a document.
|
||||
3. A measurement without its sample count is not evidence. An availability of
|
||||
100% over zero samples and one over 8,640 samples are different claims, so
|
||||
the sample count travels with the number.
|
||||
4. A report with zero findings is not a pass unless something was examined.
|
||||
The verdict is computed from the report, and `examined === 0` always gives
|
||||
`not-assessed`.
|
||||
|
||||
The unflattering outputs stay unflattering. A missing value is not a pass, and
|
||||
nothing in this package turns one into a pass.
|
||||
|
||||
## Determinations
|
||||
|
||||
```ts
|
||||
type Determination = 'pass' | 'fail' | 'not-applicable' | 'not-assessed'
|
||||
```
|
||||
|
||||
`rollUp` combines several. Any fail wins. An empty list is `not-assessed`,
|
||||
because `[].every(...)` being true is the trap this type closes. One unassessed
|
||||
item keeps the roll-up at `not-assessed`, so healthy items cannot dilute it.
|
||||
|
||||
```ts
|
||||
rollUp([]) // 'not-assessed'
|
||||
rollUp(['pass', 'not-assessed']) // 'not-assessed'
|
||||
rollUp(['pass', 'pass', 'fail']) // 'fail'
|
||||
```
|
||||
|
||||
`label(d)` gives a short badge (`MET`, `NOT MET`, `N/A`, `NOT ASSESSED`).
|
||||
`describe(d, subject)` gives a sentence that still makes sense when pasted into
|
||||
an email on its own.
|
||||
|
||||
## Evidence
|
||||
|
||||
```ts
|
||||
import { evidence, cite, describeMeasurement } from '@extant2000/evidence-record'
|
||||
|
||||
evidence.file('docker-compose.yml', 42, 'ports: ["2222:22"]')
|
||||
evidence.record('requirements', 'a1b2c3', 'parent_id', 'null')
|
||||
evidence.command('docker compose config', 0)
|
||||
evidence.measurement('uptime', null, 0, { unit: '%', window: '2026-07' })
|
||||
evidence.document('NIST SP 800-53 Rev 5', 'AC-2')
|
||||
```
|
||||
|
||||
`cite()` renders each kind in the form people already read it in: `path:line`
|
||||
for source, `table#id (field)` for rows, `$ command → exit 0` for commands.
|
||||
`citeVerbose()` adds the excerpt and note. `describeMeasurement()` reads the
|
||||
null case out loud: "No samples for Availability in 2026-07; it cannot be
|
||||
computed."
|
||||
|
||||
## Reports
|
||||
|
||||
```ts
|
||||
import { evidence, overall, validateReport, formatReport } from '@extant2000/evidence-record'
|
||||
|
||||
const report = {
|
||||
capability: 'PortCheck',
|
||||
scope: '12 compose services',
|
||||
examined: 12,
|
||||
findings: [
|
||||
{ id: 'f1', summary: 'SSH published on a host port', determination: 'fail',
|
||||
severity: 'critical', evidence: [evidence.file('docker-compose.yml', 42, 'ports: ["2222:22"]')] },
|
||||
{ id: 'f2', summary: 'Admin UI bound to loopback only', determination: 'pass',
|
||||
severity: 'info', evidence: [evidence.file('docker-compose.yml', 88)] },
|
||||
],
|
||||
}
|
||||
|
||||
overall(report) // 'fail', computed from the findings
|
||||
validateReport(report) // [] when every conclusion carries a citation
|
||||
formatReport(report) // worst first, one citation per finding
|
||||
```
|
||||
|
||||
Output of `formatReport(report)`:
|
||||
|
||||
```
|
||||
PortCheck — 12 compose services
|
||||
NOT MET · 12 items examined · 2 findings
|
||||
|
||||
✗ CRITICAL SSH published on a host port [NOT MET]
|
||||
evidence: docker-compose.yml:42
|
||||
◦ info Admin UI bound to loopback only [MET]
|
||||
evidence: docker-compose.yml:88
|
||||
```
|
||||
|
||||
The same report with nothing examined:
|
||||
|
||||
```
|
||||
PortCheck — no services found
|
||||
NOT ASSESSED · 0 items examined · 0 findings
|
||||
|
||||
PortCheck's scope was not assessed. This is not a pass — an unassessed control is not an implemented control.
|
||||
```
|
||||
|
||||
`validateReport` flags a finding that reaches `pass`, `fail` or
|
||||
`not-applicable` with no evidence, plus duplicate ids and empty summaries. A
|
||||
`not-assessed` finding may have no evidence, since having nothing to cite is
|
||||
what it reports. `formatReport` accepts `{ evidence: 'inline' | 'full' | 'none',
|
||||
minSeverity }`. `formatRollUp(reports)` gives one line per report and a
|
||||
combined verdict, where an unrun report pulls the total to `not-assessed`.
|
||||
|
||||
## Conformance
|
||||
|
||||
A check built on this package conforms when its own tests assert that:
|
||||
|
||||
- `validateReport(report)` is empty for every report it can produce;
|
||||
- an empty input produces `not-assessed`, and the rendered output contains
|
||||
neither `✓` nor the word "clean";
|
||||
- every `fail` finding carries at least one `Evidence`;
|
||||
- the rendered output of a failure names a file, a row, or a command.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install @extant2000/evidence-record
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT. Copyright (c) 2026 Extant 2000 LLC.
|
||||
Reference in New Issue
Block a user