Skip to content

Commit

Permalink
Obfuscate secret scan findings in reports (mercedes-benz#2999)
Browse files Browse the repository at this point in the history
Added obfuscate method to obfuscate secret scan findings in reports
  • Loading branch information
lorriborri authored Mar 15, 2024
1 parent 39c7cb4 commit 7fe6a04
Show file tree
Hide file tree
Showing 9 changed files with 317 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@

public class SimpleStringUtils {

private static final String TRUNCATE_POSTFIX = "...";
private static final String OBFUSCATE_POSTFIX = "*****";

private static final Logger LOG = LoggerFactory.getLogger(SimpleStringUtils.class);
private static final String EMPTY = "";

private SimpleStringUtils() {
}
Expand Down Expand Up @@ -98,16 +102,46 @@ public static String emptyToNull(String value) {
* @throws IllegalArgumentException when max length is smaller than 4
*/
public static String truncateWhenTooLong(String string, int maxLength) {
if (string == null) {
return null;
}
if (maxLength < 4) {
throw new IllegalArgumentException("max length must be at least 4!");
}
if (string == null) {
return null;
}
if (string.length() <= maxLength) {
return string;
}
return string.substring(0, maxLength - 3) + "...";
return string.substring(0, maxLength - TRUNCATE_POSTFIX.length()) + TRUNCATE_POSTFIX;
}

/**
* obfuscates secret strings by showing only the required number of characters,
* trunc the rest and add "*****"
*
* @param string
* @param nonObfuscatedCharacters number of characters that should be shown in
* clear text (eg. 3 -> abc*****), if the value
* is negative, the original characters are shown
* without postfix, if the value is greater than
* the secret length, all characters are shown
* with the postfix
* @return obfuscated string
*/
public static String createObfuscatedString(String string, int nonObfuscatedCharacters) {
if (string == null) {
return null;
}

if (nonObfuscatedCharacters < 0) {
return string;
}

int remaining = nonObfuscatedCharacters;
if (string.length() < remaining) {
remaining = string.length();
}

return string.substring(0, remaining) + OBFUSCATE_POSTFIX;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,50 @@

public class SimpleStringUtilsTest {

@ValueSource(strings = { "a", "ab", "$*abcdefghiujklmnop" })
@ParameterizedTest
void obfuscate_always_obfuscated_when_length_0(String text) {
assertEquals("*****", SimpleStringUtils.createObfuscatedString(text, 0));
}

@Test
void obfuscate_always_obfuscated_when_length_3_but_only_2_chars() {
/* prepare */
String text = "abc";

/* execute + test */
assertEquals("ab*****", SimpleStringUtils.createObfuscatedString(text, 2));
}

@ValueSource(ints = { 3, 5, 100 })
@ParameterizedTest
void obfuscate_always_obfuscated_when_string_length_smaller_than_obfuscation_length(int length) {
/* prepare */
String text = "ab";

/* execute + test */
assertEquals("ab*****", SimpleStringUtils.createObfuscatedString(text, length));
}

@ValueSource(ints = { -1, -5, -100 })
@ParameterizedTest
void do_not_obfuscate_when_length_smaller_0(int lenght) {
/* prepare */
String text = "ab23zr9hfiedlshfl";

/* execute + test */
assertEquals("ab23zr9hfiedlshfl", SimpleStringUtils.createObfuscatedString(text, lenght));
}

@Test
void return_null_when_string_parameter_null() {
/* prepare */
String text = null;

/* execute + test */
assertNull(SimpleStringUtils.createObfuscatedString(text, 0));
}

@ValueSource(strings = { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ" })
@ParameterizedTest
void isLatinLetter_valid_values(String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void get_report_from_existing_job_returns_information_as_json_when_type_i
report.setTrafficLight(TrafficLight.YELLOW);

ScanSecHubReport scanSecHubReport = new ScanSecHubReport(report);
when(downloadReportService.getScanSecHubReport(PROJECT1_ID, jobUUID)).thenReturn(scanSecHubReport);
when(downloadReportService.getObfuscatedScanSecHubReport(PROJECT1_ID, jobUUID)).thenReturn(scanSecHubReport);

/* execute + test @formatter:off */
this.mockMvc.perform(
Expand Down Expand Up @@ -143,7 +143,7 @@ public void get_report_from_existing_job_returns_information_as_html_when_type_i

ScanSecHubReport scanSecHubReport = new ScanSecHubReport(report);
assertNotNull(scanSecHubReport.getMetaData());
when(downloadReportService.getScanSecHubReport(PROJECT1_ID, jobUUID)).thenReturn(scanSecHubReport);
when(downloadReportService.getObfuscatedScanSecHubReport(PROJECT1_ID, jobUUID)).thenReturn(scanSecHubReport);

/* execute + test @formatter:off */
this.mockMvc.perform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void run_pds_secret_scan_and_download_report_via_rest_mark_finding_0_as_f
codeCall(0).
hasColumn(14).
hasLine(12).
hasSource("531486b2bf646636a6a1bba61e78ec4a4a54efbd").
hasSource("*****").
hasLocation("UnSAFE_Bank/Backend/docker-compose.yml").
andFinding().
hasCweId(798); // gitleak has no cwe id, but importer will do fallback to CWE 798 - see https://cwe.mitre.org/data/definitions/798.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ public class DownloadScanReportService {
@Autowired
UserInputAssertion assertion;

@Autowired
private ScanReportSensitiveDataObfuscator scanReportSensitiveDataObfuscator;

@UseCaseUserDownloadsJobReport(@Step(number = 3, name = "Resolve scan report result"))
public ScanSecHubReport getScanSecHubReport(String projectId, UUID jobUUID) {
public ScanSecHubReport getObfuscatedScanSecHubReport(String projectId, UUID jobUUID) {
/* validate */
assertion.assertIsValidProjectId(projectId);
assertion.assertIsValidJobUUID(jobUUID);
Expand All @@ -51,7 +54,10 @@ public ScanSecHubReport getScanSecHubReport(String projectId, UUID jobUUID) {
}
scanAssertService.assertUserHasAccessToReport(report);

return new ScanSecHubReport(report);
ScanSecHubReport scanSecHubReport = new ScanSecHubReport(report);
scanReportSensitiveDataObfuscator.obfuscate(scanSecHubReport);

return scanSecHubReport;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public ScanSecHubReport getScanSecHubReportAsJSON(
@PathVariable("jobUUID") UUID jobUUID
) {
/* @formatter:on */
return fetchScanSecHubReport(projectId, jobUUID);
return fetchObfuscatedScanSecHubReport(projectId, jobUUID);

}

Expand All @@ -67,7 +67,7 @@ public ModelAndView getScanSecHubReportAsHTML(
@PathVariable("jobUUID") UUID jobUUID
) {
/* @formatter:on */
ScanSecHubReport scanSecHubReport = fetchScanSecHubReport(projectId, jobUUID);
ScanSecHubReport scanSecHubReport = fetchObfuscatedScanSecHubReport(projectId, jobUUID);

Map<String, Object> model = htmlModelBuilder.build(scanSecHubReport);
return new ModelAndView("report/html/report", model);
Expand All @@ -87,8 +87,8 @@ public String getScanSecHubReportAsSpdxJson(
return spdxDocument;
}

private ScanSecHubReport fetchScanSecHubReport(String projectId, UUID jobUUID) {
return downloadReportService.getScanSecHubReport(projectId, jobUUID);
private ScanSecHubReport fetchObfuscatedScanSecHubReport(String projectId, UUID jobUUID) {
return downloadReportService.getObfuscatedScanSecHubReport(projectId, jobUUID);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.mercedesbenz.sechub.domain.scan.report;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.mercedesbenz.sechub.commons.core.util.SimpleStringUtils;
import com.mercedesbenz.sechub.commons.model.ScanType;
import com.mercedesbenz.sechub.commons.model.SecHubCodeCallStack;
import com.mercedesbenz.sechub.commons.model.SecHubFinding;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;

@Component
public class ScanReportSensitiveDataObfuscator {

@Value("${sechub.report.sensitivedata.max.nonobfuscated.characters:0}")
@MustBeDocumented("Define the amount of visible characters which are NOT obfuscated.")
int sourceVisibleLength;

private static final Logger LOG = LoggerFactory.getLogger(ScanReportSensitiveDataObfuscator.class);

/**
* Obfuscates sensitive scan report data
*
* @param report the report to obfuscate
*/
public void obfuscate(ScanSecHubReport report) {

if (report == null) {
return;
}

/* result and findings are not null */
List<SecHubFinding> findings = report.getResult().getFindings();

for (SecHubFinding finding : findings) {
/* obfuscates secrets from secret scan */
if (ScanType.SECRET_SCAN.equals(finding.getType())) {
SecHubCodeCallStack code = finding.getCode();
if (code == null) {
LOG.debug("Could not obfuscate secret: codeCallstack was null");
continue;
}

String secret = code.getSource();
if (secret == null) {
LOG.debug("Could not obfuscate secret: code source was null");
continue;
}

String obfuscated = SimpleStringUtils.createObfuscatedString(secret, sourceVisibleLength);
if (obfuscated == null) {
LOG.debug("Could not obfuscate secret: obfuscated string was null");
continue;
}
code.setSource(obfuscated);
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ void get_report_from_existing_job_returns_406_NOT_ACCEPTABLE__when_type_is_APPLI
scanReport.setTrafficLight(TrafficLight.YELLOW);

ScanSecHubReport scanSecHubReport = new ScanSecHubReport(scanReport);
when(downloadReportService.getScanSecHubReport(PROJECT1_ID, randomUUID)).thenReturn(scanSecHubReport);
when(downloadReportService.getObfuscatedScanSecHubReport(PROJECT1_ID, randomUUID)).thenReturn(scanSecHubReport);

/* execute + test @formatter:off */
this.mockMvc.perform(
Expand Down Expand Up @@ -211,7 +211,7 @@ private void internalTestAcceptedAndReturnsJSON(MediaType acceptedType) throws E
report.setTrafficLight(TrafficLight.YELLOW);

ScanSecHubReport scanSecHubReport = new ScanSecHubReport(report);
when(downloadReportService.getScanSecHubReport(PROJECT1_ID, randomUUID)).thenReturn(scanSecHubReport);
when(downloadReportService.getObfuscatedScanSecHubReport(PROJECT1_ID, randomUUID)).thenReturn(scanSecHubReport);

/* execute + test @formatter:off */
this.mockMvc.perform(
Expand All @@ -232,7 +232,7 @@ private void internalTestAcceptedAndReturnsHTML(MediaType acceptedType) throws E
report.setTrafficLight(TrafficLight.YELLOW);

ScanSecHubReport scanSecHubReport = new ScanSecHubReport(report);
when(downloadReportService.getScanSecHubReport(PROJECT1_ID, randomUUID)).thenReturn(scanSecHubReport);
when(downloadReportService.getObfuscatedScanSecHubReport(PROJECT1_ID, randomUUID)).thenReturn(scanSecHubReport);

/* execute + test @formatter:off */
this.mockMvc.perform(
Expand Down
Loading

0 comments on commit 7fe6a04

Please sign in to comment.