foss-vuln-tracker/molerat/src/main/java/it/unitn/molerat/repos/trackers/vuln/VulnerabilityEvidenceTracke...

235 lines
9.8 KiB
Java

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<String, Set<VulnerabilityEvidence>> evidences = new LinkedHashMap<String, Set<VulnerabilityEvidence>>();
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<Changes> it = changes.iterator();
while (it.hasNext()) {
Changes currentChanges = it.next();
Set<VulnerabilityEvidence> 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<String> 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<Changes> currentChanges = repoWrapper.inferChangesFromDiff(diffTxt, leftCommit, rightCommit);
Set<VulnerabilityEvidence> 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<VulnerabilityEvidence> getEvidences(String commit) {
Set<VulnerabilityEvidence> evd = this.evidences.get(commit);
return (evd != null) ? evd : new HashSet<>();
}
public Map<String, Integer> countEvidenceInFiles(String commit) {
Map<String, Integer> counts = new TreeMap<>();
Set<VulnerabilityEvidence> 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<String, Set<VulnerabilityEvidence>> getEvidences() {
return this.evidences;
}
protected void addEvidence(VulnerabilityEvidence evidence) {
if (this.evidences.containsKey(evidence.getCommit())) {
evidences.get(evidence.getCommit()).add(evidence);
}
else {
Set<VulnerabilityEvidence> set = new HashSet<>();
set.add(evidence);
this.evidences.put(evidence.getCommit(), set);
}
}
protected abstract Set<VulnerabilityEvidence> getInitialVulnerabilityEvidence(Changes changes) throws Exception;
protected abstract Set<VulnerabilityEvidence> getVulnerabilityEvidence(String currentEvidenceCommit, String previousEvidenceCommit, Set<Changes> changes) throws Exception;
protected Set<VulnerabilityEvidence> recordVulnerabilityEvidence(Map<Integer, String> lines, Changes changes) throws Exception {
Set<VulnerabilityEvidence> evidences = new HashSet<>();
String fileContents = this.repoWrapper.doCat(changes.getPath(), changes.getLeftRevision());
SignatureExtractor se = new SignatureExtractor(fileContents);
Map<String, Set<Integer>> containers = se.getSignaturesWithLines();
for (Map.Entry<String, Set<Integer>> containerEntry : containers.entrySet()) {
for (Map.Entry<Integer, String> 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<VulnerabilityEvidence> recordVulnerabilityEvidenceForRightFile(Map<Integer, String> lines, Changes changes) throws Exception {
Set<VulnerabilityEvidence> evidences = new HashSet<>();
String fileContents = this.repoWrapper.doCat(changes.getPath(), changes.getRightRevision());
SignatureExtractor se = new SignatureExtractor(fileContents);
Map<String, Set<Integer>> containers = se.getSignaturesWithLines();
for (Map.Entry<String, Set<Integer>> containerEntry : containers.entrySet()) {
for (Map.Entry<Integer, String> 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<Integer, String> updateChangedLines(String path, String leftRev, String rightRev, Set<VulnerabilityEvidence> retain, Changes change) throws Exception {
Map<Integer, String> linesToKeep = new TreeMap<>();
String leftFile = this.repoWrapper.doCat(path, leftRev);
String rightFile = this.repoWrapper.doCat(path, rightRev);
SignatureExtractor rightSignatures = new SignatureExtractor(rightFile);
Map<String, Set<Integer>> rightLineSignatures = rightSignatures.getSignaturesWithLines();
Map<Integer, String> rightLineMappings = this.repoWrapper.getLineMappings(rightFile);
SignatureExtractor leftSignatures = new SignatureExtractor(leftFile);
Map<String, Set<Integer>> leftLineSignatures = leftSignatures.getSignaturesWithLines();
Map<Integer, String> leftLineMappings = this.repoWrapper.getLineMappings(leftFile);
for (VulnerabilityEvidence evd : retain) {
String container = evd.getContainer();
Set<Integer> lineNumbers = leftLineSignatures.get(container);
Map<Integer, String> 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<Integer, String> 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<Integer, String> eligibleCandidates = new TreeMap<>();
for (Map.Entry<Integer, String> candidateLine : leftChunk.entrySet()) {
if (candidateLine.getValue().equals(evd.getLineContents())) {
eligibleCandidates.put(candidateLine.getKey(), evd.getLineContents());
continue;
}
}
if (eligibleCandidates.size() > 1) {
Iterator<Map.Entry<Integer, String>> 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;
}
}