19package org.sleuthkit.autopsy.contentviewers.annotations;
21import java.util.ArrayList;
22import java.util.Arrays;
24import java.util.function.Function;
25import java.util.logging.Level;
26import java.util.stream.Collectors;
27import org.apache.commons.lang.StringUtils;
28import org.apache.commons.lang3.tuple.Pair;
29import org.jsoup.Jsoup;
30import org.jsoup.nodes.Document;
31import org.jsoup.nodes.Element;
32import org.openide.nodes.Node;
33import org.openide.util.NbBundle;
34import org.sleuthkit.autopsy.casemodule.Case;
35import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
36import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
37import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
38import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
39import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
40import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
41import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
42import org.sleuthkit.autopsy.coreutils.Logger;
43import org.sleuthkit.autopsy.datamodel.BlackboardArtifactItem;
44import org.sleuthkit.datamodel.AbstractFile;
45import org.sleuthkit.datamodel.AnalysisResult;
46import org.sleuthkit.datamodel.BlackboardArtifact;
47import org.sleuthkit.datamodel.BlackboardArtifactTag;
48import org.sleuthkit.datamodel.BlackboardAttribute;
49import org.sleuthkit.datamodel.Content;
50import org.sleuthkit.datamodel.ContentTag;
51import org.sleuthkit.datamodel.DataArtifact;
52import org.sleuthkit.datamodel.SleuthkitCase;
53import org.sleuthkit.datamodel.Tag;
54import org.sleuthkit.datamodel.TskCoreException;
62 "AnnotationUtils.title=Annotations",
63 "AnnotationUtils.toolTip=Displays tags and comments associated with the selected content.",
64 "AnnotationUtils.centralRepositoryEntry.title=Central Repository Comments",
65 "AnnotationUtils.centralRepositoryEntryDataLabel.case=Case:",
66 "AnnotationUtils.centralRepositoryEntryDataLabel.type=Type:",
67 "AnnotationUtils.centralRepositoryEntryDataLabel.comment=Comment:",
68 "AnnotationUtils.centralRepositoryEntryDataLabel.path=Path:",
69 "AnnotationUtils.tagEntry.title=Tags",
70 "AnnotationUtils.tagEntryDataLabel.tag=Tag:",
71 "AnnotationUtils.tagEntryDataLabel.tagUser=Examiner:",
72 "AnnotationUtils.tagEntryDataLabel.comment=Comment:",
73 "AnnotationUtils.fileHitEntry.artifactCommentTitle=Artifact Comment",
74 "AnnotationUtils.fileHitEntry.hashSetHitTitle=Hash Set Hit Comments",
75 "AnnotationUtils.fileHitEntry.interestingFileHitTitle=Interesting File Hit Comments",
76 "AnnotationUtils.fileHitEntry.interestingItemTitle=Interesting Item Comments",
77 "AnnotationUtils.fileHitEntry.setName=Set Name:",
78 "AnnotationUtils.fileHitEntry.comment=Comment:",
79 "AnnotationUtils.sourceFile.title=Source File",
80 "AnnotationUtils.onEmpty=No annotations were found for this particular item."
85 private static final String
EMPTY_HTML =
"<html><head></head><body></body></html>";
88 private static final List<ItemEntry<Tag>>
TAG_ENTRIES = Arrays.asList(
89 new ItemEntry<>(Bundle.AnnotationUtils_tagEntryDataLabel_tag(),
90 (tag) -> (tag.getName() !=
null) ? tag.getName().getDisplayName() :
null),
91 new ItemEntry<>(Bundle.AnnotationUtils_tagEntryDataLabel_tagUser(), (tag) -> tag.getUserName()),
92 new ItemEntry<>(Bundle.AnnotationUtils_tagEntryDataLabel_comment(), (tag) -> tag.getComment())
96 =
new SectionConfig<>(Bundle.AnnotationUtils_tagEntry_title(),
TAG_ENTRIES);
100 new ItemEntry<>(Bundle.AnnotationUtils_fileHitEntry_setName(),
101 (bba) ->
tryGetAttribute(bba, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)),
102 new ItemEntry<>(Bundle.AnnotationUtils_fileHitEntry_comment(),
103 (bba) ->
tryGetAttribute(bba, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT))
107 =
new SectionConfig<>(Bundle.AnnotationUtils_fileHitEntry_interestingFileHitTitle(),
ITEMSET_HIT_ENTRIES);
110 =
new SectionConfig<>(Bundle.AnnotationUtils_fileHitEntry_interestingItemTitle(),
ITEMSET_HIT_ENTRIES);
113 =
new SectionConfig<>(Bundle.AnnotationUtils_fileHitEntry_hashSetHitTitle(),
ITEMSET_HIT_ENTRIES);
116 =
new SectionConfig<>(Bundle.AnnotationUtils_fileHitEntry_artifactCommentTitle(),
ITEMSET_HIT_ENTRIES);
120 new ItemEntry<>(Bundle.AnnotationUtils_centralRepositoryEntryDataLabel_case(),
121 cai -> (cai.getCorrelationCase() !=
null) ? cai.getCorrelationCase().getDisplayName() :
null),
122 new ItemEntry<>(Bundle.AnnotationUtils_centralRepositoryEntryDataLabel_comment(), cai -> cai.getComment()),
123 new ItemEntry<>(Bundle.AnnotationUtils_centralRepositoryEntryDataLabel_path(), cai -> cai.getFilePath())
127 =
new SectionConfig<>(Bundle.AnnotationUtils_centralRepositoryEntry_title(),
CR_COMMENTS_ENTRIES);
144 static DisplayTskItems getDisplayContent(Node node) {
146 BlackboardArtifact artifact = artItem ==
null ? null : artItem.
getTskContent();
148 Content content = artItem !=
null
150 : node.getLookup().lookup(AbstractFile.class);
152 return new DisplayTskItems(artifact, content);
163 return getDisplayContent(node).getContent() !=
null;
177 Element body = html.getElementsByTag(
"body").first();
179 DisplayTskItems displayItems = getDisplayContent(node);
180 BlackboardArtifact artifact = displayItems.getArtifact();
181 Content srcContent = displayItems.getContent();
183 boolean somethingWasRendered =
false;
184 if (artifact !=
null) {
185 somethingWasRendered =
renderArtifact(body, artifact, srcContent);
187 somethingWasRendered =
renderContent(body, srcContent,
false);
190 if (!somethingWasRendered) {
210 @SuppressWarnings(
"deprecation")
211 private static
boolean renderArtifact(Element parent, BlackboardArtifact bba, Content sourceContent) {
217 contentRendered = contentRendered || crRendered;
221 if ((BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() == bba.getArtifactTypeID()
222 || BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() == bba.getArtifactTypeID()
223 || BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == bba.getArtifactTypeID()
224 || BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID() == bba.getArtifactTypeID())
228 contentRendered = contentRendered || filesetRendered;
231 Element sourceFileSection =
appendSection(parent, Bundle.AnnotationUtils_sourceFile_title());
234 Element sourceFileContainer = sourceFileSection.appendElement(
"div");
237 boolean sourceFileRendered =
renderContent(sourceFileContainer, sourceContent,
true);
239 if (!sourceFileRendered) {
240 sourceFileSection.remove();
243 return contentRendered || sourceFileRendered;
259 @SuppressWarnings(
"deprecation")
260 private static
boolean renderContent(Element parent, Content sourceContent,
boolean isSubheader) {
263 if (sourceContent instanceof AbstractFile) {
264 AbstractFile sourceFile = (AbstractFile) sourceContent;
270 contentRendered = contentRendered || crRendered;
274 getFileSetHits(sourceFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT),
279 getFileSetHits(sourceFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT),
284 getFileSetHits(sourceFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM),
288 contentRendered = contentRendered || hashsetRendered || interestingFileRendered || interestingItemRendered;
290 return contentRendered;
300 private static List<ContentTag>
getTags(Content sourceContent) {
303 return tskCase.getContentTagsByContent(sourceContent);
305 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
306 }
catch (TskCoreException ex) {
307 logger.log(Level.SEVERE,
"Exception while getting tags from the case database.", ex);
309 return new ArrayList<>();
319 private static List<BlackboardArtifactTag>
getTags(BlackboardArtifact bba) {
322 return tskCase.getBlackboardArtifactTagsByArtifact(bba);
324 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
325 }
catch (TskCoreException ex) {
326 logger.log(Level.SEVERE,
"Exception while getting tags from the case database.", ex);
328 return new ArrayList<>();
340 private static List<BlackboardArtifact>
getFileSetHits(AbstractFile sourceFile, BlackboardArtifact.ARTIFACT_TYPE type) {
343 return tskCase.getBlackboardArtifacts(type, sourceFile.getId()).stream()
345 .collect(Collectors.toList());
347 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
348 }
catch (TskCoreException ex) {
349 logger.log(Level.SEVERE,
"Exception while getting file set hits from the case database.", ex);
351 return new ArrayList<>();
362 return StringUtils.isNotBlank(
tryGetAttribute(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT));
372 private static boolean hasTskSet(BlackboardArtifact artifact) {
373 return StringUtils.isNotBlank(
tryGetAttribute(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
385 private static String
tryGetAttribute(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE attributeType) {
386 if (artifact ==
null) {
390 BlackboardAttribute attr =
null;
392 attr = artifact.getAttribute(
new BlackboardAttribute.Type(attributeType));
393 }
catch (TskCoreException ex) {
394 logger.log(Level.WARNING, String.format(
"Unable to fetch attribute of type %s for artifact %s", attributeType, artifact), ex);
401 return attr.getValueString();
414 if (artifact ==
null) {
415 return new ArrayList<>();
417 List<CorrelationAttributeInstance> instances =
new ArrayList<>();
418 if (artifact instanceof DataArtifact) {
420 }
else if (artifact instanceof AnalysisResult) {
425 .map(cai -> Pair.of(cai.getCorrelationType(), cai.getCorrelationValue()))
426 .collect(Collectors.toList());
441 if (sourceFile ==
null || StringUtils.isEmpty(sourceFile.getMd5Hash())) {
442 return new ArrayList<>();
449 logger.log(Level.SEVERE,
"Error connecting to the Central Repository database.", ex);
452 if (artifactTypes ==
null || artifactTypes.isEmpty()) {
453 return new ArrayList<>();
456 String md5 = sourceFile.getMd5Hash();
461 .map((attributeType) -> Pair.of(attributeType, md5))
462 .collect(Collectors.toList());
476 List<CorrelationAttributeInstance> instancesToRet =
new ArrayList<>();
480 for (Pair<CorrelationAttributeInstance.Type, String> typeVal : lookupKeys) {
485 .filter((cai) -> StringUtils.isNotBlank(cai.getComment()))
486 .collect(Collectors.toList()));
490 logger.log(Level.SEVERE,
"Error connecting to the Central Repository database.", ex);
492 logger.log(Level.WARNING,
"Error normalizing instance from Central Repository database.", ex);
495 return instancesToRet;
516 boolean isSubsection,
boolean isFirstSection) {
517 if (items ==
null || items.isEmpty()) {
522 if (!isFirstSection) {
526 Element sectionContainer = sectionDiv.appendElement(
"div");
547 boolean isFirst =
true;
548 for (T item : items) {
553 List<List<String>> tableData = rowHeaders.stream()
554 .map(row -> Arrays.asList(row.getItemName(), row.retrieveValue(item)))
555 .collect(Collectors.toList());
557 Element childTable =
appendTable(parent, 2, tableData,
null);
581 private static Element
appendTable(Element parent,
int columnNumber, List<List<String>> content, List<String> columnHeaders) {
582 Element table = parent.appendElement(
"table")
583 .attr(
"valign",
"top")
584 .attr(
"align",
"left");
586 if (columnHeaders !=
null && !columnHeaders.isEmpty()) {
587 Element header = table.appendElement(
"thead");
588 appendRow(header, columnHeaders, columnNumber,
true);
590 Element tableBody = table.appendElement(
"tbody");
592 content.forEach((rowData) ->
appendRow(tableBody, rowData, columnNumber,
false));
607 private static Element
appendRow(Element rowParent, List<String> data,
int columnNumber,
boolean isHeader) {
608 String cellType = isHeader ?
"th" :
"td";
609 Element row = rowParent.appendElement(
"tr");
610 for (
int i = 0; i < columnNumber; i++) {
611 Element cell = row.appendElement(cellType);
617 if (data !=
null && i < data.size()) {
618 cell.appendElement(
"span")
620 .text(StringUtils.isEmpty(data.get(i)) ?
"" : data.get(i));
635 Element sectionDiv = parent.appendElement(
"div");
636 Element header = sectionDiv.appendElement(
"h1");
637 header.text(headerText);
651 Element subsectionDiv = parent.appendElement(
"div");
652 Element header = subsectionDiv.appendElement(
"h2");
653 header.text(headerText);
655 return subsectionDiv;
665 static class ItemEntry<T> {
667 private final String itemName;
668 private final Function<T, String> valueRetriever;
670 ItemEntry(String itemName, Function<T, String> valueRetriever) {
671 this.itemName = itemName;
672 this.valueRetriever = valueRetriever;
675 String getItemName() {
679 Function<T, String> getValueRetriever() {
680 return valueRetriever;
683 String retrieveValue(T
object) {
684 return valueRetriever.apply(
object);
693 static class SectionConfig<T> {
695 private final String title;
696 private final List<ItemEntry<T>> attributes;
698 SectionConfig(String title, List<ItemEntry<T>> attributes) {
700 this.attributes = attributes;
714 List<ItemEntry<T>> getAttributes() {
723 static class DisplayTskItems {
725 private final BlackboardArtifact artifact;
726 private final Content content;
735 DisplayTskItems(BlackboardArtifact artifact, Content content) {
736 this.artifact = artifact;
737 this.content = content;
743 BlackboardArtifact getArtifact() {
750 Content getContent() {
SleuthkitCase getSleuthkitCase()
static Case getCurrentCaseThrows()
static final int FILES_TYPE_ID
static List< CorrelationAttributeInstance > makeCorrAttrsForSearch(AnalysisResult analysisResult)
static Element appendRow(Element rowParent, List< String > data, int columnNumber, boolean isHeader)
static List< BlackboardArtifact > getFileSetHits(AbstractFile sourceFile, BlackboardArtifact.ARTIFACT_TYPE type)
static final String EMPTY_HTML
static final SectionConfig< BlackboardArtifact > HASHSET_CONFIG
static final List< ItemEntry< CorrelationAttributeInstance > > CR_COMMENTS_ENTRIES
static final List< ItemEntry< BlackboardArtifact > > ITEMSET_HIT_ENTRIES
static final SectionConfig< Tag > TAG_CONFIG
static final SectionConfig< BlackboardArtifact > ARTIFACT_COMMENT_CONFIG
static< T > Element appendVerticalEntryTables(Element parent, List<? extends T > items, List< ItemEntry< T > > rowHeaders)
static List< CorrelationAttributeInstance > getCorrelationAttributeComments(List< Pair< CorrelationAttributeInstance.Type, String > > lookupKeys)
static List< BlackboardArtifactTag > getTags(BlackboardArtifact bba)
static final SectionConfig< BlackboardArtifact > INTERESTING_ITEM_CONFIG
static final Logger logger
static List< CorrelationAttributeInstance > getCentralRepositoryData(AbstractFile sourceFile)
static Element appendSection(Element parent, String headerText)
static< T > boolean appendEntries(Element parent, AnnotationUtils.SectionConfig< T > config, List<? extends T > items, boolean isSubsection, boolean isFirstSection)
static List< ContentTag > getTags(Content sourceContent)
static boolean renderContent(Element parent, Content sourceContent, boolean isSubheader)
static final SectionConfig< CorrelationAttributeInstance > CR_COMMENTS_CONFIG
static String tryGetAttribute(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE attributeType)
static final SectionConfig< BlackboardArtifact > INTERESTING_FILE_CONFIG
static List< CorrelationAttributeInstance > getCentralRepositoryData(BlackboardArtifact artifact)
static Element appendTable(Element parent, int columnNumber, List< List< String > > content, List< String > columnHeaders)
static final List< ItemEntry< Tag > > TAG_ENTRIES
static Document buildDocument(Node node)
static boolean isSupported(Node node)
static boolean hasTskSet(BlackboardArtifact artifact)
static boolean renderArtifact(Element parent, BlackboardArtifact bba, Content sourceContent)
static boolean hasTskComment(BlackboardArtifact artifact)
static Element appendSubsection(Element parent, String headerText)
static String getTextClassName()
static String getHeaderClassName()
static String getKeyColumnClassName()
static String getIndentedClassName()
static String getSpacedSectionClassName()
synchronized static Logger getLogger(String name)
Content getSourceContent()
static CentralRepository getInstance()
List< CorrelationAttributeInstance > getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type type, String value)
List< CorrelationAttributeInstance.Type > getDefinedCorrelationTypes()
static boolean isEnabled()