Merge pull request #7814 from gdicristofaro/7072dev2

7072 develop score changes
This commit is contained in:
eugene7646 2023-07-11 15:02:25 -04:00 committed by GitHub
commit 3e3b788a38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 273 additions and 53 deletions

View File

@ -143,14 +143,16 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
// either way, add in reports node // either way, add in reports node
nodes.add(new Reports()); nodes.add(new Reports());
} else { } else {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
// data source by type view // data source by type view
nodes = Arrays.asList( nodes = Arrays.asList(
new DataSourcesByType(), new DataSourcesByType(),
new Views(Case.getCurrentCaseThrows().getSleuthkitCase()), new Views(skCase),
new DataArtifacts(), new DataArtifacts(),
new AnalysisResults(), new AnalysisResults(),
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase()), new OsAccounts(skCase),
new Tags(), new Tags(),
new ScoreContent(skCase),
new Reports() new Reports()
); );
} }

View File

@ -29,6 +29,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
/** /**
* Data source grouping node - an optional grouping node in the data tree view * Data source grouping node - an optional grouping node in the data tree view
@ -74,13 +75,15 @@ class DataSourceGroupingNode extends DisplayableItemNode {
long dsObjId = dataSource.getId(); long dsObjId = dataSource.getId();
try { try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
return new RootContentChildren(Arrays.asList( return new RootContentChildren(Arrays.asList(
new DataSources(dsObjId), new DataSources(dsObjId),
new Views(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId), new Views(skCase, dsObjId),
new DataArtifacts(dsObjId), new DataArtifacts(dsObjId),
new AnalysisResults(dsObjId), new AnalysisResults(dsObjId),
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId), new OsAccounts(skCase, dsObjId),
new Tags(dsObjId) new Tags(dsObjId),
new ScoreContent(skCase, dsObjId)
)); ));
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {

View File

@ -20,33 +20,46 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.sql.SQLException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.WeakListeners; import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler; import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent; import org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.Category; import org.sleuthkit.datamodel.BlackboardArtifact.Category;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.ContentVisitor;
import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.DerivedFile;
@ -110,6 +123,7 @@ public class ScoreContent implements AutopsyVisitableItem {
/** /**
* Constructor assuming no data source filtering. * Constructor assuming no data source filtering.
*
* @param skCase The sleuthkit case. * @param skCase The sleuthkit case.
*/ */
public ScoreContent(SleuthkitCase skCase) { public ScoreContent(SleuthkitCase skCase) {
@ -118,6 +132,7 @@ public class ScoreContent implements AutopsyVisitableItem {
/** /**
* Constructor. * Constructor.
*
* @param skCase The sleuthkit case. * @param skCase The sleuthkit case.
* @param dsObjId The data source object id to filter on if > 0. * @param dsObjId The data source object id to filter on if > 0.
*/ */
@ -153,11 +168,17 @@ public class ScoreContent implements AutopsyVisitableItem {
Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED
); );
private static final Set<String> CASE_EVENTS_OF_INTEREST_STRS = CASE_EVENTS_OF_INTEREST.stream()
.map(evt -> evt.name())
.collect(Collectors.toSet());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestModuleEvent.CONTENT_CHANGED); private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestModuleEvent.CONTENT_CHANGED);
/** /**
* Returns a property change listener listening for possible updates to aggregate score updates for files. * Returns a property change listener listening for possible updates to
* aggregate score updates for files.
*
* @param onRefresh Action on refresh. * @param onRefresh Action on refresh.
* @param onRemove Action to remove listener (i.e. case close). * @param onRemove Action to remove listener (i.e. case close).
* @return The property change listener. * @return The property change listener.
@ -182,7 +203,7 @@ public class ScoreContent implements AutopsyVisitableItem {
if (evt.getNewValue() == null && onRemove != null) { if (evt.getNewValue() == null && onRemove != null) {
onRemove.run(); onRemove.run();
} }
} else if (CASE_EVENTS_OF_INTEREST.contains(eventType)) { } else if (CASE_EVENTS_OF_INTEREST_STRS.contains(eventType)) {
// only refresh if there is a current case. // only refresh if there is a current case.
try { try {
Case.getCurrentCaseThrows(); Case.getCurrentCaseThrows();
@ -199,34 +220,61 @@ public class ScoreContent implements AutopsyVisitableItem {
} }
/** /**
* The sql where statement for the files. * The sql where statement for the content.
*
* @param filter The filter type.
* @param objIdAlias The alias for the object id of the content. Must be sql
* safe.
* @param dsIdAlias The alias for the data source id. Must be sql safe.
* @param filteringDSObjId The data source object id to filter on if > 0.
* @return The sql where statement.
* @throws IllegalArgumentException
*/
private static String getFilter(ScoreContent.ScoreContentFilter filter, String objIdAlias, String dsIdAlias, long filteringDSObjId) throws IllegalArgumentException {
String aggregateScoreFilter = getScoreFilter(filter);
String query = " " + objIdAlias + " IN (SELECT tsk_aggregate_score.obj_id FROM tsk_aggregate_score WHERE " + aggregateScoreFilter + ") ";
if (filteringDSObjId > 0) {
query += " AND " + dsIdAlias + " = " + filteringDSObjId;
}
return query;
}
private static String getScoreFilter(ScoreContentFilter filter) throws IllegalArgumentException {
switch (filter) {
case SUS_ITEM_FILTER:
return " tsk_aggregate_score.significance = " + Significance.LIKELY_NOTABLE.getId()
+ " AND (tsk_aggregate_score.priority = " + Priority.NORMAL.getId() + " OR tsk_aggregate_score.priority = " + Priority.OVERRIDE.getId() + " )";
case BAD_ITEM_FILTER:
return " tsk_aggregate_score.significance = " + Significance.NOTABLE.getId()
+ " AND (tsk_aggregate_score.priority = " + Priority.NORMAL.getId() + " OR tsk_aggregate_score.priority = " + Priority.OVERRIDE.getId() + " )";
default:
throw new IllegalArgumentException(MessageFormat.format("Unsupported filter type to get suspect content: {0}", filter));
}
}
/**
* Returns a sql where statement for files.
*
* @param filter The filter type. * @param filter The filter type.
* @param filteringDSObjId The data source object id to filter on if > 0. * @param filteringDSObjId The data source object id to filter on if > 0.
* @return The sql where statement. * @return The sql where statement.
* @throws IllegalArgumentException * @throws IllegalArgumentException
*/ */
static private String getFileFilter(ScoreContent.ScoreContentFilter filter, long filteringDSObjId) throws IllegalArgumentException { private static String getFileFilter(ScoreContent.ScoreContentFilter filter, long filteringDsObjId) throws IllegalArgumentException {
String aggregateScoreFilter = ""; return getFilter(filter, "obj_id", "data_source_obj_id", filteringDsObjId);
switch (filter) { }
case SUS_ITEM_FILTER:
aggregateScoreFilter = " tsk_aggregate_score.significance = " + Significance.LIKELY_NOTABLE.getId() + " AND (tsk_aggregate_score.priority = " + Priority.NORMAL.getId() + " OR tsk_aggregate_score.priority = " + Priority.OVERRIDE.getId() + " )";
break; /**
case BAD_ITEM_FILTER: * Returns a sql where statement for files.
aggregateScoreFilter = " tsk_aggregate_score.significance = " + Significance.NOTABLE.getId() + " AND (tsk_aggregate_score.priority = " + Priority.NORMAL.getId() + " OR tsk_aggregate_score.priority = " + Priority.OVERRIDE.getId() + " )"; *
break; * @param filter The filter type.
* @param filteringDSObjId The data source object id to filter on if > 0.
default: * @return The sql where statement.
throw new IllegalArgumentException(MessageFormat.format("Unsupported filter type to get suspect content: {0}", filter)); * @throws IllegalArgumentException
*/
} private static String getDataArtifactFilter(ScoreContent.ScoreContentFilter filter, long filteringDsObjId) throws IllegalArgumentException {
return getFilter(filter, "artifacts.artifact_obj_id", "artifacts.data_source_obj_id", filteringDsObjId);
String query = " obj_id IN (SELECT tsk_aggregate_score.obj_id FROM tsk_aggregate_score WHERE " + aggregateScoreFilter + ") ";
if (filteringDSObjId > 0) {
query += " AND data_source_obj_id = " + filteringDSObjId;
}
return query;
} }
/** /**
@ -424,7 +472,34 @@ public class ScoreContent implements AutopsyVisitableItem {
* @return * @return
*/ */
private static long calculateItems(SleuthkitCase sleuthkitCase, ScoreContent.ScoreContentFilter filter, long datasourceObjId) throws TskCoreException { private static long calculateItems(SleuthkitCase sleuthkitCase, ScoreContent.ScoreContentFilter filter, long datasourceObjId) throws TskCoreException {
return sleuthkitCase.countFilesWhere(getFileFilter(filter, datasourceObjId)); AtomicLong retVal = new AtomicLong(0L);
AtomicReference<SQLException> exRef = new AtomicReference(null);
String query = " COUNT(tsk_aggregate_score.obj_id) AS count FROM tsk_aggregate_score WHERE\n"
+ getScoreFilter(filter) + "\n"
+ ((datasourceObjId > 0) ? "AND tsk_aggregate_score.data_source_obj_id = \n" + datasourceObjId : "")
+ " AND tsk_aggregate_score.obj_id IN\n"
+ " (SELECT tsk_files.obj_id AS obj_id FROM tsk_files UNION\n"
+ " SELECT blackboard_artifacts.artifact_obj_id AS obj_id FROM blackboard_artifacts WHERE blackboard_artifacts.artifact_type_id IN\n"
+ " (SELECT artifact_type_id FROM blackboard_artifact_types WHERE category_type = " + Category.DATA_ARTIFACT.getID() + ")) ";
sleuthkitCase.getCaseDbAccessManager().select(query, (rs) -> {
try {
if (rs.next()) {
retVal.set(rs.getLong("count"));
}
} catch (SQLException ex) {
exRef.set(ex);
}
});
SQLException sqlEx = exRef.get();
if (sqlEx != null) {
throw new TskCoreException(
MessageFormat.format("A sql exception occurred fetching results with query: SELECT {0}", query),
sqlEx);
} else {
return retVal.get();
}
} }
@Override @Override
@ -466,7 +541,7 @@ public class ScoreContent implements AutopsyVisitableItem {
/** /**
* Children showing files for a score filter. * Children showing files for a score filter.
*/ */
static class ScoreContentChildren extends BaseChildFactory<AbstractFile> implements RefreshThrottler.Refresher { static class ScoreContentChildren extends BaseChildFactory<Content> implements RefreshThrottler.Refresher {
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this); private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
@ -515,15 +590,21 @@ public class ScoreContent implements AutopsyVisitableItem {
return ScoreContent.isRefreshRequired(evt); return ScoreContent.isRefreshRequired(evt);
} }
private List<AbstractFile> runFsQuery() { private List<Content> runFsQuery() {
List<AbstractFile> ret = new ArrayList<>(); List<Content> ret = new ArrayList<>();
String query = null; String fileFilter = null;
String dataArtifactFilter = null;
try { try {
query = getFileFilter(filter, datasourceObjId); fileFilter = getFileFilter(filter, datasourceObjId);
ret = skCase.findAllFilesWhere(query); dataArtifactFilter = getDataArtifactFilter(filter, datasourceObjId);
ret.addAll(skCase.findAllFilesWhere(fileFilter));
ret.addAll(skCase.getBlackboard().getDataArtifactsWhere(dataArtifactFilter));
} catch (TskCoreException | IllegalArgumentException e) { } catch (TskCoreException | IllegalArgumentException e) {
logger.log(Level.SEVERE, "Error getting files for the deleted content view using: " + StringUtils.defaultString(query, "<null>"), e); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format(
"Error getting files for the deleted content view using file filter: {0} data artifact filter: {1}",
StringUtils.defaultString(fileFilter, "<null>"),
StringUtils.defaultString(dataArtifactFilter, "<null>")), e); //NON-NLS
} }
return ret; return ret;
@ -531,66 +612,201 @@ public class ScoreContent implements AutopsyVisitableItem {
} }
@Override @Override
protected List<AbstractFile> makeKeys() { protected List<Content> makeKeys() {
return runFsQuery(); return runFsQuery();
} }
@Override @Override
protected Node createNodeForKey(AbstractFile key) { protected Node createNodeForKey(Content key) {
return key.accept(new ContentVisitor.Default<AbstractNode>() { return key.accept(new ContentVisitor.Default<AbstractNode>() {
public FileNode visit(AbstractFile f) { public FileNode visit(AbstractFile f) {
return new FileNode(f, false); return new ScoreFileNode(f, false);
} }
public FileNode visit(FsContent f) { public FileNode visit(FsContent f) {
return new FileNode(f, false); return new ScoreFileNode(f, false);
} }
@Override @Override
public FileNode visit(LayoutFile f) { public FileNode visit(LayoutFile f) {
return new FileNode(f, false); return new ScoreFileNode(f, false);
} }
@Override @Override
public FileNode visit(File f) { public FileNode visit(File f) {
return new FileNode(f, false); return new ScoreFileNode(f, false);
} }
@Override @Override
public FileNode visit(Directory f) { public FileNode visit(Directory f) {
return new FileNode(f, false); return new ScoreFileNode(f, false);
} }
@Override @Override
public FileNode visit(VirtualDirectory f) { public FileNode visit(VirtualDirectory f) {
return new FileNode(f, false); return new ScoreFileNode(f, false);
} }
@Override @Override
public AbstractNode visit(SlackFile sf) { public AbstractNode visit(SlackFile sf) {
return new FileNode(sf, false); return new ScoreFileNode(sf, false);
} }
@Override @Override
public AbstractNode visit(LocalFile lf) { public AbstractNode visit(LocalFile lf) {
return new FileNode(lf, false); return new ScoreFileNode(lf, false);
} }
@Override @Override
public AbstractNode visit(DerivedFile df) { public AbstractNode visit(DerivedFile df) {
return new FileNode(df, false); return new ScoreFileNode(df, false);
} }
@Override
public AbstractNode visit(BlackboardArtifact ba) {
return new ScoreArtifactNode(ba);
}
@Override @Override
protected AbstractNode defaultVisit(Content di) { protected AbstractNode defaultVisit(Content di) {
if (di instanceof AbstractFile) { if (di instanceof AbstractFile) {
return visit((AbstractFile) di); return visit((AbstractFile) di);
} else { } else {
throw new UnsupportedOperationException("Not supported for this type of Displayable Item: " + di.toString()); throw new UnsupportedOperationException("Not supported for this type of Displayable Item: " + di.toString());
} }
} }
}); });
} }
} }
} }
private static final String SOURCE_PROP = "Source";
private static final String TYPE_PROP = "Type";
private static final String PATH_PROP = "Path";
private static final String DATE_PROP = "Created Date";
private static Sheet createScoreSheet(String type, String path, Long time) {
Sheet sheet = new Sheet();
Sheet.Set sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
List<NodeProperty<?>> properties = new ArrayList<>();
properties.add(new NodeProperty<>(
SOURCE_PROP,
SOURCE_PROP,
NO_DESCR,
StringUtils.defaultString(path)));
properties.add(new NodeProperty<>(
TYPE_PROP,
TYPE_PROP,
NO_DESCR,
type));
if (StringUtils.isNotBlank(path)) {
properties.add(new NodeProperty<>(
PATH_PROP,
PATH_PROP,
NO_DESCR,
path));
}
if (time != null && time > 0) {
properties.add(new NodeProperty<>(
DATE_PROP,
DATE_PROP,
NO_DESCR,
TimeZoneUtils.getFormattedTime(time)));
}
properties.forEach((property) -> {
sheetSet.put(property);
});
return sheet;
}
public static class ScoreArtifactNode extends BlackboardArtifactNode {
private static final Logger logger = Logger.getLogger(ScoreArtifactNode.class.getName());
private static final List<BlackboardAttribute.Type> TIME_ATTRS = Arrays.asList(
BlackboardAttribute.Type.TSK_DATETIME,
BlackboardAttribute.Type.TSK_DATETIME_ACCESSED,
BlackboardAttribute.Type.TSK_DATETIME_RCVD,
BlackboardAttribute.Type.TSK_DATETIME_SENT,
BlackboardAttribute.Type.TSK_DATETIME_CREATED,
BlackboardAttribute.Type.TSK_DATETIME_MODIFIED,
BlackboardAttribute.Type.TSK_DATETIME_START,
BlackboardAttribute.Type.TSK_DATETIME_END,
BlackboardAttribute.Type.TSK_DATETIME_DELETED,
BlackboardAttribute.Type.TSK_DATETIME_PASSWORD_RESET,
BlackboardAttribute.Type.TSK_DATETIME_PASSWORD_FAIL
);
private static final Map<Integer, Integer> TIME_ATTR_IMPORTANCE = IntStream.range(0, TIME_ATTRS.size())
.mapToObj(idx -> Pair.of(TIME_ATTRS.get(idx).getTypeID(), idx))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v1));
public ScoreArtifactNode(BlackboardArtifact artifact) {
super(artifact);
}
private Long getTime(BlackboardArtifact artifact) {
try {
BlackboardAttribute timeAttr = artifact.getAttributes().stream()
.filter((attr) -> TIME_ATTR_IMPORTANCE.keySet().contains(attr.getAttributeType().getTypeID()))
.sorted(Comparator.comparing(attr -> TIME_ATTR_IMPORTANCE.get(attr.getAttributeType().getTypeID())))
.findFirst()
.orElse(null);
if (timeAttr != null) {
return timeAttr.getValueLong();
} else {
return (artifact.getParent() instanceof AbstractFile) ? ((AbstractFile) artifact.getParent()).getCtime() : null;
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "An exception occurred while fetching time for artifact", ex);
return null;
}
}
@Override
protected synchronized Sheet createSheet() {
try {
return createScoreSheet(
this.content.getType().getDisplayName(),
this.content.getUniquePath(),
getTime(this.content)
);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "An error occurred while fetching sheet data for score artifact.", ex);
return new Sheet();
}
}
}
@Messages("ScoreContent_ScoreFileNode_type=File")
public static class ScoreFileNode extends FileNode {
private static final Logger logger = Logger.getLogger(ScoreFileNode.class.getName());
public ScoreFileNode(AbstractFile af, boolean directoryBrowseMode) {
super(af, directoryBrowseMode);
}
@Override
protected synchronized Sheet createSheet() {
try {
return createScoreSheet(
Bundle.ScoreContent_ScoreFileNode_type(),
this.content.getUniquePath(),
this.content.getCtime()
);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "An error occurred while fetching sheet data for score file.", ex);
return new Sheet();
}
}
}
} }

View File

@ -46,8 +46,7 @@ public class ViewsNode extends DisplayableItemNode {
// add it back in if we can filter the results to a more managable size. // add it back in if we can filter the results to a more managable size.
// new RecentFiles(sleuthkitCase), // new RecentFiles(sleuthkitCase),
new DeletedContent(sleuthkitCase, dsObjId), new DeletedContent(sleuthkitCase, dsObjId),
new FileSize(sleuthkitCase, dsObjId), new FileSize(sleuthkitCase, dsObjId))
new ScoreContent(sleuthkitCase, dsObjId))
), ),
Lookups.singleton(NAME) Lookups.singleton(NAME)
); );