1
0
mirror of https://github.com/elisspace/autopsy.git synced 2026-09-06 02:24:30 +00:00

Merge pull request #3053 from millmanorama/2960-only_store_artifact_id_in_KeywordHit

2960 only store artifact id in keyword hit
This commit is contained in:
Richard Cordovano
2017-09-11 09:46:43 -04:00
committed by GitHub
5 changed files with 96 additions and 53 deletions

View File

@@ -18,71 +18,99 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Stores the fact that file or an artifact associated with a file had a keyword
* hit. All instances make both the document id of the Solr document where the
* keyword was found and the object Id available to clients. Artifact keyword
* hits also make the artifact available to clients.
* Represents the fact that a file or an artifact associated with a file had a
* keyword hit. All instances make both the document id of the Solr document
* where the keyword was found and the object id of the file available to
* clients. Keyword hits on the indexed text of an artifact also make the
* artifact available to clients.
*/
class KeywordHit implements Comparable<KeywordHit> {
private static final String GET_CONTENT_ID_FROM_ARTIFACT_ID = "SELECT obj_id FROM blackboard_artifacts WHERE artifact_id = ";
private final String solrDocumentId;
private final long solrObjectId;
private final int chunkId;
private final String snippet;
private final long contentID;
private final BlackboardArtifact artifact;
private final boolean hitOnArtifact;
private final String hit;
public String getHit() {
return hit;
}
/**
* Constructor
*
* @param solrDocumentId The id of the document this hit is in.
* @param snippet A small amount of text from the document containing
* the hit.
* @param hit The exact text from the document that was the hit.
* For some searches (ie substring, regex) this will be
* different than the search term.
*
* @throws TskCoreException If there is a problem getting the underlying
* content associated with a hit on the text of an
* artifact.
*/
KeywordHit(String solrDocumentId, String snippet, String hit) throws TskCoreException {
this.snippet = StringUtils.stripToEmpty(snippet);
this.hit = hit;
this.solrDocumentId = solrDocumentId;
/**
/*
* Parse the Solr document id to get the Solr object id and chunk id.
* The Solr object id will either be a file id or an artifact id from
* the case database.
* The Solr object id will either be the object id of a file id or an
* artifact id from the case database.
*
* For every object (file or artifact) there will at least two Solr
* documents. One contains object metadata (chunk #1) and the second and
* subsequent documents contain chunks of the text.
*/
final int separatorIndex = solrDocumentId.indexOf(Server.CHUNK_ID_SEPARATOR);
if (-1 != separatorIndex) {
this.solrObjectId = Long.parseLong(solrDocumentId.substring(0, separatorIndex));
this.chunkId = Integer.parseInt(solrDocumentId.substring(separatorIndex + 1));
} else {
String[] split = solrDocumentId.split(Server.CHUNK_ID_SEPARATOR);
if (split.length == 1) {
//chunk 0 has only the bare document id without the chunk id.
this.solrObjectId = Long.parseLong(solrDocumentId);
this.chunkId = 0;
} else {
this.solrObjectId = Long.parseLong(split[0]);
this.chunkId = Integer.parseInt(split[1]);
}
/*
* If the high order bit of the object id is set (ie, it is negative),
* the hit was in an artifact, look up the artifact.
*/
if (this.solrObjectId < 0) {
//artifacts have negative obj ids
hitOnArtifact = this.solrObjectId < 0;
if (hitOnArtifact) {
// If the hit was in an artifact, look up the source content for the artifact.
SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase();
this.artifact = caseDb.getBlackboardArtifact(this.solrObjectId);
contentID = artifact.getObjectID();
try (SleuthkitCase.CaseDbQuery executeQuery =
caseDb.executeQuery(GET_CONTENT_ID_FROM_ARTIFACT_ID + this.solrObjectId);
ResultSet resultSet = executeQuery.getResultSet();) {
if (resultSet.next()) {
contentID = resultSet.getLong("obj_id");
} else {
throw new TskCoreException("Failed to get obj_id for artifact with artifact_id =" + this.solrObjectId + ". No matching artifact was found.");
}
} catch (SQLException ex) {
throw new TskCoreException("Error getting obj_id for artifact with artifact_id =" + this.solrObjectId, ex);
}
} else {
//else the object id is for content.
this.artifact = null;
contentID = this.solrObjectId;
}
}
String getHit() {
return hit;
}
String getSolrDocumentId() {
return this.solrDocumentId;
}
@@ -113,17 +141,20 @@ class KeywordHit implements Comparable<KeywordHit> {
* @return
*/
boolean isArtifactHit() {
return (null != this.artifact);
return hitOnArtifact;
}
/**
* If this hit is in the indexed text of an artifact, get that artifact.
*
* @return The artifact whose indexed text this hit is in, or null if it is
* not an artifacts hit.
* @return The artifact whose indexed text this hit is in.
*/
BlackboardArtifact getArtifact() {
return this.artifact;
Optional<Long> getArtifactID() {
if (hitOnArtifact) {
return Optional.of(solrObjectId);
} else {
return Optional.empty();
}
}
@Override

View File

@@ -186,12 +186,20 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
}
String hitName = hit.isArtifactHit()
? hit.getArtifact().getDisplayName() + " Artifact" //NON-NLS
: contentName;
String hitName;
if (hit.isArtifactHit()) {
try {
hitName = tskCase.getBlackboardArtifact(hit.getArtifactID().get()).getDisplayName() + " Artifact"; //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting blckboard artifact by id", ex);
return false;
}
} else {
hitName = contentName;
}
hitNumber++;
tempList.add(new KeyValueQueryContent(hitName, properties, hitNumber, hit.getSolrObjectId(), content, queryRequest, queryResults));
}
// Add all the nodes to toPopulate at once. Minimizes node creation

View File

@@ -225,9 +225,11 @@ class LuceneQuery implements KeywordSearchQuery {
}
}
if (hit.isArtifactHit()) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, hit.getArtifact().getArtifactID()));
}
hit.getArtifactID().ifPresent(artifactID
-> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
);
try {
bba.addAttributes(attributes); //write out to bb

View File

@@ -417,7 +417,7 @@ final class RegexQuery implements KeywordSearchQuery {
final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
if (hit.isArtifactHit()) {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifact().getArtifactID())); //NON-NLS
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
} else {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
}
@@ -480,9 +480,10 @@ final class RegexQuery implements KeywordSearchQuery {
if (snippet != null) {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
}
if (hit.isArtifactHit()) {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, hit.getArtifact().getArtifactID()));
}
hit.getArtifactID().ifPresent(artifactID
-> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
);
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));

