Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CaseUcoReportModule.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2018-2020 Basis Technology Corp.
6  * Project Contact/Architect: carrier <at> sleuthkit <dot> org
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package org.sleuthkit.autopsy.report.modules.caseuco;
21 
22 import com.google.gson.Gson;
23 import com.google.gson.GsonBuilder;
24 import com.google.gson.JsonElement;
25 import com.google.gson.stream.JsonWriter;
26 
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.io.OutputStreamWriter;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.util.ArrayDeque;
35 import java.util.Deque;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.logging.Level;
40 import java.util.stream.Collectors;
41 import javax.swing.JPanel;
42 import org.openide.util.NbBundle;
50 import org.sleuthkit.caseuco.CaseUcoExporter;
51 import org.sleuthkit.caseuco.ContentNotExportableException;
52 import org.sleuthkit.datamodel.AbstractFile;
53 import org.sleuthkit.datamodel.BlackboardArtifact;
54 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
55 import org.sleuthkit.datamodel.Content;
56 import org.sleuthkit.datamodel.DataSource;
57 import org.sleuthkit.datamodel.TskCoreException;
58 import org.sleuthkit.datamodel.TskData;
59 import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
60 
65 public final class CaseUcoReportModule implements GeneralReportModule {
66 
67  private static final Logger logger = Logger.getLogger(CaseUcoReportModule.class.getName());
69 
70  //Supported types of TSK_FS_FILES
71  private static final Set<Short> SUPPORTED_TYPES = new HashSet<Short>() {
72  {
73  add(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.getValue());
74  add(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue());
75  add(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT.getValue());
76  }
77  };
78 
79  private static final String REPORT_FILE_NAME = "CASE_UCO_output";
80  private static final String EXTENSION = "jsonld";
81 
82  // Hidden constructor for the report
83  private CaseUcoReportModule() {
84  }
85 
86  // Get the default implementation of this report
87  public static synchronized CaseUcoReportModule getDefault() {
88  return SINGLE_INSTANCE;
89  }
90 
91  @Override
92  public String getName() {
93  return NbBundle.getMessage(this.getClass(), "CaseUcoReportModule.getName.text");
94  }
95 
96  @Override
97  public JPanel getConfigurationPanel() {
98  return null; // No configuration panel
99  }
100 
101  @Override
102  public String getRelativeFilePath() {
103  return REPORT_FILE_NAME + "." + EXTENSION;
104  }
105 
106  @Override
107  public String getDescription() {
108  return NbBundle.getMessage(this.getClass(), "CaseUcoReportModule.getDesc.text");
109  }
110 
116  public static String getReportFileName() {
117  return REPORT_FILE_NAME;
118  }
119 
120  @Override
121  public boolean supportsDataSourceSelection() {
122  return true;
123  }
124 
131  @NbBundle.Messages({
132  "CaseUcoReportModule.notInitialized=CASE-UCO settings panel has not been initialized",
133  "CaseUcoReportModule.noDataSourceSelected=No data source selected for CASE-UCO report",
134  "CaseUcoReportModule.ioError=I/O error encountered while generating report",
135  "CaseUcoReportModule.noCaseOpen=No case is currently open",
136  "CaseUcoReportModule.tskCoreException=TskCoreException [%s] encountered while generating the report. Please reference the log for more details.",
137  "CaseUcoReportModule.processingDataSource=Processing datasource: %s",
138  "CaseUcoReportModule.ingestWarning=Warning, this report will be created before ingest services completed",
139  "CaseUcoReportModule.unableToCreateDirectories=Unable to create directory for CASE-UCO report",
140  "CaseUcoReportModule.srcModuleName=CASE-UCO Report"
141  })
142  @Override
143  public void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel) {
144  try {
145  // Check if ingest has finished
146  warnIngest(progressPanel);
147 
148  //Create report paths if they don't already exist.
149  Path reportDirectory = Paths.get(settings.getReportDirectoryPath());
150  try {
151  Files.createDirectories(reportDirectory);
152  } catch (IOException ex) {
153  logger.log(Level.WARNING, "Unable to create directory for CASE-UCO report.", ex);
155  Bundle.CaseUcoReportModule_unableToCreateDirectories());
156  return;
157  }
158 
159  Case currentCase = Case.getCurrentCaseThrows();
160 
161  Path caseJsonReportFile = reportDirectory.resolve(REPORT_FILE_NAME + "." + EXTENSION);
162 
163  try (OutputStream stream = new FileOutputStream(caseJsonReportFile.toFile());
164  JsonWriter reportWriter = new JsonWriter(new OutputStreamWriter(stream, "UTF-8"))) {
165  Gson gson = new GsonBuilder().setPrettyPrinting().create();
166  reportWriter.setIndent(" ");
167  reportWriter.beginObject();
168  reportWriter.name("@graph");
169  reportWriter.beginArray();
170 
171  CaseUcoExporter exporter = new CaseUcoExporter(currentCase.getSleuthkitCase());
172  for (JsonElement element : exporter.exportSleuthkitCase()) {
173  gson.toJson(element, reportWriter);
174  }
175 
176  // Get a list of selected data sources to process.
177  List<DataSource> dataSources = getSelectedDataSources(currentCase, settings);
178 
179  progressPanel.setIndeterminate(false);
180  progressPanel.setMaximumProgress(dataSources.size());
181  progressPanel.start();
182 
183  // First stage of reporting is for files and data sources.
184  // Iterate through each data source and dump all files contained
185  // in that data source.
186  for (int i = 0; i < dataSources.size(); i++) {
187  DataSource dataSource = dataSources.get(i);
188  progressPanel.updateStatusLabel(String.format(
189  Bundle.CaseUcoReportModule_processingDataSource(),
190  dataSource.getName()));
191  // Add the data source export.
192  for (JsonElement element : exporter.exportDataSource(dataSource)) {
193  gson.toJson(element, reportWriter);
194  }
195  // Search all children of the data source.
196  performDepthFirstSearch(dataSource, gson, exporter, reportWriter);
197  progressPanel.setProgress(i + 1);
198  }
199 
200  // Second stage of reporting handles artifacts.
201  Set<Long> dataSourceIds = dataSources.stream()
202  .map((datasource) -> datasource.getId())
203  .collect(Collectors.toSet());
204 
205  logger.log(Level.INFO, "Writing all artifacts to the CASE-UCO report. "
206  + "Keyword hits will be skipped as they can't be represented"
207  + " in CASE format.");
208 
209  // Write all standard artifacts that are contained within the
210  // selected data sources.
211  for (ARTIFACT_TYPE artType : currentCase.getSleuthkitCase().getBlackboardArtifactTypesInUse()) {
212  if(artType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT)) {
213  // Keyword hits cannot be represented in CASE.
214  continue;
215  }
216 
217  for (BlackboardArtifact artifact : currentCase.getSleuthkitCase().getBlackboardArtifacts(artType)) {
218  if (dataSourceIds.contains(artifact.getDataSource().getId())) {
219  try {
220  for (JsonElement element : exporter.exportBlackboardArtifact(artifact)) {
221  gson.toJson(element, reportWriter);
222  }
223  } catch (ContentNotExportableException ex) {
224  logger.log(Level.INFO, String.format("Unable to export blackboard artifact (id: %d, type: %d) to CASE/UCO. "
225  + "The artifact type is either not supported or the artifact instance does not have any "
226  + "exportable attributes.", artifact.getId(), artType.getTypeID()));
227  } catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
228  logger.log(Level.WARNING, String.format("Artifact instance (id: %d, type: %d) contained a "
229  + "malformed json attribute.", artifact.getId(), artType.getTypeID()), ex);
230  }
231  }
232  }
233  }
234 
235  reportWriter.endArray();
236  reportWriter.endObject();
237  }
238 
239  currentCase.addReport(caseJsonReportFile.toString(),
240  Bundle.CaseUcoReportModule_srcModuleName(),
243  } catch (IOException ex) {
244  logger.log(Level.WARNING, "I/O error encountered while generating the report.", ex);
246  Bundle.CaseUcoReportModule_ioError());
247  } catch (NoCurrentCaseException ex) {
248  logger.log(Level.WARNING, "No case open.", ex);
250  Bundle.CaseUcoReportModule_noCaseOpen());
251  } catch (TskCoreException ex) {
252  logger.log(Level.WARNING, "TskCoreException encounted while generating the report.", ex);
254  String.format(Bundle.CaseUcoReportModule_tskCoreException(), ex.toString()));
255  }
256 
258  }
259 
263  private List<DataSource> getSelectedDataSources(Case currentCase, GeneralReportSettings settings) throws TskCoreException {
264  return currentCase.getSleuthkitCase().getDataSources().stream()
265  .filter((dataSource) -> {
266  if (settings.getSelectedDataSources() == null) {
267  // Assume all data sources if list is null.
268  return true;
269  }
270  return settings.getSelectedDataSources().contains(dataSource.getId());
271  })
272  .collect(Collectors.toList());
273  }
274 
278  private void warnIngest(ReportProgressPanel progressPanel) {
280  progressPanel.updateStatusLabel(Bundle.CaseUcoReportModule_ingestWarning());
281  }
282  }
283 
287  private void performDepthFirstSearch(DataSource dataSource,
288  Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter) throws IOException, TskCoreException {
289 
290  Deque<Content> stack = new ArrayDeque<>();
291  stack.addAll(dataSource.getChildren());
292 
293  //Depth First Search the data source tree.
294  while (!stack.isEmpty()) {
295  Content current = stack.pop();
296  if (current instanceof AbstractFile) {
297  AbstractFile file = (AbstractFile) (current);
298  if (SUPPORTED_TYPES.contains(file.getMetaType().getValue())) {
299 
300  for (JsonElement element : exporter.exportAbstractFile(file)) {
301  gson.toJson(element, reportWriter);
302  }
303  }
304  }
305 
306  for (Content child : current.getChildren()) {
307  stack.push(child);
308  }
309  }
310  }
311 }
static synchronized IngestManager getInstance()
void performDepthFirstSearch(DataSource dataSource, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter)
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1932
void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
Void create(ProgressIndicator progressIndicator, Object additionalParams)
Definition: Case.java:2226
List< DataSource > getSelectedDataSources(Case currentCase, GeneralReportSettings settings)

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