Skip to content
Open
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
38 changes: 38 additions & 0 deletions action/finding-evidence-bounds.test.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use strict";

const assert = require("node:assert/strict");
const test = require("node:test");
const { validateFindingEvidenceBounds } = require("./post.cts");

function evidence(retained: number, sampled: number, truncated: boolean): any {
return {
findings: Array.from({ length: retained }, () => ({})),
findings_truncated: truncated,
counters: {
total_violations: sampled,
sampled_violations: sampled,
},
};
}

test("accepts resident finding evidence at producer boundaries", () => {
assert.equal(validateFindingEvidenceBounds(evidence(0, 0, false)).findings.length, 0);
assert.equal(validateFindingEvidenceBounds(evidence(1, 1, false)).findings.length, 1);
assert.equal(validateFindingEvidenceBounds(evidence(1024, 1024, false)).findings.length, 1024);
assert.equal(validateFindingEvidenceBounds(evidence(1024, 1025, true)).findings.length, 1024);
});

test("rejects resident finding evidence the producer cannot emit", () => {
for (const invalid of [
{ ...evidence(0, 0, false), findings: "not-an-array" },
evidence(1025, 1025, false),
evidence(1023, 1024, true),
evidence(1, 2, false),
evidence(1024, 1024, true),
]) {
assert.throws(
() => validateFindingEvidenceBounds(invalid),
/bounded network findings/,
);
}
});
34 changes: 31 additions & 3 deletions action/post.cts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const MANIFEST = path.join(ACTION_ROOT, "bundle-manifest.json");
const EVIDENCE_SETTLE_INTERVAL_MILLISECONDS = 40;
const EVIDENCE_SETTLE_MAX_READS = 4;
const EVIDENCE_SETTLE_TIMEOUT_NANOSECONDS = 160_000_000n;
const MAX_RETAINED_FINDINGS = 1024;
const CHILD_ENV = {
LANG: "C.UTF-8",
LC_ALL: "C.UTF-8",
Expand Down Expand Up @@ -99,6 +100,31 @@ function networkEvidenceCounters(report: any): { total: number; sampled: number
};
}

function validateFindingEvidenceBounds(report: any): any {
const findings = report.findings;
const counters = networkEvidenceCounters(report);
if (
!Array.isArray(findings) ||
findings.length > MAX_RETAINED_FINDINGS ||
typeof report.findings_truncated !== "boolean" ||
(
report.findings_truncated &&
findings.length !== MAX_RETAINED_FINDINGS
) ||
(
!report.findings_truncated &&
counters.sampled !== findings.length
) ||
(
report.findings_truncated &&
counters.sampled <= findings.length
)
) {
throw new Error("Fence resident report does not contain bounded network findings");
}
return report;
}

function settleResidentReport(
reportPath: string,
unit: string,
Expand Down Expand Up @@ -221,7 +247,9 @@ function main(): void {
readJsonBounded(reportPath, MAX_REPORT_BYTES, "Fence report"),
false,
);
const report = settleResidentReport(reportPath, paths.unit, initialReport);
const report = validateFindingEvidenceBounds(
settleResidentReport(reportPath, paths.unit, initialReport),
);
let dnsEvidence;
const effectiveDnsReportPath = dnsReportPath || paths.dnsReport;
if (fs.existsSync(effectiveDnsReportPath)) {
Expand Down Expand Up @@ -318,7 +346,7 @@ function main(): void {
for (const warning of resultsStorageWarnings(dnsEvidence)) {
log.warning(warning);
}
validateReport(report, true);
validateFindingEvidenceBounds(validateReport(report, true));
const auditDestinationCount = auditSummary.hostnameRows.length + auditSummary.ipRows.length;
const evidenceLine = log.postEvidenceLine(report, auditDestinationCount);
if (evidenceLine) {
Expand All @@ -337,4 +365,4 @@ if (require.main === module) {
}
}

module.exports = { main, settleResidentReport };
module.exports = { main, settleResidentReport, validateFindingEvidenceBounds };
2 changes: 1 addition & 1 deletion script/test-action-wrapper
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ source script/env "$@"
require_cmd node

node -e 'require("./action/lib.cts"); require("./action/main.cts"); require("./action/post.cts")'
node --test action/test.cts
node --test action/test.cts action/finding-evidence-bounds.test.cts
python3 -B -E script/lib/host_observation.py --self-test
python3 -B -E script/lib/action_bundle_host.py --self-test
script/test-action-bundle-provenance