66package org.sleuthkit.autopsy.casemodule.services;
77
88import com.google.gson.Gson;
9+ import com.google.gson.GsonBuilder;
10+ import com.google.gson.JsonArray;
11+ import com.google.gson.JsonDeserializationContext;
12+ import com.google.gson.JsonDeserializer;
13+ import com.google.gson.JsonElement;
14+ import com.google.gson.JsonObject;
15+ import com.google.gson.JsonParseException;
16+ import com.google.gson.JsonSyntaxException;
17+ import java.lang.reflect.Type;
918import java.io.File;
1019import java.io.FileWriter;
1120import java.io.IOException;
1726import javax.annotation.concurrent.Immutable;
1827import java.io.FileFilter;
1928import java.io.FileReader;
29+ import java.util.logging.Level;
30+ import org.sleuthkit.autopsy.coreutils.Logger;
2031import org.sleuthkit.autopsy.coreutils.PlatformUtil;
32+ import org.sleuthkit.datamodel.TagName;
33+ import org.sleuthkit.datamodel.TskData;
2134
2235/**
2336 * Definition of a tag set.
2437 */
2538@Immutable
2639final public class TagSetDefinition {
40+
41+ private static final Logger LOGGER = Logger.getLogger(TagSetDefinition.class.getName());
2742
2843 private final static String FILE_NAME_TEMPLATE = "%s-tag-set.json";
2944 private final static Path TAGS_USER_CONFIG_DIR = Paths.get(PlatformUtil.getUserConfigDirectory(), "tags");
@@ -102,10 +117,24 @@ static synchronized List<TagSetDefinition> readTagSetDefinitions() throws IOExce
102117 }
103118
104119 File[] fileList = dir.listFiles(new TagSetJsonFileFilter());
105- Gson gson = new Gson();
120+ if (fileList == null) {
121+ return tagSetList;
122+ }
123+
124+ Gson gson = new GsonBuilder()
125+ .registerTypeAdapter(TagSetDefinition.class, new TagSetDefinitionDeserializer()) // Use custom deserializer
126+ .create();
127+
106128 for (File file : fileList) {
107129 try (FileReader reader = new FileReader(file)) {
108- tagSetList.add(gson.fromJson(reader, TagSetDefinition.class));
130+ TagSetDefinition tagSet = gson.fromJson(reader, TagSetDefinition.class);
131+ if (tagSet != null) {
132+ tagSetList.add(tagSet);
133+ }
134+ } catch (JsonSyntaxException e) {
135+ LOGGER.log(Level.SEVERE, "Skipping invalid JSON file: " + file.getName() + " - " + e.getMessage());
136+ } catch (IOException e) {
137+ LOGGER.log(Level.SEVERE, "Error reading file: " + file.getName() + " - " + e.getMessage());
109138 }
110139 }
111140
@@ -132,4 +161,56 @@ public boolean accept(File file) {
132161 }
133162
134163 }
164+
165+ // Custom JSON Deserializer for TagSetDefinition to support legacy user tags and tag set JSON files.
166+ // In TSK release 4.13.0 and Autopsy release 4.22.0 we:
167+ // 1) renamed "TskData.KnownStatus" to "TskData.TagType"
168+ // 2) renamed "TagSetDefinition.knownStatus" to "TagSetDefinition.tagType"
169+ // 3) "TskData.KnownStatus" of "unknown" used to carry a score of "suspicious".
170+ // Now "TskData.TagType" of "unknown" carries a score of "unknown".
171+ //
172+ private static class TagSetDefinitionDeserializer implements JsonDeserializer<TagSetDefinition> {
173+
174+ @Override
175+ public TagSetDefinition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
176+ JsonObject jsonObject = json.getAsJsonObject();
177+
178+ String name = jsonObject.has("name") ? jsonObject.get("name").getAsString() : null;
179+ JsonArray tagArray = jsonObject.has("tagNameDefinitionList") ? jsonObject.getAsJsonArray("tagNameDefinitionList") : new JsonArray();
180+ List<TagNameDefinition> tagNameDefinitions = new ArrayList<>();
181+
182+ for (JsonElement element : tagArray) {
183+ JsonObject tagObject = element.getAsJsonObject();
184+
185+ String displayName = tagObject.has("displayName") ? tagObject.get("displayName").getAsString() : null;
186+ String description = tagObject.has("description") ? tagObject.get("description").getAsString() : null;
187+ TagName.HTML_COLOR color = context.deserialize(tagObject.get("color"), TagName.HTML_COLOR.class);
188+
189+ TskData.TagType tagType = null;
190+ // Handle tagType vs knownStatus
191+ if (tagObject.has("tagType") && !tagObject.get("tagType").isJsonNull()) {
192+ tagType = context.deserialize(tagObject.get("tagType"), TskData.TagType.class);
193+ } else if (tagObject.has("knownStatus") && !tagObject.get("knownStatus").isJsonNull()) {
194+ TskData.TagType legacyStatus = context.deserialize(tagObject.get("knownStatus"), TskData.TagType.class);
195+
196+ // "UNKNOWN" tag type used to carry an automatic "SUSPICIOUS" score.
197+ // If knownStatus was "UNKNOWN", use "SUSPICIOUS" instead
198+ if (legacyStatus == TskData.TagType.UNKNOWN) {
199+ tagType = TskData.TagType.SUSPICIOUS;
200+ } else {
201+ tagType = legacyStatus;
202+ }
203+ }
204+
205+ if (tagType == null) {
206+ LOGGER.log(Level.SEVERE, "Failed to initialize tagType for tag: {0}. Skipping entry.", displayName);
207+ continue;
208+ }
209+
210+ tagNameDefinitions.add(new TagNameDefinition(displayName, description, color, tagType));
211+ }
212+
213+ return new TagSetDefinition(name, tagNameDefinitions);
214+ }
215+ }
135216}
0 commit comments