Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,19 @@ public static final class FileReference {
public String path;
public boolean active;
public Integer line;
public String problematicLine;

public FileReference() {
}

public FileReference(String relativePath, boolean b, int line) {
public FileReference(String relativePath, boolean b, int line, String problematicLine) {
this.path = relativePath;
this.active = b;
this.line = line;
if (!problematicLine.isEmpty()) {
this.problematicLine = problematicLine;
}

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public void addAllIgnoredEntry(ScanIssue issueToIgnore, String clickId) {
fileRefs.add(new IgnoreEntry.FileReference(
ignoreFileManager.normalizePath(issue.getFilePath()),
true,
issue.getLocations().get(0).getLine()));
issue.getLocations().get(0).getLine(), ""));
scanFileAndUpdateResults(issue.getFilePath(), issue.getScanEngine());
return true;
});
Expand Down Expand Up @@ -343,7 +343,7 @@ public IgnoreEntry convertToIgnoredEntryIac(ScanIssue detail, String clickId) {
ignoreEntry.setSimilarityId(vulnerability.getSimilarityId());
ignoreEntry.setSeverity(vulnerability.getSeverity());
ignoreEntry.setDescription(vulnerability.getDescription());
ignoreEntry.setFiles(List.of(new IgnoreEntry.FileReference(relativePath, true, line)));
ignoreEntry.setFiles(List.of(new IgnoreEntry.FileReference(relativePath, true, line, "")));
return ignoreEntry;
});
ifExistingIssue(detail, entry);
Expand Down Expand Up @@ -383,7 +383,7 @@ public IgnoreEntry convertToIgnoredEntryAsca(ScanIssue detail, String clickId) {
ignoreEntry.setRuleId(detail.getRuleId());
ignoreEntry.setSeverity(vulnerability.getSeverity());
ignoreEntry.setDescription(vulnerability.getDescription());
ignoreEntry.setFiles(List.of(new IgnoreEntry.FileReference(relativePath, true, line)));
ignoreEntry.setFiles(List.of(new IgnoreEntry.FileReference(relativePath, true, line, vulnerability.getProblematicLine())));
return ignoreEntry;
});
ifExistingIssue(detail, entry);
Expand All @@ -404,7 +404,7 @@ public IgnoreEntry convertToIgnoredEntry(ScanIssue detail, String clickId) {
String vulnerabilityKey = createJsonKeyForIgnoreEntry(detail, clickId);
int line = detail.getLocations().get(0).getLine();
IgnoreEntry entry = ignoreFileManager.getIgnoreData().computeIfAbsent(vulnerabilityKey, k -> {
IgnoreEntry.FileReference fileRef = new IgnoreEntry.FileReference(relativePath, true, line);
IgnoreEntry.FileReference fileRef = new IgnoreEntry.FileReference(relativePath, true, line, "");
ArrayList<IgnoreEntry.FileReference> fileReference = new ArrayList<>();
fileReference.add(fileRef);
IgnoreEntry ignoreEntry = new IgnoreEntry();
Expand Down Expand Up @@ -444,6 +444,15 @@ public IgnoreEntry convertToIgnoredEntry(ScanIssue detail, String clickId) {
private void ifExistingIssue(ScanIssue detail, IgnoreEntry entry) {
String relativePath = ignoreFileManager.normalizePath(detail.getFilePath());
int line = detail.getLocations().get(0).getLine();
String problematicLine = null;
if (detail.getScanEngine() == ScanEngine.ASCA && detail.getVulnerabilities() != null && !detail.getVulnerabilities().isEmpty()) {
// Find the matching vulnerability for this line
for (Vulnerability v : detail.getVulnerabilities()) {
// You may want to match by line or other criteria if needed
problematicLine = v.getProblematicLine();
break; // Use the first for now, or improve as needed
}
}
// Ensure files list is mutable
if (!(entry.getFiles() instanceof ArrayList)) {
entry.setFiles(new ArrayList<>(entry.getFiles()));
Expand All @@ -453,8 +462,13 @@ private void ifExistingIssue(ScanIssue detail, IgnoreEntry entry) {
.findFirst();
if (existing.isPresent()) {
existing.get().setActive(true);
// Update problematicLine if ASCA and not set or different
if (detail.getScanEngine() == ScanEngine.ASCA && problematicLine != null &&
(existing.get().getProblematicLine() == null || !existing.get().getProblematicLine().equals(problematicLine))) {
existing.get().setProblematicLine(problematicLine);
}
} else {
entry.getFiles().add(new IgnoreEntry.FileReference(relativePath, true, line));
entry.getFiles().add(new IgnoreEntry.FileReference(relativePath, true, line, problematicLine));
}
}

Expand Down Expand Up @@ -721,4 +735,165 @@ private List<String> createIgnoreKeysForScanIssue(ScanIssue scanIssue) {
}
return keys;
}

/**
* Updates line numbers for ignored ASCA entries based on new scan results, using problematicLine content for matching.
* For each ignored file reference, if a scan result with the same problematicLine is found, update the line number in the ignore entry.
* Removes file references and ignore entries that are no longer present in the scan result.
*
* @param fullScanResults The scan results containing updated line numbers and issues
* @param filePath The path of the file that was scanned and needs line number updates
*/
public void updateLineNumbersForIgnoredEntriesByProblematicLine(ScanResult<?> fullScanResults, String filePath) {
List<ScanIssue> allIssuesForFile = fullScanResults.getIssues();
if (allIssuesForFile == null || allIssuesForFile.isEmpty()) {
LOGGER.debug(String.format("ASCA-Ignore: No issues found in scan results for file: %s", filePath));
return;
}
String relativePath = ignoreFileManager.normalizePath(filePath);
boolean hasChanges = false;
// Build a list of all vulnerabilities with their problematicLine and line number
List<VulnerabilityWithLine> vulnerabilitiesWithLine = new ArrayList<>();
Set<String> presentProblematicLines = new HashSet<>();
for (ScanIssue scanIssue : allIssuesForFile) {
if (scanIssue.getVulnerabilities() != null) {
for (Vulnerability v : scanIssue.getVulnerabilities()) {
int line = (scanIssue.getLocations() != null && !scanIssue.getLocations().isEmpty())
? scanIssue.getLocations().get(0).getLine() : 0;
vulnerabilitiesWithLine.add(new VulnerabilityWithLine(v.getProblematicLine(), line));
if (v.getProblematicLine() != null) {
presentProblematicLines.add(v.getProblematicLine());
}
}
}
}
// Collect keys to remove
List<String> keysToRemove = new ArrayList<>();
// Iterate through all ignore entries using normal for-each
for (Map.Entry<String, IgnoreEntry> mapEntry : ignoreFileManager.getIgnoreData().entrySet()) {
IgnoreEntry ignoreEntry = mapEntry.getValue();
if (!ScanEngine.ASCA.toString().equalsIgnoreCase(String.valueOf(ignoreEntry.getType()))) {
continue; // Only process ASCA entries
}
// Remove file references that are not present in the scan result
List<IgnoreEntry.FileReference> fileRefs = ignoreEntry.getFiles();
List<IgnoreEntry.FileReference> fileRefsToRemove = new ArrayList<>();
for (IgnoreEntry.FileReference fileRef : fileRefs) {
if (fileRef.getPath().equals(relativePath) && fileRef.isActive()) {
String ignoredProblematicLine = fileRef.getProblematicLine();
// Find a matching vulnerability by problematicLine (null-safe)
VulnerabilityWithLine match = vulnerabilitiesWithLine.stream()
.filter(vwl -> Objects.equals(vwl.problematicLine, ignoredProblematicLine))
.findFirst().orElse(null);
if (match != null && match.line > 0 && fileRef.getLine() != match.line) {
fileRef.setLine(match.line);
hasChanges = true;
}
// If problematicLine is not present in the scan result, mark this file reference for removal
if (ignoredProblematicLine == null || !presentProblematicLines.contains(ignoredProblematicLine)) {
fileRefsToRemove.add(fileRef);
hasChanges = true;
}
}
}
// Remove marked file references
if (!fileRefsToRemove.isEmpty()) {
fileRefs.removeAll(fileRefsToRemove);
}
// If no file references left, mark the key for removal
if (ignoreEntry.getFiles().isEmpty()) {
keysToRemove.add(mapEntry.getKey());
}
}
// Remove keys from ignore data
for (String keyToRemove : keysToRemove) {
ignoreFileManager.getIgnoreData().remove(keyToRemove);
hasChanges = true;
}
if (hasChanges) {
ignoreFileManager.saveIgnoreDataToDisk();
LOGGER.info(String.format("ASCA-Ignore: Line numbers and obsolete entries updated by problematicLine and saved for file: %s", relativePath));
} else {
LOGGER.debug(String.format("ASCA-Ignore: No line number or entry changes detected by problematicLine for file: %s", relativePath));
}
}

// Helper class for matching problematicLine and line together
private static class VulnerabilityWithLine {
String problematicLine;
int line;
VulnerabilityWithLine(String problematicLine, int line) {
this.problematicLine = problematicLine;
this.line = line;
}
}

/**
* Removes all ASCA ignore entries and file references for a file when there are no issues in the scan result.
*
* @param filePath The path of the file for which ignore entries should be removed
* @return true if any ignore entries were removed, false otherwise
*/
public void removeIgnoreEntriesForFileIfEmpty(String filePath) {
String relativePath = ignoreFileManager.normalizePath(filePath);
List<String> keysToRemove = new ArrayList<>();
boolean removed = false;
for (Map.Entry<String, IgnoreEntry> mapEntry : ignoreFileManager.getIgnoreData().entrySet()) {
IgnoreEntry ignoreEntry = mapEntry.getValue();
if (!ScanEngine.ASCA.toString().equalsIgnoreCase(String.valueOf(ignoreEntry.getType()))) {
continue;
}
// Remove file references for this file
List<IgnoreEntry.FileReference> fileRefs = ignoreEntry.getFiles();
fileRefs.removeIf(fileRef -> fileRef.getPath().equals(relativePath));
// If no file references left, mark the key for removal
if (ignoreEntry.getFiles().isEmpty()) {
keysToRemove.add(mapEntry.getKey());
}
}
// Remove keys from ignore data
for (String keyToRemove : keysToRemove) {
ignoreFileManager.getIgnoreData().remove(keyToRemove);
removed = true;
}
if (removed) {
ignoreFileManager.saveIgnoreDataToDisk();
LOGGER.info(String.format("ASCA-Ignore: Removed ignore entries for file with no issues: %s", relativePath));
}
}

public boolean isIgnored(ScanIssue issue, List<IgnoreEntry> ignoreEntries, String filePath) {
String normalizedPath = ignoreFileManager.normalizePath(filePath);
boolean isAsca = issue.getScanEngine() == ScanEngine.ASCA;
// For ASCA, check problematicLine for all vulnerabilities
if (isAsca && issue.getVulnerabilities() != null && !issue.getVulnerabilities().isEmpty()) {
for (Vulnerability vuln : issue.getVulnerabilities()) {
String issueProblematicLine = vuln.getProblematicLine();
for (IgnoreEntry entry : ignoreEntries) {
for (IgnoreEntry.FileReference ref : entry.getFiles()) {
boolean pathMatch = ref.isActive() && ref.getPath().equals(normalizedPath);
boolean problematicLineMatch = (issueProblematicLine == null && ref.getProblematicLine() == null)
|| (issueProblematicLine != null && issueProblematicLine.equals(ref.getProblematicLine()));
if (pathMatch && problematicLineMatch) {
return true;
}
}
}
}
return false;
}
// Default: match by path and line
int issueLine = issue.getLocations() != null && !issue.getLocations().isEmpty()
? issue.getLocations().get(0).getLine()
: -1;
for (IgnoreEntry entry : ignoreEntries) {
for (IgnoreEntry.FileReference ref : entry.getFiles()) {
if (ref.isActive() && ref.getPath().equals(normalizedPath) && ref.getLine() == issueLine) {
return true;
}
}
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ public Vulnerability() {
private String expectedValue;
private String title;
private String SimilarityId;
private String problematicLine;

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class AscaScanResultAdaptor implements com.checkmarx.intellij.devassist.c
private final String filePath;
private final List<ScanIssue> scanIssues;


/**
* Constructs an instance of {@code AscaScanResultAdaptor} with the specified ASCA scan results.
* This adapter allows conversion and processing of ASCA scan results into a standardized format.
Expand Down Expand Up @@ -80,7 +81,6 @@ private List<ScanIssue> buildIssues() {
if (scanDetails.isEmpty()) {
return Collections.emptyList();
}

// Group scan details by line number, then sort by severity precedence
Map<Integer, List<ScanDetail>> groupedIssues = scanDetails.stream()
.filter(Objects::nonNull)
Expand Down Expand Up @@ -192,6 +192,7 @@ private Vulnerability createVulnerability(ScanDetail scanDetail, String override
vulnerability.setSeverity(mapSeverity(scanDetail.getSeverity()));
vulnerability.setRemediationAdvise(scanDetail.getRemediationAdvise());
vulnerability.setTitle(scanDetail.getRuleName());
vulnerability.setProblematicLine(scanDetail.getProblematicLine());

return vulnerability;
}
Expand Down Expand Up @@ -225,4 +226,7 @@ private String getUniqueId(ScanDetail scanIssue) {
}
return ScanEngine.ASCA.name();
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.checkmarx.intellij.common.wrapper.CxWrapperFactory;
import com.checkmarx.intellij.devassist.basescanner.BaseScannerService;
import com.checkmarx.intellij.devassist.configuration.ScannerConfig;
import com.checkmarx.intellij.devassist.ignore.IgnoreEntry;
import com.checkmarx.intellij.devassist.ignore.IgnoreFileManager;
import com.checkmarx.intellij.devassist.ignore.IgnoreManager;
import com.checkmarx.intellij.devassist.telemetry.TelemetryService;
import com.checkmarx.intellij.devassist.utils.DevAssistConstants;
Expand All @@ -28,6 +30,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
* Realtime ASCA scanner service that integrates with the realtime scanner system.
Expand All @@ -36,6 +40,7 @@
public class AscaScannerService extends BaseScannerService<ScanResult> {
private static final Logger LOGGER = Utils.getLogger(AscaScannerService.class);
private static final String ASCA_DIR = "CxASCA";
private static final Object SCAN_LOCK = new Object();

/**
* Creates an ASCA scanner service with the default ASCA realtime configuration.
Expand Down Expand Up @@ -145,8 +150,26 @@ public com.checkmarx.intellij.devassist.common.ScanResult<ScanResult> scan(@NotN
LOGGER.debug("ASCA scanner: scan completed - " + uri + " (" + issueCount + " issues found)");

AscaScanResultAdaptor scanResultAdaptor = new AscaScanResultAdaptor(ascaResult, uri);
TelemetryService.logScanResults(scanResultAdaptor, ScanEngine.ASCA);
return scanResultAdaptor;

// Filter out ignored issues based on problematicLine
IgnoreManager ignoreManager = new IgnoreManager(psiFile.getProject());
IgnoreFileManager ignoreFileManager = IgnoreFileManager.getInstance(psiFile.getProject());
List<IgnoreEntry> ignoreEntries = ignoreFileManager.getAllIgnoreEntries();
List<com.checkmarx.intellij.devassist.model.ScanIssue> filteredIssues = new ArrayList<>();
for (com.checkmarx.intellij.devassist.model.ScanIssue issue : scanResultAdaptor.getIssues()) {
if (!ignoreManager.isIgnored(issue, ignoreEntries, uri)) {
filteredIssues.add(issue);
}
}
// Return a new adaptor with only non-ignored issues
AscaScanResultAdaptor filteredAdaptor = new AscaScanResultAdaptor(ascaResult, uri) {
@Override
public List<com.checkmarx.intellij.devassist.model.ScanIssue> getIssues() {
return filteredIssues;
}
};
TelemetryService.logScanResults(filteredAdaptor, ScanEngine.ASCA);
return filteredAdaptor;

} catch (Exception e) {
LOGGER.warn("ASCA scanner: scan error for file: " + uri, e);
Expand Down Expand Up @@ -181,12 +204,12 @@ private ScanResult runAscaScan(PsiFile file, Project project, boolean ascLatestV
return null;
}

synchronized (SCAN_LOCK) {
String tempFilePath = saveTempFile(file.getName(), fileContent);
if (tempFilePath == null) {
LOGGER.warn("Failed to create temporary file for ASCA scan.");
return null;
}

try {
LOGGER.info(Strings.join("Starting ASCA scan on file: ", virtualFile.getPath()));
ScanResult scanResult = scanAscaFile(tempFilePath, ascLatestVersion, agent, DevAssistUtils.getIgnoreFilePath(project));
Expand All @@ -200,6 +223,7 @@ private ScanResult runAscaScan(PsiFile file, Project project, boolean ascLatestV
} finally {
deleteFile(tempFilePath);
}
}
}

/**
Expand Down Expand Up @@ -441,7 +465,9 @@ private void updateIgnoredFileDataOnLatestResult(String tempFilePath, Project pr
ScanResult fullScanResult = scanAscaFile(tempFilePath, ascLatestVersion, agent, "");
if (fullScanResult.getScanDetails() != null && !fullScanResult.getScanDetails().isEmpty()) {
AscaScanResultAdaptor fullScanResultAdaptor = new AscaScanResultAdaptor(fullScanResult, filePath);
ignoreManager.updateLineNumbersForIgnoredEntries(fullScanResultAdaptor, filePath);
ignoreManager.updateLineNumbersForIgnoredEntriesByProblematicLine(fullScanResultAdaptor, filePath);
}else{
ignoreManager.removeIgnoreEntriesForFileIfEmpty(filePath);
}
}
} catch (Exception e) {
Expand Down
Loading