19 package org.sleuthkit.autopsy.modules.interestingitems;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.Serializable;
26 import java.nio.file.Path;
27 import java.nio.file.Paths;
28 import java.util.Arrays;
29 import java.util.HashMap;
30 import java.util.List;
32 import java.util.logging.Level;
33 import java.util.regex.Pattern;
34 import java.util.regex.PatternSyntaxException;
35 import javax.xml.parsers.DocumentBuilder;
36 import javax.xml.parsers.DocumentBuilderFactory;
37 import javax.xml.parsers.ParserConfigurationException;
38 import org.openide.util.io.NbObjectInputStream;
39 import org.openide.util.io.NbObjectOutputStream;
49 import org.w3c.dom.Document;
50 import org.w3c.dom.Element;
51 import org.w3c.dom.NodeList;
53 class InterestingItemsFilesSetSettings
implements Serializable {
55 private static final long serialVersionUID = 1L;
58 private static final String FILE_SETS_ROOT_TAG =
"INTERESTING_FILE_SETS";
59 private static final String DESC_ATTR =
"description";
60 private static final String IGNORE_KNOWN_FILES_ATTR =
"ignoreKnown";
61 private static final String PATH_REGEX_ATTR =
"pathRegex";
62 private static final String TYPE_FILTER_VALUE_ALL =
"all";
63 private static final String TYPE_FILTER_VALUE_FILES_AND_DIRS =
"files_and_dirs";
64 private static final String IGNORE_UNALLOCATED_SPACE =
"ingoreUnallocated";
65 private static final String PATH_FILTER_ATTR =
"pathFilter";
66 private static final String TYPE_FILTER_VALUE_DIRS =
"dir";
67 private static final String REGEX_ATTR =
"regex";
68 private static final List<String> illegalFileNameChars = FilesSetsManager.getIllegalFileNameChars();
69 private static final String FILE_SET_TAG =
"INTERESTING_FILE_SET";
70 private static final String NAME_RULE_TAG =
"NAME";
71 private static final String NAME_ATTR =
"name";
72 private static final String DAYS_INCLUDED_ATTR =
"daysIncluded";
73 private static final String MIME_ATTR =
"mimeType";
74 private static final String FS_COMPARATOR_ATTR =
"comparatorSymbol";
75 private static final String FS_SIZE_ATTR =
"sizeValue";
76 private static final String FS_UNITS_ATTR =
"sizeUnits";
77 private static final String TYPE_FILTER_VALUE_FILES =
"file";
78 private static final String XML_ENCODING =
"UTF-8";
79 private static final Logger logger = Logger.getLogger(InterestingItemsFilesSetSettings.class.getName());
80 private static final String TYPE_FILTER_ATTR =
"typeFilter";
81 private static final String EXTENSION_RULE_TAG =
"EXTENSION";
83 private Map<String, FilesSet> filesSets;
85 InterestingItemsFilesSetSettings(Map<String, FilesSet> filesSets) {
86 this.filesSets = filesSets;
92 Map<String, FilesSet> getFilesSets() {
103 private static String readRuleName(Element elem) {
105 String ruleName = elem.getAttribute(NAME_ATTR);
117 private static Map<String, FilesSet> readSerializedDefinitions(String serialFileName)
throws FilesSetsManager.FilesSetsManagerException {
118 Path filePath = Paths.get(PlatformUtil.getUserConfigDirectory(), serialFileName);
119 File fileSetFile = filePath.toFile();
120 String filePathStr = filePath.toString();
121 if (fileSetFile.exists()) {
123 try (
final NbObjectInputStream in =
new NbObjectInputStream(
new FileInputStream(filePathStr))) {
124 InterestingItemsFilesSetSettings filesSetsSettings = (InterestingItemsFilesSetSettings) in.readObject();
125 return filesSetsSettings.getFilesSets();
127 }
catch (IOException | ClassNotFoundException ex) {
128 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Failed to read settings from %s", filePathStr), ex);
131 return new HashMap<>();
146 private static ParentPathCondition readPathCondition(Element ruleElement)
throws FilesSetsManager.FilesSetsManagerException {
149 ParentPathCondition pathCondition = null;
150 if (!ruleElement.getAttribute(PATH_FILTER_ATTR).isEmpty() || !ruleElement.getAttribute(PATH_REGEX_ATTR).isEmpty()) {
151 String path = ruleElement.getAttribute(PATH_FILTER_ATTR);
152 String pathRegex = ruleElement.getAttribute(PATH_REGEX_ATTR);
153 if (!pathRegex.isEmpty() && path.isEmpty()) {
155 Pattern pattern = Pattern.compile(pathRegex);
156 pathCondition =
new ParentPathCondition(pattern);
157 }
catch (PatternSyntaxException ex) {
158 logger.log(Level.SEVERE,
"Error compiling " + PATH_REGEX_ATTR +
" regex, ignoring malformed path condition definition", ex);
159 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"error compiling %s regex", PATH_REGEX_ATTR), ex);
161 }
else if (!path.isEmpty() && pathRegex.isEmpty()) {
162 pathCondition =
new ParentPathCondition(path);
164 if (pathCondition == null) {
166 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Error creating path condition for rule %s", readRuleName(ruleElement)));
169 return pathCondition;
183 private static DateCondition readDateCondition(Element ruleElement)
throws FilesSetsManager.FilesSetsManagerException {
186 DateCondition dateCondition = null;
187 if (!ruleElement.getAttribute(DAYS_INCLUDED_ATTR).isEmpty()) {
188 String daysIncluded = ruleElement.getAttribute(DAYS_INCLUDED_ATTR);
189 if (!daysIncluded.isEmpty()) {
191 dateCondition =
new DateCondition(Integer.parseInt(daysIncluded));
192 }
catch (NumberFormatException ex) {
193 logger.log(Level.SEVERE,
"Error creating condition for " + daysIncluded +
", ignoring malformed date condition definition", ex);
194 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"error compiling %s regex", DAYS_INCLUDED_ATTR), ex);
198 return dateCondition;
208 private static Pattern compileRegex(String regex) {
210 return Pattern.compile(regex);
211 }
catch (PatternSyntaxException ex) {
212 logger.log(Level.SEVERE,
"Error compiling rule regex: " + ex.getMessage(), ex);
229 private static FilesSet.Rule readRule(Element elem)
throws FilesSetsManager.FilesSetsManagerException {
230 String ruleName = readRuleName(elem);
231 FileNameCondition nameCondition = readNameCondition(elem);
232 MetaTypeCondition metaCondition = readMetaTypeCondition(elem);
233 ParentPathCondition pathCondition = readPathCondition(elem);
234 MimeTypeCondition mimeCondition = readMimeCondition(elem);
235 FileSizeCondition sizeCondition = readSizeCondition(elem);
236 DateCondition dateCondition = readDateCondition(elem);
237 if (metaCondition == null || (nameCondition == null && pathCondition == null && mimeCondition == null && sizeCondition == null && dateCondition == null)) {
238 logger.log(Level.WARNING,
"Error Reading Rule, " + ruleName +
" was either missing a meta condition or contained only a meta condition. No rule was imported.");
239 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Invalid Rule in FilesSet xml, missing necessary conditions for %s", ruleName));
241 return new FilesSet.Rule(ruleName, nameCondition, metaCondition, pathCondition, mimeCondition, sizeCondition, dateCondition);
255 private static FileNameCondition readNameCondition(Element elem)
throws FilesSetsManager.FilesSetsManagerException {
256 FileNameCondition nameCondition = null;
257 String content = elem.getTextContent();
258 String regex = elem.getAttribute(REGEX_ATTR);
259 if (content != null && !content.isEmpty()) {
260 if ((!regex.isEmpty() && regex.equalsIgnoreCase(
"true")) || content.contains(
"*")) {
261 Pattern pattern = compileRegex(content);
262 if (pattern != null) {
263 if (elem.getTagName().equals(NAME_RULE_TAG)) {
264 nameCondition =
new FilesSet.Rule.FullNameCondition(pattern);
265 }
else if (elem.getTagName().equals(EXTENSION_RULE_TAG)) {
266 nameCondition =
new FilesSet.Rule.ExtensionCondition(pattern);
268 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Name condition has invalid tag name of %s for rule %s", elem.getTagName(), readRuleName(elem)));
271 logger.log(Level.SEVERE,
"Error compiling " + elem.getTagName() +
" regex, ignoring malformed '{0}' rule definition", readRuleName(elem));
272 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"error compiling %s regex in rule %s", REGEX_ATTR, readRuleName(elem)));
275 for (String illegalChar : illegalFileNameChars) {
276 if (content.contains(illegalChar)) {
277 logger.log(Level.SEVERE, elem.getTagName() +
" content has illegal chars, ignoring malformed '{0}' rule definition",
new Object[]{elem.getTagName(), readRuleName(elem)});
278 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"File name has illegal character of %s in rule %s", illegalChar, readRuleName(elem)));
281 if (elem.getTagName().equals(NAME_RULE_TAG)) {
282 nameCondition =
new FilesSet.Rule.FullNameCondition(content);
283 }
else if (elem.getTagName().equals(EXTENSION_RULE_TAG)) {
284 nameCondition =
new FilesSet.Rule.ExtensionCondition(Arrays.asList(content.split(
",")));
288 return nameCondition;
299 private static MimeTypeCondition readMimeCondition(Element elem) {
300 MimeTypeCondition mimeCondition = null;
301 if (!elem.getAttribute(MIME_ATTR).isEmpty()) {
302 mimeCondition =
new MimeTypeCondition(elem.getAttribute(MIME_ATTR));
307 return mimeCondition;
321 private static FileSizeCondition readSizeCondition(Element elem)
throws FilesSetsManager.FilesSetsManagerException {
322 FileSizeCondition sizeCondition = null;
323 if (!elem.getAttribute(FS_COMPARATOR_ATTR).isEmpty() && !elem.getAttribute(FS_SIZE_ATTR).isEmpty() && !elem.getAttribute(FS_UNITS_ATTR).isEmpty()) {
325 FileSizeCondition.COMPARATOR comparator = FileSizeCondition.COMPARATOR.fromSymbol(elem.getAttribute(FS_COMPARATOR_ATTR));
326 FileSizeCondition.SIZE_UNIT sizeUnit = FileSizeCondition.SIZE_UNIT.fromName(elem.getAttribute(FS_UNITS_ATTR));
327 int size = Integer.parseInt(elem.getAttribute(FS_SIZE_ATTR));
328 sizeCondition =
new FileSizeCondition(comparator, sizeUnit, size);
329 }
catch (NumberFormatException nfEx) {
330 logger.log(Level.SEVERE,
"Value in file size attribute was not an integer, unable to create FileSizeCondition for rule: " + readRuleName(elem), nfEx);
331 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Non integer size in FilesSet XML for rule %s", readRuleName(elem)), nfEx);
332 }
catch (IllegalArgumentException iaEx) {
333 logger.log(Level.SEVERE,
"Invalid Comparator symbol or Size Unit set in FilesSet xml, unable to create FileSizeCondition for rule: " + readRuleName(elem), iaEx);
334 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Invalid Comparator or Size unit in FilesSet XML for rule %s", readRuleName(elem)), iaEx);
337 else if (!elem.getAttribute(FS_COMPARATOR_ATTR).isEmpty() || !elem.getAttribute(FS_SIZE_ATTR).isEmpty() || !elem.getAttribute(FS_UNITS_ATTR).isEmpty()) {
338 logger.log(Level.SEVERE,
"Invalid Comparator symbol or Size Unit set in FilesSet xml, unable to create FileSizeCondition for rule: " + readRuleName(elem));
339 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"XML malformed missing at least one fileSize attribute for rule %s", readRuleName(elem)));
341 return sizeCondition;
354 private static void readFilesSet(Element setElem, Map<String, FilesSet> filesSets, String filePath)
throws FilesSetsManager.FilesSetsManagerException {
356 String setName = setElem.getAttribute(NAME_ATTR);
357 if (setName.isEmpty()) {
358 logger.log(Level.SEVERE,
"Found {0} element without required {1} attribute, ignoring malformed file set definition in FilesSet definition file at {2}",
new Object[]{FILE_SET_TAG, NAME_ATTR, filePath});
361 if (filesSets.containsKey(setName)) {
362 logger.log(Level.SEVERE,
"Found duplicate definition of set named {0} in FilesSet definition file at {1}, discarding duplicate set",
new Object[]{setName, filePath});
366 String description = setElem.getAttribute(DESC_ATTR);
369 String ignoreKnown = setElem.getAttribute(IGNORE_KNOWN_FILES_ATTR);
370 boolean ignoreKnownFiles =
false;
371 if (!ignoreKnown.isEmpty()) {
372 ignoreKnownFiles = Boolean.parseBoolean(ignoreKnown);
376 String ignoreUnallocated = setElem.getAttribute(IGNORE_UNALLOCATED_SPACE);
377 boolean ignoreUnallocatedSpace =
false;
378 if (!ignoreUnallocated.isEmpty()) {
379 ignoreUnallocatedSpace = Boolean.parseBoolean(ignoreUnallocated);
382 Map<String, FilesSet.Rule> rules =
new HashMap<>();
383 NodeList allRuleElems = setElem.getChildNodes();
384 for (
int j = 0; j < allRuleElems.getLength(); ++j) {
385 if (allRuleElems.item(j) instanceof Element) {
386 Element elem = (Element) allRuleElems.item(j);
387 FilesSet.Rule rule = readRule(elem);
389 if (!rules.containsKey(rule.getUuid())) {
390 rules.put(rule.getUuid(), rule);
392 logger.log(Level.SEVERE,
"Found duplicate rule {0} for set named {1} in FilesSet definition file at {2}, discarding malformed set",
new Object[]{rule.getUuid(), setName, filePath});
396 logger.log(Level.SEVERE,
"Found malformed rule for set named {0} in FilesSet definition file at {1}, discarding malformed set",
new Object[]{setName, filePath});
404 FilesSet set =
new FilesSet(setName, description, ignoreKnownFiles, ignoreUnallocatedSpace, rules);
405 filesSets.put(set.getName(), set);
423 static Map<String, FilesSet> readDefinitionsFile(String fileName, String legacyFileName)
throws FilesSetsManager.FilesSetsManagerException {
424 Map<String, FilesSet> filesSets = readSerializedDefinitions(fileName);
425 if (!filesSets.isEmpty()) {
429 if (!legacyFileName.isEmpty()) {
430 return readDefinitionsXML(Paths.get(PlatformUtil.getUserConfigDirectory(), legacyFileName).toFile());
448 static Map<String, FilesSet> readDefinitionsXML(File xmlFile)
throws FilesSetsManager.FilesSetsManagerException {
449 Map<String, FilesSet> filesSets =
new HashMap<>();
450 if (!xmlFile.exists()) {
454 if (!xmlFile.canRead()) {
455 logger.log(Level.SEVERE,
"FilesSet definition file at {0} exists, but cannot be read", xmlFile.getPath());
459 Document doc = XMLUtil.loadDoc(InterestingItemsFilesSetSettings.class, xmlFile.getPath());
461 logger.log(Level.SEVERE,
"FilesSet definition file at {0}", xmlFile.getPath());
465 Element root = doc.getDocumentElement();
467 logger.log(Level.SEVERE,
"Failed to get root {0} element tag of FilesSet definition file at {1}",
new Object[]{FILE_SETS_ROOT_TAG, xmlFile.getPath()});
471 NodeList setElems = root.getElementsByTagName(FILE_SET_TAG);
472 for (
int i = 0; i < setElems.getLength(); ++i) {
473 readFilesSet((Element) setElems.item(i), filesSets, xmlFile.getPath());
488 static boolean writeDefinitionsFile(String fileName, Map<String, FilesSet> interestingFilesSets)
throws FilesSetsManager.FilesSetsManagerException {
489 try (
final NbObjectOutputStream out =
new NbObjectOutputStream(
new FileOutputStream(Paths.get(PlatformUtil.getUserConfigDirectory(), fileName).toString()))) {
490 out.writeObject(
new InterestingItemsFilesSetSettings(interestingFilesSets));
491 }
catch (IOException ex) {
492 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Failed to write settings to %s", fileName), ex);
506 static boolean exportXmlDefinitionsFile(File xmlFile, List<FilesSet> interestingFilesSets) {
507 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
510 DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
511 Document doc = docBuilder.newDocument();
512 Element rootElement = doc.createElement(FILE_SETS_ROOT_TAG);
513 doc.appendChild(rootElement);
515 for (FilesSet set : interestingFilesSets) {
517 Element setElement = doc.createElement(FILE_SET_TAG);
518 setElement.setAttribute(NAME_ATTR, set.getName());
519 setElement.setAttribute(DESC_ATTR, set.getDescription());
520 setElement.setAttribute(IGNORE_KNOWN_FILES_ATTR, Boolean.toString(set.ignoresKnownFiles()));
523 for (FilesSet.Rule rule : set.getRules().values()) {
528 FileNameCondition nameCondition = rule.getFileNameCondition();
533 if (nameCondition instanceof FilesSet.Rule.FullNameCondition) {
534 ruleElement = doc.createElement(NAME_RULE_TAG);
536 ruleElement = doc.createElement(EXTENSION_RULE_TAG);
539 ruleElement.setAttribute(NAME_ATTR, rule.getName());
540 if (nameCondition != null) {
542 ruleElement.setAttribute(REGEX_ATTR, Boolean.toString(nameCondition.isRegex()));
544 ruleElement.setTextContent(nameCondition.getTextToMatch());
547 MetaTypeCondition typeCondition = rule.getMetaTypeCondition();
548 switch (typeCondition.getMetaType()) {
550 ruleElement.setAttribute(TYPE_FILTER_ATTR, TYPE_FILTER_VALUE_FILES);
553 ruleElement.setAttribute(TYPE_FILTER_ATTR, TYPE_FILTER_VALUE_DIRS);
556 ruleElement.setAttribute(TYPE_FILTER_ATTR, TYPE_FILTER_VALUE_ALL);
560 ParentPathCondition pathCondition = rule.getPathCondition();
561 if (pathCondition != null) {
562 if (pathCondition.isRegex()) {
563 ruleElement.setAttribute(PATH_REGEX_ATTR, pathCondition.getTextToMatch());
565 ruleElement.setAttribute(PATH_FILTER_ATTR, pathCondition.getTextToMatch());
569 MimeTypeCondition mimeCondition = rule.getMimeTypeCondition();
570 if (mimeCondition != null) {
571 ruleElement.setAttribute(MIME_ATTR, mimeCondition.getMimeType());
574 FileSizeCondition sizeCondition = rule.getFileSizeCondition();
575 if (sizeCondition != null) {
576 ruleElement.setAttribute(FS_COMPARATOR_ATTR, sizeCondition.getComparator().getSymbol());
577 ruleElement.setAttribute(FS_SIZE_ATTR, Integer.toString(sizeCondition.getSizeValue()));
578 ruleElement.setAttribute(FS_UNITS_ATTR, sizeCondition.getUnit().getName());
582 DateCondition dateCondition = rule.getDateCondition();
583 if (dateCondition != null) {
584 ruleElement.setAttribute(DAYS_INCLUDED_ATTR, Integer.toString(dateCondition.getDaysIncluded()));
587 setElement.appendChild(ruleElement);
589 rootElement.appendChild(setElement);
593 return XMLUtil.saveDoc(InterestingItemsFilesSetSettings.class, xmlFile.getPath(), XML_ENCODING, doc);
594 }
catch (ParserConfigurationException ex) {
595 logger.log(Level.SEVERE,
"Error writing interesting files definition file to " + xmlFile.getPath(), ex);
611 private static MetaTypeCondition readMetaTypeCondition(Element ruleElement)
throws FilesSetsManager.FilesSetsManagerException {
612 MetaTypeCondition metaCondition = null;
615 if (!ruleElement.getAttribute(TYPE_FILTER_ATTR).isEmpty()) {
616 String conditionAttribute = ruleElement.getAttribute(TYPE_FILTER_ATTR);
617 if (!conditionAttribute.isEmpty()) {
618 switch (conditionAttribute) {
619 case TYPE_FILTER_VALUE_FILES:
620 metaCondition =
new MetaTypeCondition(MetaTypeCondition.Type.FILES);
622 case TYPE_FILTER_VALUE_DIRS:
623 metaCondition =
new MetaTypeCondition(MetaTypeCondition.Type.DIRECTORIES);
625 case TYPE_FILTER_VALUE_ALL:
626 case TYPE_FILTER_VALUE_FILES_AND_DIRS:
627 metaCondition =
new MetaTypeCondition(MetaTypeCondition.Type.ALL);
630 logger.log(Level.SEVERE,
"Found {0} " + TYPE_FILTER_ATTR +
" attribute with unrecognized value ''{0}'', ignoring malformed rule definition", conditionAttribute);
632 throw new FilesSetsManager.FilesSetsManagerException(String.format(
"Malformed XML for Metatype condition, %s, in rule %s", conditionAttribute, readRuleName(ruleElement)));
636 if (metaCondition == null) {
639 metaCondition =
new MetaTypeCondition(MetaTypeCondition.Type.FILES);
641 return metaCondition;