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;
45 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_TYPE;
46 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_VALUE;
47 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OTHER_CASES;
48 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
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() {