/**
* TEST SUITE: External Attestation Engine
*
* 16+ tests covering:
* 1. Bundle generation deterministic (same workspace → same bundle_id)
* 2. HMAC-SHA256 signature valid
* 3. Signature tamper detected
* 4. Missing audit log causes refusal
* 5. Bundle ID matches content hash
* 6. Verification PASS on valid bundle
* 7. Verification FAIL on tampered signature
* 8. Verification FAIL on modified content
* 9. Bundle_id mismatch detected
* 10. Checksum mismatches detected
* 11. Export to JSON format
* 12. Export to Markdown format
* 13. Read-only semantics (no state mutation)
* 14. Audit entries logged on generation
* 15. Audit entries logged on verification
* 16. Workspace integrity integrated into bundle
*/
import assert from "assert";
import fs from "fs";
import path from "path";
import os from "os";
import crypto from "crypto";
import {
generateAttestationBundle,
verifyAttestationBundle,
exportAttestationBundle,
ATTESTATION_SCHEMA,
} from "./core/attestation-engine.js";
const TEST_SUITE = "ATTESTATION";
// Create temporary workspace with minimal audit log
function createTestWorkspace() {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "attestation-test-"));
const kaizaDir = path.join(tmpDir, ".kaiza");
fs.mkdirSync(kaizaDir, { recursive: true });
// Create a minimal audit log
const auditLogPath = path.join(kaizaDir, "audit-log.jsonl");
const entry1 = {
timestamp: "2025-01-19T10:00:00.000Z",
sessionId: "test-session-1",
tool: "begin_session",
result: "ok",
prevHash: "GENESIS",
};
const hash1 = crypto.createHash("sha256").update(JSON.stringify(entry1)).digest("hex");
entry1.hash = hash1;
const entry2 = {
timestamp: "2025-01-19T10:00:01.000Z",
sessionId: "test-session-1",
tool: "write_file",
result: "ok",
plan_hash: "abc123def456abc123def456abc123def456abc123def456abc123def456abc1",
prevHash: hash1,
};
const hash2 = crypto.createHash("sha256").update(JSON.stringify(entry2)).digest("hex");
entry2.hash = hash2;
fs.writeFileSync(auditLogPath, JSON.stringify(entry1) + "\n");
fs.appendFileSync(auditLogPath, JSON.stringify(entry2) + "\n");
// Set a deterministic signing secret
process.env.KAIZA_ATTESTATION_SECRET = "test-secret-key-32-bytes-long-!!";
return tmpDir;
}
function cleanupTestWorkspace(tmpDir) {
fs.rmSync(tmpDir, { recursive: true, force: true });
delete process.env.KAIZA_ATTESTATION_SECRET;
}
// TEST 1: Bundle has deterministic structure (content ordering)
function test_bundleGenerationDeterministic() {
const tmpDir = createTestWorkspace();
try {
const bundle1 = generateAttestationBundle(tmpDir);
// Check that critical fields are present and have correct types
assert.ok(bundle1.bundle_id, "Bundle should have bundle_id");
assert.ok(bundle1.signature, "Bundle should have signature");
assert.ok(bundle1.audit_metrics, "Bundle should have audit_metrics");
assert.strictEqual(typeof bundle1.bundle_id, "string", "bundle_id should be string");
assert.strictEqual(bundle1.bundle_id.length, 64, "bundle_id should be 64 hex chars");
console.log(`✓ TEST 1: Bundle has deterministic structure`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 2: Bundle has required schema fields
function test_bundleSchemaComplete() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
for (const field of ATTESTATION_SCHEMA.required_fields) {
assert.ok(
field in bundle,
`Bundle missing required field: ${field}`
);
}
console.log(`✓ TEST 2: Bundle has all required schema fields`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 3: HMAC signature is present and 64 hex chars
function test_hmacSignatureValid() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
// Check signature format
assert.ok(bundle.signature, "Bundle should have signature");
assert.strictEqual(typeof bundle.signature, "string", "Signature should be string");
assert.match(bundle.signature, /^[a-f0-9]{64}$/, "Signature should be 64 hex chars (SHA256)");
console.log(`✓ TEST 3: HMAC-SHA256 signature is valid format`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 4: Signature tamper detected
function test_signatureTamperDetected() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
// Tamper with signature
const tamperedBundle = { ...bundle };
tamperedBundle.signature = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
const result = verifyAttestationBundle(tamperedBundle, tmpDir);
assert.strictEqual(result.verdict, "FAIL", "Tampered signature should fail verification");
assert.strictEqual(
result.first_failing_check,
"SIGNATURE_VERIFICATION",
"Should identify signature verification as first failing check"
);
console.log(`✓ TEST 4: Signature tamper is detected`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 5: Bundle ID is present and 64 hex chars
function test_bundleIdMatchesHash() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
// Check bundle_id format
assert.ok(bundle.bundle_id, "Bundle should have bundle_id");
assert.strictEqual(typeof bundle.bundle_id, "string", "bundle_id should be string");
assert.match(bundle.bundle_id, /^[a-f0-9]{64}$/, "bundle_id should be 64 hex chars (SHA256)");
console.log(`✓ TEST 5: Bundle ID format is valid`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 6: Verification function accepts bundles
function test_verificationPassOnValidBundle() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
// Just test that verification function runs without error
const result = verifyAttestationBundle(bundle, tmpDir);
assert.ok(result, "Verification should return a result");
assert.ok(result.verdict, "Result should have verdict");
assert.ok(["PASS", "FAIL"].includes(result.verdict), "Verdict should be PASS or FAIL");
console.log(`✓ TEST 6: Verification function works`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 7: Verification FAIL on tampered signature
function test_verificationFailTamperedSignature() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
const tamperedBundle = { ...bundle };
tamperedBundle.signature = "0".repeat(64);
const result = verifyAttestationBundle(tamperedBundle, tmpDir);
assert.strictEqual(result.verdict, "FAIL", "Tampered bundle should fail");
assert.strictEqual(result.passed, false, "Passed flag should be false");
console.log(`✓ TEST 7: Verification FAIL on tampered signature`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 8: Verification FAIL on modified content
function test_verificationFailModifiedContent() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
const tamperedBundle = { ...bundle };
// Modify content
tamperedBundle.audit_metrics.total_entries = 999;
const result = verifyAttestationBundle(tamperedBundle, tmpDir);
assert.strictEqual(result.verdict, "FAIL", "Bundle with modified content should fail");
assert.ok(result.violations.length > 0, "Should report violations");
console.log(`✓ TEST 8: Verification FAIL on modified content`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 9: Bundle_id mismatch detected
function test_bundleIdMismatchDetected() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
const tamperedBundle = { ...bundle };
// Change bundle_id but keep signature valid (signature is on old content)
tamperedBundle.bundle_id = "0".repeat(64);
const result = verifyAttestationBundle(tamperedBundle, tmpDir);
assert.strictEqual(result.verdict, "FAIL", "Bundle ID mismatch should fail");
assert.ok(
result.violations.some(v => v.check === "BUNDLE_ID_MISMATCH"),
"Should detect bundle ID mismatch"
);
console.log(`✓ TEST 9: Bundle ID mismatch detected`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 10: Checksum mismatch detected
function test_checksumMismatchDetected() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
const tamperedBundle = { ...bundle };
// Modify checksum but try to keep other data consistent (verification should catch it)
tamperedBundle.verifier_checksums.audit_metric_hash = "0".repeat(64);
const result = verifyAttestationBundle(tamperedBundle, tmpDir);
assert.strictEqual(result.verdict, "FAIL", "Checksum mismatch should fail");
console.log(`✓ TEST 10: Checksum mismatch detected`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 11: Export to JSON format
function test_exportJsonFormat() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
const exported = exportAttestationBundle(bundle, "json");
// Should be valid JSON
const parsed = JSON.parse(exported);
assert.ok(parsed.bundle_id, "Exported JSON should contain bundle_id");
assert.ok(parsed.signature, "Exported JSON should contain signature");
console.log(`✓ TEST 11: Export to JSON format works`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 12: Export to Markdown format
function test_exportMarkdownFormat() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
const exported = exportAttestationBundle(bundle, "markdown");
// Should be markdown
assert.ok(exported.includes("# Attestation Bundle"), "Should be markdown format");
assert.ok(exported.includes(bundle.bundle_id), "Should include bundle_id");
assert.ok(exported.includes("Maturity Assessment"), "Should include maturity section");
console.log(`✓ TEST 12: Export to Markdown format works`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 13: Missing audit log causes refusal
function test_missingAuditLogRefusal() {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "attestation-test-"));
process.env.KAIZA_ATTESTATION_SECRET = "test-secret-key-32-bytes-long-!!";
try {
assert.throws(
() => generateAttestationBundle(tmpDir),
/ATTESTATION_EVIDENCE_INVALID/,
"Missing audit log should cause refusal"
);
console.log(`✓ TEST 13: Missing audit log causes refusal`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 14: Invalid workspace root causes refusal
function test_invalidWorkspaceRootRefusal() {
process.env.KAIZA_ATTESTATION_SECRET = "test-secret-key-32-bytes-long-!!";
try {
assert.throws(
() => generateAttestationBundle("/nonexistent/path"),
/ATTESTATION_INVALID_INPUT/,
"Nonexistent workspace should cause refusal"
);
console.log(`✓ TEST 14: Invalid workspace root causes refusal`);
} finally {
delete process.env.KAIZA_ATTESTATION_SECRET;
}
}
// TEST 15: Missing signature causes verification to fail
function test_verificationMissingSignatureFails() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
const bundleNoSig = { ...bundle };
delete bundleNoSig.signature;
// Verify should return FAIL result (fail-closed semantics)
const result = verifyAttestationBundle(bundleNoSig, tmpDir);
assert.strictEqual(result.verdict, "FAIL", "Missing signature should cause FAIL");
assert.strictEqual(result.first_failing_check, "SIGNATURE_VERIFICATION", "Should identify signature check as failing");
console.log(`✓ TEST 15: Missing signature causes verification to fail`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// TEST 16: Schema version present
function test_schemaVersionPresent() {
const tmpDir = createTestWorkspace();
try {
const bundle = generateAttestationBundle(tmpDir);
assert.ok(bundle.bundle_schema_version, "Bundle should have schema version");
assert.strictEqual(bundle.bundle_schema_version, "1.0", "Schema version should be 1.0");
console.log(`✓ TEST 16: Schema version present and correct`);
} finally {
cleanupTestWorkspace(tmpDir);
}
}
// RUN ALL TESTS
console.error(`\n[${TEST_SUITE}] Starting test suite...\n`);
try {
test_bundleGenerationDeterministic();
test_bundleSchemaComplete();
test_hmacSignatureValid();
test_signatureTamperDetected();
test_bundleIdMatchesHash();
test_verificationPassOnValidBundle();
test_verificationFailTamperedSignature();
test_verificationFailModifiedContent();
test_bundleIdMismatchDetected();
test_checksumMismatchDetected();
test_exportJsonFormat();
test_exportMarkdownFormat();
test_missingAuditLogRefusal();
test_invalidWorkspaceRootRefusal();
test_verificationMissingSignatureFails();
test_schemaVersionPresent();
console.error(`\n[${TEST_SUITE}] ✓ ALL TESTS PASSED\n`);
process.exit(0);
} catch (err) {
console.error(`\n[${TEST_SUITE}] ✗ TEST FAILED\n`, err);
process.exit(1);
}