fetches all relevant configurations

This commit is contained in:
Greg DiCristofaro 2022-01-26 12:30:40 -05:00
parent 0d9c9c4c28
commit afe8d9c5e7
8 changed files with 234 additions and 60 deletions

View File

@ -468,7 +468,7 @@ public final class DataResultTopComponent extends TopComponent implements DataRe
* Displays results of querying the DAO for an artifact type and set name. * Displays results of querying the DAO for an artifact type and set name.
* @param params The search parameters. * @param params The search parameters.
*/ */
public void displayAnalysisResultSet(AnalysisResultConfigSearchParam params) { public void displayAnalysisResultConfig(AnalysisResultConfigSearchParam params) {
dataResultPanel.displayAnalysisResultSet(params); dataResultPanel.displayAnalysisResultSet(params);
} }

View File

@ -659,6 +659,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
String query = "res.search_term,\n" String query = "res.search_term,\n"
+ " res.search_type,\n" + " res.search_type,\n"
+ " MIN(res.configuration) AS configuration,\n"
+ " SUM(res.count) AS count,\n" + " SUM(res.count) AS count,\n"
+ " -- when there are multiple keyword groupings, return true for has children\n" + " -- when there are multiple keyword groupings, return true for has children\n"
+ " CASE\n" + " CASE\n"
@ -681,6 +682,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
+ " FROM (\n" + " FROM (\n"
+ " -- get pertinent attribute values for artifacts\n" + " -- get pertinent attribute values for artifacts\n"
+ " SELECT art.artifact_id, \n" + " SELECT art.artifact_id, \n"
+ " ar.configuration,\n"
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name,\n" + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name,\n"
+ " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
@ -690,6 +692,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword\n" + BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword\n"
+ " FROM blackboard_artifacts art\n" + " FROM blackboard_artifacts art\n"
+ " LEFT JOIN tsk_analysis_results ar ON ar.artifact_obj_id = art.artifact_obj_id\n"
+ " WHERE art.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + "\n" + " WHERE art.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + "\n"
+ dataSourceClause + dataSourceClause
+ " ) attr_res\n" + " ) attr_res\n"
@ -719,6 +722,8 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
int searchType = resultSet.getInt("search_type"); int searchType = resultSet.getInt("search_type");
long count = resultSet.getLong("count"); long count = resultSet.getLong("count");
boolean hasChildren = resultSet.getBoolean("has_children"); boolean hasChildren = resultSet.getBoolean("has_children");
// only a unique applicable configuration if no child tree nodes
String configuration = hasChildren ? null : resultSet.getString("configuration");
TskData.KeywordSearchQueryType searchTypeEnum TskData.KeywordSearchQueryType searchTypeEnum
= Stream.of(TskData.KeywordSearchQueryType.values()) = Stream.of(TskData.KeywordSearchQueryType.values())
@ -734,7 +739,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
TreeItemDTO<KeywordSearchTermParams> treeItem = new TreeItemDTO<>( TreeItemDTO<KeywordSearchTermParams> treeItem = new TreeItemDTO<>(
KeywordSearchTermParams.getTypeId(), KeywordSearchTermParams.getTypeId(),
new KeywordSearchTermParams(setName, searchTerm, TskData.KeywordSearchQueryType.valueOf(searchType), hasChildren, dataSourceId), new KeywordSearchTermParams(setName, searchTerm, TskData.KeywordSearchQueryType.valueOf(searchType), configuration, hasChildren, dataSourceId),
searchTermModified, searchTermModified,
searchTermModified, searchTermModified,
displayCount displayCount
@ -810,9 +815,11 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
: "res.set_name = ?"; : "res.set_name = ?";
String query = "keyword, \n" String query = "keyword, \n"
+ " MIN(configuration) AS configuration,\n"
+ " COUNT(*) AS count \n" + " COUNT(*) AS count \n"
+ "FROM (\n" + "FROM (\n"
+ " SELECT art.artifact_id, \n" + " SELECT art.artifact_id, \n"
+ " ar.configuration,"
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name,\n" + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name,\n"
+ " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
@ -822,6 +829,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword\n" + BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword\n"
+ " FROM blackboard_artifacts art\n" + " FROM blackboard_artifacts art\n"
+ " LEFT JOIN tsk_analysis_results ar ON art.artifact_obj_id = ar.artifact_obj_id\n"
+ " WHERE art.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + "\n" + " WHERE art.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + "\n"
+ dataSourceClause + dataSourceClause
+ ") res\n" + ") res\n"
@ -867,13 +875,14 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
try { try {
while (resultSet.next()) { while (resultSet.next()) {
String keyword = resultSet.getString("keyword"); String keyword = resultSet.getString("keyword");
String configuration = resultSet.getString("configuration");
long count = resultSet.getLong("count"); long count = resultSet.getLong("count");
TreeDisplayCount displayCount = indeterminateMatches.contains(keyword) TreeDisplayCount displayCount = indeterminateMatches.contains(keyword)
? TreeDisplayCount.INDETERMINATE ? TreeDisplayCount.INDETERMINATE
: TreeDisplayCount.getDeterminate(count); : TreeDisplayCount.getDeterminate(count);
items.add(createKWHitsTreeItem(dataSourceId, setName, keyword, regexStr, searchType, displayCount)); items.add(createKWHitsTreeItem(dataSourceId, setName, keyword, regexStr, searchType, configuration, displayCount));
} }
} catch (SQLException ex) { } catch (SQLException ex) {
logger.log(Level.WARNING, "An error occurred while fetching results from result set.", ex); logger.log(Level.WARNING, "An error occurred while fetching results from result set.", ex);
@ -888,11 +897,11 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
private static TreeItemDTO<KeywordHitSearchParam> createKWHitsTreeItem( private static TreeItemDTO<KeywordHitSearchParam> createKWHitsTreeItem(
Long dataSourceId, String setName, String keyword, String regexStr, Long dataSourceId, String setName, String keyword, String regexStr,
TskData.KeywordSearchQueryType searchType, TreeDisplayCount displayCount) { TskData.KeywordSearchQueryType searchType, String configuration, TreeDisplayCount displayCount) {
return new TreeItemDTO<>( return new TreeItemDTO<>(
KeywordHitSearchParam.getTypeId(), KeywordHitSearchParam.getTypeId(),
new KeywordHitSearchParam(dataSourceId, setName, keyword, regexStr, searchType), new KeywordHitSearchParam(dataSourceId, setName, keyword, regexStr, searchType, configuration),
keyword == null ? "" : keyword, keyword == null ? "" : keyword,
keyword == null ? "" : keyword, keyword == null ? "" : keyword,
displayCount displayCount
@ -943,8 +952,10 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
} }
} }
String configuration = (art instanceof AnalysisResult) ? ((AnalysisResult) art).getConfiguration() : null;
// data source id is null for KeywordHitSearchParam so that key lookups can be done without data source id. // data source id is null for KeywordHitSearchParam so that key lookups can be done without data source id.
return Pair.of(new KeywordHitSearchParam(null, setName, keywordMatch, searchTerm, searchType), dataSourceId); return Pair.of(new KeywordHitSearchParam(null, setName, keywordMatch, searchTerm, searchType, configuration), dataSourceId);
} }
@Override @Override
@ -997,7 +1008,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
SubDAOUtils.invalidateKeys(this.configHitCache, ar -> Pair.of(Pair.of(ar.getArtifactType(), ar.getConfiguration()), ar.getDataSourceId()), configMap); SubDAOUtils.invalidateKeys(this.configHitCache, ar -> Pair.of(Pair.of(ar.getArtifactType(), ar.getConfiguration()), ar.getDataSourceId()), configMap);
SubDAOUtils.invalidateKeys(this.keywordHitCache, kw -> Pair.of( SubDAOUtils.invalidateKeys(this.keywordHitCache, kw -> Pair.of(
// null data source for lookup // null data source for lookup
new KeywordHitSearchParam(null, kw.getConfiguration(), kw.getKeyword(), kw.getRegex(), kw.getSearchType()), new KeywordHitSearchParam(null, kw.getSetName(), kw.getKeyword(), kw.getRegex(), kw.getSearchType(), kw.getConfiguration()),
kw.getDataSourceId() kw.getDataSourceId()
), keywordHitsMap); ), keywordHitsMap);
@ -1031,11 +1042,11 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
Map<Boolean, List<KeywordHitEvent>> keywordHitEvts = keywordHitsMap.entrySet().stream() Map<Boolean, List<KeywordHitEvent>> keywordHitEvts = keywordHitsMap.entrySet().stream()
.flatMap(entry -> { .flatMap(entry -> {
KeywordHitSearchParam params = entry.getKey(); KeywordHitSearchParam params = entry.getKey();
String setName = params.getConfiguration(); String setName = params.getSetName();
String searchString = params.getRegex(); String searchString = params.getRegex();
TskData.KeywordSearchQueryType queryType = params.getSearchType(); TskData.KeywordSearchQueryType queryType = params.getSearchType();
String match = params.getKeyword(); String match = params.getKeyword();
return entry.getValue().stream().map(dsId -> new KeywordHitEvent(setName, searchString, queryType, match, dsId)); return entry.getValue().stream().map(dsId -> new KeywordHitEvent(setName, searchString, queryType, match, params.getConfiguration(), dsId));
}) })
.collect(Collectors.partitioningBy(kwe -> kwe.getSetName() == null)); .collect(Collectors.partitioningBy(kwe -> kwe.getSetName() == null));
@ -1090,6 +1101,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
khEvt.getMatch(), khEvt.getMatch(),
khEvt.getSearchString(), khEvt.getSearchString(),
khEvt.getSearchType(), khEvt.getSearchType(),
khEvt.getConfiguration(),
displayCount displayCount
); );
} else if (arEvt instanceof AnalysisResultConfigEvent) { } else if (arEvt instanceof AnalysisResultConfigEvent) {
@ -1138,6 +1150,102 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
} }
/**
* Returns all the configurations for keyword hits for the given filtering parameters.
* @param setName The set name as defined by TSK_SET_NAME. If null, assumed to be ad hoc result.
* @param regex The TSK_KEYWORD_REGEXP value. If null, no filtering by regex occurs.
* @param searchType The TSK_KEYWORD_SEARCH_TYPE value. If null, no filtering by search type occurs.
* @param dataSourceId The data source object id. If null, no filtering by data source occurs.
* @return The distinct configurations.
* @throws ExecutionException
*/
public List<String> getKeywordHitConfigurations(String setName, String regex, TskData.KeywordSearchQueryType searchType, Long dataSourceId) throws ExecutionException {
String setNameClause = setName == null
// if set name is null, then there should be no set name attribute associated with this
? "(SELECT "
+ " COUNT(*) FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID()
+ " AND attr.value_text IS NOT NULL "
+ " AND LEN(attr.value_text) > 0) = 0"
// otherwise, see if the set name attribute matches expected value
: "? IN (SELECT attr.value_text FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID()
+ " )";
String regexClause = regex == null
? null
: "? IN (SELECT attr.value_text FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_KEYWORD_REGEXP.getTypeID()
+ " )";
String searchTypeClause = searchType == null
? null
: "? IN (SELECT attr.value_int32 FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
+ " )";
String dataSourceClause = dataSourceId == null
? null
: "art.data_source_obj_id = ?";
String clauses = Stream.of(setNameClause, regexClause, searchTypeClause, dataSourceClause)
.filter(s -> s != null)
.map(s -> " (" + s + ") ")
.collect(Collectors.joining("AND"));
String query = "DISTINCT(ar.configuration) AS configuration \n"
+ "FROM tsk_analysis_results ar\n"
+ "LEFT JOIN blackboard_artifacts"
+ "WHERE " + clauses;
// get artifact types and counts
try (CaseDbPreparedStatement preparedStatement = getCase().getCaseDbAccessManager().prepareSelect(query)) {
int paramIdx = 0;
if (setName != null) {
preparedStatement.setString(++paramIdx, setName);
}
if (regex != null) {
preparedStatement.setString(++paramIdx, regex);
}
if (searchType != null) {
preparedStatement.setInt(++paramIdx, searchType.getType());
}
if (dataSourceId != null) {
preparedStatement.setLong(++paramIdx, dataSourceId);
}
List<String> configurations = new ArrayList<>();
getCase().getCaseDbAccessManager().select(preparedStatement, (resultSet) -> {
try {
while (resultSet.next()) {
configurations.add(resultSet.getString("configuration"));
}
} catch (SQLException ex) {
logger.log(Level.WARNING, "An error occurred while fetching results from result set.", ex);
}
});
return configurations;
} catch (SQLException | NoCurrentCaseException | TskCoreException ex) {
throw new ExecutionException(MessageFormat.format(
"An error occurred while fetching configurations for counts where setName = {0} regex = {1} and search type = {2}",
setName == null ? "<null>" : setName,
regex == null ? "<null>" : regex,
searchType == null ? "<null>" : searchType.name()),
ex);
}
}
/** /**
* A tree item for an analysis result that can indicate if it has child tree * A tree item for an analysis result that can indicate if it has child tree
* nodes due to configuration. * nodes due to configuration.

View File

@ -40,8 +40,8 @@ public class KeywordHitSearchParam extends KeywordSearchTermParams {
private final String regex; private final String regex;
private final TskData.KeywordSearchQueryType searchType; private final TskData.KeywordSearchQueryType searchType;
public KeywordHitSearchParam(Long dataSourceId, String setName, String keyword, String regex, TskData.KeywordSearchQueryType searchType) { public KeywordHitSearchParam(Long dataSourceId, String setName, String keyword, String regex, TskData.KeywordSearchQueryType searchType, String configuration) {
super(setName, regex, searchType, StringUtils.isNotBlank(keyword) && !Objects.equals(regex, keyword), dataSourceId); super(setName, regex, searchType, configuration, StringUtils.isNotBlank(keyword) && !Objects.equals(regex, keyword), dataSourceId);
this.keyword = keyword; this.keyword = keyword;
this.regex = regex; this.regex = regex;
this.searchType = searchType; this.searchType = searchType;

View File

@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.TskData;
/** /**
* Parameters for a keyword search term. * Parameters for a keyword search term.
*/ */
public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam { public class KeywordSearchTermParams extends AnalysisResultSetSearchParam {
private static final String TYPE_ID = "KEYWORD_SEARCH_TERMS"; private static final String TYPE_ID = "KEYWORD_SEARCH_TERMS";
@ -36,32 +36,32 @@ public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
return TYPE_ID; return TYPE_ID;
} }
private final String searchTerm; private final String searchTerm;
private final Boolean hasChildren; private final Boolean hasChildren;
private final TskData.KeywordSearchQueryType searchType; private final TskData.KeywordSearchQueryType searchType;
private final String configuration;
/** /**
* Main constructor. * Main constructor.
* *
* @param setName The set name. * @param setName The set name.
* @param searchTerm The search term (determined from regex or keyword). * @param searchTerm The search term (determined from regex or keyword).
* @param searchType The keyword search type attribute. * @param searchType The keyword search type attribute.
* @param hasChildren Whether or not this search term has children tree * @param configuration The configuration of the analysis results set if
* nodes (i.e. url regex search that further divides * hasChildren is false.
* into different urls). * @param hasChildren Whether or not this search term has children tree
* @param dataSourceId The data source id or null. * nodes (i.e. url regex search that further divides
* into different urls).
* @param dataSourceId The data source id or null.
*/ */
public KeywordSearchTermParams(String setName, String searchTerm, TskData.KeywordSearchQueryType searchType, boolean hasChildren, Long dataSourceId) { public KeywordSearchTermParams(String setName, String searchTerm, TskData.KeywordSearchQueryType searchType, String configuration, boolean hasChildren, Long dataSourceId) {
super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId, setName); super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId, setName);
this.searchTerm = searchTerm; this.searchTerm = searchTerm;
this.hasChildren = hasChildren; this.hasChildren = hasChildren;
this.searchType = searchType; this.searchType = searchType;
this.configuration = configuration;
} }
/** /**
* @return The search term (determined from regex or keyword). * @return The search term (determined from regex or keyword).
*/ */
@ -76,6 +76,11 @@ public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
public boolean hasChildren() { public boolean hasChildren() {
return hasChildren; return hasChildren;
} }
public String getConfiguration() {
return configuration;
}
/** /**
* @return The keyword search type value. * @return The keyword search type value.
*/ */
@ -112,6 +117,5 @@ public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
} }
return super.equals(obj); return super.equals(obj);
} }
} }

