Parse one downloaded checksum manifest without trusting line truncation or * substring matches. This non-header symbol is intentionally exercised by the * focused CLI regression tests. Duplicate entries are accepted only when they * name the exact artifact and normalize to the same SHA-256 digest. */
| 7115 | * focused CLI regression tests. Duplicate entries are accepted only when they |
| 7116 | * name the exact artifact and normalize to the same SHA-256 digest. */ |
| 7117 | int cbm_cli_checksum_manifest_digest(const char *manifest_path, const char *archive_name, char *out, |
| 7118 | size_t out_size) { |
| 7119 | if (out && out_size > 0) { |
| 7120 | out[0] = '\0'; |
| 7121 | } |
| 7122 | if (!manifest_path || !archive_name || !archive_name[0] || !out || out_size < SHA256_BUF_SIZE || |
| 7123 | strchr(archive_name, '\n') || strchr(archive_name, '\r')) { |
| 7124 | return CLI_ERR; |
| 7125 | } |
| 7126 | |
| 7127 | FILE *fp = cbm_fopen(manifest_path, "rb"); |
| 7128 | if (!fp) { |
| 7129 | return CLI_ERR; |
| 7130 | } |
| 7131 | unsigned char *manifest = malloc(CHECKSUM_MANIFEST_MAX_BYTES + 1U); |
| 7132 | if (!manifest) { |
| 7133 | (void)fclose(fp); |
| 7134 | return CLI_ERR; |
| 7135 | } |
| 7136 | size_t manifest_length = 0; |
| 7137 | bool read_ok = true; |
| 7138 | while (manifest_length <= CHECKSUM_MANIFEST_MAX_BYTES) { |
| 7139 | size_t capacity = CHECKSUM_MANIFEST_MAX_BYTES + 1U - manifest_length; |
| 7140 | size_t count = fread(manifest + manifest_length, 1, capacity, fp); |
| 7141 | manifest_length += count; |
| 7142 | if (ferror(fp)) { |
| 7143 | read_ok = false; |
| 7144 | break; |
| 7145 | } |
| 7146 | if (feof(fp)) { |
| 7147 | break; |
| 7148 | } |
| 7149 | if (count == 0) { |
| 7150 | read_ok = false; |
| 7151 | break; |
| 7152 | } |
| 7153 | } |
| 7154 | if (fclose(fp) != 0) { |
| 7155 | read_ok = false; |
| 7156 | } |
| 7157 | if (!read_ok || manifest_length == 0 || manifest_length > CHECKSUM_MANIFEST_MAX_BYTES || |
| 7158 | memchr(manifest, '\0', manifest_length)) { |
| 7159 | free(manifest); |
| 7160 | return CLI_ERR; |
| 7161 | } |
| 7162 | |
| 7163 | size_t archive_name_length = strlen(archive_name); |
| 7164 | char selected[SHA256_BUF_SIZE] = {0}; |
| 7165 | bool found = false; |
| 7166 | const unsigned char *cursor = manifest; |
| 7167 | const unsigned char *end = manifest + manifest_length; |
| 7168 | while (cursor < end) { |
| 7169 | const unsigned char *newline = memchr(cursor, '\n', (size_t)(end - cursor)); |
| 7170 | const unsigned char *line_end = newline ? newline : end; |
| 7171 | size_t line_length = (size_t)(line_end - cursor); |
| 7172 | if (line_length > 0 && cursor[line_length - 1U] == (unsigned char)'\r') { |
| 7173 | line_length--; |
| 7174 | } |
no test coverage detected