Implemented the DoD, tested and commented the code

This commit is contained in:
U-BASIS\dsmyda 2020-12-16 17:38:47 -05:00
parent 37e4dce789
commit 477d8c6d72
10 changed files with 195 additions and 108 deletions

View File

@ -1,3 +1,7 @@
# {0} - month abbreviation
# {1} - day of month
# {2} - year
DiscoveryAttributes.ActivityDateGroupKey.getDisplayNameTemplate=Week of {0} {1}, {2}
DiscoveryAttributes.GroupingAttributeType.datasource.displayName=Data Source
DiscoveryAttributes.GroupingAttributeType.fileType.displayName=File Type
DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date
@ -20,11 +24,9 @@ DiscoveryKeyUtils.DataSourceGroupKey.datasourceAndID={0}(ID: {1})
# {0} - Data source ID
DiscoveryKeyUtils.DataSourceGroupKey.idOnly=Data source (ID: {0})
DiscoveryKeyUtils.FileTagGroupKey.noSets=None
DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available
DiscoveryKeyUtils.HashHitsGroupKey.noHashHits=None
DiscoveryKeyUtils.InterestingItemGroupKey.noSets=None
DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None
DiscoveryKeyUtils.LastActivityDateGroupKey.noDate=No Date Available
DiscoveryKeyUtils.NoGroupingGroupKey.allFiles=All Files
DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None
# {0} - domain

View File