View File

@ -32,28 +32,35 @@ public class KeywordHitEvent extends AnalysisResultEvent {
private final String match; private final String match;
private final TskData.KeywordSearchQueryType searchType; private final TskData.KeywordSearchQueryType searchType;
private final String setName; private final String setName;
private final String configuration;
/** /**
* Main constructor. * Main constructor.
* *
* @param setName The set name. * @param setName The set name.
* @param searchString The search string or regex. * @param searchString The search string or regex.
* @param searchType THe search type. * @param searchType THe search type.
* @param match The match string. * @param match The match string.
* @param dataSourceId The data source id. * @param configuration The configuration of the analysis result.
* @param dataSourceId The data source id.
*/ */
public KeywordHitEvent(String setName, String searchString, TskData.KeywordSearchQueryType searchType, String match, long dataSourceId) { public KeywordHitEvent(String setName, String searchString, TskData.KeywordSearchQueryType searchType, String match, String configuration, long dataSourceId) {
super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId); super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId);
this.setName = setName; this.setName = setName;
this.searchString = searchString; this.searchString = searchString;
this.match = match; this.match = match;
this.searchType = searchType; this.searchType = searchType;
this.configuration = configuration;
} }
public String getConfiguration() {
return configuration;
}
public String getSetName() { public String getSetName() {
return setName; return setName;
} }
public String getSearchString() { public String getSearchString() {
return searchString; return searchString;
} }