View File

@@ -359,7 +359,7 @@ final class TermsComponentQuery implements KeywordSearchQuery {
final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
if (hit.isArtifactHit()) {
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.getArtifact().getArtifactID())); //NON-NLS
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())); //NON-NLS
} else {
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(), hit.getContentID())); //NON-NLS
}
@@ -398,7 +398,7 @@ final class TermsComponentQuery implements KeywordSearchQuery {
* hit.
*/
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile)content;
AbstractFile file = (AbstractFile) content;
if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
|| file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
@@ -422,9 +422,10 @@ final class TermsComponentQuery implements KeywordSearchQuery {
if (snippet != null) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
}
if (hit.isArtifactHit()) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, hit.getArtifact().getArtifactID()));
}
hit.getArtifactID().ifPresent(
artifactID -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
);
// TermsComponentQuery is now being used exclusively for substring searches.
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
@@ -472,12 +473,12 @@ final class TermsComponentQuery implements KeywordSearchQuery {
* Creates an attribute of the the given type to the given artifact with a
* value parsed from the snippet for a credit account number hit.
*
* @param attributesMap A map of artifact attribute objects, used to avoid
* creating duplicate attributes.
* @param attrType The type of attribute to create.
* @param groupName The group name of the regular expression that was
* used to parse the attribute data.
* @param matcher A matcher for the snippet.
* @param attributeMap A map of artifact attribute objects, used to avoid
* creating duplicate attributes.
* @param attrType The type of attribute to create.
* @param groupName The group name of the regular expression that was
* used to parse the attribute data.
* @param matcher A matcher for the snippet.
*/
static private void addAttributeIfNotAlreadyCaptured(Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap, ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) {
BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType);