Skip to content

Commit 8377bd7

Browse files
authored
Merge pull request #124 from pwssOrg/46-compare-hash-values-previous-vs-new-scans
Hash comparison between scans
2 parents a0e4fb0 + dd3e8e6 commit 8377bd7

5 files changed

Lines changed: 91 additions & 19 deletions

File tree

File-Integrity-Scanner/src/main/java/org/pwss/file_integrity_scanner/component/FileHashComputer.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.pwss.file_integrity_scanner.component;
22

33
import lib.pwss.hash.FileHashHandler;
4+
import lib.pwss.hash.compare.util.HashCompareUtil;
45
import lib.pwss.hash.model.HashForFilesOutput;
6+
import org.pwss.file_integrity_scanner.msr.domain.model.entities.checksum.Checksum;
57
import org.springframework.stereotype.Component;
68

79
import java.io.File;
@@ -27,4 +29,21 @@ public FileHashComputer() {
2729
public HashForFilesOutput computeHashes(File file) {
2830
return fileHashHandler.GetAllHashes(file);
2931
}
32+
33+
/**
34+
* Compares the hashes of two checksum objects.
35+
* <p>
36+
* This method compares the SHA-256, SHA-3, and Blake2b hashes of the provided
37+
* checksum objects using XOR and Java's equals method. It returns true only if
38+
* all three hash comparisons match.
39+
*
40+
* @param first the first checksum object to compare
41+
* @param second the second checksum object to compare
42+
* @return true if all hash comparisons match, false otherwise
43+
*/
44+
public boolean compareHashes(Checksum first, Checksum second) {
45+
return HashCompareUtil.compareUsingXorAndJavaEquals(first.getChecksumSha256(), second.getChecksumSha256()) &&
46+
HashCompareUtil.compareUsingXorAndJavaEquals(first.getChecksumSha3(), second.getChecksumSha3()) &&
47+
HashCompareUtil.compareUsingXorAndJavaEquals(first.getChecksumBlake2b(), second.getChecksumBlake2b());
48+
}
3049
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package org.pwss.file_integrity_scanner.msr.repository;
22

33
import org.pwss.file_integrity_scanner.msr.domain.model.entities.checksum.Checksum;
4+
import org.pwss.file_integrity_scanner.msr.domain.model.entities.file.File;
45
import org.springframework.data.jpa.repository.JpaRepository;
56
import org.springframework.stereotype.Repository;
67

8+
import java.util.List;
9+
import java.util.Optional;
10+
711
@Repository
812
public interface ChecksumRepository extends JpaRepository<Checksum, Long> {
13+
Optional<List<Checksum>> findByFile(File file);
914
}

File-Integrity-Scanner/src/main/java/org/pwss/file_integrity_scanner/msr/service/checksum/ChecksumService.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,23 @@
22

33

44
import org.pwss.file_integrity_scanner.msr.domain.model.entities.checksum.Checksum;
5+
import org.pwss.file_integrity_scanner.msr.domain.model.entities.file.File;
6+
7+
import java.util.List;
58

69
public interface ChecksumService {
10+
11+
/**
12+
* Retrieves a list of checksums associated with the specified file.
13+
* <p>
14+
* This method queries the database to find all checksum entities
15+
* that are linked to the given file.
16+
*
17+
* @param file the file for which to retrieve associated checksums
18+
* @return a list of checksums associated with the specified file
19+
*/
20+
List<Checksum> findByFile(File file);
21+
722
/**
823
* Saves a checksum entity to the database.
924
*

File-Integrity-Scanner/src/main/java/org/pwss/file_integrity_scanner/msr/service/checksum/ChecksumServiceImpl.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,37 @@
11
package org.pwss.file_integrity_scanner.msr.service.checksum;
22

33
import org.pwss.file_integrity_scanner.msr.domain.model.entities.checksum.Checksum;
4+
import org.pwss.file_integrity_scanner.msr.domain.model.entities.file.File;
45
import org.pwss.file_integrity_scanner.msr.repository.ChecksumRepository;
56
import org.pwss.file_integrity_scanner.msr.service.BaseService;
67
import org.springframework.stereotype.Service;
78

9+
import java.util.Collections;
10+
import java.util.List;
11+
import java.util.Optional;
12+
813

914
@Service
1015
public class ChecksumServiceImpl extends BaseService<ChecksumRepository> implements ChecksumService {
1116

17+
private final org.slf4j.Logger log;
18+
1219
public ChecksumServiceImpl(ChecksumRepository repository) {
1320
super(repository);
21+
this.log = org.slf4j.LoggerFactory.getLogger(ChecksumServiceImpl.class);
22+
}
23+
24+
@Override
25+
public List<Checksum> findByFile(File file) {
26+
Optional<List<Checksum>> mOptional = repository.findByFile(file);
27+
28+
if (mOptional.isPresent()) {
29+
return mOptional.get();
30+
} else {
31+
log.warn("No checksums found for file: {}", file.getPath());
32+
return Collections.emptyList();
33+
}
34+
1435
}
1536

1637
@Override

File-Integrity-Scanner/src/main/java/org/pwss/file_integrity_scanner/msr/service/scan/ScanServiceImpl.java

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,16 @@ public class ScanServiceImpl extends BaseService<ScanRepository> implements Scan
5353
private final FileHashComputer fileHashComputer;
5454

5555
private final org.slf4j.Logger log;
56-
5756
private final DateTimeFormatter timeAndDateStringForLogFormat;
57+
58+
// Map to hold active scan tasks, keyed by directory path
5859
private final ConcurrentMap<String, ScanTaskState> activeScanTasks;
60+
5961
// Flag to indicate if an ongoing scan should be stopped
6062
private boolean stopRequested = false;
6163

62-
private final int SCAN_TASK_MONITOR_DELAY = 5000; // Delay in milliseconds for monitoring scan tasks
64+
// Delay in milliseconds for monitoring scan tasks
65+
private final int SCAN_TASK_MONITOR_DELAY = 5000;
6366

6467
@Autowired
6568
public ScanServiceImpl(ScanRepository repository,
@@ -282,22 +285,14 @@ private void processFile(File file, Scan scanInstance) {
282285

283286
MonitoredDirectory mDirectory = scanInstance.getMonitoredDirectory();
284287

285-
if (monitoredDirectoryService.isBaseLineEstablished(mDirectory)) {
286-
287-
// Compare current hash to previous hash (Ticket #46)
288-
289-
} else {
290-
// Scan without comparing hashes for this Monitored Directory
291-
}
292-
293288
if (fileInDatabase) {
294289
// Fetch existing entity and update fields
295290
fileEntity = fileService.findByPath(file.getPath());
296291
fileEntity.setSize(file.length());
297292
OffsetDateTime lastModified = Instant.ofEpochMilli(file.lastModified())
298293
.atOffset(ZoneOffset.UTC);
299294
fileEntity.setMtime(lastModified);
300-
log.info("Updating existing file in DB: {}", fileEntity.getPath());
295+
log.debug("Updating existing file in DB: {}", fileEntity.getPath());
301296
} else {
302297
// Create new entity
303298
fileEntity = new org.pwss.file_integrity_scanner.msr.domain.model.entities.file.File();
@@ -308,22 +303,39 @@ private void processFile(File file, Scan scanInstance) {
308303
OffsetDateTime lastModified = Instant.ofEpochMilli(file.lastModified())
309304
.atOffset(ZoneOffset.UTC);
310305
fileEntity.setMtime(lastModified);
311-
log.info("Adding new file to DB: {}", fileEntity.getPath());
306+
log.debug("Adding new file to DB: {}", fileEntity.getPath());
312307
}
313308

314309
fileService.save(fileEntity);
315310

316-
Checksum checksums = new Checksum();
317-
checksums.setChecksumSha256(computedHashes.sha256());
318-
checksums.setChecksumSha3(computedHashes.sha3());
319-
checksums.setChecksumBlake2b(computedHashes.blake2());
320-
checksums.setFile(fileEntity);
321-
checksumService.save(checksums);
311+
Checksum checksum = new Checksum();
312+
checksum.setChecksumSha256(computedHashes.sha256());
313+
checksum.setChecksumSha3(computedHashes.sha3());
314+
checksum.setChecksumBlake2b(computedHashes.blake2());
315+
checksum.setFile(fileEntity);
316+
checksumService.save(checksum);
317+
318+
// If the baseline is established, check if the file has changed
319+
if (monitoredDirectoryService.isBaseLineEstablished(mDirectory)) {
320+
List<Checksum> dbChecksums = checksumService.findByFile(fileEntity);
321+
if (!dbChecksums.isEmpty()) {
322+
// TODO: Maybe not getFirst but let's discuss this later
323+
Checksum oldChecksum = dbChecksums.getFirst();
324+
if (fileHashComputer.compareHashes(oldChecksum, checksum)) {
325+
log.info("File {} has not changed since last scan ✅", fileEntity.getPath());
326+
} else {
327+
log.warn("File {} has changed since last scan ⚠️", fileEntity.getPath());
328+
// TODO: Figure out what to do with changed files, add some notes to scan summary?
329+
}
330+
} else {
331+
log.info("No existing checksum found for file from prior scans {}", fileEntity.getPath());
332+
}
333+
}
322334

323335
ScanSummary scanSummary = new ScanSummary();
324336
scanSummary.setFile(fileEntity);
325337
scanSummary.setScan(scanInstance);
326-
scanSummary.setChecksum(checksums);
338+
scanSummary.setChecksum(checksum);
327339
scanSummaryService.save(scanSummary);
328340
} catch (OutOfMemoryError memoryError) {
329341
log.warn("Out of memory error while processing file: {}. Skipping file.", file.getPath());

0 commit comments

Comments
 (0)