6
6
package org .sleuthkit .autopsy .casemodule .services ;
7
7
8
8
import 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 ;
9
18
import java .io .File ;
10
19
import java .io .FileWriter ;
11
20
import java .io .IOException ;
17
26
import javax .annotation .concurrent .Immutable ;
18
27
import java .io .FileFilter ;
19
28
import java .io .FileReader ;
29
+ import java .util .logging .Level ;
30
+ import org .sleuthkit .autopsy .coreutils .Logger ;
20
31
import org .sleuthkit .autopsy .coreutils .PlatformUtil ;
32
+ import org .sleuthkit .datamodel .TagName ;
33
+ import org .sleuthkit .datamodel .TskData ;
21
34
22
35
/**
23
36
* Definition of a tag set.
24
37
*/
25
38
@ Immutable
26
39
final public class TagSetDefinition {
40
+
41
+ private static final Logger LOGGER = Logger .getLogger (TagSetDefinition .class .getName ());
27
42
28
43
private final static String FILE_NAME_TEMPLATE = "%s-tag-set.json" ;
29
44
private final static Path TAGS_USER_CONFIG_DIR = Paths .get (PlatformUtil .getUserConfigDirectory (), "tags" );
@@ -102,10 +117,24 @@ static synchronized List<TagSetDefinition> readTagSetDefinitions() throws IOExce
102
117
}
103
118
104
119
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
+
106
128
for (File file : fileList ) {
107
129
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 ());
109
138
}
110
139
}
111
140
@@ -132,4 +161,56 @@ public boolean accept(File file) {
132
161
}
133
162
134
163
}
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
+ }
135
216
}
0 commit comments