| 1 | package com.renomad.minum.database; | |
| 2 | ||
| 3 | import com.renomad.minum.utils.CryptoUtils; | |
| 4 | ||
| 5 | import java.io.IOException; | |
| 6 | import java.nio.charset.StandardCharsets; | |
| 7 | import java.nio.file.Files; | |
| 8 | import java.nio.file.Path; | |
| 9 | import java.security.MessageDigest; | |
| 10 | import java.security.NoSuchAlgorithmException; | |
| 11 | import java.util.Collection; | |
| 12 | import java.util.List; | |
| 13 | ||
| 14 | /** | |
| 15 | * This class contains some functions related to the "checksum" feature | |
| 16 | * of the database. The "checksum" is a hash that is created when the | |
| 17 | * database writes data to a file, and is checked when reading that file. | |
| 18 | * <br> | |
| 19 | * If the value is different than expected while reading, an exception will be thrown | |
| 20 | * indicating data corruption. | |
| 21 | */ | |
| 22 | public class ChecksumUtility { | |
| 23 | ||
| 24 | private ChecksumUtility() { | |
| 25 | // not intended to be instantiated | |
| 26 | } | |
| 27 | ||
| 28 | /** | |
| 29 | * Given a path to a consolidated database file, check its data against | |
| 30 | * the recorded checksum and throw an exception if it doesn't match. | |
| 31 | * @param fullPathToConsolidatedFile the path to a consolidated database file | |
| 32 | * @param data the list of strings of data in a consolidated database file | |
| 33 | */ | |
| 34 | static boolean compareWithChecksum(Path fullPathToConsolidatedFile, List<String> data) { | |
| 35 | // check against the checksum, if it exists. If it is not there or blank, just move on. | |
| 36 | Path checksumPath = fullPathToConsolidatedFile.resolveSibling(fullPathToConsolidatedFile.getFileName() + ".checksum"); | |
| 37 |
1
1. compareWithChecksum : negated conditional → KILLED |
if (Files.exists(checksumPath)) { |
| 38 | String existingChecksumValue; | |
| 39 | try { | |
| 40 | existingChecksumValue = Files.readString(checksumPath); | |
| 41 | } catch (IOException e) { | |
| 42 | throw new DbChecksumException(e); | |
| 43 | } | |
| 44 | String checksumString = buildChecksum(data); | |
| 45 |
1
1. compareWithChecksum : negated conditional → KILLED |
if (!checksumString.equals(existingChecksumValue)) { |
| 46 | throw new DbChecksumException(generateChecksumErrorMessage(fullPathToConsolidatedFile)); | |
| 47 | } | |
| 48 | } | |
| 49 |
1
1. compareWithChecksum : replaced boolean return with false for com/renomad/minum/database/ChecksumUtility::compareWithChecksum → KILLED |
return true; |
| 50 | } | |
| 51 | ||
| 52 | static String generateChecksumErrorMessage(Path fullPathToConsolidatedFile) { | |
| 53 |
1
1. generateChecksumErrorMessage : replaced return value with "" for com/renomad/minum/database/ChecksumUtility::generateChecksumErrorMessage → KILLED |
return """ |
| 54 | | |
| 55 | WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING | |
| 56 | ************************************************************************************** | |
| 57 | ************************************************************************************** | |
| 58 | | |
| 59 | Checksum failure for %s | |
| 60 | | |
| 61 | THIS FILE IS CORRUPTED, AND NEEDS TO BE RESTORED FROM BACKUP! | |
| 62 | | |
| 63 | The checksum file is at %s.checksum | |
| 64 | | |
| 65 | Next steps: This warning means the data does not align with its checksum, meaning | |
| 66 | it has changed by something other than the program. That is, it is corrupted data. | |
| 67 | The best thing is to restore the data from backup for this. Other options are to | |
| 68 | review the data. Deleting the checksum file will cause this complaint to stop. | |
| 69 | | |
| 70 | ************************************************************************************** | |
| 71 | ************************************************************************************** | |
| 72 | WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING | |
| 73 | | |
| 74 | """.stripIndent().formatted(fullPathToConsolidatedFile, fullPathToConsolidatedFile); | |
| 75 | } | |
| 76 | ||
| 77 | static String buildChecksum(Collection<String> updatedData) { | |
| 78 | // build a hash for this data | |
| 79 | MessageDigest messageDigestSha256 = getMessageDigest("SHA-256"); | |
| 80 | for (String item : updatedData) { | |
| 81 |
1
1. buildChecksum : removed call to java/security/MessageDigest::update → KILLED |
messageDigestSha256.update(item.getBytes(StandardCharsets.US_ASCII)); |
| 82 | } | |
| 83 | byte[] hash = messageDigestSha256.digest(); | |
| 84 |
1
1. buildChecksum : replaced return value with "" for com/renomad/minum/database/ChecksumUtility::buildChecksum → KILLED |
return CryptoUtils.bytesToHex(hash); |
| 85 | } | |
| 86 | ||
| 87 | static MessageDigest getMessageDigest(String algorithm) { | |
| 88 | try { | |
| 89 |
1
1. getMessageDigest : replaced return value with null for com/renomad/minum/database/ChecksumUtility::getMessageDigest → KILLED |
return MessageDigest.getInstance(algorithm); |
| 90 | } catch (NoSuchAlgorithmException e) { | |
| 91 | throw new DbChecksumException(e); | |
| 92 | } | |
| 93 | } | |
| 94 | ||
| 95 | } | |
Mutations | ||
| 37 |
1.1 |
|
| 45 |
1.1 |
|
| 49 |
1.1 |
|
| 53 |
1.1 |
|
| 81 |
1.1 |
|
| 84 |
1.1 |
|
| 89 |
1.1 |