19 package org.sleuthkit.autopsy.keywordsearch;
 
   21 import com.google.common.base.CharMatcher;
 
   22 import java.util.ArrayList;
 
   23 import java.util.Collection;
 
   24 import java.util.HashMap;
 
   25 import java.util.HashSet;
 
   26 import java.util.List;
 
   29 import java.util.logging.Level;
 
   30 import java.util.regex.Matcher;
 
   31 import java.util.regex.Pattern;
 
   32 import org.apache.commons.lang.StringUtils;
 
   33 import org.apache.solr.client.solrj.SolrQuery;
 
   34 import org.apache.solr.client.solrj.response.TermsResponse.Term;
 
   35 import org.openide.util.Exceptions;
 
   45 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
 
   47 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
 
   58 final class TermsComponentQuery 
implements KeywordSearchQuery {
 
   60     private static final Logger LOGGER = Logger.getLogger(TermsComponentQuery.class.getName());
 
   61     private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
 
   62     private static final String SEARCH_HANDLER = 
"/terms"; 
 
   63     private static final String SEARCH_FIELD = Server.Schema.TEXT.toString();
 
   64     private static final int TERMS_SEARCH_TIMEOUT = 90 * 1000; 
 
   65     private static final String CASE_INSENSITIVE = 
"case_insensitive"; 
 
   66     private static final boolean DEBUG_FLAG = Version.Type.DEVELOPMENT.equals(Version.getBuildType());
 
   67     private static final int MAX_TERMS_QUERY_RESULTS = 20000;
 
   69     private final KeywordList keywordList;
 
   70     private final Keyword originalKeyword;
 
   71     private final List<KeywordQueryFilter> filters = 
new ArrayList<>(); 
 
   73     private String searchTerm;
 
   74     private boolean searchTermIsEscaped;
 
   86     static final Pattern CREDIT_CARD_NUM_PATTERN
 
   87             = Pattern.compile(
"(?<ccn>[2-6]([ -]?[0-9]){11,18})");
 
   88     static final Pattern CREDIT_CARD_TRACK1_PATTERN = Pattern.compile(
 
  100             + 
"(?<accountNumber>[2-6]([ -]?[0-9]){11,18})"  
  102             + 
"(?<name>[^^]{2,26})"  
  104             + 
"(?:(?:\\^|(?<expiration>\\d{4}))"  
  105             + 
"(?:(?:\\^|(?<serviceCode>\\d{3}))" 
  106             + 
"(?:(?<discretionary>[^?]*)"  
  110     static final Pattern CREDIT_CARD_TRACK2_PATTERN = Pattern.compile(
 
  121             + 
"(?<accountNumber>[2-6]([ -]?[0-9]){11,18})"  
  123             + 
"(?:(?<expiration>\\d{4})"  
  124             + 
"(?:(?<serviceCode>\\d{3})"  
  125             + 
"(?:(?<discretionary>[^:;<=>?]*)"  
  129     static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = 
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID);
 
  147     TermsComponentQuery(KeywordList keywordList, Keyword keyword) {
 
  148         this.keywordList = keywordList;
 
  149         this.originalKeyword = keyword;
 
  150         this.searchTerm = keyword.getSearchTerm();
 
  160     public KeywordList getKeywordList() {
 
  171     public String getQueryString() {
 
  172         return originalKeyword.getSearchTerm();
 
  183     public boolean isLiteral() {
 
  192     public void setSubstringQuery() {
 
  193         searchTerm = 
".*" + searchTerm + 
".*";
 
  200     public void escape() {
 
  201         searchTerm = Pattern.quote(originalKeyword.getSearchTerm());
 
  202         searchTermIsEscaped = 
true;
 
  211     public boolean isEscaped() {
 
  212         return searchTermIsEscaped;
 
  222     public String getEscapedQueryString() {
 
  223         return this.searchTerm;
 
  232     public boolean validate() {
 
  233         if (searchTerm.isEmpty()) {
 
  237             Pattern.compile(searchTerm);
 
  239         } 
catch (IllegalArgumentException ex) {
 
  251     public void setField(String field) {
 
  261     public void addFilter(KeywordQueryFilter filter) {
 
  262         this.filters.add(filter);
 
  276     public QueryResults performQuery() throws KeywordSearchModuleException, NoOpenCoreException {
 
  281         final SolrQuery termsQuery = 
new SolrQuery();
 
  282         termsQuery.setRequestHandler(SEARCH_HANDLER);
 
  283         termsQuery.setTerms(
true);
 
  284         termsQuery.setTermsRegexFlag(CASE_INSENSITIVE);
 
  285         termsQuery.setTermsRegex(searchTerm);
 
  286         termsQuery.addTermsField(SEARCH_FIELD);
 
  287         termsQuery.setTimeAllowed(TERMS_SEARCH_TIMEOUT);
 
  288         termsQuery.setShowDebugInfo(DEBUG_FLAG);
 
  289         termsQuery.setTermsLimit(MAX_TERMS_QUERY_RESULTS);
 
  290         List<Term> terms = KeywordSearch.getServer().queryTerms(termsQuery).getTerms(SEARCH_FIELD);
 
  294         QueryResults results = 
new QueryResults(
this);
 
  295         for (Term term : terms) {
 
  300             if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
 
  301                 Matcher matcher = CREDIT_CARD_NUM_PATTERN.matcher(term.getTerm());
 
  302                 if (
false == matcher.find()
 
  303                         || 
false == CreditCardValidator.isValidCCN(matcher.group(
"ccn"))) {
 
  318             String escapedTerm = KeywordSearchUtil.escapeLuceneQuery(term.getTerm());
 
  319             LuceneQuery termQuery = 
new LuceneQuery(keywordList, 
new Keyword(escapedTerm, 
true, 
true));
 
  320             filters.forEach(termQuery::addFilter); 
 
  321             QueryResults termQueryResult = termQuery.performQuery();
 
  322             Set<KeywordHit> termHits = 
new HashSet<>();
 
  323             for (Keyword word : termQueryResult.getKeywords()) {
 
  324                 termHits.addAll(termQueryResult.getResults(word));
 
  326             results.addResult(
new Keyword(term.getTerm(), 
false, 
true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), 
new ArrayList<>(termHits));
 
  348     public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
 
  353         if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
 
  354             createCCNAccount(content, hit, snippet, listName);
 
  362         BlackboardArtifact newArtifact;
 
  363         Collection<BlackboardAttribute> attributes = 
new ArrayList<>();
 
  365         attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
 
  366         attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm()));
 
  369             newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
 
  371         } 
catch (TskCoreException ex) {
 
  372             LOGGER.log(Level.SEVERE, 
"Error adding artifact for keyword hit to blackboard", ex); 
 
  376         if (StringUtils.isNotBlank(listName)) {
 
  377             attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
 
  379         if (snippet != null) {
 
  380             attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
 
  383         hit.getArtifactID().ifPresent(
 
  384                 artifactID -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
 
  388         attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
 
  391             newArtifact.addAttributes(attributes);
 
  393         } 
catch (TskCoreException e) {
 
  394             LOGGER.log(Level.SEVERE, 
"Error adding bb attributes for terms search artifact", e); 
 
  399     private void createCCNAccount(Content content, KeywordHit hit, String snippet, String listName) {
 
  401         if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
 
  402             LOGGER.log(Level.SEVERE, 
"Keyword hit is not a credit card number"); 
 
  411         Collection<BlackboardAttribute> attributes = 
new ArrayList<>();
 
  413         Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = 
new HashMap<>();
 
  414         Matcher matcher = CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet());
 
  415         if (matcher.find()) {
 
  416             parseTrack1Data(parsedTrackAttributeMap, matcher);
 
  418         matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet());
 
  419         if (matcher.find()) {
 
  420             parseTrack2Data(parsedTrackAttributeMap, matcher);
 
  422         final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
 
  423         if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
 
  424             if (hit.isArtifactHit()) {
 
  425                 LOGGER.log(Level.SEVERE, String.format(
"Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifactID().get())); 
 
  429                     contentId = hit.getContentID();
 
  430                 } 
catch (TskCoreException ex) {
 
  431                     LOGGER.log(Level.SEVERE, String.format(
"Failed to content id from keyword hit: term = %s, snippet = '%s'", searchTerm, hit.getSnippet()), ex); 
 
  434                     LOGGER.log(Level.SEVERE, String.format(
"Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), contentId)); 
 
  436                     LOGGER.log(Level.SEVERE, String.format(
"Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s'", searchTerm, hit.getSnippet())); 
 
  441         attributes.addAll(parsedTrackAttributeMap.values());
 
  447         final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8));
 
  448         CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
 
  449         if (binInfo != null) {
 
  450             binInfo.getScheme().ifPresent(scheme
 
  451                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme)));
 
  452             binInfo.getCardType().ifPresent(cardType
 
  453                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType)));
 
  454             binInfo.getBrand().ifPresent(brand
 
  455                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand)));
 
  456             binInfo.getBankName().ifPresent(bankName
 
  457                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName)));
 
  458             binInfo.getBankPhoneNumber().ifPresent(phoneNumber
 
  459                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber)));
 
  460             binInfo.getBankURL().ifPresent(url
 
  461                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url)));
 
  462             binInfo.getCountry().ifPresent(country
 
  463                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country)));
 
  464             binInfo.getBankCity().ifPresent(city
 
  465                     -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city)));
 
  472         if (content instanceof AbstractFile) {
 
  473             AbstractFile file = (AbstractFile) content;
 
  474             if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
 
  475                     || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
 
  476                 attributes.add(
new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
 
  480         if (StringUtils.isNotBlank(listName)) {
 
  481             attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
 
  483         if (snippet != null) {
 
  484             attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
 
  487         hit.getArtifactID().ifPresent(
 
  488                 artifactID -> attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
 
  492         attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
 
  498             AccountFileInstance ccAccountInstance = Case.getOpenCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString(), MODULE_NAME, content);
 
  499             ccAccountInstance.addAttributes(attributes);
 
  500         } 
catch (TskCoreException | NoCurrentCaseException ex) {
 
  501             LOGGER.log(Level.SEVERE, 
"Error creating CCN account instance", ex); 
 
  514     static private void parseTrack2Data(Map<BlackboardAttribute.Type, BlackboardAttribute> attributesMap, Matcher matcher) {
 
  515         addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_NUMBER, 
"accountNumber", matcher);
 
  516         addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_EXPIRATION, 
"expiration", matcher);
 
  517         addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_SERVICE_CODE, 
"serviceCode", matcher);
 
  518         addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_DISCRETIONARY, 
"discretionary", matcher);
 
  519         addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_LRC, 
"LRC", matcher);
 
  531     static private void parseTrack1Data(Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap, Matcher matcher) {
 
  532         parseTrack2Data(attributeMap, matcher);
 
  533         addAttributeIfNotAlreadyCaptured(attributeMap, ATTRIBUTE_TYPE.TSK_NAME_PERSON, 
"name", matcher);
 
  547     static private void addAttributeIfNotAlreadyCaptured(Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap, ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) {
 
  548         BlackboardAttribute.Type type = 
new BlackboardAttribute.Type(attrType);
 
  549         attributeMap.computeIfAbsent(type, (BlackboardAttribute.Type t) -> {
 
  550             String value = matcher.group(groupName);
 
  551             if (attrType.equals(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)) {
 
  552                 value = CharMatcher.anyOf(
" -").removeFrom(value);
 
  554             if (StringUtils.isNotBlank(value)) {
 
  555                 return new BlackboardAttribute(attrType, MODULE_NAME, value);