mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
show hash hit pin on groups and add hash hit counts to tooltip
This commit is contained in:
parent
497ce91da4
commit
6537ebe92c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Copyright 2013-15 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,14 +18,11 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.events;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.joda.time.Interval;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.timeline.events.type.EventType;
|
||||
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
@ -47,26 +44,19 @@ public class AggregateEvent {
|
||||
|
||||
private final DescriptionLOD lod;
|
||||
|
||||
public AggregateEvent(Interval spanningInterval, EventType type, Set<Long> eventIDs, String description, DescriptionLOD lod) {
|
||||
private final Set<Long> hashHits;
|
||||
|
||||
public AggregateEvent(Interval spanningInterval, EventType type, Set<Long> eventIDs, Set<Long> hashHits, String description, DescriptionLOD lod) {
|
||||
|
||||
this.span = spanningInterval;
|
||||
this.type = type;
|
||||
this.hashHits = hashHits;
|
||||
this.description = description;
|
||||
|
||||
this.eventIDs = eventIDs;
|
||||
this.lod = lod;
|
||||
}
|
||||
|
||||
public AggregateEvent(Interval spanningInterval, EventType type, List<String> events, String description, DescriptionLOD lod) {
|
||||
|
||||
this.span = spanningInterval;
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
|
||||
this.eventIDs = new HashSet<>(Collections2.transform(events, Long::valueOf));
|
||||
this.lod = lod;
|
||||
}
|
||||
|
||||
/** @return the actual interval from the first event to the last event */
|
||||
public Interval getSpan() {
|
||||
return span;
|
||||
@ -76,6 +66,10 @@ public class AggregateEvent {
|
||||
return Collections.unmodifiableSet(eventIDs);
|
||||
}
|
||||
|
||||
public Set<Long> getEventIDsWithHashHits() {
|
||||
return Collections.unmodifiableSet(hashHits);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
@ -101,11 +95,10 @@ public class AggregateEvent {
|
||||
if (!ag1.getDescription().equals(ag2.getDescription())) {
|
||||
throw new IllegalArgumentException("aggregate events are not compatible they have different descriptions");
|
||||
}
|
||||
HashSet<Long> ids = new HashSet<>(ag1.getEventIDs());
|
||||
ids.addAll(ag2.getEventIDs());
|
||||
Sets.SetView<Long> idsUnion = Sets.union(ag1.getEventIDs(), ag2.getEventIDs());
|
||||
Sets.SetView<Long> hashHitsUnion = Sets.union(ag1.getEventIDsWithHashHits(), ag2.getEventIDsWithHashHits());
|
||||
|
||||
//TODO: check that types/descriptions are actually the same -jm
|
||||
return new AggregateEvent(IntervalUtils.span(ag1.span, ag2.span), ag1.getType(), ids, ag1.getDescription(), ag1.lod);
|
||||
return new AggregateEvent(IntervalUtils.span(ag1.span, ag2.span), ag1.getType(), idsUnion, hashHitsUnion, ag1.getDescription(), ag1.lod);
|
||||
}
|
||||
|
||||
public DescriptionLOD getLOD() {
|
||||
|
@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.timeline.events.type.RootEventType;
|
||||
import org.sleuthkit.autopsy.timeline.filters.DataSourceFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.DataSourcesFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.Filter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.HashHitFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.HideKnownFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.IntersectionFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.RootFilter;
|
||||
@ -106,7 +107,7 @@ public final class FilteredEventsModel {
|
||||
dataSourceFilter.setSelected(Boolean.TRUE);
|
||||
dataSourcesFilter.addDataSourceFilter(dataSourceFilter);
|
||||
});
|
||||
return new RootFilter(new HideKnownFilter(), new TextFilter(), new TypeFilter(RootEventType.getInstance()), dataSourcesFilter);
|
||||
return new RootFilter(new HideKnownFilter(), new HashHitFilter(), new TextFilter(), new TypeFilter(RootEventType.getInstance()), dataSourcesFilter);
|
||||
}
|
||||
|
||||
public FilteredEventsModel(EventsRepository repo, ReadOnlyObjectProperty<ZoomParams> currentStateProperty) {
|
||||
@ -151,6 +152,9 @@ public final class FilteredEventsModel {
|
||||
public TimeLineEvent getEventById(Long eventID) {
|
||||
return repo.getEventById(eventID);
|
||||
}
|
||||
public Set<TimeLineEvent> getEventsById(Collection<Long> eventIDs) {
|
||||
return repo.getEventsById(eventIDs);
|
||||
}
|
||||
|
||||
public Set<Long> getEventIDs(Interval timeRange, Filter filter) {
|
||||
final Interval overlap;
|
||||
@ -279,5 +283,4 @@ public final class FilteredEventsModel {
|
||||
return requestedLOD.get();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 Basis Technology Corp.
|
||||
* Copyright 2014-15 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -28,7 +28,9 @@ public class TimeLineEvent {
|
||||
|
||||
private final Long eventID;
|
||||
|
||||
private final Long fileID, time;
|
||||
private final Long fileID;
|
||||
|
||||
private final Long time;
|
||||
|
||||
private final Long artifactID;
|
||||
|
||||
@ -38,6 +40,26 @@ public class TimeLineEvent {
|
||||
|
||||
private final TskData.FileKnown known;
|
||||
|
||||
private final boolean hashHit;
|
||||
|
||||
public TimeLineEvent(Long eventID, Long objID, Long artifactID, Long time, EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known, boolean hashHit) {
|
||||
this.eventID = eventID;
|
||||
this.fileID = objID;
|
||||
this.artifactID = artifactID;
|
||||
this.time = time;
|
||||
this.subType = type;
|
||||
|
||||
this.fullDescription = fullDescription;
|
||||
this.medDescription = medDescription;
|
||||
this.shortDescription = shortDescription;
|
||||
this.known = known;
|
||||
this.hashHit = hashHit;
|
||||
}
|
||||
|
||||
public boolean isHashHit() {
|
||||
return hashHit;
|
||||
}
|
||||
|
||||
public Long getArtifactID() {
|
||||
return artifactID;
|
||||
}
|
||||
@ -71,20 +93,6 @@ public class TimeLineEvent {
|
||||
return shortDescription;
|
||||
}
|
||||
|
||||
public TimeLineEvent(Long eventID, Long objID, Long artifactID, Long time,
|
||||
EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known) {
|
||||
this.eventID = eventID;
|
||||
this.fileID = objID;
|
||||
this.artifactID = artifactID;
|
||||
this.time = time;
|
||||
this.subType = type;
|
||||
|
||||
this.fullDescription = fullDescription;
|
||||
this.medDescription = medDescription;
|
||||
this.shortDescription = shortDescription;
|
||||
this.known = known;
|
||||
}
|
||||
|
||||
public TskData.FileKnown getKnown() {
|
||||
return known;
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -44,6 +43,8 @@ import java.util.TimeZone;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.Interval;
|
||||
@ -89,7 +90,8 @@ public class EventDB {
|
||||
FULL_DESCRIPTION("full_description"), // NON-NLS
|
||||
MED_DESCRIPTION("med_description"), // NON-NLS
|
||||
SHORT_DESCRIPTION("short_description"), // NON-NLS
|
||||
TIME("time"); // NON-NLS
|
||||
TIME("time"),
|
||||
HASH_HIT("hash_hit"); // NON-NLS
|
||||
|
||||
private final String columnName;
|
||||
|
||||
@ -358,10 +360,10 @@ public class EventDB {
|
||||
return getDBInfo(DBInfoKey.LAST_OBJECT_ID, -1);
|
||||
}
|
||||
|
||||
boolean hasDataSourceInfo() {
|
||||
boolean hasNewColumns() {
|
||||
/* this relies on the fact that no tskObj has ID 0 but 0 is the default
|
||||
* value for the datasource_id column in the events table. */
|
||||
return hasDataSourceIDColumn()
|
||||
return hasHashHitColumn() && hasDataSourceIDColumn()
|
||||
&& (getDataSourceIDs().isEmpty() == false);
|
||||
}
|
||||
|
||||
@ -466,26 +468,35 @@ public class EventDB {
|
||||
+ " full_description TEXT, " // NON-NLS
|
||||
+ " med_description TEXT, " // NON-NLS
|
||||
+ " short_description TEXT, " // NON-NLS
|
||||
+ " known_state INTEGER)"; // NON-NLS
|
||||
+ " known_state INTEGER,"
|
||||
+ " hash_hit INTEGER)"; //boolean // NON-NLS
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "problem creating database table", ex); // NON-NLS
|
||||
}
|
||||
|
||||
boolean hasDSInfo = hasDataSourceIDColumn();
|
||||
if (hasDSInfo == false) {
|
||||
if (hasDataSourceIDColumn() == false) {
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
String sql = "ALTER TABLE events ADD COLUMN datasource_id INTEGER"; // NON-NLS
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "problem creating database table", ex); // NON-NLS
|
||||
LOGGER.log(Level.SEVERE, "problem upgrading database table", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
if (hasHashHitColumn() == false) {
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
String sql = "ALTER TABLE events ADD COLUMN hash_hit INTEGER"; // NON-NLS
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "problem upgrading database table", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
insertRowStmt = prepareStatement(
|
||||
"INSERT INTO events (datasource_id,file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description, known_state) " // NON-NLS
|
||||
+ "VALUES (?,?,?,?,?,?,?,?,?,?)"); // NON-NLS
|
||||
"INSERT INTO events (datasource_id,file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description, known_state, hash_hit) " // NON-NLS
|
||||
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?)"); // NON-NLS
|
||||
|
||||
getDataSourceIDsStmt = prepareStatement("select distinct datasource_id from events"); // NON-NLS
|
||||
getMaxTimeStmt = prepareStatement("select Max(time) as max from events"); // NON-NLS
|
||||
@ -496,14 +507,7 @@ public class EventDB {
|
||||
} catch (SQLException sQLException) {
|
||||
LOGGER.log(Level.SEVERE, "failed to prepareStatment", sQLException); // NON-NLS
|
||||
}
|
||||
if (hasDataSourceInfo() == false) {
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
String sql = "ALTER TABLE events ADD COLUMN datasource_id INTEGER"; // NON-NLS
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "problem creating database table", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
String sql = "CREATE INDEX if not exists file_idx ON events(file_id)"; // NON-NLS
|
||||
@ -551,24 +555,41 @@ public class EventDB {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasDataSourceIDColumn() {
|
||||
/**
|
||||
*
|
||||
* @param dbColumn the value of dbColumn
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
private boolean hasDBColumn(final EventTableColumn dbColumn) {
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
|
||||
ResultSet executeQuery = stmt.executeQuery("PRAGMA table_info(events)");
|
||||
while (executeQuery.next()) {
|
||||
if (EventTableColumn.DATA_SOURCE_ID.toString().equals(executeQuery.getString("name"))) {
|
||||
if (dbColumn.toString().equals(executeQuery.getString("name"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "problem creating database table", ex); // NON-NLS
|
||||
LOGGER.log(Level.SEVERE, "problem executing pragma", ex); // NON-NLS
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void insertEvent(long time, EventType type, long datasourceID, Long objID, Long artifactID, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known) {
|
||||
private boolean hasDataSourceIDColumn() {
|
||||
return hasDBColumn(EventTableColumn.DATA_SOURCE_ID);
|
||||
}
|
||||
|
||||
private boolean hasHashHitColumn() {
|
||||
return hasDBColumn(EventTableColumn.HASH_HIT);
|
||||
}
|
||||
|
||||
void insertEvent(long time, EventType type, long datasourceID, Long objID,
|
||||
Long artifactID, String fullDescription, String medDescription,
|
||||
String shortDescription, TskData.FileKnown known, boolean hashHit) {
|
||||
|
||||
EventTransaction trans = beginTransaction();
|
||||
insertEvent(time, type, datasourceID, objID, artifactID, fullDescription, medDescription, shortDescription, known, trans);
|
||||
insertEvent(time, type, datasourceID, objID, artifactID, fullDescription, medDescription, shortDescription, known, hashHit, trans);
|
||||
commitTransaction(trans, true);
|
||||
}
|
||||
|
||||
@ -576,10 +597,14 @@ public class EventDB {
|
||||
* use transactions to update files
|
||||
*
|
||||
* @param f
|
||||
* @param tr
|
||||
* @param transaction
|
||||
*/
|
||||
void insertEvent(long time, EventType type, long datasourceID, Long objID, Long artifactID, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known, EventTransaction tr) {
|
||||
if (tr.isClosed()) {
|
||||
void insertEvent(long time, EventType type, long datasourceID, Long objID,
|
||||
Long artifactID, String fullDescription, String medDescription,
|
||||
String shortDescription, TskData.FileKnown known, boolean hashHit,
|
||||
EventTransaction transaction) {
|
||||
|
||||
if (transaction.isClosed()) {
|
||||
throw new IllegalArgumentException("can't update database with closed transaction"); // NON-NLS
|
||||
}
|
||||
int typeNum;
|
||||
@ -591,7 +616,7 @@ public class EventDB {
|
||||
DBLock.lock();
|
||||
try {
|
||||
|
||||
//"INSERT INTO events (datasource_id, file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description) "
|
||||
//"INSERT INTO events (datasource_id,file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description, known_state, hashHit) "
|
||||
insertRowStmt.clearParameters();
|
||||
insertRowStmt.setLong(1, datasourceID);
|
||||
if (objID != null) {
|
||||
@ -618,6 +643,7 @@ public class EventDB {
|
||||
insertRowStmt.setString(9, shortDescription);
|
||||
|
||||
insertRowStmt.setByte(10, known == null ? TskData.FileKnown.UNKNOWN.getFileKnownValue() : known.getFileKnownValue());
|
||||
insertRowStmt.setInt(11, hashHit ? 1 : 0);
|
||||
|
||||
insertRowStmt.executeUpdate();
|
||||
|
||||
@ -693,16 +719,15 @@ public class EventDB {
|
||||
}
|
||||
|
||||
private TimeLineEvent constructTimeLineEvent(ResultSet rs) throws SQLException {
|
||||
EventType type = RootEventType.allTypes.get(rs.getInt(EventTableColumn.SUB_TYPE.toString()));
|
||||
return new TimeLineEvent(rs.getLong(EventTableColumn.EVENT_ID.toString()),
|
||||
rs.getLong(EventTableColumn.FILE_ID.toString()),
|
||||
rs.getLong(EventTableColumn.ARTIFACT_ID.toString()),
|
||||
rs.getLong(EventTableColumn.TIME.toString()),
|
||||
type,
|
||||
rs.getLong(EventTableColumn.TIME.toString()), RootEventType.allTypes.get(rs.getInt(EventTableColumn.SUB_TYPE.toString())),
|
||||
rs.getString(EventTableColumn.FULL_DESCRIPTION.toString()),
|
||||
rs.getString(EventTableColumn.MED_DESCRIPTION.toString()),
|
||||
rs.getString(EventTableColumn.SHORT_DESCRIPTION.toString()),
|
||||
TskData.FileKnown.valueOf(rs.getByte(EventTableColumn.KNOWN.toString())));
|
||||
TskData.FileKnown.valueOf(rs.getByte(EventTableColumn.KNOWN.toString())),
|
||||
rs.getInt(EventTableColumn.HASH_HIT.toString()) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -815,7 +840,9 @@ public class EventDB {
|
||||
|
||||
//get all agregate events in this time unit
|
||||
DBLock.lock();
|
||||
String query = "select strftime('" + strfTimeFormat + "',time , 'unixepoch'" + (TimeLineController.getTimeZone().get().equals(TimeZone.getDefault()) ? ", 'localtime'" : "") + ") as interval, group_concat(event_id) as event_ids, Min(time), Max(time), " + descriptionColumn + ", " + useSubTypeHelper(useSubTypes)
|
||||
String query = "select strftime('" + strfTimeFormat + "',time , 'unixepoch'" + (TimeLineController.getTimeZone().get().equals(TimeZone.getDefault()) ? ", 'localtime'" : "") + ") as interval,"
|
||||
+ " group_concat(event_id) as event_ids, Min(time), Max(time), " + descriptionColumn + ", " + useSubTypeHelper(useSubTypes)
|
||||
// + " , (select group_concat(event_id) as ids_with_hash_hits from events where event_id IN event_ids)"
|
||||
+ " from events where time >= " + start + " and time < " + end + " and " + SQLHelper.getSQLWhere(filter) // NON-NLS
|
||||
+ " group by interval, " + useSubTypeHelper(useSubTypes) + " , " + descriptionColumn // NON-NLS
|
||||
+ " order by Min(time)"; // NON-NLS
|
||||
@ -831,12 +858,23 @@ public class EventDB {
|
||||
stopwatch.stop();
|
||||
//System.out.println(stopwatch.elapsedMillis() / 1000.0 + " seconds");
|
||||
while (rs.next()) {
|
||||
String eventIDS = rs.getString("event_ids");
|
||||
HashSet<Long> hashHits = new HashSet<>();
|
||||
try (Statement st2 = con.createStatement();) {
|
||||
|
||||
ResultSet executeQuery = st2.executeQuery("select event_id from events where event_id in (" + eventIDS + ") and hash_hit = 1");
|
||||
while (executeQuery.next()) {
|
||||
hashHits.add(executeQuery.getLong(EventTableColumn.EVENT_ID.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
EventType type = useSubTypes ? RootEventType.allTypes.get(rs.getInt(EventTableColumn.SUB_TYPE.toString())) : BaseTypes.values()[rs.getInt(EventTableColumn.BASE_TYPE.toString())];
|
||||
|
||||
AggregateEvent aggregateEvent = new AggregateEvent(
|
||||
new Interval(rs.getLong("Min(time)") * 1000, rs.getLong("Max(time)") * 1000, TimeLineController.getJodaTimeZone()), // NON-NLS
|
||||
type,
|
||||
Arrays.asList(rs.getString("event_ids").split(",")), // NON-NLS
|
||||
Stream.of(eventIDS.split(",")).map(Long::valueOf).collect(Collectors.toSet()), // NON-NLS
|
||||
hashHits,
|
||||
rs.getString(descriptionColumn), lod);
|
||||
|
||||
//put events in map from type/descrition -> event
|
||||
|
@ -33,6 +33,7 @@ import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableMap;
|
||||
@ -175,6 +176,13 @@ public class EventsRepository {
|
||||
return idToEventCache.getUnchecked(eventID);
|
||||
}
|
||||
|
||||
public Set<TimeLineEvent> getEventsById(Collection<Long> eventIDs) {
|
||||
return eventIDs.stream()
|
||||
.map(idToEventCache::getUnchecked)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
}
|
||||
|
||||
public List<AggregateEvent> getAggregatedEvents(ZoomParams params) {
|
||||
|
||||
return aggregateEventsCache.getUnchecked(params);
|
||||
@ -213,7 +221,7 @@ public class EventsRepository {
|
||||
}
|
||||
|
||||
public boolean hasDataSourceInfo() {
|
||||
return eventDB.hasDataSourceInfo();
|
||||
return eventDB.hasNewColumns();
|
||||
}
|
||||
|
||||
private class DBPopulationWorker extends SwingWorker<Void, ProgressWindow.ProgressUpdate> {
|
||||
@ -268,20 +276,21 @@ public class EventsRepository {
|
||||
String shortDesc = datasourceName + "/" + StringUtils.defaultIfBlank(rootFolder, "");
|
||||
String medD = datasourceName + parentPath;
|
||||
final TskData.FileKnown known = f.getKnown();
|
||||
boolean hashHit = f.getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT) > 0;
|
||||
|
||||
//insert it into the db if time is > 0 => time is legitimate (drops logical files)
|
||||
long time;
|
||||
if (f.getAtime() > 0) {
|
||||
eventDB.insertEvent(f.getAtime(), FileSystemTypes.FILE_ACCESSED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, trans);
|
||||
eventDB.insertEvent(f.getAtime(), FileSystemTypes.FILE_ACCESSED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashHit, trans);
|
||||
}
|
||||
if (f.getMtime() > 0) {
|
||||
eventDB.insertEvent(f.getMtime(), FileSystemTypes.FILE_MODIFIED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, trans);
|
||||
eventDB.insertEvent(f.getMtime(), FileSystemTypes.FILE_MODIFIED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashHit, trans);
|
||||
}
|
||||
if (f.getCtime() > 0) {
|
||||
eventDB.insertEvent(f.getCtime(), FileSystemTypes.FILE_CHANGED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, trans);
|
||||
eventDB.insertEvent(f.getCtime(), FileSystemTypes.FILE_CHANGED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashHit, trans);
|
||||
}
|
||||
if (f.getCrtime() > 0) {
|
||||
eventDB.insertEvent(f.getCrtime(), FileSystemTypes.FILE_CREATED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, trans);
|
||||
eventDB.insertEvent(f.getCrtime(), FileSystemTypes.FILE_CREATED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashHit, trans);
|
||||
}
|
||||
|
||||
process(Arrays.asList(new ProgressWindow.ProgressUpdate(i, numFiles,
|
||||
@ -381,7 +390,9 @@ public class EventsRepository {
|
||||
|
||||
if (eventDescription != null && eventDescription.getTime() > 0L) { //insert it into the db if time is > 0 => time is legitimate
|
||||
long datasourceID = skCase.getContentById(bbart.getObjectID()).getDataSource().getId();
|
||||
eventDB.insertEvent(eventDescription.getTime(), type, datasourceID, bbart.getObjectID(), bbart.getArtifactID(), eventDescription.getFullDescription(), eventDescription.getMedDescription(), eventDescription.getShortDescription(), null, trans);
|
||||
|
||||
boolean hashHit = skCase.getAbstractFileById(bbart.getObjectID()).getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT) > 0;
|
||||
eventDB.insertEvent(eventDescription.getTime(), type, datasourceID, bbart.getObjectID(), bbart.getArtifactID(), eventDescription.getFullDescription(), eventDescription.getMedDescription(), eventDescription.getShortDescription(), null, hashHit, trans);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
@ -14,6 +14,7 @@ import org.sleuthkit.autopsy.timeline.filters.AbstractFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.DataSourceFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.DataSourcesFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.Filter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.HashHitFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.HideKnownFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.IntersectionFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.TextFilter;
|
||||
@ -59,6 +60,8 @@ public class SQLHelper {
|
||||
result = getSQLWhere((DataSourcesFilter) filter);
|
||||
} else if (filter instanceof HideKnownFilter) {
|
||||
result = getSQLWhere((HideKnownFilter) filter);
|
||||
} else if (filter instanceof HashHitFilter) {
|
||||
result = getSQLWhere((HashHitFilter) filter);
|
||||
} else if (filter instanceof TextFilter) {
|
||||
result = getSQLWhere((TextFilter) filter);
|
||||
} else if (filter instanceof TypeFilter) {
|
||||
@ -79,6 +82,10 @@ public class SQLHelper {
|
||||
return (filter.isSelected()) ? "(" + EventDB.EventTableColumn.KNOWN.toString() + " is not '" + TskData.FileKnown.KNOWN.getFileKnownValue() + "')" // NON-NLS
|
||||
: "1";
|
||||
}
|
||||
static String getSQLWhere(HashHitFilter filter) {
|
||||
return (filter.isSelected()) ? "(" + EventDB.EventTableColumn.HASH_HIT.toString() + " is not 0)" // NON-NLS
|
||||
: "1";
|
||||
}
|
||||
|
||||
static String getSQLWhere(DataSourceFilter filter) {
|
||||
return (filter.isSelected()) ? "(" + EventDB.EventTableColumn.DATA_SOURCE_ID.toString() + " = '" + filter.getDataSourceID() + "')" : "1";
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.filters;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class HashHitFilter extends AbstractFilter {
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages("hashHitFilter.displayName.text=Show only Hash Hits")
|
||||
public String getDisplayName() {
|
||||
return Bundle.hashHitFilter_displayName_text();
|
||||
}
|
||||
|
||||
public HashHitFilter() {
|
||||
super();
|
||||
getActiveProperty().set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashHitFilter copyOf() {
|
||||
HashHitFilter hashHitFilter = new HashHitFilter();
|
||||
hashHitFilter.setSelected(isSelected());
|
||||
hashHitFilter.setDisabled(isDisabled());
|
||||
return hashHitFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHTMLReportString() {
|
||||
return "only hash hits" + getStringCheckBox();// NON-NLS
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final HashHitFilter other = (HashHitFilter) obj;
|
||||
|
||||
return isSelected() == other.isSelected();
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ import javafx.collections.FXCollections;
|
||||
public class RootFilter extends IntersectionFilter<Filter> {
|
||||
|
||||
private final HideKnownFilter knwonFilter;
|
||||
private final HashHitFilter hashFilter;
|
||||
private final TextFilter textFilter;
|
||||
private final TypeFilter typeFilter;
|
||||
private final DataSourcesFilter dataSourcesFilter;
|
||||
@ -35,9 +36,10 @@ public class RootFilter extends IntersectionFilter<Filter> {
|
||||
return dataSourcesFilter;
|
||||
}
|
||||
|
||||
public RootFilter(HideKnownFilter knownFilter, TextFilter textFilter, TypeFilter typeFilter, DataSourcesFilter dataSourceFilter) {
|
||||
super(FXCollections.observableArrayList(knownFilter, textFilter, dataSourceFilter, typeFilter));
|
||||
public RootFilter(HideKnownFilter knownFilter,HashHitFilter hashFilter, TextFilter textFilter, TypeFilter typeFilter, DataSourcesFilter dataSourceFilter) {
|
||||
super(FXCollections.observableArrayList(knownFilter,hashFilter, textFilter, dataSourceFilter, typeFilter));
|
||||
this.knwonFilter = knownFilter;
|
||||
this.hashFilter = hashFilter;
|
||||
this.textFilter = textFilter;
|
||||
this.typeFilter = typeFilter;
|
||||
this.dataSourcesFilter = dataSourceFilter;
|
||||
@ -45,7 +47,7 @@ public class RootFilter extends IntersectionFilter<Filter> {
|
||||
|
||||
@Override
|
||||
public RootFilter copyOf() {
|
||||
RootFilter filter = new RootFilter(knwonFilter.copyOf(), textFilter.copyOf(), typeFilter.copyOf(), dataSourcesFilter.copyOf());
|
||||
RootFilter filter = new RootFilter(knwonFilter.copyOf(),hashFilter.copyOf(), textFilter.copyOf(), typeFilter.copyOf(), dataSourcesFilter.copyOf());
|
||||
filter.setSelected(isSelected());
|
||||
filter.setDisabled(isDisabled());
|
||||
return filter;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Copyright 2013-15 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,8 +18,13 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
@ -58,17 +63,25 @@ import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.ColorUtilities;
|
||||
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.events.AggregateEvent;
|
||||
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.events.TimeLineEvent;
|
||||
import org.sleuthkit.autopsy.timeline.filters.Filter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.TextFilter;
|
||||
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/** Represents an {@link AggregateEvent} in a {@link EventDetailChart}. */
|
||||
public class AggregateEventNode extends StackPane {
|
||||
|
||||
private static final Image HASH_PIN = new Image(AggregateEventNode.class.getResourceAsStream("/org/sleuthkit/autopsy/images/hashset_hits.png"));
|
||||
private final static Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS
|
||||
private final static Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS
|
||||
|
||||
@ -130,15 +143,25 @@ public class AggregateEventNode extends StackPane {
|
||||
|
||||
private SimpleObjectProperty<DescriptionLOD> descLOD = new SimpleObjectProperty<>();
|
||||
private DescriptionVisibility descrVis;
|
||||
private final SleuthkitCase sleuthkitCase;
|
||||
private final FilteredEventsModel eventsModel;
|
||||
private Map<String, Long> hashSetCounts = null;
|
||||
private Tooltip tooltip;
|
||||
|
||||
public AggregateEventNode(final AggregateEvent event, AggregateEventNode parentEventNode, EventDetailChart chart) {
|
||||
this.event = event;
|
||||
descLOD.set(event.getLOD());
|
||||
this.parentEventNode = parentEventNode;
|
||||
this.chart = chart;
|
||||
sleuthkitCase = chart.getController().getAutopsyCase().getSleuthkitCase();
|
||||
eventsModel = chart.getController().getEventsModel();
|
||||
final Region region = new Region();
|
||||
HBox.setHgrow(region, Priority.ALWAYS);
|
||||
final HBox hBox = new HBox(descrLabel, countLabel, region, minusButton, plusButton);
|
||||
ImageView imageView = new ImageView(HASH_PIN);
|
||||
final HBox hBox = new HBox(descrLabel, countLabel, region, imageView, minusButton, plusButton);
|
||||
if (event.getEventIDsWithHashHits().isEmpty()) {
|
||||
hBox.getChildren().remove(imageView);
|
||||
}
|
||||
hBox.setPrefWidth(USE_COMPUTED_SIZE);
|
||||
hBox.setMinWidth(USE_PREF_SIZE);
|
||||
hBox.setPadding(new Insets(2, 5, 2, 5));
|
||||
@ -229,11 +252,41 @@ public class AggregateEventNode extends StackPane {
|
||||
}
|
||||
|
||||
private void installTooltip() {
|
||||
Tooltip.install(AggregateEventNode.this, new Tooltip(
|
||||
|
||||
if (tooltip == null) {
|
||||
String collect = "";
|
||||
if (!event.getEventIDsWithHashHits().isEmpty()) {
|
||||
if (Objects.isNull(hashSetCounts)) {
|
||||
hashSetCounts = new HashMap<>();
|
||||
try {
|
||||
for (TimeLineEvent tle : eventsModel.getEventsById(event.getEventIDsWithHashHits())) {
|
||||
ArrayList<BlackboardArtifact> blackboardArtifacts = sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, tle.getFileID());
|
||||
for (BlackboardArtifact artf : blackboardArtifacts) {
|
||||
for (BlackboardAttribute attr : artf.getAttributes()) {
|
||||
if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||
hashSetCounts.merge(attr.getValueString(), 1L, Long::sum);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(AggregateEventNode.class.getName()).log(Level.SEVERE, "Error getting hashset hit info for event.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
collect = hashSetCounts.entrySet().stream()
|
||||
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
}
|
||||
tooltip = new Tooltip(
|
||||
NbBundle.getMessage(this.getClass(), "AggregateEventNode.installTooltip.text",
|
||||
getEvent().getEventIDs().size(), getEvent().getType(), getEvent().getDescription(),
|
||||
getEvent().getSpan().getStart().toString(TimeLineController.getZonedFormatter()),
|
||||
getEvent().getSpan().getEnd().toString(TimeLineController.getZonedFormatter()))));
|
||||
getEvent().getSpan().getEnd().toString(TimeLineController.getZonedFormatter()))
|
||||
+ (collect.isEmpty() ? "" : "\n\nHash Set Hits\n" + collect));
|
||||
Tooltip.install(AggregateEventNode.this, tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
public Pane getSubNodePane() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
Timeline.ui.detailview.tooltip.text={0}\nRight-click to remove.\nRight-drag to reposition.
|
||||
AggregateEventNode.installTooltip.text={0} {1} events\n{2}\nbetween {3}\nand {4}
|
||||
AggregateEventNode.installTooltip.text={0} {1} events\n{2}\nbetween\t{3}\nand \t{4}
|
||||
AggregateEventNode.loggedTask.name=Load sub events
|
||||
DetailViewPane.loggedTask.name=Update Details
|
||||
DetailViewPane.loggedTask.preparing=preparing
|
||||
|
Loading…
x
Reference in New Issue
Block a user