19 package org.sleuthkit.autopsy.centralrepository.ingestmodule;
 
   21 import java.util.ArrayList;
 
   22 import java.util.Arrays;
 
   23 import java.util.Collection;
 
   24 import java.util.Iterator;
 
   25 import java.util.List;
 
   26 import java.util.Optional;
 
   28 import java.util.logging.Level;
 
   29 import java.util.stream.Collectors;
 
   30 import org.openide.util.NbBundle;
 
   57 class CentralRepoIngestModuleUtils {
 
   59     private static final Logger LOGGER = Logger.getLogger(CentralRepoDataArtifactIngestModule.class.getName());
 
   60     private static final int MAX_PREV_CASES_FOR_NOTABLE_SCORE = 10;
 
   61     private static final int MAX_PREV_CASES_FOR_PREV_SEEN = 20;
 
   62     private final static String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName();
 
   72     static List<CorrelationAttributeInstance> getOccurrencesInOtherCases(CorrelationAttributeInstance corrAttr, 
long ingestJobId) {
 
   73         List<CorrelationAttributeInstance> previousOccurrences = 
new ArrayList<>();
 
   75             CentralRepository centralRepo = CentralRepository.getInstance();
 
   76             previousOccurrences = centralRepo.getArtifactInstancesByTypeValue(corrAttr.getCorrelationType(), corrAttr.getCorrelationValue());
 
   77             for (Iterator<CorrelationAttributeInstance> iterator = previousOccurrences.iterator(); iterator.hasNext();) {
 
   78                 CorrelationAttributeInstance prevOccurrence = iterator.next();
 
   79                 if (prevOccurrence.getCorrelationCase().getCaseUUID().equals(corrAttr.getCorrelationCase().getCaseUUID())) {
 
   83         } 
catch (CorrelationAttributeNormalizationException ex) {
 
   84             LOGGER.log(Level.WARNING, String.format(
"Error normalizing correlation attribute value for 's' (job ID=%d)", corrAttr, ingestJobId), ex); 
 
   85         } 
catch (CentralRepoException ex) {
 
   86             LOGGER.log(Level.SEVERE, String.format(
"Error getting previous occurences of correlation attribute 's' (job ID=%d)", corrAttr, ingestJobId), ex); 
 
   88         return previousOccurrences;
 
  103         "CentralRepoIngestModule_notableSetName=Previously Tagged As Notable (Central Repository)",
 
  104         "# {0} - list of cases",
 
  105         "CentralRepoIngestModule_notableJustification=Previously marked as notable in cases {0}" 
  107     static void makePrevNotableAnalysisResult(Content content, Set<String> previousCases, CorrelationAttributeInstance.Type corrAttrType, String corrAttrValue, 
long dataSourceObjId, 
long ingestJobId) {
 
  108         String prevCases = previousCases.stream().collect(Collectors.joining(
","));
 
  109         String justification = Bundle.CentralRepoIngestModule_notableJustification(prevCases);
 
  110         Collection<BlackboardAttribute> attributes = Arrays.asList(
 
  111                 new BlackboardAttribute(TSK_SET_NAME, MODULE_NAME, Bundle.CentralRepoIngestModule_notableSetName()),
 
  112                 new BlackboardAttribute(TSK_CORRELATION_TYPE, MODULE_NAME, corrAttrType.getDisplayName()),
 
  113                 new BlackboardAttribute(TSK_CORRELATION_VALUE, MODULE_NAME, corrAttrValue),
 
  114                 new BlackboardAttribute(TSK_OTHER_CASES, MODULE_NAME, prevCases));
 
  115         Optional<AnalysisResult> result = makeAndPostAnalysisResult(content, BlackboardArtifact.Type.TSK_PREVIOUSLY_NOTABLE, attributes, 
"", Score.SCORE_NOTABLE, justification, dataSourceObjId, ingestJobId);
 
  116         if (result.isPresent()) {
 
  117             postNotableMessage(content, previousCases, corrAttrValue, result.get());
 
  134         "CentralRepoIngestModule_prevSeenSetName=Previously Seen (Central Repository)",
 
  135         "# {0} - list of cases",
 
  136         "CentralRepoIngestModule_prevSeenJustification=Previously seen in cases {0}" 
  138     static void makePrevSeenAnalysisResult(Content content, Set<String> previousCases, CorrelationAttributeInstance.Type corrAttrType, String corrAttrValue, 
long dataSourceObjId, 
long ingestJobId) {
 
  139         Optional<Score> score = calculateScore(previousCases.size());
 
  140         if (score.isPresent()) {
 
  141             String prevCases = previousCases.stream().collect(Collectors.joining(
","));
 
  142             String justification = Bundle.CentralRepoIngestModule_prevSeenJustification(prevCases);
 
  143             Collection<BlackboardAttribute> analysisResultAttributes = Arrays.asList(
 
  144                     new BlackboardAttribute(TSK_SET_NAME, MODULE_NAME, Bundle.CentralRepoIngestModule_prevSeenSetName()),
 
  145                     new BlackboardAttribute(TSK_CORRELATION_TYPE, MODULE_NAME, corrAttrType.getDisplayName()),
 
  146                     new BlackboardAttribute(TSK_CORRELATION_VALUE, MODULE_NAME, corrAttrValue),
 
  147                     new BlackboardAttribute(TSK_OTHER_CASES, MODULE_NAME, prevCases));
 
  148             makeAndPostAnalysisResult(content, BlackboardArtifact.Type.TSK_PREVIOUSLY_SEEN, analysisResultAttributes, 
"", score.get(), justification, dataSourceObjId, ingestJobId);
 
  162         "CentralRepoIngestModule_prevUnseenJustification=Previously seen in zero cases" 
  164     static void makePrevUnseenAnalysisResult(Content content, CorrelationAttributeInstance.Type corrAttrType, String corrAttrValue, 
long dataSourceObjId, 
long ingestJobId) {
 
  165         Collection<BlackboardAttribute> attributesForNewArtifact = Arrays.asList(
 
  166                 new BlackboardAttribute(TSK_CORRELATION_TYPE, MODULE_NAME, corrAttrType.getDisplayName()),
 
  167                 new BlackboardAttribute(TSK_CORRELATION_VALUE, MODULE_NAME, corrAttrValue));
 
  168         makeAndPostAnalysisResult(content, BlackboardArtifact.Type.TSK_PREVIOUSLY_UNSEEN, attributesForNewArtifact, 
"", Score.SCORE_LIKELY_NOTABLE, Bundle.CentralRepoIngestModule_prevUnseenJustification(), dataSourceObjId, ingestJobId);
 
  180     static Optional<Score> calculateScore(
int numPreviousCases) {
 
  182         if (numPreviousCases <= MAX_PREV_CASES_FOR_NOTABLE_SCORE) {
 
  183             score = Score.SCORE_LIKELY_NOTABLE;
 
  184         } 
else if (numPreviousCases > MAX_PREV_CASES_FOR_NOTABLE_SCORE && numPreviousCases <= MAX_PREV_CASES_FOR_PREV_SEEN) {
 
  185             score = Score.SCORE_NONE;
 
  187         return Optional.ofNullable(score);
 
  206     private static Optional<AnalysisResult> makeAndPostAnalysisResult(Content content, BlackboardArtifact.Type analysisResultType, Collection<BlackboardAttribute> analysisResultAttrs, String configuration, Score score, String justification, 
long dataSourceObjId, 
long ingestJobId) {
 
  207         AnalysisResult analysisResult = null;
 
  209             Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
 
  210             if (!blackboard.artifactExists(content, analysisResultType, analysisResultAttrs)) {
 
  211                 analysisResult = content.newAnalysisResult(analysisResultType, score, null, configuration, justification, analysisResultAttrs, dataSourceObjId).getAnalysisResult();
 
  213                     blackboard.postArtifact(analysisResult, MODULE_NAME, ingestJobId);
 
  214                 } 
catch (Blackboard.BlackboardException ex) {
 
  215                     LOGGER.log(Level.SEVERE, String.format(
"Error posting analysis result '%s' to blackboard for content 's' (job ID=%d)", analysisResult, content, ingestJobId), ex); 
 
  218         } 
catch (NoCurrentCaseException | TskCoreException ex) {
 
  219             LOGGER.log(Level.SEVERE, String.format(
"Error creating %s analysis result for content '%s' (job ID=%d)", analysisResultType, content, ingestJobId), ex); 
 
  221         return Optional.ofNullable(analysisResult);
 
  238         "# {0} - Name of item that is Notable",
 
  239         "CentralRepoIngestModule_notable_inbox_msg_subject=Notable: {0}" 
  241     private static void postNotableMessage(Content content, Set<String> otherCases, String corrAttrValue, AnalysisResult analysisResult) {
 
  242         String msgSubject = null;
 
  243         String msgDetails = null;
 
  244         String msgKey = corrAttrValue;
 
  245         if (content instanceof AbstractFile) {
 
  246             AbstractFile file = (AbstractFile) content;
 
  247             msgSubject = Bundle.CentralRepoIngestModule_notable_inbox_msg_subject(file.getName());
 
  248             msgDetails = makeNotableFileMessage(file, otherCases);
 
  249         } 
else if (content instanceof DataArtifact) {
 
  250             DataArtifact artifact = (DataArtifact) content;
 
  251             msgSubject = Bundle.CentralRepoIngestModule_notable_inbox_msg_subject(artifact.getDisplayName());
 
  252             msgDetails = makeNotableDataArtifactMessage(artifact, corrAttrValue, otherCases);
 
  254             LOGGER.log(Level.SEVERE, 
"Unsupported Content, cannot post ingest inbox message");
 
  256         if (msgSubject != null && msgDetails != null) {
 
  257             IngestServices.getInstance().postMessage(
 
  258                     IngestMessage.createDataMessage(
 
  278         "CentralRepoIngestModule_filename_inbox_msg_header=File Name",
 
  279         "CentralRepoIngestModule_md5Hash_inbox_msg_header=MD5 Hash",
 
  280         "CentralRepoIngestModule_prev_cases_inbox_msg_header=Previous Cases" 
  282     private static String makeNotableFileMessage(AbstractFile file, Set<String> otherCases) {
 
  283         StringBuilder message = 
new StringBuilder(1024);
 
  284         message.append(
"<table border='0' cellpadding='4' width='280'>"); 
 
  285         addTableRowMarkup(message, Bundle.CentralRepoIngestModule_filename_inbox_msg_header(), file.getName());
 
  286         addTableRowMarkup(message, Bundle.CentralRepoIngestModule_md5Hash_inbox_msg_header(), file.getMd5Hash());
 
  287         addTableRowMarkup(message, Bundle.CentralRepoIngestModule_prev_cases_inbox_msg_header(), otherCases.stream().collect(Collectors.joining(
",")));
 
  288         return message.toString();
 
  303         "CentralRepoIngestModule_artifact_type_inbox_msg_header=Artifact Type",
 
  304         "CentralRepoIngestModule_notable_attr_inbox_msg_header=Notable Attribute" 
  306     private static String makeNotableDataArtifactMessage(DataArtifact artifact, String corrAttrValue, Set<String> otherCases) {
 
  307         StringBuilder message = 
new StringBuilder(1024);
 
  308         message.append(
"<table border='0' cellpadding='4' width='280'>"); 
 
  309         addTableRowMarkup(message, Bundle.CentralRepoIngestModule_artifact_type_inbox_msg_header(), artifact.getDisplayName());
 
  310         addTableRowMarkup(message, Bundle.CentralRepoIngestModule_notable_attr_inbox_msg_header(), corrAttrValue);
 
  311         addTableRowMarkup(message, Bundle.CentralRepoIngestModule_prev_cases_inbox_msg_header(), otherCases.stream().collect(Collectors.joining(
",")));
 
  312         message.append(
"</table>"); 
 
  313         return message.toString();
 
  323     private static void addTableRowMarkup(StringBuilder message, String headerText, String cellText) {
 
  324         message.append(
"<tr>"); 
 
  325         message.append(
"<th>").append(headerText).append(
"</th>"); 
 
  326         message.append(
"<td>").append(cellText).append(
"</td>"); 
 
  327         message.append(
"</tr>"); 
 
  333     private CentralRepoIngestModuleUtils() {