Autopsy  4.19.3
Graphical digital forensics platform for The Sleuth Kit and other tools.
ScoreContent.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2023 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.text.MessageFormat;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.EnumSet;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.logging.Level;
32 import org.apache.commons.lang3.StringUtils;
33 import org.openide.nodes.AbstractNode;
34 import org.openide.nodes.ChildFactory;
35 import org.openide.nodes.Children;
36 import org.openide.nodes.Node;
37 import org.openide.nodes.Sheet;
38 import org.openide.util.NbBundle;
39 import org.openide.util.WeakListeners;
40 import org.openide.util.lookup.Lookups;
48 import org.sleuthkit.datamodel.AbstractFile;
49 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
50 import org.sleuthkit.datamodel.Content;
51 import org.sleuthkit.datamodel.ContentVisitor;
52 import org.sleuthkit.datamodel.DerivedFile;
53 import org.sleuthkit.datamodel.Directory;
54 import org.sleuthkit.datamodel.File;
55 import org.sleuthkit.datamodel.FsContent;
56 import org.sleuthkit.datamodel.LayoutFile;
57 import org.sleuthkit.datamodel.LocalFile;
58 import org.sleuthkit.datamodel.Score.Priority;
59 import org.sleuthkit.datamodel.Score.Significance;
60 import org.sleuthkit.datamodel.SlackFile;
61 import org.sleuthkit.datamodel.SleuthkitCase;
62 import org.sleuthkit.datamodel.TskCoreException;
63 import org.sleuthkit.datamodel.VirtualDirectory;
64 
68 public class ScoreContent implements AutopsyVisitableItem {
69 
70  private SleuthkitCase skCase;
71  private final long filteringDSObjId; // 0 if not filtering/grouping by data source
72 
73  @NbBundle.Messages({"ScoreContent_badFilter_text=Bad Items",
74  "ScoreContent_susFilter_text=Suspicious Items"})
75  public enum ScoreContentFilter implements AutopsyVisitableItem {
76 
77  BAD_ITEM_FILTER(0, "BAD_ITEM_FILTER",
78  Bundle.ScoreContent_badFilter_text()),
79  SUS_ITEM_FILTER(1, "SUS_ITEM_FILTER",
80  Bundle.ScoreContent_susFilter_text());
81 
82  private int id;
83  private String name;
84  private String displayName;
85 
86  private ScoreContentFilter(int id, String name, String displayName) {
87  this.id = id;
88  this.name = name;
89  this.displayName = displayName;
90 
91  }
92 
93  public String getName() {
94  return this.name;
95  }
96 
97  public int getId() {
98  return this.id;
99  }
100 
101  public String getDisplayName() {
102  return this.displayName;
103  }
104 
105  @Override
106  public <T> T accept(AutopsyItemVisitor<T> visitor) {
107  return visitor.visit(this);
108  }
109  }
110 
115  public ScoreContent(SleuthkitCase skCase) {
116  this(skCase, 0);
117  }
118 
124  public ScoreContent(SleuthkitCase skCase, long dsObjId) {
125  this.skCase = skCase;
126  this.filteringDSObjId = dsObjId;
127  }
128 
132  long filteringDataSourceObjId() {
133  return this.filteringDSObjId;
134  }
135 
136  @Override
137  public <T> T accept(AutopsyItemVisitor<T> visitor) {
138  return visitor.visit(this);
139  }
140 
144  public SleuthkitCase getSleuthkitCase() {
145  return this.skCase;
146  }
147 
148  private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
155  );
158 
165  private static PropertyChangeListener getPcl(final Runnable onRefresh, final Runnable onRemove) {
166  return (PropertyChangeEvent evt) -> {
167  String eventType = evt.getPropertyName();
168  if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
169  // only refresh if there is a current case.
170  try {
172  if (onRefresh != null) {
173  onRefresh.run();
174  }
175  } catch (NoCurrentCaseException notUsed) {
179  }
180  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
181  // case was closed. Remove listeners so that we don't get called with a stale case handle
182  if (evt.getNewValue() == null && onRemove != null) {
183  onRemove.run();
184  }
185  } else if (CASE_EVENTS_OF_INTEREST.contains(eventType)) {
186  // only refresh if there is a current case.
187  try {
189  if (onRefresh != null) {
190  onRefresh.run();
191  }
192  } catch (NoCurrentCaseException notUsed) {
196  }
197  }
198  };
199  }
200 
208  static private String getFileFilter(ScoreContent.ScoreContentFilter filter, long filteringDSObjId) throws IllegalArgumentException {
209  String aggregateScoreFilter = "";
210  switch (filter) {
211  case SUS_ITEM_FILTER:
212  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() + " )";
213 
214  break;
215  case BAD_ITEM_FILTER:
216  aggregateScoreFilter = " tsk_aggregate_score.significance = " + Significance.NOTABLE.getId() + " AND (tsk_aggregate_score.priority = " + Priority.NORMAL.getId() + " OR tsk_aggregate_score.priority = " + Priority.OVERRIDE.getId() + " )";
217  break;
218 
219  default:
220  throw new IllegalArgumentException(MessageFormat.format("Unsupported filter type to get suspect content: {0}", filter));
221 
222  }
223 
224  String query = " obj_id IN (SELECT tsk_aggregate_score.obj_id FROM tsk_aggregate_score WHERE " + aggregateScoreFilter + ") ";
225 
226  if (filteringDSObjId > 0) {
227  query += " AND data_source_obj_id = " + filteringDSObjId;
228  }
229  return query;
230  }
231 
239  private static boolean isRefreshRequired(PropertyChangeEvent evt) {
240  String eventType = evt.getPropertyName();
241  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
242  // check if current case is active before updating
243  try {
245  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
246  if (null != event && Category.ANALYSIS_RESULT.equals(event.getBlackboardArtifactType().getCategory())) {
247  return true;
248  }
249  } catch (NoCurrentCaseException notUsed) {
253  }
254  }
255  return false;
256  }
257 
261  public static class ScoreContentsNode extends DisplayableItemNode {
262 
263  @NbBundle.Messages("ScoreContent_ScoreContentNode_name=Score")
264  private static final String NAME = Bundle.ScoreContent_ScoreContentNode_name();
265 
266  ScoreContentsNode(SleuthkitCase skCase, long datasourceObjId) {
267  super(Children.create(new ScoreContentsChildren(skCase, datasourceObjId), true), Lookups.singleton(NAME));
268  super.setName(NAME);
269  super.setDisplayName(NAME);
270  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/red-circle-exclamation.png"); //NON-NLS
271  }
272 
273  @Override
274  public boolean isLeafTypeNode() {
275  return false;
276  }
277 
278  @Override
279  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
280  return visitor.visit(this);
281  }
282 
283  @Override
284  @NbBundle.Messages({
285  "ScoreContent_createSheet_name_displayName=Name",
286  "ScoreContent_createSheet_name_desc=no description"})
287  protected Sheet createSheet() {
288  Sheet sheet = super.createSheet();
289  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
290  if (sheetSet == null) {
291  sheetSet = Sheet.createPropertiesSet();
292  sheet.put(sheetSet);
293  }
294 
295  sheetSet.put(new NodeProperty<>("Name", //NON-NLS
296  Bundle.ScoreContent_createSheet_name_displayName(),
297  Bundle.ScoreContent_createSheet_name_desc(),
298  NAME));
299  return sheet;
300  }
301 
302  @Override
303  public String getItemType() {
304  return getClass().getName();
305  }
306  }
307 
311  public static class ScoreContentsChildren extends ChildFactory.Detachable<ScoreContent.ScoreContentFilter> implements RefreshThrottler.Refresher {
312 
313  private SleuthkitCase skCase;
314  private final long datasourceObjId;
315 
317 
318  private final PropertyChangeListener pcl = getPcl(
319  () -> ScoreContentsChildren.this.refresh(false),
320  () -> ScoreContentsChildren.this.removeNotify());
321 
322  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
323 
325 
326  public ScoreContentsChildren(SleuthkitCase skCase, long dsObjId) {
327  this.skCase = skCase;
328  this.datasourceObjId = dsObjId;
329  }
330 
331  @Override
332  protected void addNotify() {
333  super.addNotify();
334  refreshThrottler.registerForIngestModuleEvents();
338  }
339 
340  @Override
341  protected void removeNotify() {
342  refreshThrottler.unregisterEventListener();
346  typeNodeMap.clear();
347  }
348 
349  @Override
350  public void refresh() {
351  refresh(false);
352  }
353 
354  @Override
355  public boolean isRefreshRequired(PropertyChangeEvent evt) {
356  return ScoreContent.isRefreshRequired(evt);
357  }
358 
359  @Override
360  protected boolean createKeys(List<ScoreContent.ScoreContentFilter> list) {
361  list.addAll(Arrays.asList(ScoreContent.ScoreContentFilter.values()));
362  typeNodeMap.values().forEach(nd -> nd.updateDisplayName());
363  return true;
364  }
365 
366  @Override
368  ScoreContentsChildren.ScoreContentNode nd = new ScoreContentsChildren.ScoreContentNode(skCase, key, datasourceObjId);
369  typeNodeMap.put(key, nd);
370  return nd;
371  }
372 
376  public class ScoreContentNode extends DisplayableItemNode {
377 
378  private static final Logger logger = Logger.getLogger(ScoreContentNode.class.getName());
380  private final long datasourceObjId;
381 
382  ScoreContentNode(SleuthkitCase skCase, ScoreContent.ScoreContentFilter filter, long dsObjId) {
383  super(Children.create(new ScoreContentChildren(filter, skCase, dsObjId), true), Lookups.singleton(filter.getDisplayName()));
384  this.filter = filter;
385  this.datasourceObjId = dsObjId;
386  init();
387  }
388 
389  private void init() {
390  super.setName(filter.getName());
391 
392  String tooltip = filter.getDisplayName();
393  this.setShortDescription(tooltip);
394  switch (this.filter) {
395  case SUS_ITEM_FILTER:
396  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/yellow-circle-yield.png"); //NON-NLS
397  break;
398  default:
399  case BAD_ITEM_FILTER:
400  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/red-circle-exclamation.png"); //NON-NLS
401  break;
402  }
403 
404  updateDisplayName();
405  }
406 
407  void updateDisplayName() {
408  //get count of children without preloading all child nodes
409  long count = 0;
410  try {
411  count = calculateItems(skCase, filter, datasourceObjId);
412  } catch (TskCoreException ex) {
413  logger.log(Level.WARNING, "An error occurred while fetching file counts", ex);
414  }
415  super.setDisplayName(filter.getDisplayName() + " (" + count + ")");
416  }
417 
426  private static long calculateItems(SleuthkitCase sleuthkitCase, ScoreContent.ScoreContentFilter filter, long datasourceObjId) throws TskCoreException {
427  return sleuthkitCase.countFilesWhere(getFileFilter(filter, datasourceObjId));
428  }
429 
430  @Override
431  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
432  return visitor.visit(this);
433  }
434 
435  @Override
436  @NbBundle.Messages({
437  "ScoreContent_createSheet_filterType_displayName=Type",
438  "ScoreContent_createSheet_filterType_desc=no description"})
439  protected Sheet createSheet() {
440  Sheet sheet = super.createSheet();
441  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
442  if (sheetSet == null) {
443  sheetSet = Sheet.createPropertiesSet();
444  sheet.put(sheetSet);
445  }
446 
447  sheetSet.put(new NodeProperty<>("Type", //NON_NLS
448  Bundle.ScoreContent_createSheet_filterType_displayName(),
449  Bundle.ScoreContent_createSheet_filterType_desc(),
450  filter.getDisplayName()));
451 
452  return sheet;
453  }
454 
455  @Override
456  public boolean isLeafTypeNode() {
457  return true;
458  }
459 
460  @Override
461  public String getItemType() {
462  return DisplayableItemNode.FILE_PARENT_NODE_KEY;
463  }
464  }
465 
469  static class ScoreContentChildren extends BaseChildFactory<AbstractFile> implements RefreshThrottler.Refresher {
470 
471  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
472 
473  private final PropertyChangeListener pcl = getPcl(
474  () -> ScoreContentChildren.this.refresh(false),
475  () -> ScoreContentChildren.this.removeNotify());
476 
477  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
478 
479  private final SleuthkitCase skCase;
480  private final ScoreContent.ScoreContentFilter filter;
481  private static final Logger logger = Logger.getLogger(ScoreContentChildren.class.getName());
482 
483  private final long datasourceObjId;
484 
485  ScoreContentChildren(ScoreContent.ScoreContentFilter filter, SleuthkitCase skCase, long datasourceObjId) {
486  super(filter.getName(), new ViewsKnownAndSlackFilter<>());
487  this.skCase = skCase;
488  this.filter = filter;
489  this.datasourceObjId = datasourceObjId;
490  }
491 
492  @Override
493  protected void onAdd() {
494  refreshThrottler.registerForIngestModuleEvents();
498  }
499 
500  @Override
501  protected void onRemove() {
502  refreshThrottler.unregisterEventListener();
503  IngestManager.getInstance().removeIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
504  IngestManager.getInstance().removeIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
505  Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
506  }
507 
508  @Override
509  public void refresh() {
510  refresh(false);
511  }
512 
513  @Override
514  public boolean isRefreshRequired(PropertyChangeEvent evt) {
515  return ScoreContent.isRefreshRequired(evt);
516  }
517 
518  private List<AbstractFile> runFsQuery() {
519  List<AbstractFile> ret = new ArrayList<>();
520 
521  String query = null;
522  try {
523  query = getFileFilter(filter, datasourceObjId);
524  ret = skCase.findAllFilesWhere(query);
525  } catch (TskCoreException | IllegalArgumentException e) {
526  logger.log(Level.SEVERE, "Error getting files for the deleted content view using: " + StringUtils.defaultString(query, "<null>"), e); //NON-NLS
527  }
528 
529  return ret;
530 
531  }
532 
533  @Override
534  protected List<AbstractFile> makeKeys() {
535  return runFsQuery();
536  }
537 
538  @Override
539  protected Node createNodeForKey(AbstractFile key) {
540  return key.accept(new ContentVisitor.Default<AbstractNode>() {
541  public FileNode visit(AbstractFile f) {
542  return new FileNode(f, false);
543  }
544 
545  public FileNode visit(FsContent f) {
546  return new FileNode(f, false);
547  }
548 
549  @Override
550  public FileNode visit(LayoutFile f) {
551  return new FileNode(f, false);
552  }
553 
554  @Override
555  public FileNode visit(File f) {
556  return new FileNode(f, false);
557  }
558 
559  @Override
560  public FileNode visit(Directory f) {
561  return new FileNode(f, false);
562  }
563 
564  @Override
565  public FileNode visit(VirtualDirectory f) {
566  return new FileNode(f, false);
567  }
568 
569  @Override
570  public AbstractNode visit(SlackFile sf) {
571  return new FileNode(sf, false);
572  }
573 
574  @Override
575  public AbstractNode visit(LocalFile lf) {
576  return new FileNode(lf, false);
577  }
578 
579  @Override
580  public AbstractNode visit(DerivedFile df) {
581  return new FileNode(df, false);
582  }
583 
584  @Override
585  protected AbstractNode defaultVisit(Content di) {
586  if (di instanceof AbstractFile) {
587  return visit((AbstractFile) di);
588  } else {
589  throw new UnsupportedOperationException("Not supported for this type of Displayable Item: " + di.toString());
590  }
591  }
592  });
593  }
594  }
595  }
596 }
boolean createKeys(List< ScoreContent.ScoreContentFilter > list)
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static boolean isRefreshRequired(PropertyChangeEvent evt)
static synchronized IngestManager getInstance()
public< T > T accept(AutopsyItemVisitor< T > visitor)
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
ScoreContent(SleuthkitCase skCase, long dsObjId)
final Map< ScoreContentFilter, ScoreContentsChildren.ScoreContentNode > typeNodeMap
void removeIngestJobEventListener(final PropertyChangeListener listener)
Node createNodeForKey(ScoreContent.ScoreContentFilter key)
static long calculateItems(SleuthkitCase sleuthkitCase, ScoreContent.ScoreContentFilter filter, long datasourceObjId)
void addIngestJobEventListener(final PropertyChangeListener listener)
static String getFileFilter(ScoreContent.ScoreContentFilter filter, long filteringDSObjId)
ScoreContentFilter(int id, String name, String displayName)
void addIngestModuleEventListener(final PropertyChangeListener listener)
static final Set< Case.Events > CASE_EVENTS_OF_INTEREST
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:711
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:756
static PropertyChangeListener getPcl(final Runnable onRefresh, final Runnable onRemove)

Copyright © 2012-2022 Basis Technology. Generated on: Tue Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.