19 package org.sleuthkit.autopsy.contentviewers.annotations;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.List;
24 import java.util.function.Function;
25 import java.util.logging.Level;
26 import java.util.stream.Collectors;
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.commons.lang3.tuple.Pair;
29 import org.jsoup.Jsoup;
30 import org.jsoup.nodes.Document;
31 import org.jsoup.nodes.Element;
32 import org.openide.nodes.Node;
33 import org.openide.util.NbBundle;
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())
95 private static final SectionConfig<Tag>
TAG_CONFIG
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);
112 private static final SectionConfig<BlackboardArtifact>
HASHSET_CONFIG
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())
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;
176 Document html = Jsoup.parse(EMPTY_HTML);
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() {
static String tryGetAttribute(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE attributeType)
static List< CorrelationAttributeInstance > getCentralRepositoryData(AbstractFile sourceFile)
static final SectionConfig< BlackboardArtifact > INTERESTING_ITEM_CONFIG
static Element appendRow(Element rowParent, List< String > data, int columnNumber, boolean isHeader)
static Element appendSubsection(Element parent, String headerText)
static String getSpacedSectionClassName()
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 String getTextClassName()
static final SectionConfig< BlackboardArtifact > HASHSET_CONFIG
static final SectionConfig< BlackboardArtifact > INTERESTING_FILE_CONFIG
static Element appendSection(Element parent, String headerText)
static final String EMPTY_HTML
static List< BlackboardArtifact > getFileSetHits(AbstractFile sourceFile, BlackboardArtifact.ARTIFACT_TYPE type)
static String getKeyColumnClassName()
static< T > Element appendVerticalEntryTables(Element parent, List<?extends T > items, List< ItemEntry< T >> rowHeaders)
static final List< ItemEntry< CorrelationAttributeInstance > > CR_COMMENTS_ENTRIES
static String getIndentedClassName()
List< CorrelationAttributeInstance.Type > getDefinedCorrelationTypes()
static List< BlackboardArtifactTag > getTags(BlackboardArtifact bba)
static List< CorrelationAttributeInstance > makeCorrAttrsForSearch(AnalysisResult analysisResult)
static final SectionConfig< BlackboardArtifact > ARTIFACT_COMMENT_CONFIG
static boolean hasTskComment(BlackboardArtifact artifact)
static boolean hasTskSet(BlackboardArtifact artifact)
static Element appendTable(Element parent, int columnNumber, List< List< String >> content, List< String > columnHeaders)
static final SectionConfig< CorrelationAttributeInstance > CR_COMMENTS_CONFIG
SleuthkitCase getSleuthkitCase()
static boolean renderContent(Element parent, Content sourceContent, boolean isSubheader)
static final List< ItemEntry< BlackboardArtifact > > ITEMSET_HIT_ENTRIES
static boolean isSupported(Node node)
static Document buildDocument(Node node)
static String getHeaderClassName()
static List< CorrelationAttributeInstance > getCentralRepositoryData(BlackboardArtifact artifact)
static List< CorrelationAttributeInstance > getCorrelationAttributeComments(List< Pair< CorrelationAttributeInstance.Type, String >> lookupKeys)
List< CorrelationAttributeInstance > getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type type, String value)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
static final List< ItemEntry< Tag > > TAG_ENTRIES
static final Logger logger
Content getSourceContent()
static boolean renderArtifact(Element parent, BlackboardArtifact bba, Content sourceContent)
static CentralRepository getInstance()
static final SectionConfig< Tag > TAG_CONFIG
static final int FILES_TYPE_ID
static boolean isEnabled()