package it.unitn.molerat.repos.trackers.vuln; import it.unitn.molerat.evidence.Changes; import it.unitn.molerat.repos.trackers.AbstractEvidenceTracker; import it.unitn.molerat.repos.utils.SignatureExtractor; import it.unitn.molerat.evidence.VulnerabilityEvidence; import it.unitn.molerat.repos.wrappers.RepoWrapper; import java.util.*; public abstract class VulnerabilityEvidenceTracker extends AbstractEvidenceTracker { protected Map> evidences = new LinkedHashMap>(); public VulnerabilityEvidenceTracker(RepoWrapper wrapper, String fixedRev) throws Exception { super(wrapper, fixedRev); } @Override public void trackEvidence() throws Exception { // I. Filter all non-Java files that have been changed this.changes = filterNonJavaChanges(this.changes); // ------------------------------------------------- // II. Get the initial molerat.evidence from the fix Iterator it = changes.iterator(); while (it.hasNext()) { Changes currentChanges = it.next(); Set tempVulnEvidences = getInitialVulnerabilityEvidence(currentChanges); for (VulnerabilityEvidence evidence : tempVulnEvidences) { addEvidence(evidence); } commits.add(currentChanges.getLeftRevision()); } // ------------------------------------------------- // III. Collect evidences across the rest of commits String rightCommit = this.vulnRevision; Iterator commitIterator = restOfCommits.iterator(); String commit = ""; while (commitIterator.hasNext()) { commit = commitIterator.next(); // Get the current changes String leftCommit = commit; String diffTxt = repoWrapper.doDiff(leftCommit, rightCommit); Set currentChanges = repoWrapper.inferChangesFromDiff(diffTxt, leftCommit, rightCommit); Set newEvidences = new HashSet<>(); newEvidences = getVulnerabilityEvidence(leftCommit, rightCommit, currentChanges); // did the method/file/molerat.evidence disappear? if (newEvidences.size() == 0) { break; } else { for (VulnerabilityEvidence evidence : newEvidences) { addEvidence(evidence); } rightCommit = leftCommit; commits.add(commit); } } if (commit.equals("")) { return; } } public Set getEvidences(String commit) { Set evd = this.evidences.get(commit); return (evd != null) ? evd : new HashSet<>(); } public Map countEvidenceInFiles(String commit) { Map counts = new TreeMap<>(); Set evd = getEvidences(commit); for (VulnerabilityEvidence e : evd) { String filename = e.getPath(); if (!counts.containsKey(filename)) { if (e.getLineNumber() != 0) { counts.put(filename, 1); } else { counts.put(filename, 0); } } else { if (e.getLineNumber() == 0) { counts.replace(filename, 0); } else { int locCount = counts.get(filename); locCount++; counts.replace(filename, locCount); } } } return counts; } public Map> getEvidences() { return this.evidences; } protected void addEvidence(VulnerabilityEvidence evidence) { if (this.evidences.containsKey(evidence.getCommit())) { evidences.get(evidence.getCommit()).add(evidence); } else { Set set = new HashSet<>(); set.add(evidence); this.evidences.put(evidence.getCommit(), set); } } protected abstract Set getInitialVulnerabilityEvidence(Changes changes) throws Exception; protected abstract Set getVulnerabilityEvidence(String currentEvidenceCommit, String previousEvidenceCommit, Set changes) throws Exception; protected Set recordVulnerabilityEvidence(Map lines, Changes changes) throws Exception { Set evidences = new HashSet<>(); String fileContents = this.repoWrapper.doCat(changes.getPath(), changes.getLeftRevision()); SignatureExtractor se = new SignatureExtractor(fileContents); Map> containers = se.getSignaturesWithLines(); for (Map.Entry> containerEntry : containers.entrySet()) { for (Map.Entry lineEntry : lines.entrySet()) { if (containerEntry.getValue().contains(lineEntry.getKey())) { VulnerabilityEvidence evidence = new VulnerabilityEvidence( changes.getPath(), changes.getLeftRevision(), containerEntry.getKey(), lineEntry.getKey(), lineEntry.getValue() ); evidences.add(evidence); } } } return evidences; } protected Set recordVulnerabilityEvidenceForRightFile(Map lines, Changes changes) throws Exception { Set evidences = new HashSet<>(); String fileContents = this.repoWrapper.doCat(changes.getPath(), changes.getRightRevision()); SignatureExtractor se = new SignatureExtractor(fileContents); Map> containers = se.getSignaturesWithLines(); for (Map.Entry> containerEntry : containers.entrySet()) { for (Map.Entry lineEntry : lines.entrySet()) { if (containerEntry.getValue().contains(lineEntry.getKey())) { VulnerabilityEvidence evidence = new VulnerabilityEvidence( changes.getPath(), changes.getRightRevision(), containerEntry.getKey(), lineEntry.getKey(), lineEntry.getValue() ); evidences.add(evidence); } } } return evidences; } protected Map updateChangedLines(String path, String leftRev, String rightRev, Set retain, Changes change) throws Exception { Map linesToKeep = new TreeMap<>(); String leftFile = this.repoWrapper.doCat(path, leftRev); String rightFile = this.repoWrapper.doCat(path, rightRev); SignatureExtractor rightSignatures = new SignatureExtractor(rightFile); Map> rightLineSignatures = rightSignatures.getSignaturesWithLines(); Map rightLineMappings = this.repoWrapper.getLineMappings(rightFile); SignatureExtractor leftSignatures = new SignatureExtractor(leftFile); Map> leftLineSignatures = leftSignatures.getSignaturesWithLines(); Map leftLineMappings = this.repoWrapper.getLineMappings(leftFile); for (VulnerabilityEvidence evd : retain) { String container = evd.getContainer(); Set lineNumbers = leftLineSignatures.get(container); Map leftChunk = new TreeMap<>(); // the left container might not exist anymore... if (lineNumbers == null) { continue; } // otherwise, process for (int lineNumber : lineNumbers) { leftChunk.put(lineNumber, leftLineMappings.get(lineNumber)); } lineNumbers = rightLineSignatures.get(container); Map rightChunk = new TreeMap<>(); for (int lineNumber : lineNumbers) { rightChunk.put(lineNumber, rightLineMappings.get(lineNumber)); } String leftLine = leftChunk.get(evd.getLineNumber()); String rightLine = rightChunk.get(evd.getLineNumber()); if (leftLine != null && rightLine != null && leftLine.equals(rightLine)) { linesToKeep.put(evd.getLineNumber(), evd.getLineContents()); continue; } else { if (rightLine != null && rightLine.equals(evd.getLineContents())) { Map eligibleCandidates = new TreeMap<>(); for (Map.Entry candidateLine : leftChunk.entrySet()) { if (candidateLine.getValue().equals(evd.getLineContents())) { eligibleCandidates.put(candidateLine.getKey(), evd.getLineContents()); continue; } } if (eligibleCandidates.size() > 1) { Iterator> it = eligibleCandidates.entrySet().iterator(); int leftLineNumber = it.next().getKey(); int rightLineNumber = evd.getLineNumber(); int minimumDistance = rightLineNumber - leftLineNumber; while (it.hasNext()) { leftLineNumber = it.next().getKey(); int tempMinimumDistance = rightLineNumber - leftLineNumber; minimumDistance = (Math.abs(tempMinimumDistance) < Math.abs(minimumDistance)) ? tempMinimumDistance : minimumDistance; } int newLineNumber = rightLineNumber - minimumDistance; String newLineContents = eligibleCandidates.get(newLineNumber); linesToKeep.put(newLineNumber, newLineContents); } else { linesToKeep.putAll(eligibleCandidates); } } } } return linesToKeep; } }