(archivePath, archiveName)
| 516 | |
| 517 | // Fetch checksums.txt and verify the archive hash. |
| 518 | async function verifyChecksum(archivePath, archiveName) { |
| 519 | const url = `https://github.com/${REPO}/releases/download/v${VERSION}/checksums.txt`; |
| 520 | const tmpChecksums = archivePath + '.checksums'; |
| 521 | try { |
| 522 | await download(url, tmpChecksums, MAX_CHECKSUM_MANIFEST_BYTES); |
| 523 | const manifestSize = fs.statSync(tmpChecksums).size; |
| 524 | if (manifestSize > MAX_CHECKSUM_MANIFEST_BYTES) { |
| 525 | throw new Error('checksums.txt exceeds the 1 MiB safety limit'); |
| 526 | } |
| 527 | const expected = parseExpectedChecksum( |
| 528 | fs.readFileSync(tmpChecksums, 'utf-8'), archiveName, |
| 529 | ); |
| 530 | const actual = crypto |
| 531 | .createHash('sha256') |
| 532 | .update(fs.readFileSync(archivePath)) |
| 533 | .digest('hex'); |
| 534 | if (expected !== actual) { |
| 535 | throw new Error( |
| 536 | `Checksum mismatch for ${archiveName}:\n expected: ${expected}\n actual: ${actual}`, |
| 537 | ); |
| 538 | } |
| 539 | process.stdout.write('codebase-memory-mcp: checksum verified.\n'); |
| 540 | } finally { |
| 541 | try { fs.unlinkSync(tmpChecksums); } catch (_) { /* ignore */ } |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | async function main() { |
| 546 | const platform = getPlatform(); |
no test coverage detected