Autopsy 4.23.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CyberTriageData.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2026 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 */
19package org.sleuthkit.autopsy.datamodel;
20
21import com.google.gson.JsonElement;
22import com.google.gson.JsonObject;
23import com.google.gson.JsonParseException;
24import com.google.gson.JsonParser;
25import com.google.gson.JsonPrimitive;
26import java.sql.ResultSet;
27import java.sql.SQLException;
28import java.time.Instant;
29import java.time.ZoneOffset;
30import java.time.format.DateTimeFormatter;
31import java.util.List;
32import java.util.Map;
33import java.util.logging.Level;
34import org.openide.nodes.ChildFactory;
35import org.openide.nodes.Children;
36import org.openide.nodes.Node;
37import org.openide.nodes.Sheet;
38import org.openide.util.lookup.Lookups;
39import org.sleuthkit.autopsy.coreutils.Logger;
40import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
41import org.sleuthkit.datamodel.SleuthkitCase;
42import org.sleuthkit.datamodel.TskCoreException;
43
49public class CyberTriageData implements AutopsyVisitableItem {
50
51 private final SleuthkitCase skCase;
52
53 public CyberTriageData(SleuthkitCase skCase) {
54 this.skCase = skCase;
55 }
56
57 public SleuthkitCase getSleuthkitCase() {
58 return skCase;
59 }
60
61 @Override
62 public <T> T accept(AutopsyItemVisitor<T> visitor) {
63 return visitor.visit(this);
64 }
65
70 public static final String CT_JSON_ATTRIBUTE_TYPE_NAME = "CT_JSON_DATA_ATTRIBUTE";
71
76 public static boolean isCyberTriageDatabase(SleuthkitCase skCase) {
77 try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(
78 "SELECT name FROM sqlite_master WHERE type='table' AND name='ct_errors'")) {
79 return dbQuery.getResultSet().next();
80 } catch (TskCoreException | SQLException ex) {
81 return false;
82 }
83 }
84
95 public static void addCtJsonProperties(Map<String, Object> map, String json) {
96 if (json == null || json.isEmpty()) {
97 return;
98 }
99 try {
100 JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
101 for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
102 JsonElement value = entry.getValue();
103 if (value.isJsonNull()) {
104 continue;
105 }
106 String key = "CT " + entry.getKey();
107 if (value.isJsonPrimitive()) {
108 JsonPrimitive primitive = value.getAsJsonPrimitive();
109 if (primitive.isNumber()) {
110 String lowerName = entry.getKey().toLowerCase();
111 if (lowerName.contains("date") || lowerName.contains("time")) {
112 long numVal = primitive.getAsLong();
113 // CT stores some timestamps in milliseconds; values > 10^10
114 // are ms and must be converted to seconds for TimeZoneUtils.
115 long seconds = (numVal > 10_000_000_000L) ? numVal / 1000 : numVal;
116 map.put(key, TimeZoneUtils.getFormattedTime(seconds));
117 } else {
118 map.put(key, primitive.getAsNumber());
119 }
120 } else if (primitive.isBoolean()) {
121 map.put(key, primitive.getAsBoolean());
122 } else {
123 map.put(key, primitive.getAsString());
124 }
125 } else {
126 // Nested object or array — store as string
127 map.put(key, value.toString());
128 }
129 }
130 } catch (JsonParseException | IllegalStateException ex) {
131 Logger.getLogger(CyberTriageData.class.getName())
132 .log(Level.WARNING, "Failed to parse CT_JSON_DATA_ATTRIBUTE value", ex);
133 }
134 }
135
136 // -------------------------------------------------------------------------
137 // Root node
138 // -------------------------------------------------------------------------
139
143 public static class RootNode extends DisplayableItemNode {
144
145 private static final String DISPLAY_NAME = "Addl. Cyber Triage Data";
146 private static final String ICON_PATH = "org/sleuthkit/autopsy/images/extracted_content.png";
147
148 public RootNode(SleuthkitCase skCase) {
149 super(Children.create(new RootChildFactory(skCase), true),
150 Lookups.singleton(DISPLAY_NAME));
151 setName(DISPLAY_NAME);
152 setDisplayName(DISPLAY_NAME);
153 setIconBaseWithExtension(ICON_PATH);
154 }
155
156 @Override
157 public boolean isLeafTypeNode() {
158 return false;
159 }
160
161 @Override
162 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
163 return visitor.visit(this);
164 }
165
166 @Override
167 public String getItemType() {
168 return getClass().getName();
169 }
170 }
171
176 private static class RootChildFactory extends ChildFactory<String> {
177
178 private final SleuthkitCase skCase;
179
180 RootChildFactory(SleuthkitCase skCase) {
181 this.skCase = skCase;
182 }
183
184 @Override
185 protected boolean createKeys(List<String> list) {
186 list.add("ERRORS");
187 return true;
188 }
189
190 @Override
191 protected Node createNodeForKey(String key) {
192 if ("ERRORS".equals(key)) {
193 return new ErrorsNode(skCase);
194 }
195 return null;
196 }
197 }
198
199 // -------------------------------------------------------------------------
200 // Errors node
201 // -------------------------------------------------------------------------
202
206 public static class ErrorsNode extends DisplayableItemNode {
207
208 private static final String DISPLAY_NAME = "Errors";
209 private static final String ICON_PATH = "org/sleuthkit/autopsy/images/error-icon-16.png";
210
211 public ErrorsNode(SleuthkitCase skCase) {
212 super(Children.create(new ErrorsChildFactory(skCase), true),
213 Lookups.singleton(DISPLAY_NAME));
214 setName(DISPLAY_NAME);
215 setDisplayName(DISPLAY_NAME);
216 setIconBaseWithExtension(ICON_PATH);
217 }
218
219 @Override
220 public boolean isLeafTypeNode() {
221 return false;
222 }
223
224 @Override
225 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
226 return visitor.visit(this);
227 }
228
229 @Override
230 public String getItemType() {
231 return getClass().getName();
232 }
233 }
234
235 // -------------------------------------------------------------------------
236 // Error data and leaf nodes
237 // -------------------------------------------------------------------------
238
242 public static class CtError {
243
244 public final long id;
245 public final String title;
246 public final String description;
247 public final String stackTrace;
248 public final long timestamp;
249 public final String severity;
250
251 CtError(long id, String title, String description,
252 String stackTrace, long timestamp, String severity) {
253 this.id = id;
254 this.title = title;
255 this.description = description;
256 this.stackTrace = stackTrace;
257 this.timestamp = timestamp;
258 this.severity = severity;
259 }
260 }
261
265 private static class ErrorsChildFactory extends ChildFactory<CtError> {
266
267 private static final Logger logger = Logger.getLogger(ErrorsChildFactory.class.getName());
268 private final SleuthkitCase skCase;
269
270 ErrorsChildFactory(SleuthkitCase skCase) {
271 this.skCase = skCase;
272 }
273
274 @Override
275 protected boolean createKeys(List<CtError> list) {
276 String query = "SELECT id, title, description, stack_trace, time_stamp, severity "
277 + "FROM ct_errors ORDER BY time_stamp DESC";
278 try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(query)) {
279 ResultSet rs = dbQuery.getResultSet();
280 while (rs.next()) {
281 list.add(new CtError(
282 rs.getLong("id"),
283 rs.getString("title"),
284 rs.getString("description"),
285 rs.getString("stack_trace"),
286 rs.getLong("time_stamp"),
287 rs.getString("severity")));
288 }
289 } catch (TskCoreException | SQLException ex) {
290 logger.log(Level.WARNING, "Failed to query ct_errors table", ex);
291 }
292 return true;
293 }
294
295 @Override
296 protected Node createNodeForKey(CtError error) {
297 return new ErrorNode(error);
298 }
299 }
300
304 public static class ErrorNode extends DisplayableItemNode {
305
306 private static final String ICON_PATH = "org/sleuthkit/autopsy/images/warning-icon-16.png";
307 private static final DateTimeFormatter DATE_FORMAT
308 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);
309
310 private final CtError error;
311
312 ErrorNode(CtError error) {
313 super(Children.LEAF, Lookups.singleton(error));
314 this.error = error;
315 setName(Long.toString(error.id));
316 setDisplayName(error.title);
317 setIconBaseWithExtension(ICON_PATH);
318 }
319
320 @Override
321 public boolean isLeafTypeNode() {
322 return true;
323 }
324
325 @Override
326 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
327 return visitor.visit(this);
328 }
329
330 @Override
331 public String getItemType() {
332 return getClass().getName();
333 }
334
335 @Override
336 protected Sheet createSheet() {
337 Sheet sheet = super.createSheet();
338 Sheet.Set props = sheet.get(Sheet.PROPERTIES);
339 if (props == null) {
340 props = Sheet.createPropertiesSet();
341 sheet.put(props);
342 }
343 props.put(new NodeProperty<>("Severity", "Severity", "Severity of the error",
344 error.severity != null ? error.severity : ""));
345 props.put(new NodeProperty<>("Title", "Title", "Error title",
346 error.title != null ? error.title : ""));
347 props.put(new NodeProperty<>("Description", "Description", "Error description",
348 error.description != null ? error.description : ""));
349 props.put(new NodeProperty<>("Timestamp", "Timestamp", "When the error occurred",
350 DATE_FORMAT.format(Instant.ofEpochMilli(error.timestamp))));
351 props.put(new NodeProperty<>("StackTrace", "Stack Trace", "Error stack trace",
352 error.stackTrace != null ? error.stackTrace : ""));
353 return sheet;
354 }
355 }
356}
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static String getFormattedTime(long epochTime)
static boolean isCyberTriageDatabase(SleuthkitCase skCase)
static void addCtJsonProperties(Map< String, Object > map, String json)

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