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. */
| 6758 | * focused CLI regression tests. Duplicate entries are accepted only when they |
| 6759 | * name the exact artifact and normalize to the same SHA-256 digest. */ |
| 6760 | int cbm_cli_checksum_manifest_digest(const char *manifest_path, const char *archive_name, char *out, |
| 6761 | size_t out_size) { |
| 6762 | if (out && out_size > 0) { |
| 6763 | out[0] = '\0'; |
| 6764 | } |
| 6765 | if (!manifest_path || !archive_name || !archive_name[0] || !out || out_size < SHA256_BUF_SIZE || |
| 6766 | strchr(archive_name, '\n') || strchr(archive_name, '\r')) { |
| 6767 | return CLI_ERR; |
| 6768 | } |
| 6769 | |
| 6770 | FILE *fp = cbm_fopen(manifest_path, "rb"); |
| 6771 | if (!fp) { |
| 6772 | return CLI_ERR; |
| 6773 | } |
| 6774 | unsigned char *manifest = malloc(CHECKSUM_MANIFEST_MAX_BYTES + 1U); |
| 6775 | if (!manifest) { |
| 6776 | (void)fclose(fp); |
| 6777 | return CLI_ERR; |
| 6778 | } |
| 6779 | size_t manifest_length = 0; |
| 6780 | bool read_ok = true; |
| 6781 | while (manifest_length <= CHECKSUM_MANIFEST_MAX_BYTES) { |
| 6782 | size_t capacity = CHECKSUM_MANIFEST_MAX_BYTES + 1U - manifest_length; |
| 6783 | size_t count = fread(manifest + manifest_length, 1, capacity, fp); |
| 6784 | manifest_length += count; |
| 6785 | if (ferror(fp)) { |
| 6786 | read_ok = false; |
| 6787 | break; |
| 6788 | } |
| 6789 | if (feof(fp)) { |
| 6790 | break; |
| 6791 | } |
| 6792 | if (count == 0) { |
| 6793 | read_ok = false; |
| 6794 | break; |
| 6795 | } |
| 6796 | } |
| 6797 | if (fclose(fp) != 0) { |
| 6798 | read_ok = false; |
| 6799 | } |
| 6800 | if (!read_ok || manifest_length == 0 || manifest_length > CHECKSUM_MANIFEST_MAX_BYTES || |
| 6801 | memchr(manifest, '\0', manifest_length)) { |
| 6802 | free(manifest); |
| 6803 | return CLI_ERR; |
| 6804 | } |
| 6805 | |
| 6806 | size_t archive_name_length = strlen(archive_name); |
| 6807 | char selected[SHA256_BUF_SIZE] = {0}; |
| 6808 | bool found = false; |
| 6809 | const unsigned char *cursor = manifest; |
| 6810 | const unsigned char *end = manifest + manifest_length; |
| 6811 | while (cursor < end) { |
| 6812 | const unsigned char *newline = memchr(cursor, '\n', (size_t)(end - cursor)); |
| 6813 | const unsigned char *line_end = newline ? newline : end; |
| 6814 | size_t line_length = (size_t)(line_end - cursor); |
| 6815 | if (line_length > 0 && cursor[line_length - 1U] == (unsigned char)'\r') { |
| 6816 | line_length--; |
| 6817 | } |
no test coverage detected