19 package org.sleuthkit.autopsy.discovery.search;
21 import java.sql.ResultSet;
22 import java.sql.SQLException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
31 import java.util.logging.Level;
32 import org.openide.util.NbBundle;
47 import java.util.StringJoiner;
49 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CATEGORIZATION;
125 static class DataSourceAttribute
extends AttributeType {
128 public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
129 return new DiscoveryKeyUtils.DataSourceGroupKey(result);
136 static class FileTypeAttribute
extends AttributeType {
139 public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
140 return new DiscoveryKeyUtils.FileTypeGroupKey(file);
148 static class DomainCategoryAttribute
extends AttributeType {
151 public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
152 return new DiscoveryKeyUtils.DomainCategoryGroupKey(result);
156 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
157 CentralRepository centralRepoDb)
throws DiscoveryException {
159 Map<String, String> domainsToCategories = getDomainsWithWebCategories(caseDb);
160 for (Result result : results) {
161 if (result instanceof ResultDomain) {
162 ResultDomain domain = (ResultDomain) result;
163 String webCategory = domainsToCategories.get(domain.getDomain());
164 domain.setWebCategory(webCategory);
167 }
catch (TskCoreException | InterruptedException ex) {
168 throw new DiscoveryException(
"Error fetching TSK_WEB_CATEGORY artifacts from the database", ex);
177 private Map<String, String> getDomainsWithWebCategories(SleuthkitCase caseDb)
throws TskCoreException, InterruptedException {
178 Map<String, String> domainToCategory =
new HashMap<>();
180 for (BlackboardArtifact artifact : caseDb.getBlackboardArtifacts(TSK_WEB_CATEGORIZATION)) {
181 if (Thread.currentThread().isInterrupted()) {
182 throw new InterruptedException();
185 BlackboardAttribute webCategory = artifact.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
186 BlackboardAttribute domain = artifact.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN));
188 if (webCategory != null && domain != null) {
189 String webCatDisplayName = webCategory.getValueString();
190 String domainDisplayName = domain.getValueString().trim().toLowerCase();
191 domainToCategory.put(domainDisplayName, webCatDisplayName);
195 return domainToCategory;
202 static class KeywordListAttribute
extends AttributeType {
205 public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
206 return new DiscoveryKeyUtils.KeywordListGroupKey((ResultFile) file);
210 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
211 CentralRepository centralRepoDb)
throws DiscoveryException {
215 String selectQuery =
createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
216 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
218 SetKeywordListNamesCallback callback =
new SetKeywordListNamesCallback(results);
220 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
221 }
catch (TskCoreException ex) {
222 throw new DiscoveryException(
"Error looking up keyword list attributes", ex);
233 List<Result> resultFiles;
241 this.resultFiles = resultFiles;
248 Map<Long, ResultFile> tempMap =
new HashMap<>();
249 for (
Result result : resultFiles) {
259 Long objId = rs.getLong(
"object_id");
260 String keywordListName = rs.getString(
"set_name");
264 }
catch (SQLException ex) {
265 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
268 }
catch (SQLException ex) {
269 logger.log(Level.SEVERE,
"Failed to get keyword list names", ex);
285 final Map<String, List<ResultDomain>> resultDomainTable =
new HashMap<>();
286 for (ResultDomain domainInstance : domainsBatch) {
288 final String domainValue = domainInstance.getDomain();
290 final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain,
new ArrayList<>());
291 bucket.add(domainInstance);
292 resultDomainTable.put(normalizedDomain, bucket);
294 logger.log(Level.INFO, String.format(
"Domain [%s] failed normalization, skipping...", domainInstance.getDomain()));
297 return resultDomainTable;
306 StringJoiner joiner =
new StringJoiner(
", ");
307 for (String value : values) {
308 joiner.add(
"'" + value +
"'");
310 return joiner.toString();
316 static class PreviouslyNotableAttribute
extends AttributeType {
318 static final int DOMAIN_BATCH_SIZE = 500;
326 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
327 CentralRepository centralRepoDb)
throws DiscoveryException {
329 if (centralRepoDb != null) {
330 processFilesWithCr(results, centralRepoDb);
334 private void processFilesWithCr(List<Result> results, CentralRepository centralRepo)
throws DiscoveryException {
336 List<ResultDomain> domainsBatch =
new ArrayList<>();
337 for (Result result : results) {
339 domainsBatch.add((ResultDomain) result);
340 if (domainsBatch.size() == DOMAIN_BATCH_SIZE) {
341 queryPreviouslyNotable(domainsBatch, centralRepo);
342 domainsBatch.clear();
347 queryPreviouslyNotable(domainsBatch, centralRepo);
350 private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo)
throws DiscoveryException {
351 if (domainsBatch.isEmpty()) {
356 final CorrelationAttributeInstance.Type attributeType = centralRepo.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
357 final Map<String, List<ResultDomain>> resultDomainTable =
organizeByValue(domainsBatch, attributeType);
358 final String values =
createCSV(resultDomainTable.keySet());
360 final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
361 final String domainFrequencyQuery =
" value AS domain_name "
362 +
"FROM " + tableName +
" "
363 +
"WHERE value IN (" + values +
") "
364 +
"AND known_status = " + TskData.FileKnown.BAD.getFileKnownValue();
366 final DomainPreviouslyNotableCallback previouslyNotableCallback =
new DomainPreviouslyNotableCallback(resultDomainTable);
367 centralRepo.processSelectClause(domainFrequencyQuery, previouslyNotableCallback);
369 if (previouslyNotableCallback.getCause() != null) {
370 throw previouslyNotableCallback.getCause();
372 }
catch (CentralRepoException | SQLException ex) {
373 throw new DiscoveryException(
"Fatal exception encountered querying the CR.", ex);
389 while (resultSet.next()) {
390 String domain = resultSet.getString(
"domain_name");
391 List<ResultDomain> domainInstances = domainLookup.get(domain);
392 for (ResultDomain domainInstance : domainInstances) {
393 domainInstance.markAsPreviouslyNotableInCR();
396 }
catch (SQLException ex) {
404 SQLException getCause() {
413 static class FrequencyAttribute
extends AttributeType {
415 static final int BATCH_SIZE = 50;
417 static final int DOMAIN_BATCH_SIZE = 500;
420 public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
421 return new DiscoveryKeyUtils.FrequencyGroupKey(file);
425 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
426 CentralRepository centralRepoDb)
throws DiscoveryException {
427 if (centralRepoDb == null) {
428 for (Result result : results) {
434 processResultFilesForCR(results, centralRepoDb);
446 private void processResultFilesForCR(List<Result> results,
447 CentralRepository centralRepoDb)
throws DiscoveryException {
448 List<ResultFile> currentFiles =
new ArrayList<>();
449 Set<String> hashesToLookUp =
new HashSet<>();
450 List<ResultDomain> domainsToQuery =
new ArrayList<>();
451 for (Result result : results) {
454 if (result.
getKnown() == TskData.FileKnown.KNOWN) {
459 ResultFile file = (ResultFile) result;
460 if (file.getFirstInstance().getMd5Hash() != null
461 && !file.getFirstInstance().getMd5Hash().isEmpty()) {
462 hashesToLookUp.add(file.getFirstInstance().getMd5Hash());
463 currentFiles.add(file);
466 if (hashesToLookUp.size() >= BATCH_SIZE) {
469 hashesToLookUp.clear();
470 currentFiles.clear();
473 domainsToQuery.add((ResultDomain) result);
474 if (domainsToQuery.size() == DOMAIN_BATCH_SIZE) {
477 domainsToQuery.clear();
497 if (domainsToQuery.isEmpty()) {
502 final Map<String, List<ResultDomain>> resultDomainTable =
organizeByValue(domainsToQuery, attributeType);
503 final String values =
createCSV(resultDomainTable.keySet());
505 final String domainFrequencyQuery =
" value AS domain_name, COUNT(value) AS frequency FROM"
506 +
"(SELECT DISTINCT case_id, value FROM "
508 +
" WHERE value IN ("
510 +
")) AS foo GROUP BY value";
513 centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
515 if (frequencyCallback.getCause() != null) {
516 throw frequencyCallback.getCause();
543 while (resultSet.next()) {
544 String domain = resultSet.getString(
"domain_name");
545 Long frequency = resultSet.getLong(
"frequency");
547 List<ResultDomain> domainInstances = domainLookup.get(domain);
548 for (ResultDomain domainInstance : domainInstances) {
552 }
catch (SQLException ex) {
562 SQLException getCause() {
573 private final List<ResultFile>
files;
581 this.files =
new ArrayList<>(
files);
588 while (resultSet.next()) {
589 String hash = resultSet.getString(1);
590 int count = resultSet.getInt(2);
591 for (Iterator<ResultFile> iterator = files.iterator(); iterator.hasNext();) {
604 }
catch (SQLException ex) {
605 logger.log(Level.WARNING,
"Error getting frequency counts from Central Repository", ex);
620 return new DiscoveryKeyUtils.HashHitsGroupKey((ResultFile) result);
624 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
625 CentralRepository centralRepoDb)
throws DiscoveryException {
629 String selectQuery =
createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
630 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
632 HashSetNamesCallback callback =
new HashSetNamesCallback(results);
634 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
635 }
catch (TskCoreException ex) {
636 throw new DiscoveryException(
"Error looking up hash set attributes", ex);
646 List<Result> results;
654 this.results = results;
661 Map<Long, ResultFile> tempMap =
new HashMap<>();
662 for (
Result result : results) {
672 Long objId = rs.getLong(
"object_id");
673 String hashSetName = rs.getString(
"set_name");
675 tempMap.get(objId).addHashSetName(hashSetName);
677 }
catch (SQLException ex) {
678 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
681 }
catch (SQLException ex) {
682 logger.log(Level.SEVERE,
"Failed to get hash set names", ex);
691 static class InterestingItemAttribute
extends AttributeType {
699 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
700 CentralRepository centralRepoDb)
throws DiscoveryException {
704 String selectQuery =
createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
705 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
707 InterestingFileSetNamesCallback callback =
new InterestingFileSetNamesCallback(results);
709 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
710 }
catch (TskCoreException ex) {
711 throw new DiscoveryException(
"Error looking up interesting file set attributes", ex);
722 List<Result> results;
731 this.results = results;
738 Map<Long, ResultFile> tempMap =
new HashMap<>();
739 for (
Result result : results) {
749 Long objId = rs.getLong(
"object_id");
750 String setName = rs.getString(
"set_name");
752 tempMap.get(objId).addInterestingSetName(setName);
754 }
catch (SQLException ex) {
755 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
758 }
catch (SQLException ex) {
759 logger.log(Level.SEVERE,
"Failed to get interesting file set names", ex);
768 static class LastActivityDateAttribute
extends AttributeType {
780 static class FirstActivityDateAttribute
extends AttributeType {
783 public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
784 return new DiscoveryKeyUtils.FirstActivityDateGroupKey(result);
793 static class PageViewsAttribute
extends AttributeType {
796 public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
797 return new DiscoveryKeyUtils.PageViewsGroupKey(result);
804 static class ObjectDetectedAttribute
extends AttributeType {
807 public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
808 return new DiscoveryKeyUtils.ObjectDetectedGroupKey((ResultFile) file);
812 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
813 CentralRepository centralRepoDb)
throws DiscoveryException {
817 String selectQuery =
createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(),
818 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
820 ObjectDetectedNamesCallback callback =
new ObjectDetectedNamesCallback(results);
822 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
823 }
catch (TskCoreException ex) {
824 throw new DiscoveryException(
"Error looking up object detected attributes", ex);
835 List<Result> results;
843 this.results = results;
850 Map<Long, ResultFile> tempMap =
new HashMap<>();
851 for (
Result result : results) {
861 Long objId = rs.getLong(
"object_id");
862 String setName = rs.getString(
"set_name");
864 tempMap.get(objId).addObjectDetectedName(setName);
866 }
catch (SQLException ex) {
867 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
870 }
catch (SQLException ex) {
871 logger.log(Level.SEVERE,
"Failed to get object detected names", ex);
888 public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
889 CentralRepository centralRepoDb)
throws DiscoveryException {
892 for (Result result : results) {
896 ResultFile file = (ResultFile) result;
897 List<ContentTag> contentTags = caseDb.getContentTagsByContent(file.getFirstInstance());
899 for (ContentTag tag : contentTags) {
900 result.
addTagName(tag.getName().getDisplayName());
903 }
catch (TskCoreException ex) {
904 throw new DiscoveryException(
"Error looking up file tag attributes", ex);
913 "DiscoveryAttributes.GroupingAttributeType.fileType.displayName=File Type",
914 "DiscoveryAttributes.GroupingAttributeType.frequency.displayName=Past Occurrences",
915 "DiscoveryAttributes.GroupingAttributeType.keywordList.displayName=Keyword",
916 "DiscoveryAttributes.GroupingAttributeType.size.displayName=File Size",
917 "DiscoveryAttributes.GroupingAttributeType.datasource.displayName=Data Source",
918 "DiscoveryAttributes.GroupingAttributeType.parent.displayName=Parent Folder",
919 "DiscoveryAttributes.GroupingAttributeType.hash.displayName=Hash Set",
920 "DiscoveryAttributes.GroupingAttributeType.interestingItem.displayName=Interesting Item",
921 "DiscoveryAttributes.GroupingAttributeType.tag.displayName=Tag",
922 "DiscoveryAttributes.GroupingAttributeType.object.displayName=Object Detected",
923 "DiscoveryAttributes.GroupingAttributeType.lastDate.displayName=Last Activity Date",
924 "DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date",
925 "DiscoveryAttributes.GroupingAttributeType.pageViews.displayName=Page Views",
926 "DiscoveryAttributes.GroupingAttributeType.none.displayName=None",
927 "DiscoveryAttributes.GroupingAttributeType.previouslyNotable.displayName=Previous Notability",
928 "DiscoveryAttributes.GroupingAttributeType.webCategory.displayName=Domain Category"})
931 FREQUENCY(
new FrequencyAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_frequency_displayName()),
932 KEYWORD_LIST_NAME(
new KeywordListAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_keywordList_displayName()),
933 DATA_SOURCE(
new DataSourceAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_datasource_displayName()),
935 HASH_LIST_NAME(
new HashHitsAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_hash_displayName()),
936 INTERESTING_ITEM_SET(
new InterestingItemAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_interestingItem_displayName()),
937 FILE_TAG(
new FileTagAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_tag_displayName()),
938 OBJECT_DETECTED(
new ObjectDetectedAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_object_displayName()),
939 LAST_ACTIVITY_DATE(
new LastActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_lastDate_displayName()),
940 FIRST_ACTIVITY_DATE(
new FirstActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_firstDate_displayName()),
941 PAGE_VIEWS(
new PageViewsAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_pageViews_displayName()),
942 NO_GROUPING(
new NoGroupingAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_none_displayName()),
943 PREVIOUSLY_NOTABLE(
new PreviouslyNotableAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_previouslyNotable_displayName()),
944 DOMAIN_CATEGORY(
new DomainCategoryAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_webCategory_displayName());
958 this.attributeType = attributeType;
959 this.displayName = displayName;
973 return attributeType;
982 return Arrays.asList(FILE_SIZE, FREQUENCY, PARENT_PATH, OBJECT_DETECTED, HASH_LIST_NAME, INTERESTING_ITEM_SET);
992 return Arrays.asList(PAGE_VIEWS, FREQUENCY, LAST_ACTIVITY_DATE, FIRST_ACTIVITY_DATE, PREVIOUSLY_NOTABLE, DOMAIN_CATEGORY);
994 return Arrays.asList(PAGE_VIEWS, LAST_ACTIVITY_DATE, FIRST_ACTIVITY_DATE, DOMAIN_CATEGORY);
1009 if (hashesToLookUp.isEmpty()) {
1013 String hashes = String.join(
"','", hashesToLookUp);
1014 hashes =
"'" + hashes +
"'";
1019 String selectClause =
" value, COUNT(value) FROM "
1020 +
"(SELECT DISTINCT case_id, value FROM " + tableName
1021 +
" WHERE value IN ("
1023 +
")) AS foo GROUP BY value";
1029 logger.log(Level.WARNING,
"Error getting frequency counts from Central Repository", ex);
1051 String objIdList =
"";
1052 for (
Result result : results) {
1057 if (!objIdList.isEmpty()) {
1065 return "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS set_name "
1066 +
"FROM blackboard_artifacts "
1067 +
"INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id "
1068 +
"WHERE blackboard_attributes.artifact_type_id=\'" + artifactTypeID +
"\' "
1069 +
"AND blackboard_attributes.attribute_type_id=\'" + setNameAttrID +
"\' "
1070 +
"AND blackboard_artifacts.obj_id IN (" + objIdList
static String createSetNameClause(List< Result > results, int artifactTypeID, int setNameAttrID)
AttributeType getAttributeType()
DiscoveryKeyUtils.GroupKey getGroupKey(Result file)
DomainPreviouslyNotableCallback(Map< String, List< ResultDomain >> domainLookup)
static List< GroupingAttributeType > getOptionsForGroupingForDomains()
DomainFrequencyCallback(Map< String, List< ResultDomain >> domainLookup)
SearchData.Frequency getFrequency()
void process(ResultSet rs)
abstract TskData.FileKnown getKnown()
static final Logger logger
void process(ResultSet resultSet)
static void computeFrequency(Set< String > hashesToLookUp, List< ResultFile > currentFiles, CentralRepository centralRepoDb)
void addTagName(String tagName)
static Frequency fromCount(long count)
abstract SearchData.Type getType()
FrequencyCallback(List< ResultFile > files)
GroupingAttributeType(AttributeType attributeType, String displayName)
static List< GroupingAttributeType > getOptionsForGroupingForFiles()
final void setFrequency(SearchData.Frequency frequency)
static String correlationTypeToInstanceTableName(CorrelationAttributeInstance.Type type)
static String normalize(CorrelationAttributeInstance.Type attributeType, String data)
AbstractFile getFirstInstance()
void process(ResultSet resultSet)
DiscoveryKeyUtils.GroupKey getGroupKey(Result result)
void addAttributeToResults(List< Result > results, SleuthkitCase caseDb, CentralRepository centralRepoDb)
static final int DOMAIN_TYPE_ID
void process(ResultSet rs)
abstract DiscoveryKeyUtils.GroupKey getGroupKey(Result result)
final List< ResultFile > files
final Map< String, List< ResultDomain > > domainLookup
void process(ResultSet rs)
void addKeywordListName(String keywordListName)
final Map< String, List< ResultDomain > > domainLookup
void process(ResultSet rs)
static Map< String, List< ResultDomain > > organizeByValue(List< ResultDomain > domainsBatch, CorrelationAttributeInstance.Type attributeType)
synchronized static Logger getLogger(String name)
CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId)
void process(ResultSet resultSet)
static String createCSV(Set< String > values)
void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback)
static void queryDomainFrequency(List< ResultDomain > domainsToQuery, CentralRepository centralRepository)
static final int FILES_TYPE_ID
final AttributeType attributeType
static boolean isEnabled()