Autopsy 4.22.1
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 */
20package org.sleuthkit.autopsy.report.modules.caseuco;
21
22import com.google.gson.Gson;
23import com.google.gson.GsonBuilder;
24import com.google.gson.JsonElement;
25import com.google.gson.stream.JsonWriter;
26
27import java.io.FileOutputStream;
28import java.io.IOException;
29import java.io.OutputStream;
30import java.io.OutputStreamWriter;
31import java.nio.file.Files;
32import java.nio.file.Path;
33import java.nio.file.Paths;
34import java.util.ArrayDeque;
35import java.util.Deque;
36import java.util.HashSet;
37import java.util.List;
38import java.util.Set;
39import java.util.logging.Level;
40import java.util.stream.Collectors;
41import javax.swing.JPanel;
42import org.openide.util.NbBundle;
43import org.sleuthkit.autopsy.casemodule.Case;
44import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
45import org.sleuthkit.autopsy.coreutils.Logger;
46import org.sleuthkit.autopsy.ingest.IngestManager;
47import org.sleuthkit.autopsy.report.GeneralReportModule;
48import org.sleuthkit.autopsy.report.GeneralReportSettings;
49import org.sleuthkit.autopsy.report.ReportProgressPanel;
50import org.sleuthkit.caseuco.CaseUcoExporter;
51import org.sleuthkit.caseuco.ContentNotExportableException;
52import org.sleuthkit.datamodel.AbstractFile;
53import org.sleuthkit.datamodel.BlackboardArtifact;
54import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
55import org.sleuthkit.datamodel.Content;
56import org.sleuthkit.datamodel.DataSource;
57import org.sleuthkit.datamodel.TskCoreException;
58import org.sleuthkit.datamodel.TskData;
59import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
60
65public 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
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
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}
void addReport(String localPath, String srcModuleName, String reportName)
Definition Case.java:1929
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static synchronized IngestManager getInstance()
void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel)
List< DataSource > getSelectedDataSources(Case currentCase, GeneralReportSettings settings)
void performDepthFirstSearch(DataSource dataSource, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.