Sleuth Kit Java Bindings (JNI)  4.12.1
Java bindings for using The Sleuth Kit
ScoringManager.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
21 import java.sql.PreparedStatement;
22 import java.sql.ResultSet;
23 import java.sql.SQLException;
24 import java.sql.Statement;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.logging.Logger;
33 import java.util.stream.Collectors;
38 
44 public class ScoringManager {
45 
46  private static final Logger LOGGER = Logger.getLogger(ScoringManager.class.getName());
47 
48  private final SleuthkitCase db;
49 
57  this.db = skCase;
58  }
59 
69  public Score getAggregateScore(long objId) throws TskCoreException {
71  try (CaseDbConnection connection = db.getConnection()) {
72  return getAggregateScore(objId, connection);
73  } finally {
75  }
76  }
77 
88  public Map<Long, Score> getAggregateScores(List<Long> objIds) throws TskCoreException {
89 
90  if (objIds.isEmpty()) {
91  return Collections.emptyMap();
92  }
93 
94  // We need to deduplicate the list of object IDs. Otherwise the map
95  // below breaks and throws an exception.
96  Set<Long> set = new HashSet<>(objIds);
97 
98  String queryString = "SELECT obj_id, significance, priority FROM tsk_aggregate_score WHERE obj_id in "
99  + set.stream().map(l -> l.toString()).collect(Collectors.joining(",", "(", ")"));
100 
101  Map<Long, Score> results = set.stream().collect(Collectors.toMap( key -> key, key -> Score.SCORE_UNKNOWN));
103  try (CaseDbConnection connection = db.getConnection()) {
104  try (Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, queryString)) {
105  while (rs.next()) {
106  Long objId = rs.getLong("obj_id");
107  Score score = new Score(Significance.fromID(rs.getInt("significance")), Priority.fromID(rs.getInt("priority")));
108  results.put(objId, score);
109  }
110  } catch (SQLException ex) {
111  throw new TskCoreException("SQLException thrown while running query: " + queryString, ex);
112  }
113  } finally {
115  }
116  return results;
117  }
118 
119 
131  private Score getAggregateScore(long objId, CaseDbTransaction transaction) throws TskCoreException {
132  CaseDbConnection connection = transaction.getConnection();
133  return getAggregateScore(objId, connection);
134  }
135 
146  private Score getAggregateScore(long objId, CaseDbConnection connection) throws TskCoreException {
147  String queryString = "SELECT significance, priority FROM tsk_aggregate_score WHERE obj_id = " + objId;
148  try (Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, queryString)) {
149  if (rs.next()) {
150  return new Score(Significance.fromID(rs.getInt("significance")), Priority.fromID(rs.getInt("priority")));
151  } else {
152  return Score.SCORE_UNKNOWN;
153  }
154  } catch (SQLException ex) {
155  throw new TskCoreException("SQLException thrown while running query: " + queryString, ex);
156  }
157  }
158 
159 
170  private void setAggregateScore(long objId, Long dataSourceObjectId, Score score, CaseDbTransaction transaction) throws TskCoreException {
171 
172  String insertSQLString = "INSERT INTO tsk_aggregate_score (obj_id, data_source_obj_id, significance , priority) VALUES (?, ?, ?, ?)"
173  + " ON CONFLICT (obj_id) DO UPDATE SET significance = ?, priority = ?";
174 
175  CaseDbConnection connection = transaction.getConnection();
176  try {
177  PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQLString, Statement.NO_GENERATED_KEYS);
178  preparedStatement.clearParameters();
179 
180  preparedStatement.setLong(1, objId);
181  if (dataSourceObjectId != null) {
182  preparedStatement.setLong(2, dataSourceObjectId);
183  } else {
184  preparedStatement.setNull(2, java.sql.Types.NULL);
185  }
186  preparedStatement.setInt(3, score.getSignificance().getId());
187  preparedStatement.setInt(4, score.getPriority().getId());
188 
189  preparedStatement.setInt(5, score.getSignificance().getId());
190  preparedStatement.setInt(6, score.getPriority().getId());
191 
192  connection.executeUpdate(preparedStatement);
193  } catch (SQLException ex) {
194  throw new TskCoreException(String.format("Error updating aggregate score, query: %s for objId = %d", insertSQLString, objId), ex);//NON-NLS
195  }
196 
197  }
198 
199 
200 
215  Score updateAggregateScoreAfterAddition(long objId, Long dataSourceObjectId, Score newResultScore, CaseDbTransaction transaction) throws TskCoreException {
216 
217  /* get an exclusive write lock on the DB before we read anything so that we know we are
218  * the only one reading existing scores and updating. The risk is that two computers
219  * could update the score and the aggregate score ends up being incorrect.
220  *
221  * NOTE: The alternative design is to add a 'version' column for opportunistic locking
222  * and calculate these outside of a transaction. We opted for table locking for performance
223  * reasons so that we can still add the analysis results in a batch. That remains an option
224  * if we get into deadlocks with the current design.
225  */
226  try {
227  CaseDbConnection connection = transaction.getConnection();
228  connection.getAggregateScoreTableWriteLock();
229  } catch (SQLException ex) {
230  throw new TskCoreException("Error getting exclusive write lock on aggregate score table", ex);//NON-NLS
231  }
232 
233 
234  // Get the current score
235  Score currentAggregateScore = ScoringManager.this.getAggregateScore(objId, transaction);
236 
237  // If current score is Unknown And newscore is not Unknown - allow None (good) to be recorded
238  // or if the new score is higher than the current score
239  if ( (currentAggregateScore.compareTo(Score.SCORE_UNKNOWN) == 0 && newResultScore.compareTo(Score.SCORE_UNKNOWN) != 0)
240  || (Score.getScoreComparator().compare(newResultScore, currentAggregateScore) > 0)) {
241  setAggregateScore(objId, dataSourceObjectId, newResultScore, transaction);
242 
243  // register score change in the transaction.
244  transaction.registerScoreChange(new ScoreChange(objId, dataSourceObjectId, currentAggregateScore, newResultScore));
245  return newResultScore;
246  } else {
247  // return the current score
248  return currentAggregateScore;
249  }
250  }
251 
262  Score updateAggregateScoreAfterDeletion(long objId, Long dataSourceObjectId, CaseDbTransaction transaction) throws TskCoreException {
263 
264  CaseDbConnection connection = transaction.getConnection();
265 
266  /* get an exclusive write lock on the DB before we read anything so that we know we are
267  * the only one reading existing scores and updating. The risk is that two computers
268  * could update the score and the aggregate score ends up being incorrect.
269  *
270  * NOTE: The alternative design is to add a 'version' column for opportunistic locking
271  * and calculate these outside of a transaction. We opted for table locking for performance
272  * reasons so that we can still add the analysis results in a batch. That remains an option
273  * if we get into deadlocks with the current design.
274  */
275  try {
276  connection.getAggregateScoreTableWriteLock();
277  } catch (SQLException ex) {
278  throw new TskCoreException("Error getting exclusive write lock on aggregate score table", ex);//NON-NLS
279  }
280 
281  // Get the current score
282  Score currentScore = ScoringManager.this.getAggregateScore(objId, transaction);
283 
284  // Calculate the score from scratch by getting all of them and getting the highest
285  List<AnalysisResult> analysisResults = db.getBlackboard().getAnalysisResults(objId, connection);
286  Score newScore = Score.SCORE_UNKNOWN;
287  for (AnalysisResult iter : analysisResults) {
288  Score iterScore = iter.getScore();
289  if (Score.getScoreComparator().compare(iterScore, newScore) > 0) {
290  newScore = iterScore;
291  }
292  }
293 
294  // get the maximum score of the calculated aggregate score of analysis results
295  // or the score derived from the maximum known status of a content tag on this content.
296  Optional<Score> tagScore = db.getTaggingManager().getMaxTagKnownStatus(objId, transaction)
297  .map(knownStatus -> TaggingManager.getTagScore(knownStatus));
298 
299  if (tagScore.isPresent() && Score.getScoreComparator().compare(tagScore.get(), newScore) > 0) {
300  newScore = tagScore.get();
301  }
302 
303  // only change the DB if we got a new score.
304  if (newScore.compareTo(currentScore) != 0) {
305  setAggregateScore(objId, dataSourceObjectId, newScore, transaction);
306 
307  // register the score change with the transaction so an event can be fired for it.
308  transaction.registerScoreChange(new ScoreChange(objId, dataSourceObjectId, currentScore, newScore));
309  }
310  return newScore;
311  }
312 
323  public long getContentCount(long dataSourceObjectId, Score.Significance significance) throws TskCoreException {
325  try (CaseDbConnection connection = db.getConnection()) {
326  return getContentCount(dataSourceObjectId, significance, connection);
327  } finally {
329  }
330  }
331 
332 
345  private long getContentCount(long dataSourceObjectId, Score.Significance significance, CaseDbConnection connection) throws TskCoreException {
346  String queryString = "SELECT COUNT(obj_id) AS count FROM tsk_aggregate_score"
347  + " WHERE data_source_obj_id = " + dataSourceObjectId
348  + " AND significance = " + significance.getId();
349 
350  try (Statement statement = connection.createStatement();
351  ResultSet resultSet = connection.executeQuery(statement, queryString);) {
352 
353  long count = 0;
354  if (resultSet.next()) {
355  count = resultSet.getLong("count");
356  }
357  return count;
358  } catch (SQLException ex) {
359  throw new TskCoreException("Error getting count of items with significance = " + significance.toString(), ex);
360  }
361  }
362 
373  public List<Content> getContent(long dataSourceObjectId, Score.Significance significance) throws TskCoreException {
375  try (CaseDbConnection connection = db.getConnection()) {
376  return getContent(dataSourceObjectId, significance, connection);
377  } finally {
379  }
380  }
381 
394  private List<Content> getContent(long dataSourceObjectId, Score.Significance significance, CaseDbConnection connection) throws TskCoreException {
395  String queryString = "SELECT obj_id FROM tsk_aggregate_score"
396  + " WHERE data_source_obj_id = " + dataSourceObjectId
397  + " AND significance = " + significance.getId();
398 
399  try (Statement statement = connection.createStatement();
400  ResultSet resultSet = connection.executeQuery(statement, queryString);) {
401 
402  List<Content> items = new ArrayList<>();
403  while (resultSet.next()) {
404  long objId = resultSet.getLong("obj_id");
405  items.add(db.getContentById(objId));
406  }
407  return items;
408  } catch (SQLException ex) {
409  throw new TskCoreException("Error getting list of items with significance = " + significance.toString(), ex);
410  }
411  }
412 }
static Priority fromID(int id)
Definition: Score.java:184
static Significance fromID(int id)
Definition: Score.java:118
List< Content > getContent(long dataSourceObjectId, Score.Significance significance)
static final Score SCORE_UNKNOWN
Definition: Score.java:213
Map< Long, Score > getAggregateScores(List< Long > objIds)
synchronized TaggingManager getTaggingManager()
long getContentCount(long dataSourceObjectId, Score.Significance significance)
List< AnalysisResult > getAnalysisResults(long dataSourceObjId, Integer artifactTypeID)

Copyright © 2011-2021 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.