mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-02 07:53:41 +08:00
cli: fix self-update checksum computation and fail closed
The macOS digest path invoked `shasum -a CBM_SZ_256` — a literal string (not a macro; string literals do not expand) naming an invalid algorithm, so the digest always failed on macOS, and the self-update path then treated a could-not-verify result as non-fatal and installed the download regardless. Use the correct algorithm, and install only a positively-verified download: a mismatch, a missing checksum entry, or an unavailable hash tool now all abort. Adds a known-vector digest regression test. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
+12
-7
@@ -2968,15 +2968,16 @@ static bool prompt_yn(const char *question) {
|
||||
/* Minimum line length in checksums.txt: CBM_SZ_64 hex + 2 spaces + 1 char filename */
|
||||
#define CHECKSUM_LINE_MIN (SHA256_HEX_LEN + 2)
|
||||
|
||||
/* Compute SHA-CBM_SZ_256 of a file using platform tools (sha256sum/shasum).
|
||||
* Writes CBM_SZ_64-char hex digest + NUL to out. Returns 0 on success. */
|
||||
static int sha256_file(const char *path, char *out, size_t out_size) {
|
||||
/* Compute the SHA-256 of a file using platform tools (sha256sum/shasum).
|
||||
* Writes a 64-char hex digest + NUL to out. Returns 0 on success. Not static:
|
||||
* exercised directly by the self-update checksum regression test. */
|
||||
int cbm_cli_sha256_file(const char *path, char *out, size_t out_size) {
|
||||
if (out_size < SHA256_BUF_SIZE) {
|
||||
return CLI_ERR;
|
||||
}
|
||||
char cmd[CLI_BUF_1K];
|
||||
#ifdef __APPLE__
|
||||
snprintf(cmd, sizeof(cmd), "shasum -a CBM_SZ_256 '%s' 2>/dev/null", path);
|
||||
snprintf(cmd, sizeof(cmd), "shasum -a 256 '%s' 2>/dev/null", path);
|
||||
#else
|
||||
snprintf(cmd, sizeof(cmd), "sha256sum '%s' 2>/dev/null", path);
|
||||
#endif
|
||||
@@ -3107,8 +3108,8 @@ static int verify_download_checksum(const char *archive_path, const char *archiv
|
||||
}
|
||||
|
||||
char actual[SHA256_BUF_SIZE] = {0};
|
||||
if (sha256_file(archive_path, actual, sizeof(actual)) != 0) {
|
||||
(void)fprintf(stderr, "warning: sha256sum/shasum not available — skipping verification\n");
|
||||
if (cbm_cli_sha256_file(archive_path, actual, sizeof(actual)) != 0) {
|
||||
(void)fprintf(stderr, "error: could not compute checksum (sha256 tool unavailable)\n");
|
||||
return CLI_ERR;
|
||||
}
|
||||
|
||||
@@ -4250,8 +4251,12 @@ static int download_verify_install(const char *url, const char *ext, const char
|
||||
const char *portable = (strcmp(os, "linux") == 0) ? "-portable" : "";
|
||||
snprintf(archive_name, sizeof(archive_name), "codebase-memory-mcp-%s%s-%s%s.%s",
|
||||
want_ui ? "ui-" : "", os, arch, portable, ext);
|
||||
/* Fail closed: install only a positively-verified download. A mismatch,
|
||||
* a missing checksum entry, or an unavailable hash tool (crc != 0) all
|
||||
* abort rather than install an unverified binary. */
|
||||
int crc = verify_download_checksum(tmp_archive, archive_name);
|
||||
if (crc == CLI_TRUE) {
|
||||
if (crc != 0) {
|
||||
(void)fprintf(stderr, "error: refusing to install an unverified download\n");
|
||||
cbm_unlink(tmp_archive);
|
||||
return CLI_TRUE;
|
||||
}
|
||||
|
||||
@@ -3010,11 +3010,38 @@ TEST(cli_print_tool_help_issue680) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* The self-update path verifies a downloaded archive against a published
|
||||
* checksum. That check is only meaningful if the digest is actually computed —
|
||||
* a broken hash command (it once invoked `shasum -a CBM_SZ_256`, an invalid
|
||||
* algorithm, from a bad macro rename inside the shell string) makes every
|
||||
* digest fail, and the caller then falls through and installs unverified.
|
||||
* Guard the digest itself against a known vector. */
|
||||
extern int cbm_cli_sha256_file(const char *path, char *out, size_t out_size);
|
||||
|
||||
TEST(cli_sha256_file_matches_known_vector) {
|
||||
char path[512];
|
||||
snprintf(path, sizeof(path), "%s/cbm_sha_XXXXXX", cbm_tmpdir());
|
||||
int fd = cbm_mkstemp(path);
|
||||
ASSERT_TRUE(fd >= 0);
|
||||
FILE *fp = fdopen(fd, "wb");
|
||||
ASSERT_NOT_NULL(fp);
|
||||
fwrite("abc", 1, 3, fp); /* sha256("abc") is a NIST test vector */
|
||||
fclose(fp);
|
||||
|
||||
char digest[128] = {0};
|
||||
int rc = cbm_cli_sha256_file(path, digest, sizeof(digest));
|
||||
remove(path);
|
||||
ASSERT_EQ(rc, 0);
|
||||
ASSERT_STR_EQ(digest, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
* Suite definition
|
||||
* ═══════════════════════════════════════════════════════════════════ */
|
||||
|
||||
SUITE(cli) {
|
||||
RUN_TEST(cli_sha256_file_matches_known_vector);
|
||||
/* Version (2 tests — selfupdate_test.go) */
|
||||
RUN_TEST(cli_compare_versions);
|
||||
RUN_TEST(cli_version_get_set);
|
||||
|
||||
Reference in New Issue
Block a user