diff --git a/.gitignore b/.gitignore index 9ae14c5cac..d4e0942232 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ Core/src/org/sleuthkit/autopsy/casemodule/docs/screenshot.png /test/script/*/*.xml .DS_Store .*.swp +KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/.~lock.ranges.csv# diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINRange.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINRange.java index c876ee11f8..1b9bb56560 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINRange.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINRange.java @@ -1,93 +1,89 @@ package org.sleuthkit.autopsy.keywordsearch; /** - * Representation of a range of Issuer Identifiaction Numbers for the same bank. + * Representation of a range of Issuer/Bank Identifiaction Numbers (IIN/BIN) for + * the * same bank. */ -public class IINRange { +class IINRange { - private final int IIN_start; - private final int IIN_end; - private final int number_length; - private final CreditCardScheme scheme; + private final int IINStart; + private final int IINEnd; + private final int numberLength; + private final PaymentCardScheme scheme; private final String brand; private final PaymentCardType type; private final String country; - private final String bank_name; - private final String bank_logo; - private final String bank_url; - private final String bank_phone; - private final String bank_city; + private final String bankName; + private final String bankURL; + private final String bankPhoneNumber; + private final String bankCity; enum PaymentCardType { DEBIT, DREDIT; } - enum CreditCardScheme { + enum PaymentCardScheme { AMEX, VISA, MASTERCARD, DINERS, DISCOVER } - IINRange(int IIN_start, int IIN_end, int number_length, CreditCardScheme scheme, String brand, PaymentCardType type, String country, String bank_name, String bank_logo, String bank_url, String bank_phone, String bank_city) { - this.IIN_start = IIN_start; - this.IIN_end = IIN_end; - this.number_length = number_length; + IINRange(int IIN_start, int IIN_end, int number_length, PaymentCardScheme scheme, String brand, PaymentCardType type, String country, String bank_name, String bank_url, String bank_phone, String bank_city) { + this.IINStart = IIN_start; + this.IINEnd = IIN_end; + this.numberLength = number_length; this.scheme = scheme; this.brand = brand; this.type = type; this.country = country; - this.bank_name = bank_name; - this.bank_logo = bank_logo; - this.bank_url = bank_url; - this.bank_phone = bank_phone; - this.bank_city = bank_city; + this.bankName = bank_name; + + this.bankURL = bank_url; + this.bankPhoneNumber = bank_phone; + this.bankCity = bank_city; } - public int getIIN_start() { - return IIN_start; + int getIINstart() { + return IINStart; } - public int getIIN_end() { - return IIN_end; + int getIINend() { + return IINEnd; } - public int getNumber_length() { - return number_length; + int getNumber_length() { + return numberLength; } - public CreditCardScheme getScheme() { + PaymentCardScheme getPaymentCardScheme() { return scheme; } - public String getBrand() { + String getBrand() { return brand; } - public PaymentCardType getType() { + PaymentCardType getPaymentCardType() { return type; } - public String getCountry() { + String getCountry() { return country; } - public String getBank_name() { - return bank_name; + String getBankName() { + return bankName; } - public String getBank_logo() { - return bank_logo; + String getBankURL() { + return bankURL; } - public String getBank_url() { - return bank_url; + String getBankPhoneNumber() { + return bankPhoneNumber; } - public String getBank_phone() { - return bank_phone; - } - - public String getBank_city() { - return bank_city; + String getBankCity() { + return bankCity; } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINValidator.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINValidator.java index b12509cd87..84a6965a2e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINValidator.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IINValidator.java @@ -17,12 +17,12 @@ import org.apache.commons.csv.CSVRecord; /** * - */ public class IINValidator { RangeMap iinRanges = TreeRangeMap.create(); - private IINValidator() throws FileNotFoundException, IOException { + + IINValidator() throws FileNotFoundException, IOException { Reader in = new FileReader("ranges.csv"); Iterable records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in); for (CSVRecord record : records) { @@ -30,18 +30,24 @@ public class IINValidator { IINRange iinRange = new IINRange(Integer.parseInt(record.get("iin_start")), Integer.parseInt(record.get("iin_end")), Integer.parseInt(record.get("number_length")), - IINRange.CreditCardScheme.valueOf(record.get("scheme")), + IINRange.PaymentCardScheme.valueOf(record.get("scheme")), record.get("brand"), IINRange.PaymentCardType.valueOf(record.get("type")), record.get("country"), record.get("bank_name"), - record.get("bank_logo"), record.get("bank_url"), record.get("bank_phone"), record.get("bank_city")); - iinRanges.put(Range.closed(iinRange.getIIN_start(), iinRange.getIIN_end()), iinRange); + iinRanges.put(Range.closed(iinRange.getIINstart(), iinRange.getIINend()), iinRange); } + } + boolean contains(int iin) { + return iinRanges.get(iin) != null; + } + + IINRange getIINRange(int iin) { + return iinRanges.get(iin); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java index 8b79367316..3abde18780 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java @@ -19,6 +19,7 @@ // package org.sleuthkit.autopsy.keywordsearch; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -31,6 +32,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.response.TermsResponse.Term; +import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.datamodel.AbstractFile; @@ -51,6 +53,7 @@ final class TermComponentQuery implements KeywordSearchQuery { private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); private static final BlackboardAttribute.Type SOLR_DOCUMENT_ID_TYPE = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SOLR_DOCUMENT_ID); + private static final BlackboardAttribute.Type ACCOUNT_NUMBER_TYPE = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER); //TODO: move these regex and the luhn check to a new class, something like: CreditCardNumberValidator /* @@ -108,9 +111,19 @@ final class TermComponentQuery implements KeywordSearchQuery { private final Keyword keyword; private boolean isEscaped; private final List filters = new ArrayList<>(); + private IINValidator iinValidator; TermComponentQuery(KeywordList keywordList, Keyword keyword) { this.keyword = keyword; + + if (keyword.getType() == ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER) { + try { + iinValidator = new IINValidator(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + this.keywordList = keywordList; this.escapedQuery = keyword.getQuery(); } @@ -177,33 +190,59 @@ final class TermComponentQuery implements KeywordSearchQuery { @Override public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(String termHit, KeywordHit hit, String snippet, String listName) { - BlackboardArtifact bba; + BlackboardArtifact newArtifact; Collection attributes = new ArrayList<>(); try { - //if the keyword hit matched the credit card number keyword/regex... + //if the keyword hit matched the credit card number keyword/regex... if (keyword.getType() == ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER) { - bba = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT); + newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT); // make account artifact //try to match it against the track 1 regex Matcher matcher = TRACK1_PATTERN.matcher(hit.getSnippet()); if (matcher.find()) { - parseTrack1Data(bba, matcher); + parseTrack1Data(newArtifact, matcher); } //then try to match it against the track 2 regex matcher = TRACK2_PATTERN.matcher(hit.getSnippet()); if (matcher.find()) { - parseTrack2Data(bba, matcher); + parseTrack2Data(newArtifact, matcher); } if (hit.getContent() instanceof AbstractFile) { AbstractFile file = (AbstractFile) hit.getContent(); if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { - bba.addAttribute(new BlackboardAttribute(SOLR_DOCUMENT_ID_TYPE, MODULE_NAME, hit.getSolrDocumentId())); + newArtifact.addAttribute(new BlackboardAttribute(SOLR_DOCUMENT_ID_TYPE, MODULE_NAME, hit.getSolrDocumentId())); } } + + String ccn = newArtifact.getAttribute(ACCOUNT_NUMBER_TYPE).getValueString(); + final int iin = Integer.parseInt(ccn.substring(0, 7)); + + IINRange iinRange = iinValidator.getIINRange(iin); + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CREDIT_CARD_SCHEME, MODULE_NAME, iinRange.getPaymentCardScheme().name())); + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PAYMENT_CARD_TYPE, MODULE_NAME, iinRange.getPaymentCardType().name())); + if (StringUtils.isNotBlank(iinRange.getBrand())) { + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND, MODULE_NAME, iinRange.getBrand())); + } + if (StringUtils.isNotBlank(iinRange.getBankName())) { + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, iinRange.getBankName())); + } + if (StringUtils.isNotBlank(iinRange.getBankPhoneNumber())) { + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, iinRange.getBankPhoneNumber())); + } + if (StringUtils.isNotBlank(iinRange.getBankURL())) { + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, iinRange.getBankURL())); + } + if (StringUtils.isNotBlank(iinRange.getCountry())) { + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, iinRange.getCountry())); + } + if (StringUtils.isNotBlank(iinRange.getBankCity())) { + newArtifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, iinRange.getBankCity())); + } + } else { //make keyword hit artifact - bba = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); + newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); //regex match attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, termHit)); @@ -229,8 +268,8 @@ final class TermComponentQuery implements KeywordSearchQuery { try { //TODO: do we still/really need this KeywordCachedArtifact class? - bba.addAttributes(attributes); - KeywordCachedArtifact writeResult = new KeywordCachedArtifact(bba); + newArtifact.addAttributes(attributes); + KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact); writeResult.add(attributes); return writeResult; } catch (TskCoreException e) { @@ -273,6 +312,7 @@ final class TermComponentQuery implements KeywordSearchQuery { */ QueryResults results = new QueryResults(this, keywordList); int resultSize = 0; + for (Term term : terms) { final String termStr = KeywordSearchUtil.escapeLuceneQuery(term.getTerm()); @@ -280,9 +320,15 @@ final class TermComponentQuery implements KeywordSearchQuery { //If the keyword is a credit card number, pass it through luhn validator Matcher matcher = CCN_PATTERN.matcher(term.getTerm()); matcher.find(); - if (false == LUHN_CHECK.isValid(matcher.group("ccn"))) { + final String ccn = matcher.group("ccn"); + if (false == LUHN_CHECK.isValid(ccn)) { continue; //if the hit does not pass the luhn check, skip it. } + final int iin = Integer.parseInt(ccn.substring(0, 7)); + + if (false == iinValidator.contains(iin)) { + continue; + } } /*