Autopsy  4.18.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
UserActivitySummary.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datasourcesummary.datamodel;
20 
21 import java.io.File;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.Date;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.function.Function;
35 import java.util.logging.Level;
36 import java.util.stream.Collectors;
37 import java.util.stream.Stream;
38 import org.apache.commons.lang3.StringUtils;
39 import org.apache.commons.lang3.tuple.Pair;
40 import org.openide.util.NbBundle.Messages;
42 import org.sleuthkit.datamodel.BlackboardArtifact;
43 import org.sleuthkit.datamodel.BlackboardAttribute;
44 import org.sleuthkit.datamodel.DataSource;
45 import org.sleuthkit.datamodel.TskCoreException;
49 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
50 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
51 
58 
63  private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
64  // handle Program Files and Program Files (x86) - if true, return the next folder
65  (pathList) -> {
66  if (pathList.size() < 2) {
67  return null;
68  }
69 
70  String rootParent = pathList.get(0).toUpperCase();
71  if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
72  return pathList.get(1);
73  } else {
74  return null;
75  }
76  },
77  // if there is a folder named "APPLICATION DATA" or "APPDATA"
78  (pathList) -> {
79  for (String pathEl : pathList) {
80  String uppered = pathEl.toUpperCase();
81  if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
82  return "AppData";
83  }
84  }
85  return null;
86  }
87  );
88 
89  private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED);
90  private static final BlackboardArtifact.Type TYPE_WEB_HISTORY = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY);
91 
92  private static final BlackboardAttribute.Type TYPE_DATETIME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME);
93  private static final BlackboardAttribute.Type TYPE_DATETIME_ACCESSED = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED);
94  private static final BlackboardAttribute.Type TYPE_DEVICE_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_ID);
95  private static final BlackboardAttribute.Type TYPE_DEVICE_MAKE = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE);
96  private static final BlackboardAttribute.Type TYPE_DEVICE_MODEL = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL);
97  private static final BlackboardAttribute.Type TYPE_MESSAGE_TYPE = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE);
98  private static final BlackboardAttribute.Type TYPE_TEXT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_TEXT);
99  private static final BlackboardAttribute.Type TYPE_DATETIME_RCVD = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_RCVD);
100  private static final BlackboardAttribute.Type TYPE_DATETIME_SENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_SENT);
101  private static final BlackboardAttribute.Type TYPE_DATETIME_START = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START);
102  private static final BlackboardAttribute.Type TYPE_DATETIME_END = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_END);
103  private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
104  private static final BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME);
105  private static final BlackboardAttribute.Type TYPE_PATH = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH);
106  private static final BlackboardAttribute.Type TYPE_COUNT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COUNT);
107 
108  private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
109  private static final String WINDOWS_PREFIX = "/WINDOWS";
110 
111  private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
112  private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
113 
118  private static final Comparator<TopProgramsResult> TOP_PROGRAMS_RESULT_COMPARE = (a, b) -> {
119  // first priority for sorting is the run times
120  // if non-0, this is the return value for the comparator
121  int runTimesCompare = nullableCompare(a.getRunTimes(), b.getRunTimes());
122  if (runTimesCompare != 0) {
123  return -runTimesCompare;
124  }
125 
126  // second priority for sorting is the last run date
127  // if non-0, this is the return value for the comparator
128  int lastRunCompare = nullableCompare(
129  a.getLastAccessed() == null ? null : a.getLastAccessed().getTime(),
130  b.getLastAccessed() == null ? null : b.getLastAccessed().getTime());
131 
132  if (lastRunCompare != 0) {
133  return -lastRunCompare;
134  }
135 
136  // otherwise sort alphabetically
137  return (a.getProgramName() == null ? "" : a.getProgramName())
138  .compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
139  };
140 
141  private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
142  ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
143  ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
144  ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
145  ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
146  ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
147  ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
148  ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
149  ));
150 
151  private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
152  private static final Set<String> DOMAIN_EXCLUDE_LIST = new HashSet<>(Arrays.asList("127.0.0.1", "LOCALHOST"));
153 
154  private static final long MS_PER_DAY = 1000 * 60 * 60 * 24;
155  private static final long DOMAIN_WINDOW_DAYS = 30;
156  private static final long DOMAIN_WINDOW_MS = DOMAIN_WINDOW_DAYS * MS_PER_DAY;
157 
160  private final java.util.logging.Logger logger;
161 
168  }
169 
180  SleuthkitCaseProvider provider,
181  TextTranslationService translationService,
182  java.util.logging.Logger logger) {
183 
184  this.caseProvider = provider;
185  this.translationService = translationService;
186  this.logger = logger;
187  }
188 
189  @Override
190  public Set<Integer> getArtifactTypeIdsForRefresh() {
192  }
193 
199  private void assertValidCount(int count) {
200  if (count <= 0) {
201  throw new IllegalArgumentException("Count must be greater than 0");
202  }
203  }
204 
215  public List<TopDomainsResult> getRecentDomains(DataSource dataSource, int count) throws TskCoreException, SleuthkitCaseProviderException {
216  assertValidCount(count);
217 
218  if (dataSource == null) {
219  return Collections.emptyList();
220  }
221 
222  Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource);
223  // if no recent domains, return accordingly
224  if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) {
225  return Collections.emptyList();
226  }
227 
228  final long mostRecentMs = mostRecentAndGroups.getLeft();
229  Map<String, List<Pair<BlackboardArtifact, Long>>> groups = mostRecentAndGroups.getRight();
230 
231  return groups.entrySet().stream()
232  .map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs))
233  .filter(result -> result != null)
234  // sort by number of visit times in those 30 days (max to min)
235  .sorted((a, b) -> -Long.compare(a.getVisitTimes(), b.getVisitTimes()))
236  // limit the result number to the parameter provided
237  .limit(count)
238  .collect(Collectors.toList());
239  }
240 
253  private TopDomainsResult getDomainsResult(String domain, List<Pair<BlackboardArtifact, Long>> visits, long mostRecentMs) {
254  long visitCount = 0;
255  Long thisMostRecentMs = null;
256  BlackboardArtifact thisMostRecentArtifact = null;
257 
258  for (Pair<BlackboardArtifact, Long> visitInstance : visits) {
259  BlackboardArtifact artifact = visitInstance.getLeft();
260  Long visitMs = visitInstance.getRight();
261  // make sure that visit is within window of mostRecentMS; otherwise skip it.
262  if (visitMs == null || visitMs + DOMAIN_WINDOW_MS < mostRecentMs) {
263  continue;
264  }
265 
266  // if visit is within window, increment the count and get most recent
267  visitCount++;
268  if (thisMostRecentMs == null || visitMs > thisMostRecentMs) {
269  thisMostRecentMs = visitMs;
270  thisMostRecentArtifact = artifact;
271  }
272  thisMostRecentMs = getMax(thisMostRecentMs, visitMs);
273  }
274 
275  // if there are no visits within the window, return null
276  if (visitCount <= 0 || thisMostRecentMs == null) {
277  return null;
278  } else {
279  // create a top domain result with the domain, count, and most recent visit date
280  return new TopDomainsResult(domain, visitCount, new Date(thisMostRecentMs), thisMostRecentArtifact);
281  }
282  }
283 
298  private Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException {
299  List<BlackboardArtifact> artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY,
300  dataSource, TYPE_DATETIME_ACCESSED, DataSourceInfoUtilities.SortOrder.DESCENDING, 0);
301 
302  Long mostRecentMs = null;
303  Map<String, List<Pair<BlackboardArtifact, Long>>> domainVisits = new HashMap<>();
304 
305  for (BlackboardArtifact art : artifacts) {
306  Long artifactDateSecs = DataSourceInfoUtilities.getLongOrNull(art, TYPE_DATETIME_ACCESSED);
307  String domain = DataSourceInfoUtilities.getStringOrNull(art, TYPE_DOMAIN);
308 
309  // if there isn't a last access date or domain for this artifact, it can be ignored.
310  // Also, ignore the loopback address.
311  if (artifactDateSecs == null || StringUtils.isBlank(domain) || DOMAIN_EXCLUDE_LIST.contains(domain.toUpperCase().trim())) {
312  continue;
313  }
314 
315  Long artifactDateMs = artifactDateSecs * 1000;
316 
317  // update the most recent visit date overall
318  mostRecentMs = getMax(mostRecentMs, artifactDateMs);
319 
320  //Normalize the domain to lower case.
321  domain = domain.toLowerCase().trim();
322 
323  // add this visit date to the list of dates for the domain
324  List<Pair<BlackboardArtifact, Long>> domainVisitList = domainVisits.get(domain);
325  if (domainVisitList == null) {
326  domainVisitList = new ArrayList<>();
327  domainVisits.put(domain, domainVisitList);
328  }
329 
330  domainVisitList.add(Pair.of(art, artifactDateMs));
331  }
332 
333  return Pair.of(mostRecentMs, domainVisits);
334  }
335 
344  private static Long getMax(Long num1, Long num2) {
345  if (num1 == null) {
346  return num2;
347  } else if (num2 == null) {
348  return num1;
349  } else {
350  return num2 > num1 ? num2 : num1;
351  }
352  }
353 
362  private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) {
363  String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
364  Date dateAccessed = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME_ACCESSED);
365  return (StringUtils.isNotBlank(searchString) && dateAccessed != null)
366  ? new TopWebSearchResult(searchString, dateAccessed, artifact)
367  : null;
368  }
369 
384  public List<TopWebSearchResult> getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
385  assertValidCount(count);
386 
387  if (dataSource == null) {
388  return Collections.emptyList();
389  }
390 
391  // get the artifacts
392  List<BlackboardArtifact> webSearchArtifacts = caseProvider.get().getBlackboard()
393  .getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId());
394 
395  // group by search string (case insensitive)
396  Collection<TopWebSearchResult> resultGroups = webSearchArtifacts
397  .stream()
398  // get items where search string and date is not null
400  // remove null records
401  .filter(result -> result != null)
402  // get the latest message for each search string
403  .collect(Collectors.toMap(
404  (result) -> result.getSearchString().toUpperCase(),
405  result -> result,
406  (result1, result2) -> TOP_WEBSEARCH_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
407  .values();
408 
409  // get the most recent date for each search term
410  List<TopWebSearchResult> results = resultGroups
411  .stream()
412  // get most recent searches first
413  .sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed())
414  .limit(count)
415  // get as list
416  .collect(Collectors.toList());
417 
418  // get translation if possible
419  if (translationService.hasProvider()) {
420  for (TopWebSearchResult result : results) {
421  result.setTranslatedResult(getTranslationOrNull(result.getSearchString()));
422  }
423  }
424 
425  return results;
426  }
427 
437  private String getTranslationOrNull(String original) {
438  if (!translationService.hasProvider() || StringUtils.isBlank(original)) {
439  return null;
440  }
441 
442  String translated = null;
443  try {
444  translated = translationService.translate(original);
446  logger.log(Level.WARNING, String.format("There was an error translating text: '%s'", original), ex);
447  }
448 
449  // if there is no translation or the translation is the same as the original, return null.
450  if (StringUtils.isBlank(translated)
451  || translated.toUpperCase().trim().equals(original.toUpperCase().trim())) {
452 
453  return null;
454  }
455 
456  return translated;
457  }
458 
468  if (r2.getLastAccessed()== null) {
469  return r1;
470  }
471 
472  if (r1.getLastAccessed() == null) {
473  return r2;
474  }
475 
476  return r1.getLastAccessed().compareTo(r2.getLastAccessed()) >= 0 ? r1 : r2;
477  }
478 
492  public List<TopDeviceAttachedResult> getRecentDevices(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
493  assertValidCount(count);
494 
495  if (dataSource == null) {
496  return Collections.emptyList();
497  }
498 
499  Collection<TopDeviceAttachedResult> results = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED,
500  dataSource, TYPE_DATETIME, DataSourceInfoUtilities.SortOrder.DESCENDING, 0)
501  .stream()
502  .map(artifact -> {
503  return new TopDeviceAttachedResult(
504  DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID),
505  DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME),
506  DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE),
507  DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL),
508  artifact
509  );
510  })
511  // remove Root Hub identifier
512  .filter(result -> {
513  return result.getDeviceId() == null
514  || result.getDeviceModel() == null
515  || !DEVICE_EXCLUDE_LIST.contains(result.getDeviceModel().trim().toUpperCase());
516  })
517  .collect(Collectors.toMap(result -> result.getDeviceId(), result -> result, (r1, r2) -> getMostRecentDevice(r1, r2)))
518  .values();
519 
520  return results.stream()
521  .limit(count)
522  .collect(Collectors.toList());
523  }
524 
533  private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
534  String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
535  Date date = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME);
536  return (StringUtils.isNotBlank(type) && date != null)
537  ? new TopAccountResult(type, date, artifact)
538  : null;
539  }
540 
552  private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) {
553  String type = messageType;
554 
555  Date latestDate = null;
556  if (dateAttrs != null) {
557  latestDate = Stream.of(dateAttrs)
558  .map((attr) -> DataSourceInfoUtilities.getDateOrNull(artifact, attr))
559  .filter((date) -> date != null)
560  .max((a, b) -> a.compareTo(b))
561  .orElse(null);
562  }
563 
564  return (StringUtils.isNotBlank(type) && latestDate != null)
565  ? new TopAccountResult(type, latestDate, artifact)
566  : null;
567  }
568 
583  @Messages({
584  "DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message",
585  "DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log",})
586  public List<TopAccountResult> getRecentAccounts(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
587  assertValidCount(count);
588 
589  if (dataSource == null) {
590  return Collections.emptyList();
591  }
592 
593  Stream<TopAccountResult> messageResults = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), dataSource.getId())
594  .stream()
595  .map((art) -> getMessageAccountResult(art));
596 
597  Stream<TopAccountResult> emailResults = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), dataSource.getId())
598  .stream()
599  .map((art) -> {
600  return getAccountResult(
601  art,
602  Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(),
605  });
606 
607  Stream<TopAccountResult> calllogResults = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(), dataSource.getId())
608  .stream()
609  .map((art) -> {
610  return getAccountResult(
611  art,
612  Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
615  });
616 
617  Stream<TopAccountResult> allResults = Stream.concat(messageResults, Stream.concat(emailResults, calllogResults));
618 
619  // get them grouped by account type
620  Collection<TopAccountResult> groupedResults = allResults
621  // remove null records
622  .filter(result -> result != null)
623  // get these messages grouped by account type and get the most recent of each type
624  .collect(Collectors.toMap(
625  result -> result.getAccountType(),
626  result -> result,
627  (result1, result2) -> TOP_ACCOUNT_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
628  .values();
629 
630  // get account type sorted by most recent date
631  return groupedResults
632  .stream()
633  // get most recent accounts accessed
634  .sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed())
635  // limit to count
636  .limit(count)
637  // get as list
638  .collect(Collectors.toList());
639  }
640 
649  public String getShortFolderName(String strPath, String applicationName) {
650  if (strPath == null) {
651  return "";
652  }
653 
654  List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
655 
656  File file = new File(strPath);
657  while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
658  pathEls.add(file.getName());
659  file = file.getParentFile();
660  }
661 
662  Collections.reverse(pathEls);
663 
664  for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
665  String result = matchEntry.apply(pathEls);
666  if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
667  return result;
668  }
669  }
670 
671  return "";
672  }
673 
681  private TopProgramsResult getTopProgramsResult(BlackboardArtifact artifact) {
682  String programName = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME);
683 
684  // ignore items with no name or a ntos boot identifier
685  if (StringUtils.isBlank(programName) || NTOS_BOOT_IDENTIFIER.equalsIgnoreCase(programName)) {
686  return null;
687  }
688 
689  String path = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PATH);
690 
691  // ignore windows directory
692  if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) {
693  return null;
694  }
695 
696  Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT);
697  Long longCount = (count == null) ? null : (long) count;
698 
699  return new TopProgramsResult(
700  programName,
701  path,
702  longCount,
703  DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME),
704  artifact
705  );
706  }
707 
716  private static Date getMax(Date date1, Date date2) {
717  if (date1 == null) {
718  return date2;
719  } else if (date2 == null) {
720  return date1;
721  } else {
722  return date1.compareTo(date2) > 0 ? date1 : date2;
723  }
724  }
725 
735  private static int nullableCompare(Long long1, Long long2) {
736  if (long1 == null && long2 == null) {
737  return 0;
738  } else if (long1 != null && long2 == null) {
739  return 1;
740  } else if (long1 == null && long2 != null) {
741  return -1;
742  }
743 
744  return Long.compare(long1, long2);
745  }
746 
754  private static boolean isPositiveNum(Long longNum) {
755  return longNum != null && longNum > 0;
756  }
757 
777  public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
778  assertValidCount(count);
779 
780  if (dataSource == null) {
781  return Collections.emptyList();
782  }
783 
784  // Get TopProgramsResults for each TSK_PROG_RUN artifact
785  Collection<TopProgramsResult> results = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), dataSource.getId())
786  .stream()
787  // convert to a TopProgramsResult object or null if missing critical information
788  .map((art) -> getTopProgramsResult(art))
789  // remove any null items
790  .filter((res) -> res != null)
791  // group by the program name and program path
792  // The value will be a TopProgramsResult with the max run times
793  // and most recent last run date for each program name / program path pair.
794  .collect(Collectors.toMap(
795  res -> Pair.of(
796  res.getProgramName() == null ? null : res.getProgramName().toUpperCase(),
797  res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
798  res -> res,
799  (res1, res2) -> {
800  Long maxRunTimes = getMax(res1.getRunTimes(), res2.getRunTimes());
801  Date maxDate = getMax(res1.getLastAccessed(), res2.getLastAccessed());
802  TopProgramsResult maxResult = TOP_PROGRAMS_RESULT_COMPARE.compare(res1, res2) >= 0 ? res1 : res2;
803  return new TopProgramsResult(
804  maxResult.getProgramName(),
805  maxResult.getProgramPath(),
806  maxRunTimes,
807  maxDate,
808  maxResult.getArtifact());
809  })).values();
810 
811  List<TopProgramsResult> orderedResults = results.stream()
812  .sorted(TOP_PROGRAMS_RESULT_COMPARE)
813  .collect(Collectors.toList());
814 
815  // only limit the list to count if there is no last run date and no run times.
816  if (!orderedResults.isEmpty()) {
817  TopProgramsResult topResult = orderedResults.get(0);
818  // if run times / last run information is available, the first item should have some value,
819  // and then the items should be limited accordingly.
820  if (isPositiveNum(topResult.getRunTimes())
821  || (topResult.getLastAccessed() != null && isPositiveNum(topResult.getLastAccessed().getTime()))) {
822  return orderedResults.stream().limit(count).collect(Collectors.toList());
823  }
824  }
825 
826  // otherwise return the alphabetized list with no limit applied.
827  return orderedResults;
828  }
829 
834  public static class LastAccessedArtifact {
835 
836  private final Date lastAccessed;
837  private final BlackboardArtifact artifact;
838 
845  public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) {
846  this.lastAccessed = lastAccessed;
847  this.artifact = artifact;
848  }
849 
853  public Date getLastAccessed() {
854  return lastAccessed;
855  }
856 
860  public BlackboardArtifact getArtifact() {
861  return artifact;
862  }
863  }
864 
868  public static class TopWebSearchResult extends LastAccessedArtifact {
869 
870  private final String searchString;
871  private String translatedResult;
872 
880  public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) {
881  super(dateAccessed, artifact);
882  this.searchString = searchString;
883  }
884 
888  public String getTranslatedResult() {
889  return translatedResult;
890  }
891 
897  public void setTranslatedResult(String translatedResult) {
898  this.translatedResult = translatedResult;
899  }
900 
904  public String getSearchString() {
905  return searchString;
906  }
907  }
908 
912  public static class TopDeviceAttachedResult extends LastAccessedArtifact {
913 
914  private final String deviceId;
915  private final String deviceMake;
916  private final String deviceModel;
917 
927  public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) {
928  super(dateAccessed, artifact);
929  this.deviceId = deviceId;
930  this.deviceMake = deviceMake;
931  this.deviceModel = deviceModel;
932  }
933 
937  public String getDeviceId() {
938  return deviceId;
939  }
940 
944  public String getDeviceMake() {
945  return deviceMake;
946  }
947 
951  public String getDeviceModel() {
952  return deviceModel;
953  }
954  }
955 
960  public static class TopAccountResult extends LastAccessedArtifact {
961 
962  private final String accountType;
963 
971  public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) {
972  super(lastAccess, artifact);
973  this.accountType = accountType;
974  }
975 
979  public String getAccountType() {
980  return accountType;
981  }
982  }
983 
987  public static class TopDomainsResult extends LastAccessedArtifact {
988 
989  private final String domain;
990  private final Long visitTimes;
991 
1000  public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) {
1001  super(lastVisit, artifact);
1002  this.domain = domain;
1003  this.visitTimes = visitTimes;
1004  }
1005 
1009  public String getDomain() {
1010  return domain;
1011  }
1012 
1016  public Long getVisitTimes() {
1017  return visitTimes;
1018  }
1019  }
1020 
1024  public static class TopProgramsResult extends LastAccessedArtifact {
1025 
1026  private final String programName;
1027  private final String programPath;
1028  private final Long runTimes;
1029 
1038  TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) {
1039  super(lastRun, artifact);
1040  this.programName = programName;
1041  this.programPath = programPath;
1042  this.runTimes = runTimes;
1043  }
1044 
1048  public String getProgramName() {
1049  return programName;
1050  }
1051 
1055  public String getProgramPath() {
1056  return programPath;
1057  }
1058 
1062  public Long getRunTimes() {
1063  return runTimes;
1064  }
1065  }
1066 }
static final Comparator< TopWebSearchResult > TOP_WEBSEARCH_RESULT_DATE_COMPARE
TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact)
static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact)
TopDomainsResult getDomainsResult(String domain, List< Pair< BlackboardArtifact, Long >> visits, long mostRecentMs)
List< TopAccountResult > getRecentAccounts(DataSource dataSource, int count)
UserActivitySummary(SleuthkitCaseProvider provider, TextTranslationService translationService, java.util.logging.Logger logger)
List< TopWebSearchResult > getMostRecentWebSearches(DataSource dataSource, int count)
static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact)
List< TopDeviceAttachedResult > getRecentDevices(DataSource dataSource, int count)
TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact)
TopProgramsResult getTopProgramsResult(BlackboardArtifact artifact)
TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2)
static final Comparator< TopAccountResult > TOP_ACCOUNT_RESULT_DATE_COMPARE
TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact)
List< TopProgramsResult > getTopPrograms(DataSource dataSource, int count)
Pair< Long, Map< String, List< Pair< BlackboardArtifact, Long > > > > getDomainGroupsAndMostRecent(DataSource dataSource)
List< TopDomainsResult > getRecentDomains(DataSource dataSource, int count)
static final Comparator< TopProgramsResult > TOP_PROGRAMS_RESULT_COMPARE
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact)
static final List< Function< List< String >, String > > SHORT_FOLDER_MATCHERS
static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type...dateAttrs)

Copyright © 2012-2021 Basis Technology. Generated on: Thu Jul 8 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.