@ -18,18 +18,21 @@
*/
package org.sleuthkit.autopsy.discovery.search;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.TimeZone;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.discovery.search.SearchData.PageViews;
import org.sleuthkit.autopsy.discovery.ui.MonthAbbreviation;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
@ -1134,29 +1137,34 @@ public class DiscoveryKeyUtils {
*/
static class LastActivityDateGroupKey extends GroupKey {
private final Long epochDate;
private final String dateNameString;
private ZonedDateTime currentWeekCutOff;
/**
* Construct a new LastActivityDateGroupKey.
*
* @param result The Result to create the group key for.
*/
@NbBundle.Messages({
"DiscoveryKeyUtils.LastActivityDateGroupKey.noDate=No Date Available"})
LastActivityDateGroupKey(Result result) {
if (result instanceof ResultDomain) {
epochDate = ((ResultDomain) result).getActivityEnd();
dateNameString = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(epochDate)));
ResultDomain domainResult = ((ResultDomain) result);
currentWeekCutOff = getCurrentWeekCutOff(domainResult.getActivityEnd(), domainResult);
} else {
epochDate = Long.MAX_VALUE;
dateNameString = Bundle.DiscoveryKeyUtils_LastActivityDateGroupKey_noDate();
throw new IllegalArgumentException("Expected a domain result only.");
}
}
@NbBundle.Messages({
"# {0} - month abbreviation",
"# {1} - day of month",
"# {2} - year",
"DiscoveryAttributes.ActivityDateGroupKey.getDisplayNameTemplate=Week of {0} {1}, {2}"
})
@Override
String getDisplayName() {
return getDateNameString();
MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(currentWeekCutOff.getMonthValue());
return Bundle.DiscoveryAttributes_ActivityDateGroupKey_getDisplayNameTemplate(
currentCutOffMonth.toString(), Integer.toString(currentWeekCutOff.getDayOfMonth()),
Integer.toString(currentWeekCutOff.getYear()));
}
@Override
@ -1170,53 +1178,40 @@ public class DiscoveryKeyUtils {
}
LastActivityDateGroupKey dateGroupKey = (LastActivityDateGroupKey) otherKey;
return getDateNameString().equals(dateGroupKey.getDateNameString());
return getDisplayName().equals(dateGroupKey.getDisplayName());
}
@Override
public int hashCode() {
return Objects.hash(getDateNameString());
return Objects.hash(getDisplayName());
}
@Override
public int compareTo(GroupKey otherGroupKey) {
if (otherGroupKey instanceof LastActivityDateGroupKey) {
LastActivityDateGroupKey otherDateGroupKey = (LastActivityDateGroupKey) otherGroupKey;
// Put the empty list at the end
if (this.getEpochDate().equals(Long.MAX_VALUE)) {
if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return 0;
} else {
return 1;
}
} else if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return -1;
}
return getDateNameString().compareTo(otherDateGroupKey.getDateNameString());
return Long.compare(otherDateGroupKey.currentWeekCutOff.toEpochSecond(), currentWeekCutOff.toEpochSecond());
} else {
return compareClassNames(otherGroupKey);
}
}
/**
* Get the date this group is for as a Long.
*
* @return The date.
*/
Long getEpochDate() {
return epochDate;
}
/**
* Get the name which identifies this group.
*
* @return The dateNameString.
*/
String getDateNameString() {
return dateNameString;
}
}
/**
* Get the next closed Sunday given an epoch time and timezone.
* Dates for grouping are managed on a weekly basis. Each Sunday
* acts as the boundary and representative for the week.
*/
private static ZonedDateTime getCurrentWeekCutOff(long epochSeconds, ResultDomain domainResult) {
Instant startActivityAsInsant = Instant.ofEpochSecond(epochSeconds);
// Determines the timezone using the settings panel or value parsed from the
// parent data source
TimeZone currentTimeZone = ContentUtils.getTimeZone(domainResult.getDataSource());
// Convert to a datetime using epoch and timezone.
ZonedDateTime startActivityAsDateTime = ZonedDateTime.ofInstant(startActivityAsInsant, currentTimeZone.toZoneId());
// Get the closest Sunday, which is the cut off for the current week.
// Use this cut off to perform grouping and comparing.
return startActivityAsDateTime.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
}
/**
@ -1224,29 +1219,28 @@ public class DiscoveryKeyUtils {
*/
static class FirstActivityDateGroupKey extends GroupKey {
private final Long epochDate;
private final String dateNameString;
private ZonedDateTime currentWeekCutOff;
/**
* Construct a new FirstActivityDateGroupKey.
*
* @param result The Result to create the group key for.
*/
@NbBundle.Messages({
"DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available"})
FirstActivityDateGroupKey(Result result) {
if (result instanceof ResultDomain) {
epochDate = ((ResultDomain) result).getActivityStart();
dateNameString = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(epochDate)));
ResultDomain domainResult = ((ResultDomain) result);
currentWeekCutOff = getCurrentWeekCutOff(domainResult.getActivityStart(), domainResult);
} else {
epochDate = Long.MAX_VALUE;
dateNameString = Bundle.DiscoveryKeyUtils_FirstActivityDateGroupKey_noDate();
throw new IllegalArgumentException("Expected a domain result only.");
}
}
@Override
String getDisplayName() {
return getDateNameString();
MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(currentWeekCutOff.getMonthValue());
return Bundle.DiscoveryAttributes_ActivityDateGroupKey_getDisplayNameTemplate(
currentCutOffMonth.toString(), Integer.toString(currentWeekCutOff.getDayOfMonth()),
Integer.toString(currentWeekCutOff.getYear()));
}
@Override
@ -1260,53 +1254,23 @@ public class DiscoveryKeyUtils {
}
FirstActivityDateGroupKey dateGroupKey = (FirstActivityDateGroupKey) otherKey;
return getDateNameString().equals(dateGroupKey.getDateNameString());
return getDisplayName().equals(dateGroupKey.getDisplayName());
}
@Override
public int hashCode() {
return Objects.hash(getDateNameString());
return Objects.hash(getDisplayName());
}
@Override
public int compareTo(GroupKey otherGroupKey) {
if (otherGroupKey instanceof FirstActivityDateGroupKey) {
FirstActivityDateGroupKey otherDateGroupKey = (FirstActivityDateGroupKey) otherGroupKey;
// Put the empty list at the end
if (this.getEpochDate().equals(Long.MAX_VALUE)) {
if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return 0;
} else {
return 1;
}
} else if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return -1;
}
return getDateNameString().compareTo(otherDateGroupKey.getDateNameString());
return Long.compare(otherDateGroupKey.currentWeekCutOff.toEpochSecond(), currentWeekCutOff.toEpochSecond());
} else {
return compareClassNames(otherGroupKey);
}
}
/**
* Get the date this group is for as a Long.
*
* @return The date.
*/
Long getEpochDate() {
return epochDate;
}
/**
* Get the name which identifies this group.
*
* @return The dateNameString.
*/
String getDateNameString() {
return dateNameString;
}
}
/**

View File

@ -24,12 +24,15 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException;
/**
@ -245,7 +248,8 @@ public class DomainSearch {
private String getDate(BlackboardArtifact artifact) throws TskCoreException {
for (BlackboardAttribute attribute : artifact.getAttributes()) {
if (attribute.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) {
String dateString = attribute.getDisplayString();
TimeZone timeZone = ContentUtils.getTimeZone(artifact);
String dateString = TimeUtilities.epochToTime(attribute.getValueLong(), timeZone);
if (dateString.length() >= 10) {
return dateString.substring(0, 10);
}

View File

@ -212,13 +212,14 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
final StringJoiner whereClause = new StringJoiner(" OR ");
final StringJoiner havingClause = new StringJoiner(" AND ");
String artifactTypeFilter = null;
// Capture all types by default.
ArtifactTypeFilter artifactTypeFilter = new ArtifactTypeFilter(SearchData.Type.DOMAIN.getArtifactTypes());
boolean hasDateTimeFilter = false;
for (AbstractFilter filter : filters) {
if (filter instanceof ArtifactTypeFilter) {
artifactTypeFilter = ((ArtifactTypeFilter) filter)
.getWhereClause(Arrays.asList(TSK_WEB_ACCOUNT_TYPE));
// Replace with user defined types.
artifactTypeFilter = ((ArtifactTypeFilter) filter);
} else if (!(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) {
if (filter instanceof ArtifactDateRangeFilter) {
hasDateTimeFilter = true;
@ -240,7 +241,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
havingClause.add("SUM(CASE WHEN " + domainAttributeFilter + " THEN 1 ELSE 0 END) > 0");
return Pair.of(
whereClause.toString() + ((artifactTypeFilter != null) ? " AND (" + artifactTypeFilter + ")" : ""),
whereClause.toString() + " AND (" + artifactTypeFilter.getWhereClause(Arrays.asList(TSK_WEB_ACCOUNT_TYPE)) + ")",
havingClause.toString()
);
}

View File

@ -151,7 +151,7 @@ public class ResultDomain extends Result {
}
@Override
public Content getDataSource() throws TskCoreException {
public Content getDataSource() {
return this.dataSource;
}

View File

@ -36,7 +36,13 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
public final class SearchData {
private final static long BYTES_PER_MB = 1000000;
private static final Set<BlackboardArtifact.ARTIFACT_TYPE> DOMAIN_ARTIFACT_TYPES = EnumSet.of(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY);
private static final Set<BlackboardArtifact.ARTIFACT_TYPE> DOMAIN_ARTIFACT_TYPES =
EnumSet.of(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK,
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE,
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE,
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD,
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY,
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY);
/**

View File

@ -33,6 +33,7 @@ import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@ -206,7 +207,7 @@ public class SearchFiltering {
*/
public static class ArtifactTypeFilter extends AbstractFilter {
private final List<ARTIFACT_TYPE> types;
private final Collection<ARTIFACT_TYPE> types;
/**
* Construct a new ArtifactTypeFilter.
@ -214,7 +215,7 @@ public class SearchFiltering {
* @param types The list of BlackboardArtifact types to include in
* results from.
*/
public ArtifactTypeFilter(List<ARTIFACT_TYPE> types) {
public ArtifactTypeFilter(Collection<ARTIFACT_TYPE> types) {
this.types = types;
}
@ -223,8 +224,8 @@ public class SearchFiltering {
*
* @return The list of artifact types specified by the filter.
*/
public List<ARTIFACT_TYPE> getTypes() {
return Collections.unmodifiableList(types);
public Collection<ARTIFACT_TYPE> getTypes() {
return Collections.unmodifiableCollection(types);
}
private StringJoiner joinStandardArtifactTypes() {

View File

@ -72,6 +72,18 @@ MiniTimelineArtifactListPanel.value.noValue=No value available.
MiniTimelineDateListPanel.countColumn.name=Count
MiniTimelineDateListPanel.dateColumn.name=Date
MiniTimelineDateListPanel.value.noValue=No value available.
MonthAbbreviation.aprilAbbrev=Apr
MonthAbbreviation.augustAbbrev=Aug
MonthAbbreviation.decemberAbbrev=Dec
MonthAbbreviation.feburaryAbbrev=Feb
MonthAbbreviation.januraryAbbrev=Jan
MonthAbbreviation.julyAbbrev=Jul
MonthAbbreviation.juneAbbrev=Jun
MonthAbbreviation.marchAbbrev=Mar
MonthAbbreviation.mayAbbrev=May
MonthAbbreviation.novemberAbbrev=Nov
MonthAbbreviation.octoberAbbrev=Oct
MonthAbbreviation.septemberAbbrev=Sep
ObjectDetectedFilterPanel.error.text=At least one object type name must be selected.
ParentFolderFilterPanel.error.text=At least one parent path must be entered.
PastOccurrencesFilterPanel.error.text=At least one value in the past occurrence filter must be selected.

View File

@ -22,15 +22,16 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.TimeZone;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
/**
* Class which displays a preview and details about a domain.
@ -39,7 +40,6 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
private static final long serialVersionUID = 1L;
private static final Color SELECTION_COLOR = new Color(0, 120, 215);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy", Locale.getDefault());
/**
* Creates new form DomainPanel.
@ -149,8 +149,9 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
@Override
public Component getListCellRendererComponent(JList<? extends DomainWrapper> list, DomainWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
domainNameLabel.setText(value.getResultDomain().getDomain());
String startDate = dateFormat.format(new Date(value.getResultDomain().getActivityStart() * 1000));
String endDate = dateFormat.format(new Date(value.getResultDomain().getActivityEnd() * 1000));
TimeZone timeZone = ContentUtils.getTimeZone(value.getResultDomain().getDataSource());
String startDate = formatDate(value.getResultDomain().getActivityStart(), timeZone);
String endDate = formatDate(value.getResultDomain().getActivityEnd(), timeZone);
activityLabel.setText(Bundle.DomainSummaryPanel_activity_text(startDate, endDate));
totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews());
pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days());
@ -165,6 +166,22 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
return this;
}
/**
* Formats an epoch time in a given time zone using the following pattern
*
* MMM dd YYYY
*
* The pattern below is formatted manually to reuse the MonthAbbreviation utility.
*/
private String formatDate(long epochSeconds, TimeZone timeZone) {
Instant epochSecondsAsInstant = Instant.ofEpochSecond(epochSeconds);
ZonedDateTime dateTime = ZonedDateTime.ofInstant(epochSecondsAsInstant, timeZone.toZoneId());
MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(dateTime.getMonthValue());
return String.format("%s %02d %04d",
currentCutOffMonth.toString(),
dateTime.getDayOfMonth(), dateTime.getYear());
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override

View File

@ -0,0 +1,80 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import org.openide.util.NbBundle;
/**
* Utility for representing month abbreviations
*/
@NbBundle.Messages({
"MonthAbbreviation.januraryAbbrev=Jan",
"MonthAbbreviation.feburaryAbbrev=Feb",
"MonthAbbreviation.marchAbbrev=Mar",
"MonthAbbreviation.aprilAbbrev=Apr",
"MonthAbbreviation.mayAbbrev=May",
"MonthAbbreviation.juneAbbrev=Jun",
"MonthAbbreviation.julyAbbrev=Jul",
"MonthAbbreviation.augustAbbrev=Aug",
"MonthAbbreviation.septemberAbbrev=Sep",
"MonthAbbreviation.octoberAbbrev=Oct",
"MonthAbbreviation.novemberAbbrev=Nov",
"MonthAbbreviation.decemberAbbrev=Dec"
})
public enum MonthAbbreviation {
JANURARY(Bundle.MonthAbbreviation_januraryAbbrev()),
FEBURARY(Bundle.MonthAbbreviation_feburaryAbbrev()),
MARCH(Bundle.MonthAbbreviation_marchAbbrev()),
APRIL(Bundle.MonthAbbreviation_aprilAbbrev()),
MAY(Bundle.MonthAbbreviation_mayAbbrev()),
JUNE(Bundle.MonthAbbreviation_juneAbbrev()),
JULY(Bundle.MonthAbbreviation_julyAbbrev()),
AUGUST(Bundle.MonthAbbreviation_augustAbbrev()),
SEPTEMBER(Bundle.MonthAbbreviation_septemberAbbrev()),
OCTOBER(Bundle.MonthAbbreviation_octoberAbbrev()),
NOVEMBER(Bundle.MonthAbbreviation_novemberAbbrev()),
DECEMBER(Bundle.MonthAbbreviation_decemberAbbrev());
private final String abbreviation;
MonthAbbreviation(String abbreviation) {
this.abbreviation = abbreviation;
}
@Override
public String toString() {
return this.abbreviation;
}
/**
* Converts a month value (1-12) to the appropriate abbreviation.
*
* @param value Month value (1-12).
* @return Abbreviation matching the month value, null if not found.
*/
public static MonthAbbreviation fromMonthValue(int value) {
MonthAbbreviation[] months = MonthAbbreviation.values();
for(int i = 0; i < months.length; i++) {
if (i + 1 == value) {
return months[i];
}
}
return null;
}
}