235 lines
9.8 KiB
Java
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;
|
|
}
|
|
}
|