19 package org.sleuthkit.datamodel;
21 import com.google.common.collect.ImmutableSet;
22 import java.sql.PreparedStatement;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.sql.Statement;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Objects;
34 import java.util.logging.Level;
35 import java.util.logging.Logger;
36 import java.util.stream.Collectors;
46 private static final Logger LOGGER = Logger.getLogger(
Blackboard.class.getName());
57 this.caseDb = Objects.requireNonNull(casedb,
"Cannot create Blackboard for null SleuthkitCase");
139 if (category == null) {
140 throw new BlackboardException(
"Category provided must be non-null");
149 throw new BlackboardException(
"Failed to get or add artifact type", ex);
152 throw new BlackboardException(
"Failed to get or add artifact type", ex);
179 String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList)
183 throw new BlackboardException(String.format(
"Artifact type (name = %s) is not of Analysis Result category. ", artifactType.getTypeName()));
189 conclusion, configuration, justification, attributesList, transaction);
191 return analysisResult;
196 LOGGER.log(Level.SEVERE,
"Failed to rollback transaction after exception. "
197 +
"Error invoking newAnalysisResult with dataSourceObjId: "
198 + (dataSourceObjId == null ?
"<null>" : dataSourceObjId)
199 +
", sourceObjId: " + objId, ex2);
228 String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList,
CaseDbTransaction transaction)
throws BlackboardException {
231 throw new BlackboardException(String.format(
"Artifact type (name = %s) is not of Analysis Result category. ", artifactType.getTypeName()));
236 AnalysisResult analysisResult = caseDb.newAnalysisResult(artifactType, objId, dataSourceObjId, score, conclusion, configuration, justification, transaction.getConnection());
239 if (attributesList != null && !attributesList.isEmpty()) {
244 Score aggregateScore = caseDb.
getScoringManager().updateAggregateScoreAfterAddition(objId, dataSourceObjId, analysisResult.
getScore(), transaction);
250 throw new BlackboardException(
"Failed to add analysis result.", ex);
278 if (transaction != null) {
299 List<AnalysisResult> analysisResults =
getAnalysisResultsWhere(
" arts.artifact_obj_id = " + artifactObjId, transaction.getConnection());
301 if (analysisResults.isEmpty()) {
302 throw new TskCoreException(String.format(
"Analysis Result not found for artifact obj id %d", artifactObjId));
324 CaseDbConnection connection = transaction.getConnection();
327 String deleteSQL =
"DELETE FROM blackboard_artifacts WHERE artifact_obj_id = ?";
329 PreparedStatement deleteStatement = connection.getPreparedStatement(deleteSQL, Statement.RETURN_GENERATED_KEYS);
330 deleteStatement.clearParameters();
331 deleteStatement.setLong(1, analysisResult.getId());
333 deleteStatement.executeUpdate();
336 transaction.registerDeletedAnalysisResult(analysisResult.getObjectID());
338 return caseDb.
getScoringManager().updateAggregateScoreAfterDeletion(analysisResult.getObjectID(), analysisResult.getDataSourceObjectID(), transaction);
340 }
catch (SQLException ex) {
341 throw new TskCoreException(String.format(
"Error deleting analysis result with artifact obj id %d", analysisResult.getId()), ex);
345 private final static String ANALYSIS_RESULT_QUERY_STRING =
"SELECT DISTINCT arts.artifact_id AS artifact_id, "
346 +
" arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, "
347 +
" types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"
348 +
" arts.review_status_id AS review_status_id, "
349 +
" results.conclusion AS conclusion, results.significance AS significance, results.priority AS priority, "
350 +
" results.configuration AS configuration, results.justification AS justification "
351 +
" FROM blackboard_artifacts AS arts "
352 +
" JOIN blackboard_artifact_types AS types "
353 +
" ON arts.artifact_type_id = types.artifact_type_id"
354 +
" LEFT JOIN tsk_analysis_results AS results "
355 +
" ON arts.artifact_obj_id = results.artifact_obj_id "
356 +
" WHERE arts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID()
357 +
" AND types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID();
386 return getAnalysisResultsWhere(
" arts.artifact_type_id = " + artifactTypeId +
" AND arts.data_source_obj_id = " + dataSourceObjId);
415 List<DataArtifact> getDataArtifactsBySource(
long sourceObjId)
throws TskCoreException {
417 try (CaseDbConnection connection = caseDb.getConnection()) {
418 return getDataArtifactsWhere(String.format(
" artifacts.obj_id = " + sourceObjId), connection);
454 String queryString =
"SELECT COUNT(*) AS count "
455 +
" FROM blackboard_artifacts AS arts "
456 +
" JOIN blackboard_artifact_types AS types "
457 +
" ON arts.artifact_type_id = types.artifact_type_id"
458 +
" WHERE types.category_type = " + category.getID()
459 +
" AND arts.obj_id = " + sourceObjId;
463 Statement statement = connection.createStatement();
464 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
465 if (resultSet.next()) {
466 return resultSet.getLong(
"count") > 0;
469 }
catch (SQLException ex) {
470 throw new TskCoreException(
"Error getting artifact types is use for data source." + ex.getMessage(), ex);
491 List<AnalysisResult>
getAnalysisResults(
long sourceObjId, CaseDbConnection connection)
throws TskCoreException {
510 throw new TskCoreException(String.format(
"Artifact type id %d is not in analysis result catgeory.", artifactTypeId));
513 String whereClause =
" types.artifact_type_id = " + artifactTypeId
514 +
" AND arts.obj_id = " + sourceObjId;
531 try (CaseDbConnection connection = caseDb.getConnection()) {
552 final String queryString = ANALYSIS_RESULT_QUERY_STRING
553 +
" AND " + whereClause;
555 try (Statement statement = connection.createStatement();
556 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
558 List<AnalysisResult> analysisResults = resultSetToAnalysisResults(resultSet);
559 return analysisResults;
560 }
catch (SQLException ex) {
561 throw new TskCoreException(String.format(
"Error getting analysis results for WHERE clause = '%s'", whereClause), ex);
576 String whereClause =
" arts.artifact_obj_id = " + artifactObjId;
579 if (results.isEmpty()) {
580 throw new TskCoreException(String.format(
"Error getting analysis result with id = '%d'", artifactObjId));
582 if (results.size() > 1) {
583 throw new TskCoreException(String.format(
"Multiple analysis results found with id = '%d'", artifactObjId));
586 return results.get(0);
604 private List<AnalysisResult> resultSetToAnalysisResults(ResultSet resultSet)
throws SQLException,
TskCoreException {
605 ArrayList<AnalysisResult> analysisResults =
new ArrayList<>();
607 while (resultSet.next()) {
608 analysisResults.add(
new AnalysisResult(caseDb, resultSet.getLong(
"artifact_id"), resultSet.getLong(
"obj_id"),
609 resultSet.getLong(
"artifact_obj_id"),
610 resultSet.getObject(
"data_source_obj_id") != null ? resultSet.getLong(
"data_source_obj_id") : null,
611 resultSet.getInt(
"artifact_type_id"), resultSet.getString(
"type_name"), resultSet.getString(
"display_name"),
614 resultSet.getString(
"conclusion"), resultSet.getString(
"configuration"), resultSet.getString(
"justification")));
617 return analysisResults;
620 private final static String DATA_ARTIFACT_QUERY_STRING =
"SELECT DISTINCT artifacts.artifact_id AS artifact_id, "
621 +
"artifacts.obj_id AS obj_id, artifacts.artifact_obj_id AS artifact_obj_id, artifacts.data_source_obj_id AS data_source_obj_id, artifacts.artifact_type_id AS artifact_type_id, "
622 +
" types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"
623 +
" artifacts.review_status_id AS review_status_id, "
624 +
" data_artifacts.os_account_obj_id as os_account_obj_id "
625 +
" FROM blackboard_artifacts AS artifacts "
626 +
" JOIN blackboard_artifact_types AS types "
627 +
" ON artifacts.artifact_type_id = types.artifact_type_id"
628 +
" LEFT JOIN tsk_data_artifacts AS data_artifacts "
629 +
" ON artifacts.artifact_obj_id = data_artifacts.artifact_obj_id "
630 +
" WHERE artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID()
631 +
" AND types.category_type = " + BlackboardArtifact.Category.DATA_ARTIFACT.getID();
649 throw new TskCoreException(String.format(
"Artifact type id %d is not in data artifact catgeory.", artifactTypeID));
653 try (CaseDbConnection connection = caseDb.getConnection()) {
654 String whereClause =
"artifacts.data_source_obj_id = " + dataSourceObjId
655 +
" AND artifacts.artifact_type_id = " + artifactTypeID;
657 return getDataArtifactsWhere(whereClause, connection);
677 throw new TskCoreException(String.format(
"Artifact type id %d is not in data artifact catgeory.", artifactTypeID));
681 try (CaseDbConnection connection = caseDb.getConnection()) {
682 String whereClause =
" artifacts.artifact_type_id = " + artifactTypeID;
684 return getDataArtifactsWhere(whereClause, connection);
702 try (CaseDbConnection connection = caseDb.getConnection()) {
703 String whereClause =
" artifacts.artifact_obj_id = " + artifactObjId;
705 List<DataArtifact> artifacts = getDataArtifactsWhere(whereClause, connection);
706 if (artifacts.isEmpty()) {
707 throw new TskCoreException(String.format(
"Error getting data artifact with id = '%d'", artifactObjId));
709 if (artifacts.size() > 1) {
710 throw new TskCoreException(String.format(
"Multiple data artifacts found with id = '%d'", artifactObjId));
713 return artifacts.get(0);
731 private List<DataArtifact> getDataArtifactsWhere(String whereClause, CaseDbConnection connection)
throws TskCoreException {
733 final String queryString = DATA_ARTIFACT_QUERY_STRING
734 +
" AND ( " + whereClause +
" )";
736 try (Statement statement = connection.createStatement();
737 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
739 List<DataArtifact> dataArtifacts = resultSetToDataArtifacts(resultSet, connection);
740 return dataArtifacts;
741 }
catch (SQLException ex) {
742 throw new TskCoreException(String.format(
"Error getting data artifacts with queryString = %s", queryString), ex);
762 private List<DataArtifact> resultSetToDataArtifacts(ResultSet resultSet, CaseDbConnection connection)
throws SQLException, TskCoreException {
763 ArrayList<DataArtifact> dataArtifacts =
new ArrayList<>();
765 while (resultSet.next()) {
767 Long osAccountObjId = resultSet.getLong(
"os_account_obj_id");
768 if (resultSet.wasNull()) {
769 osAccountObjId = null;
772 dataArtifacts.add(
new DataArtifact(caseDb, resultSet.getLong(
"artifact_id"), resultSet.getLong(
"obj_id"),
773 resultSet.getLong(
"artifact_obj_id"),
774 resultSet.getObject(
"data_source_obj_id") != null ? resultSet.getLong(
"data_source_obj_id") : null,
775 resultSet.getInt(
"artifact_type_id"), resultSet.getString(
"type_name"), resultSet.getString(
"display_name"),
776 BlackboardArtifact.ReviewStatus.withID(resultSet.getInt(
"review_status_id")), osAccountObjId,
false));
779 return dataArtifacts;
818 throw new BlackboardException(
"Failed to get or add attribute type", ex);
821 throw new BlackboardException(
"Failed to get or add attribute type", ex);
838 final String queryString =
"SELECT DISTINCT arts.artifact_type_id AS artifact_type_id, "
839 +
"types.type_name AS type_name, "
840 +
"types.display_name AS display_name, "
841 +
"types.category_type AS category_type "
842 +
"FROM blackboard_artifact_types AS types "
843 +
"INNER JOIN blackboard_artifacts AS arts "
844 +
"ON arts.artifact_type_id = types.artifact_type_id "
845 +
"WHERE arts.data_source_obj_id = " + dataSourceObjId;
849 Statement statement = connection.createStatement();
850 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
853 while (resultSet.next()) {
855 resultSet.getString(
"type_name"), resultSet.getString(
"display_name"),
858 return uniqueArtifactTypes;
859 }
catch (SQLException ex) {
860 throw new TskCoreException(
"Error getting artifact types is use for data source." + ex.getMessage(), ex);
879 return getArtifactsCountHelper(artifactTypeID,
880 "blackboard_artifacts.data_source_obj_id = '" + dataSourceObjId +
"';");
896 return caseDb.getArtifactsHelper(
"blackboard_artifacts.data_source_obj_id = " + dataSourceObjId
897 +
" AND blackboard_artifact_types.artifact_type_id = " + artifactTypeID +
";");
912 public List<BlackboardArtifact>
getArtifacts(Collection<BlackboardArtifact.Type> artifactTypes,
915 if (artifactTypes.isEmpty() || dataSourceObjIds.isEmpty()) {
916 return new ArrayList<>();
919 String typeQuery =
"";
921 if (!typeQuery.isEmpty()) {
924 typeQuery +=
"blackboard_artifact_types.artifact_type_id = " + type.getTypeID();
928 for (
long dsId : dataSourceObjIds) {
929 if (!dsQuery.isEmpty()) {
932 dsQuery +=
"blackboard_artifacts.data_source_obj_id = " + dsId;
935 String fullQuery =
"( " + typeQuery +
" ) AND ( " + dsQuery +
" );";
937 return caseDb.getArtifactsHelper(fullQuery);
952 private long getArtifactsCountHelper(
int artifactTypeID, String whereClause)
throws TskCoreException {
953 String queryString =
"SELECT COUNT(*) AS count FROM blackboard_artifacts "
954 +
"WHERE blackboard_artifacts.artifact_type_id = " + artifactTypeID
956 +
" AND " + whereClause;
960 Statement statement = connection.createStatement();
961 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
964 if (resultSet.next()) {
965 count = resultSet.getLong(
"count");
968 }
catch (SQLException ex) {
969 throw new TskCoreException(
"Error getting artifact types is use for data source." + ex.getMessage(), ex);
991 ArrayList<BlackboardArtifact> artifactsList;
996 artifactsList = content.getArtifacts(artifactType);
997 if (artifactsList.isEmpty()) {
1005 if (attributesMatch(artifact.getAttributes(), attributesList)) {
1031 private boolean attributesMatch(Collection<BlackboardAttribute> fileAttributesList, Collection<BlackboardAttribute> expectedAttributesList) {
1033 boolean match =
false;
1036 if (attributeType.getTypeID() != expectedAttribute.getAttributeType().getTypeID()) {
1040 Object fileAttributeValue;
1041 Object expectedAttributeValue;
1042 switch (attributeType.getValueType()) {
1044 fileAttributeValue = fileAttribute.getValueBytes();
1045 expectedAttributeValue = expectedAttribute.getValueBytes();
1048 fileAttributeValue = fileAttribute.getValueDouble();
1049 expectedAttributeValue = expectedAttribute.getValueDouble();
1052 fileAttributeValue = fileAttribute.getValueInt();
1053 expectedAttributeValue = expectedAttribute.getValueInt();
1057 fileAttributeValue = fileAttribute.getValueLong();
1058 expectedAttributeValue = expectedAttribute.getValueLong();
1062 fileAttributeValue = fileAttribute.getValueString();
1063 expectedAttributeValue = expectedAttribute.getValueString();
1066 fileAttributeValue = fileAttribute.getDisplayString();
1067 expectedAttributeValue = expectedAttribute.getDisplayString();
1075 if (fileAttributeValue instanceof byte[]) {
1076 if (Arrays.equals((byte[]) fileAttributeValue, (byte[]) expectedAttributeValue)) {
1080 }
else if (fileAttributeValue.equals(expectedAttributeValue)) {
1105 public static final class BlackboardException
extends Exception {
1107 private static final long serialVersionUID = 1L;
1114 BlackboardException(String message) {
1125 BlackboardException(String message, Throwable cause) {
1126 super(message, cause);
1147 Collection<BlackboardAttribute> attributes, Long osAccountId)
throws TskCoreException {
1150 throw new TskCoreException(String.format(
"Artifact type (name = %s) is not of Data Artifact category. ", artifactType.getTypeName()));
1156 attributes, osAccountId, transaction);
1158 return dataArtifact;
1163 LOGGER.log(Level.SEVERE,
"Failed to rollback transaction after exception. "
1164 +
"Error invoking newDataArtifact with dataSourceObjId: " + dataSourceObjId +
", sourceObjId: " + sourceObjId, ex2);
1194 throw new TskCoreException(String.format(
"Artifact type (name = %s) is not of Data Artifact category. ", artifactType.getTypeName()));
1198 CaseDbConnection connection = transaction.getConnection();
1200 PreparedStatement statement = caseDb.createInsertArtifactStatement(artifactType.getTypeID(), sourceObjId, artifact_obj_id, dataSourceObjId, connection);
1202 connection.executeUpdate(statement);
1203 try (ResultSet resultSet = statement.getGeneratedKeys()) {
1206 sourceObjId, artifact_obj_id, dataSourceObjId, artifactType.getTypeID(),
1208 osAccountObjId,
true);
1211 if (osAccountObjId != null) {
1212 String insertDataArtifactSQL =
"INSERT INTO tsk_data_artifacts (artifact_obj_id, os_account_obj_id) VALUES (?, ?)";
1214 statement = connection.getPreparedStatement(insertDataArtifactSQL, Statement.NO_GENERATED_KEYS);
1215 statement.clearParameters();
1217 statement.setLong(1, artifact_obj_id);
1218 statement.setLong(2, osAccountObjId);
1219 connection.executeUpdate(statement);
1223 if (Objects.nonNull(attributes) && !attributes.isEmpty()) {
1227 return dataArtifact;
1229 }
catch (SQLException ex) {
1230 throw new TskCoreException(String.format(
"Error creating a data artifact with type id = %d, objId = %d, and data source oj id = %d ", artifactType.getTypeID(), sourceObjId, dataSourceObjId), ex);
1241 private final String moduleName;
1243 private final ImmutableSet<BlackboardArtifact> artifacts;
1245 private ArtifactsPostedEvent(Collection<BlackboardArtifact> artifacts, String moduleName)
throws BlackboardException {
1246 Set<Integer> typeIDS = artifacts.stream()
1248 .collect(Collectors.toSet());
1250 for (Integer typeID : typeIDS) {
1254 throw new BlackboardException(
"Error getting artifact type by id.", tskCoreException);
1257 artifactTypes = ImmutableSet.copyOf(types);
1258 this.artifacts = ImmutableSet.copyOf(artifacts);
1259 this.moduleName = moduleName;
1264 return ImmutableSet.copyOf(artifacts);
1268 Set<BlackboardArtifact> tempSet = artifacts.stream()
1269 .filter(artifact -> artifact.getArtifactTypeID() == artifactType.getTypeID())
1270 .collect(Collectors.toSet());
1271 return ImmutableSet.copyOf(tempSet);
1279 return ImmutableSet.copyOf(artifactTypes);
static Priority fromID(int id)
List< BlackboardArtifact > getArtifacts(Collection< BlackboardArtifact.Type > artifactTypes, Collection< Long > dataSourceObjIds)
static Significance fromID(int id)
AnalysisResult getAnalysisResultById(long artifactObjId)
void postArtifact(BlackboardArtifact artifact, String moduleName)
CaseDbTransaction beginTransaction()
List< DataArtifact > getDataArtifacts(int artifactTypeID, long dataSourceObjId)
boolean hasAnalysisResults(long sourceObjId)
void postArtifacts(Collection< BlackboardArtifact > artifacts, String moduleName)
static Category fromID(int id)
void addAttributes(Collection< BlackboardAttribute > attributes)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountId)
DataArtifact getDataArtifactById(long artifactObjId)
Collection< BlackboardArtifact > getArtifacts()
BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName, BlackboardArtifact.Category category)
Score deleteAnalysisResult(AnalysisResult analysisResult)
List< AnalysisResult > getAnalysisResults(long sourceObjId)
Collection< BlackboardArtifact > getArtifacts(BlackboardArtifact.Type artifactType)
BlackboardAttribute.Type addArtifactAttributeType(String attrTypeString, TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName)
List< DataArtifact > getDataArtifacts(int artifactTypeID)
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList, CaseDbTransaction transaction)
List< AnalysisResult > getAnalysisResultsWhere(String whereClause)
BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName)
List< BlackboardArtifact > getArtifacts(int artifactTypeID, long dataSourceObjId)
Score deleteAnalysisResult(long artifactObjId, CaseDbTransaction transaction)
TimelineManager getTimelineManager()
void releaseSingleUserCaseReadLock()
BlackboardAttribute.Type getAttributeType(String attrTypeName)
List< AnalysisResult > getAnalysisResults(long sourceObjId, int artifactTypeId)
BlackboardArtifact.Type getArtifactType(String artTypeName)
boolean artifactExists(Content content, BlackboardArtifact.ARTIFACT_TYPE artifactType, Collection< BlackboardAttribute > attributesList)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountObjId, final CaseDbTransaction transaction)
List< AnalysisResult > getAnalysisResultsByType(int artifactTypeId, long dataSourceObjId)
Collection< BlackboardArtifact.Type > getArtifactTypes()
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList)
BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName)
boolean hasDataArtifacts(long sourceObjId)
void acquireSingleUserCaseReadLock()
long getArtifactsCount(int artifactTypeID, long dataSourceObjId)
List< BlackboardArtifact.Type > getArtifactTypesInUse(long dataSourceObjId)
static ReviewStatus withID(int id)
BlackboardArtifact.Type getArtifactType(int artTypeId)
BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName, String displayName)
List< AnalysisResult > getAnalysisResultsByType(int artifactTypeId)
ScoringManager getScoringManager()