CompositeScoringPostProcessor.java
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright 2026 Nikolas Charalambidis
*/
package dev.nichar.facepalm.engine.postprocessor;
import dev.nichar.facepalm.FacepalmConfig;
import dev.nichar.facepalm.engine.FileContext;
import dev.nichar.facepalm.engine.Finding;
import jakarta.annotation.Nonnull;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
/**
* Refines discovery findings based on the collective context of a file.
* Elevates risk for files with multiple distinct secret types and discounts high-volume noise.
*/
@Named
@Singleton
class CompositeScoringPostProcessor implements FileFindingsPostProcessor {
@Inject
private FacepalmConfig config;
@Override
public void process(@Nonnull final List<Finding> fileFindings, @Nonnull final FileContext context) {
if (fileFindings.isEmpty()) {
return;
}
final var totalInFile = fileFindings.size();
final var uniquePatterns = fileFindings.stream()
.map(Finding::getPatternName)
.distinct()
.count();
final var conf = config.getPostProcessing();
for (final var finding : fileFindings) {
// Categorize file as "noisy" if detections exceed the high-volume threshold.
if (totalInFile > conf.getHighVolumeThreshold()) {
// High volume often indicates log dumps, public datasets, or false positives.
finding.log("High Volume File (Threshold: " + conf.getHighVolumeThreshold() + ")", -25, -30);
}
// Elevate risk if a single file contains multiple distinct secret types.
if (uniquePatterns > 1) {
// Increases risk because a file containing multiple secret types is statistically more likely to be a
// real credential leak.
finding.log("Composite Risk: Multiple distinct secrets in one file", 15, 10);
}
}
}
}