Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CorrelationAttributeNormalizer.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2019 Basis Technology Corp.
6  * Contact: 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  */
20 package org.sleuthkit.autopsy.centralrepository.datamodel;
21 
22 import java.util.Arrays;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Optional;
26 import java.util.Set;
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.commons.validator.routines.DomainValidator;
29 import org.apache.commons.validator.routines.EmailValidator;
31 
36 final public class CorrelationAttributeNormalizer {
37 
38  //common seperators that may be removed for normalizing
39  private static final String SEPERATORS_REGEX = "[\\s-:]";
40 
51 
52  if (attributeType == null) {
53  throw new CentralRepoException("Attribute type was null.");
54  }
55  if (data == null) {
56  throw new CentralRepoException("Correlation value was null.");
57  }
58 
59  String trimmedData = data.trim();
60 
61  switch (attributeType.getId()) {
63  return normalizeMd5(trimmedData);
65  return normalizeDomain(trimmedData);
67  return normalizeEmail(trimmedData);
69  return normalizePhone(trimmedData);
71  return normalizeUsbId(trimmedData);
73  return verifySsid(trimmedData);
75  return normalizeMac(trimmedData);
77  return normalizeImei(trimmedData);
79  return normalizeImsi(trimmedData);
81  return normalizeIccid(trimmedData);
82 
83  default:
84  // If the atttribute is not one of the above
85  // but is one of the other default correlation types, then let the data go as is
87  for (CorrelationAttributeInstance.Type defaultCorrelationType : defaultCorrelationTypes) {
88  if (defaultCorrelationType.getId() == attributeType.getId()) {
89  return trimmedData;
90  }
91  }
92  final String errorMessage = String.format(
93  "Validator function not found for attribute type: %s",
94  attributeType.getDisplayName());
95  throw new CentralRepoException(errorMessage);
96  }
97  }
98 
108  public static String normalize(int attributeTypeId, String data) throws CorrelationAttributeNormalizationException, CentralRepoException {
110  Optional<CorrelationAttributeInstance.Type> typeOption = defaultTypes.stream().filter(attributeType -> attributeType.getId() == attributeTypeId).findAny();
111 
112  if (typeOption.isPresent()) {
113  CorrelationAttributeInstance.Type type = typeOption.get();
114  return CorrelationAttributeNormalizer.normalize(type, data);
115  } else {
116  throw new CentralRepoException(String.format("Given attributeTypeId did not correspond to any known Attribute: %s", attributeTypeId));
117  }
118  }
119 
123  private static String normalizeMd5(String data) throws CorrelationAttributeNormalizationException {
124  final String validMd5Regex = "^[a-f0-9]{32}$";
125  final String dataLowered = data.toLowerCase();
126  if (dataLowered.matches(validMd5Regex)) {
127  return dataLowered;
128  } else {
129  throw new CorrelationAttributeNormalizationException(String.format("Data purporting to be an MD5 was found not to comform to expected format: %s", data));
130  }
131  }
132 
137  private static String normalizeDomain(String data) throws CorrelationAttributeNormalizationException {
138  DomainValidator validator = DomainValidator.getInstance(true);
139  if (validator.isValid(data)) {
140  return NetworkUtils.extractDomain(data.toLowerCase());
141  } else {
142  final String validIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";
143  if (data.matches(validIpAddressRegex)) {
144  return NetworkUtils.extractDomain(data);
145  } else {
146  throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid domain: %s", data));
147  }
148  }
149  }
150 
160  static String normalizeEmail(String emailAddress) throws CorrelationAttributeNormalizationException {
161  if (isValidEmailAddress(emailAddress)) {
162  return emailAddress.toLowerCase().trim();
163  } else {
164  throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid email address: %s", emailAddress));
165  }
166  }
167 
177  static String normalizePhone(String phoneNumber) throws CorrelationAttributeNormalizationException {
178  if (isValidPhoneNumber(phoneNumber)) {
179  String normalizedNumber = phoneNumber.replaceAll("\\s+", ""); // remove spaces.
180  normalizedNumber = normalizedNumber.replaceAll("[\\-()]", ""); // remove parens & dashes.
181 
182  // ensure a min length
183  if (normalizedNumber.length() < MIN_PHONENUMBER_LEN) {
184  throw new CorrelationAttributeNormalizationException(String.format("Phone number string %s is too short ", phoneNumber));
185  }
186  return normalizedNumber;
187 
188  } else {
189  throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid phone number: %s", phoneNumber));
190  }
191  }
192 
196  private static String normalizeUsbId(String data) throws CorrelationAttributeNormalizationException {
197  //TODO replace with correct usb id validation at a later date
198  return data;
199  }
200 
214  private static String verifySsid(String data) throws CorrelationAttributeNormalizationException {
215  if (data.length() <= 32) {
216  return data;
217  } else {
218  throw new CorrelationAttributeNormalizationException("Name provided was longer than the maximum valid SSID (32 characters). Name: " + data);
219  }
220  }
221 
244  private static String normalizeIccid(String data) throws CorrelationAttributeNormalizationException {
245  final String validIccidRegex = "^89[f0-9]{17,22}$";
246  final String iccidWithoutSeperators = data.toLowerCase().replaceAll(SEPERATORS_REGEX, "");
247  if (iccidWithoutSeperators.matches(validIccidRegex)) {
248  return iccidWithoutSeperators;
249  } else {
250  throw new CorrelationAttributeNormalizationException("Data provided was not a valid ICCID. : " + data);
251  }
252  }
253 
271  private static String normalizeImsi(String data) throws CorrelationAttributeNormalizationException {
272  final String validImsiRegex = "^[0-9]{14,15}$";
273  final String imsiWithoutSeperators = data.replaceAll(SEPERATORS_REGEX, "");
274  if (imsiWithoutSeperators.matches(validImsiRegex)) {
275  return imsiWithoutSeperators;
276  } else {
277  throw new CorrelationAttributeNormalizationException("Data provided was not a valid IMSI. : " + data);
278  }
279  }
280 
295  private static String normalizeMac(String data) throws CorrelationAttributeNormalizationException {
296  final String validMacRegex = "^([a-f0-9]{12}|[a-f0-9]{16})$";
297  final String macWithoutSeperators = data.toLowerCase().replaceAll(SEPERATORS_REGEX, "");
298  if (macWithoutSeperators.matches(validMacRegex)) {
299  return macWithoutSeperators;
300  } else {
301  throw new CorrelationAttributeNormalizationException("Data provided was not a valid MAC address. : " + data);
302  }
303  }
304 
324  private static String normalizeImei(String data) throws CorrelationAttributeNormalizationException {
325  final String validImeiRegex = "^[0-9]{14,16}$";
326  final String imeiWithoutSeperators = data.replaceAll(SEPERATORS_REGEX, "");
327  if (imeiWithoutSeperators.matches(validImeiRegex)) {
328  return imeiWithoutSeperators;
329  } else {
330  throw new CorrelationAttributeNormalizationException("Data provided was not a valid IMEI. : " + data);
331  }
332  }
333 
334  // These symbols are allowed in written form of phone numbers.
335  // A '+' is allowed only as a leading digit and hence not inlcuded here.
336  // While a dialed sequence may have additonal special characters, such as #, * or ',',
337  // CR attributes represent accounts and hence those chatracter are not allowed.
338  private static final Set<String> PHONENUMBER_CHARS = new HashSet<>(Arrays.asList(
339  "-", "(", ")"
340  ));
341 
342  private static final int MIN_PHONENUMBER_LEN = 5;
343 
352  static boolean isValidPhoneNumber(String phoneNumber) {
353 
354  // A phone number may have a leading '+', special telephony chars, or digits.
355  // Anything else implies an invalid phone number.
356  for (int i = 0; i < phoneNumber.length(); i++) {
357  if ( !((i == 0 && phoneNumber.charAt(i) == '+')
358  || Character.isSpaceChar(phoneNumber.charAt(i))
359  || Character.isDigit(phoneNumber.charAt(i))
360  || PHONENUMBER_CHARS.contains(String.valueOf(phoneNumber.charAt(i))))) {
361  return false;
362  }
363  }
364 
365  // ensure a min length
366  return phoneNumber.length() >= MIN_PHONENUMBER_LEN;
367  }
368 
377  static boolean isValidEmailAddress(String emailAddress) {
378  if (!StringUtils.isEmpty(emailAddress)) {
379  EmailValidator validator = EmailValidator.getInstance(true, true);
380  return validator.isValid(emailAddress);
381  }
382 
383  return false;
384  }
385 
390  //Empty constructor
391  }
392 }
static String extractDomain(String urlString)
static String normalize(CorrelationAttributeInstance.Type attributeType, String data)

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.