Download checksums.txt and verify the archive integrity. * Returns: 0 = verified OK, 1 = mismatch (FAIL), -1 = could not verify (warning). */
| 3066 | /* Download checksums.txt and verify the archive integrity. |
| 3067 | * Returns: 0 = verified OK, 1 = mismatch (FAIL), -1 = could not verify (warning). */ |
| 3068 | static int verify_download_checksum(const char *archive_path, const char *archive_name) { |
| 3069 | char checksum_file[CLI_BUF_256]; |
| 3070 | snprintf(checksum_file, sizeof(checksum_file), "%s/cbm-checksums.txt", cbm_tmpdir()); |
| 3071 | |
| 3072 | char dl_base_buf[CLI_BUF_512]; |
| 3073 | const char *dl_base = |
| 3074 | cbm_safe_getenv("CBM_DOWNLOAD_URL", dl_base_buf, sizeof(dl_base_buf), NULL); |
| 3075 | char checksum_url[CLI_BUF_512]; |
| 3076 | if (dl_base && dl_base[0]) { |
| 3077 | snprintf(checksum_url, sizeof(checksum_url), "%s/checksums.txt", dl_base); |
| 3078 | } else { |
| 3079 | snprintf(checksum_url, sizeof(checksum_url), "%s", |
| 3080 | "https://github.com/DeusData/codebase-memory-mcp/releases/latest/download/" |
| 3081 | "checksums.txt"); |
| 3082 | } |
| 3083 | int rc = cbm_download_to_file_quiet(checksum_url, checksum_file); |
| 3084 | if (rc != 0) { |
| 3085 | (void)fprintf(stderr, |
| 3086 | "warning: could not download checksums.txt — skipping verification\n"); |
| 3087 | cbm_unlink(checksum_file); |
| 3088 | return CLI_ERR; |
| 3089 | } |
| 3090 | |
| 3091 | FILE *fp = fopen(checksum_file, "r"); |
| 3092 | cbm_unlink(checksum_file); |
| 3093 | if (!fp) { |
| 3094 | return CLI_ERR; |
| 3095 | } |
| 3096 | |
| 3097 | char expected[SHA256_BUF_SIZE] = {0}; |
| 3098 | char line[CLI_BUF_512]; |
| 3099 | while (fgets(line, sizeof(line), fp)) { |
| 3100 | /* Format: <CBM_SZ_64-char sha256> <filename>\n */ |
| 3101 | if (strlen(line) > CHECKSUM_LINE_MIN && strstr(line, archive_name)) { |
| 3102 | memcpy(expected, line, SHA256_HEX_LEN); |
| 3103 | expected[SHA256_HEX_LEN] = '\0'; |
| 3104 | break; |
| 3105 | } |
| 3106 | } |
| 3107 | (void)fclose(fp); |
| 3108 | |
| 3109 | if (expected[0] == '\0') { |
| 3110 | (void)fprintf(stderr, "warning: %s not found in checksums.txt\n", archive_name); |
| 3111 | return CLI_ERR; |
| 3112 | } |
| 3113 | |
| 3114 | char actual[SHA256_BUF_SIZE] = {0}; |
| 3115 | if (cbm_cli_sha256_file(archive_path, actual, sizeof(actual)) != 0) { |
| 3116 | (void)fprintf(stderr, "error: could not compute checksum (sha256 tool unavailable)\n"); |
| 3117 | return CLI_ERR; |
| 3118 | } |
| 3119 | |
| 3120 | if (strcmp(expected, actual) != 0) { |
| 3121 | (void)fprintf(stderr, "error: CHECKSUM MISMATCH — downloaded binary may be compromised!\n"); |
| 3122 | (void)fprintf(stderr, " expected: %s\n", expected); |
| 3123 | (void)fprintf(stderr, " actual: %s\n", actual); |
| 3124 | return CLI_TRUE; |
| 3125 | } |
no test coverage detected