View File

@ -18,17 +18,20 @@
*/ */
package org.sleuthkit.autopsy.mainui.nodes; package org.sleuthkit.autopsy.mainui.nodes;
import java.util.Collections;
import org.sleuthkit.autopsy.mainui.datamodel.KeywordSearchTermParams; import org.sleuthkit.autopsy.mainui.datamodel.KeywordSearchTermParams;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.NotImplementedException;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil; import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultDAO; import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultDAO;
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultDAO.AnalysisResultTreeItem; import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultDAO.AnalysisResultTreeItem;
@ -237,7 +240,7 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
@Override @Override
protected TreeNode<AnalysisResultConfigSearchParam> createNewNode(TreeResultsDTO.TreeItemDTO<? extends AnalysisResultConfigSearchParam> rowData) { protected TreeNode<AnalysisResultConfigSearchParam> createNewNode(TreeResultsDTO.TreeItemDTO<? extends AnalysisResultConfigSearchParam> rowData) {
return new TreeSetTypeNode(rowData); return new TreeConfigTypeNode(rowData);
} }
@Override @Override
@ -282,14 +285,14 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
/** /**
* A node for a set within an artifact type. * A node for a set within an artifact type.
*/ */
static class TreeSetTypeNode extends TreeNode<AnalysisResultConfigSearchParam> { static class TreeConfigTypeNode extends TreeNode<AnalysisResultConfigSearchParam> {
/** /**
* Main constructor. * Main constructor.
* *
* @param itemData The data to display. * @param itemData The data to display.
*/ */
TreeSetTypeNode(TreeResultsDTO.TreeItemDTO<? extends AnalysisResultConfigSearchParam> itemData) { TreeConfigTypeNode(TreeResultsDTO.TreeItemDTO<? extends AnalysisResultConfigSearchParam> itemData) {
super(itemData.getSearchParams().getArtifactType().getTypeName() + "_SET_" + itemData.getSearchParams().getConfiguration(), super(itemData.getSearchParams().getArtifactType().getTypeName() + "_SET_" + itemData.getSearchParams().getConfiguration(),
getIconPath(itemData.getSearchParams().getArtifactType()), getIconPath(itemData.getSearchParams().getArtifactType()),
itemData, itemData,
@ -299,7 +302,7 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
@Override @Override
public void respondSelection(DataResultTopComponent dataResultPanel) { public void respondSelection(DataResultTopComponent dataResultPanel) {
dataResultPanel.displayAnalysisResultSet(this.getItemData().getSearchParams()); dataResultPanel.displayAnalysisResultConfig(this.getItemData().getSearchParams());
} }
@Override @Override
@ -313,8 +316,13 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
} }
@Override @Override
public Optional<String> getAnalysisResultConfiguration() { public boolean hasAnalysisResultConfigurations() {
return Optional.of(this.getItemData().getSearchParams().getConfiguration()); return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
return Collections.singletonList(this.getItemData().getSearchParams().getConfiguration());
} }
} }
@ -356,7 +364,7 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
} }
return null; return null;
} }
@Override @Override
public int compare(TreeItemDTO<? extends AnalysisResultSetSearchParam> o1, TreeItemDTO<? extends AnalysisResultSetSearchParam> o2) { public int compare(TreeItemDTO<? extends AnalysisResultSetSearchParam> o1, TreeItemDTO<? extends AnalysisResultSetSearchParam> o2) {
return STRING_COMPARATOR.compare(o1.getSearchParams().getSetName(), o2.getSearchParams().getSetName()); return STRING_COMPARATOR.compare(o1.getSearchParams().getSetName(), o2.getSearchParams().getSetName());
@ -370,6 +378,8 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
static class KeywordSetNode extends TreeNode<AnalysisResultSetSearchParam> { static class KeywordSetNode extends TreeNode<AnalysisResultSetSearchParam> {
private static final Logger logger = Logger.getLogger(KeywordSetNode.class.getName());
/** /**
* Main constructor. * Main constructor.
* *
@ -394,8 +404,20 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
} }
@Override @Override
public Optional<String> getAnalysisResultConfiguration() { public boolean hasAnalysisResultConfigurations() {
throw new NotImplementedException("TODO"); return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
try {
return MainDAO.getInstance().getAnalysisResultDAO().getKeywordHitConfigurations(
this.getItemData().getSearchParams().getSetName(), null, null,
this.getItemData().getSearchParams().getDataSourceId());
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "An exception occurred while fetching configurations.", ex);
return Collections.emptyList();
}
} }
} }
@ -445,6 +467,7 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
this.setParams.getSetName(), this.setParams.getSetName(),
searchParam.getRegex(), searchParam.getRegex(),
searchParam.getSearchType(), searchParam.getSearchType(),
searchParam.getConfiguration(),
searchParam.hasChildren(), searchParam.hasChildren(),
this.setParams.getDataSourceId() this.setParams.getDataSourceId()
), ),
@ -480,6 +503,8 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
*/ */
static class KeywordSearchTermNode extends TreeNode<KeywordSearchTermParams> { static class KeywordSearchTermNode extends TreeNode<KeywordSearchTermParams> {
private static final Logger logger = Logger.getLogger(KeywordSearchTermNode.class.getName());
/** /**
* Main constructor. * Main constructor.
* *
@ -507,7 +532,8 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
TskData.KeywordSearchQueryType.LITERAL.equals(searchTermParams.getSearchType()) ? searchTermParams.getRegex() : null, TskData.KeywordSearchQueryType.LITERAL.equals(searchTermParams.getSearchType()) ? searchTermParams.getRegex() : null,
// if literal, no regex // if literal, no regex
TskData.KeywordSearchQueryType.LITERAL.equals(searchTermParams.getSearchType()) ? null : searchTermParams.getRegex(), TskData.KeywordSearchQueryType.LITERAL.equals(searchTermParams.getSearchType()) ? null : searchTermParams.getRegex(),
searchTermParams.getSearchType()); searchTermParams.getSearchType(),
searchTermParams.getConfiguration());
dataResultPanel.displayKeywordHits(searchParams); dataResultPanel.displayKeywordHits(searchParams);
} else { } else {
super.respondSelection(dataResultPanel); super.respondSelection(dataResultPanel);
@ -525,8 +551,26 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
} }
@Override @Override
public Optional<String> getAnalysisResultConfiguration() { public boolean hasAnalysisResultConfigurations() {
return Optional.of(this.getItemData().getSearchParams().getConfiguration()); return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
KeywordSearchTermParams searchParams = this.getItemData().getSearchParams();
if (searchParams.hasChildren()) {
try {
return MainDAO.getInstance().getAnalysisResultDAO().getKeywordHitConfigurations(
searchParams.getSetName(), searchParams.getRegex(), searchParams.getSearchType(),
searchParams.getDataSourceId());
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "An exception occurred while fetching configurations.", ex);
return Collections.emptyList();
}
} else {
return Collections.singletonList(searchParams.getConfiguration());
}
} }
} }
@ -578,10 +622,11 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
KeywordHitSearchParam.getTypeId(), KeywordHitSearchParam.getTypeId(),
new KeywordHitSearchParam( new KeywordHitSearchParam(
this.searchTermParams.getDataSourceId(), this.searchTermParams.getDataSourceId(),
this.searchTermParams.getConfiguration(), this.searchTermParams.getSetName(),
searchParam.getKeyword(), searchParam.getKeyword(),
this.searchTermParams.getRegex(), this.searchTermParams.getRegex(),
this.searchTermParams.getSearchType() this.searchTermParams.getSearchType(),
this.searchTermParams.getConfiguration()
), ),
searchParam.getKeyword() == null ? "" : searchParam.getKeyword(), searchParam.getKeyword() == null ? "" : searchParam.getKeyword(),
searchParam.getKeyword() == null ? "" : searchParam.getKeyword(), searchParam.getKeyword() == null ? "" : searchParam.getKeyword(),
@ -644,8 +689,13 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
} }
@Override @Override
public Optional<String> getAnalysisResultConfiguration() { public boolean hasAnalysisResultConfigurations() {
return Optional.of(this.getItemData().getSearchParams().getConfiguration()); return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
return Collections.singletonList(this.getItemData().getSearchParams().getConfiguration());
} }
} }

View File

@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.autopsy.mainui.nodes.actions; package org.sleuthkit.autopsy.mainui.nodes.actions;
import java.util.Collections;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory.ActionGroup; import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory.ActionGroup;
@ -243,14 +245,15 @@ public interface ActionContext {
} }
default boolean hasAnalysisResultConfigurations() {
return false;
}
/** /**
* @return Provides the node configuration if applicable or empty. The * @return Provides the node configurations if applicable or an empty list.
* optional may be null if other analysis results of the same
* artifact type have a configuration but these analysis results
* do not.
*/ */
default Optional<String> getAnalysisResultConfiguration() { default List<String> getAnalysisResultConfigurations() {
return Optional.empty(); return Collections.emptyList();
} }
/** /**

View File

@ -30,6 +30,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
@ -173,17 +174,18 @@ public final class ActionsFactory {
} }
Optional<BlackboardArtifact.Type> analysisResultType = actionContext.getAnalysisResultType(); Optional<BlackboardArtifact.Type> analysisResultType = actionContext.getAnalysisResultType();
if (analysisResultType.isPresent()) { if (analysisResultType.isPresent() && actionContext.hasAnalysisResultConfigurations()) {
Optional<String> configuration = actionContext.getAnalysisResultConfiguration();
Optional<Long> dataSourceId = actionContext.getDataSourceIdForActions(); Optional<Long> dataSourceId = actionContext.getDataSourceIdForActions();
actionGroups.add(new ActionGroup(new AbstractAction("Delete Analysis Results of Type") { actionGroups.add(new ActionGroup(new AbstractAction("Delete Analysis Results of Type") {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
List<String> configurations = actionContext.getAnalysisResultConfigurations();
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
MessageFormat.format("Stub Action for deleting analysis result type: {0} with configuration {1} and data source of {2}", MessageFormat.format("Stub Action for deleting analysis result type: {0} with configurations [{1}] and data source of {2}",
analysisResultType.get().getDisplayName(), analysisResultType.get().getDisplayName(),
configuration.map(c -> c == null ? "<Null>" : c).orElse("<Empty>"), configurations.stream().map(c -> c == null ? "<Null>" : "\"" + c + "\"").collect(Collectors.joining(",")),
dataSourceId.map(d -> Long.toString(d)).orElse("<Null or Empty>")), dataSourceId.map(d -> Long.toString(d)).orElse("<Null or Empty>")),
"Deleting...", "Deleting...",
JOptionPane.WARNING_MESSAGE); JOptionPane.WARNING_MESSAGE);