mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge pull request #7814 from gdicristofaro/7072dev2
7072 develop score changes
This commit is contained in:
commit
3e3b788a38
@ -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()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user