(record, options = {})
| 116 | } |
| 117 | |
| 118 | function verifyRecord(record, options = {}) { |
| 119 | if (!record || typeof record !== 'object') { |
| 120 | return { status: 'invalid', reason: 'record is not an object' }; |
| 121 | } |
| 122 | |
| 123 | if (!record._hash || record._hash_v !== HASH_VERSION) { |
| 124 | return { status: 'legacy', reason: 'missing hash fields' }; |
| 125 | } |
| 126 | |
| 127 | const expectedHash = hashRecord(record); |
| 128 | if (expectedHash !== record._hash) { |
| 129 | return { status: 'tampered', reason: 'hash mismatch', expectedHash, actualHash: record._hash }; |
| 130 | } |
| 131 | |
| 132 | if (record._signature) { |
| 133 | if (record._signature_alg !== HMAC_ALGORITHM || record._signature_v !== SIGNATURE_VERSION) { |
| 134 | return { status: 'signature-unsupported', reason: 'unsupported signature metadata' }; |
| 135 | } |
| 136 | const key = resolveSigningKey(options); |
| 137 | if (!key) { |
| 138 | return { status: 'verified-unsigned-key-missing', reason: 'hash valid but signing key unavailable' }; |
| 139 | } |
| 140 | const expected = signRecord({ ...record, _signature: undefined }, options)._signature; |
| 141 | if (expected !== record._signature) { |
| 142 | return { status: 'tampered', reason: 'signature mismatch', expectedSignature: expected, actualSignature: record._signature }; |
| 143 | } |
| 144 | return { status: 'verified-signed', reason: 'hash and signature valid' }; |
| 145 | } |
| 146 | |
| 147 | return { status: 'verified', reason: 'hash valid' }; |
| 148 | } |
| 149 | |
| 150 | function verifyJsonlFile(filePath, options = {}) { |
| 151 | const summary = { |
no test coverage detected