From cd9c60d87f16af9e30a0a93e329e4f0ba555c377 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 11:44:47 -0400 Subject: [PATCH] updates --- .../contentviewers/Bundle.properties-MERGED | 3 - .../AnalysisResultsContentViewer.java | 140 +++++++++++++----- 2 files changed, 103 insertions(+), 40 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 4714416d46..781b350e23 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -15,9 +15,6 @@ # governing permissions and limitations under the License. # -AnnotationsContentViewer.onEmpty=No annotations were found for this particular item. -AnnotationsContentViewer.title=Annotations -AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. ApplicationContentViewer.title=Application ApplicationContentViewer.toolTip=Displays file contents. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index 62fb5b49ea..d7d1f43423 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -32,6 +32,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.swing.JLabel; +import javax.swing.SwingWorker; import javax.swing.text.html.HTMLEditorKit; import org.apache.commons.lang3.tuple.Pair; import org.jsoup.Jsoup; @@ -44,8 +45,12 @@ import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.TskCoreException; @@ -56,12 +61,12 @@ import org.sleuthkit.datamodel.TskCoreException; public class AnalysisResultsContentViewer extends javax.swing.JPanel implements DataContentViewer { private static Logger logger = Logger.getLogger(AnalysisResultsContentViewer.class.getName()); - + /** * isPreferred value. */ private static final int PREFERRED_VALUE = 6; - + private static final String EMPTY_HTML = ""; private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily(); @@ -71,25 +76,26 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements // html stylesheet classnames for components private static final String SPACED_SECTION_CLASSNAME = "spacedSection"; private static final String HEADER_CLASSNAME = "header"; + public static final String MESSAGE_CLASSNAME = "message"; + private static final String RESULT_ANCHOR_PREFIX = "AnalysisResult_"; // how big the header should be private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE + 2; - // spacing occurring after an item private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2; private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; // additional styling for components - private static final String STYLE_SHEET_RULE = - String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", + private static final String STYLE_SHEET_RULE + = String.format(" .%s { font-size: %dpx;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE) + + String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) + String.format(" td { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ", DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) + String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); - private static String normalizeAttr(String originalAttrStr) { return (originalAttrStr == null) ? "" : originalAttrStr.trim(); } @@ -174,37 +180,51 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements private final Collection analysisResults; private final Optional selectedResult; + private final Optional aggregateScore; - NodeAnalysisResults(Collection analysisResults, Optional selectedResult) { + public NodeAnalysisResults(Collection analysisResults, Optional selectedResult, Optional aggregateScore) { this.analysisResults = analysisResults; this.selectedResult = selectedResult; + this.aggregateScore = aggregateScore; } - Collection getAnalysisResults() { + public Collection getAnalysisResults() { return analysisResults; } - Optional getSelectedResult() { + public Optional getSelectedResult() { return selectedResult; } + + public Optional getAggregateScore() { + return aggregateScore; + } } private static NodeAnalysisResults getAnalysisResults(Node node) { if (node == null) { - return new NodeAnalysisResults(Collections.emptyList(), Optional.empty()); + return new NodeAnalysisResults(Collections.emptyList(), Optional.empty(), Optional.empty()); } + Optional aggregateScore = Optional.empty(); Map allAnalysisResults = new HashMap<>(); Optional selectedResult = Optional.empty(); - AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); - if (abstractFile != null) { + for (Content content : node.getLookup().lookupAll(Content.class)) { + if (content == null || content instanceof BlackboardArtifact) { + continue; + } + try { - abstractFile.getAllAnalysisResults().stream() + aggregateScore = Optional.ofNullable(content.getAggregateScore()); + + content.getAllAnalysisResults().stream() .filter(ar -> ar != null) .forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); + + break; } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + abstractFile.getId(), ex); + logger.log(Level.SEVERE, "Unable to get analysis results for content with obj id " + content.getId(), ex); } } @@ -219,9 +239,13 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements selectedResult = filteredResults.stream() .max((a, b) -> a.getScore().compareTo(b.getScore())); + + if (!aggregateScore.isPresent()) { + aggregateScore = selectedResult.flatMap(selectedRes -> Optional.ofNullable(selectedRes.getScore())); + } } - return new NodeAnalysisResults(allAnalysisResults.values(), selectedResult); + return new NodeAnalysisResults(getScoreOrderedResults(allAnalysisResults.values()), selectedResult, aggregateScore); } private static Document render(List displayAttributes, Optional aggregateScore) { @@ -242,16 +266,24 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements @Messages("AnalysisResultsContentViewer_appendAggregateScore_displayKey=Aggregate Score") private static void appendAggregateScore(Element body, Score score) { - appendSection(body, MessageFormat.format("{0}: {1}", - Bundle.AnalysisResultsContentViewer_appendAggregateScore_displayKey(), - score.getSignificance().getDisplayName())); + appendSection(body, + MessageFormat.format("{0}: {1}", + Bundle.AnalysisResultsContentViewer_appendAggregateScore_displayKey(), + score.getSignificance().getDisplayName()), + null); + } + + private static String getAnchor(AnalysisResult analysisResult) { + return RESULT_ANCHOR_PREFIX + analysisResult.getId(); } @Messages({"# {0} - analysisResultsNumber", "AnalysisResultsContentViewer_appendResult_headerKey=Analysis Result {0}" }) private static void appendResult(Element parent, int index, ResultDisplayAttributes attrs) { - Element sectionDiv = appendSection(parent, Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1)); + Element sectionDiv = appendSection(parent, + Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1), + Optional.ofNullable(getAnchor(attrs.getAnalysisResult()))); Element table = sectionDiv.appendElement("table"); Element tableBody = table.appendElement("tbody"); @@ -269,11 +301,17 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements * * @param parent The element to append this section to. * @param headerText The text for the section. + * @param anchorId The anchor id for this section. * * @return The div for the new section. */ - private static Element appendSection(Element parent, String headerText) { + private static Element appendSection(Element parent, String headerText, Optional anchorId) { Element sectionDiv = parent.appendElement("div"); + if (anchorId.isPresent()) { + Element anchorEl = sectionDiv.appendElement("a"); + anchorEl.attr("name", anchorId.get()); + } + sectionDiv.attr("class", SPACED_SECTION_CLASSNAME); Element header = sectionDiv.appendElement("h1"); header.text(headerText); @@ -281,13 +319,14 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements return sectionDiv; } + private SwingWorker worker = null; /** * Creates new form AnalysisResultsContentViewer */ public AnalysisResultsContentViewer() { initComponents(); - + textPanel.setContentType("text/html;charset=UTF-8"); //NON-NLS HTMLEditorKit kit = new HTMLEditorKit(); textPanel.setEditorKit(kit); @@ -326,25 +365,52 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements } @Override - public void setNode(Node selectedNode) { - // GVDTODO comment, put in swing worker, scroll to location - NodeAnalysisResults nodeResults = getAnalysisResults(selectedNode); - List orderedAnalysisResults = getScoreOrderedResults(nodeResults.getAnalysisResults()); - List displayAttributes = getDisplayAttributes(orderedAnalysisResults); + @Messages({ + "AnalysisResultsContentViewer_setNode_loadingMessage=Loading...", + "AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results.",}) + public synchronized void setNode(Node node) { + resetComponent(); - Optional aggregateScore = displayAttributes.stream() - .findFirst() - .flatMap(dispAttrs -> Optional.ofNullable(dispAttrs.getAnalysisResult().getScore())); - - Document document = render(displayAttributes, aggregateScore); - - Optional selectedResult = nodeResults.getSelectedResult(); - if (selectedResult.isPresent()) { - // GVDTODO + if (worker != null) { + worker.cancel(true); + worker = null; } - + + if (node == null) { + return; + } + + showMessage(Bundle.AnalysisResultsContentViewer_setNode_loadingMessage()); + + worker = new DataFetchWorker( + (selectedNode) -> getAnalysisResults(selectedNode), + (nodeAnalysisResults) -> { + if (nodeAnalysisResults.getResultType() == ResultType.SUCCESS) { + displayResults(nodeAnalysisResults.getData()); + } else { + showMessage(Bundle.AnalysisResultsContentViewer_setNode_errorMessage()); + } + }, + node); + + worker.execute(); + } + + private void showMessage(String message) { + textPanel.setText("" + + MessageFormat.format("

{1}

", MESSAGE_CLASSNAME, message) + + ""); + } + + private void displayResults(NodeAnalysisResults nodeResults) { + List displayAttributes = getDisplayAttributes(nodeResults.getAnalysisResults()); + Document document = render(displayAttributes, nodeResults.getAggregateScore()); + Optional selectedResult = nodeResults.getSelectedResult(); textPanel.setText(document.html()); - textPanel.setCaretPosition(0); + + if (selectedResult.isPresent()) { + textPanel.scrollToReference(getAnchor(selectedResult.get())); + } } @Override