mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-20 03:24:55 +00:00
Merge pull request #2329 from millmanorama/group-bins-by-range
Group bins by range
This commit is contained in:
commit
b09b9d6d25
@ -43,7 +43,9 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.apache.commons.csv.CSVFormat;
|
import org.apache.commons.csv.CSVFormat;
|
||||||
@ -169,7 +171,7 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
* based on the state of showRejected.
|
* based on the state of showRejected.
|
||||||
*/
|
*/
|
||||||
private String getRejectedArtifactFilterClause() {
|
private String getRejectedArtifactFilterClause() {
|
||||||
return showRejected ? "" : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID(); //NON-NLS
|
return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID(); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,6 +299,7 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
* Details of a range of Issuer/Bank Identifiaction Number(s) (IIN/BIN) used
|
* Details of a range of Issuer/Bank Identifiaction Number(s) (IIN/BIN) used
|
||||||
* by a bank.
|
* by a bank.
|
||||||
*/
|
*/
|
||||||
|
@Immutable
|
||||||
static private class IINRange implements IINInfo {
|
static private class IINRange implements IINInfo {
|
||||||
|
|
||||||
private final int IINStart; //start of IIN range, 8 digits
|
private final int IINStart; //start of IIN range, 8 digits
|
||||||
@ -702,6 +705,7 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
* DataModel for a child of the ByFileNode. Represents a file(chunk) and its
|
* DataModel for a child of the ByFileNode. Represents a file(chunk) and its
|
||||||
* associated accounts.
|
* associated accounts.
|
||||||
*/
|
*/
|
||||||
|
@Immutable
|
||||||
private static class FileWithCCN {
|
private static class FileWithCCN {
|
||||||
|
|
||||||
private final long objID;
|
private final long objID;
|
||||||
@ -933,9 +937,15 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateDisplayName() {
|
private void updateDisplayName() {
|
||||||
ArrayList<Long> keys = new ArrayList<>();
|
setDisplayName(getBinRangeString() + " (" + bin.getCount() + ")"); //NON-NLS
|
||||||
accountFactory.createKeys(keys);
|
}
|
||||||
setDisplayName(bin.getBIN().toString() + " (" + keys.size() + ")"); //NON-NLS
|
|
||||||
|
private String getBinRangeString() {
|
||||||
|
if (bin.getIINStart() == bin.getIINEnd()) {
|
||||||
|
return Integer.toString(bin.getIINStart());
|
||||||
|
} else {
|
||||||
|
return bin.getIINStart() + "-" + StringUtils.difference(bin.getIINStart() + "", bin.getIINEnd() + "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -977,7 +987,7 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
|
properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
|
||||||
Bundle.Accounts_BINNode_binProperty_displayName(),
|
Bundle.Accounts_BINNode_binProperty_displayName(),
|
||||||
Bundle.Accounts_BINNode_noDescription(),
|
Bundle.Accounts_BINNode_noDescription(),
|
||||||
bin.getBIN()));
|
getBinRangeString()));
|
||||||
properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
|
properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
|
||||||
Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
|
Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
|
||||||
bin.getCount()));
|
bin.getCount()));
|
||||||
@ -1020,6 +1030,8 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<BinResult> list) {
|
protected boolean createKeys(List<BinResult> list) {
|
||||||
|
RangeMap<Integer, BinResult> ranges = TreeRangeMap.create();
|
||||||
|
|
||||||
String query
|
String query
|
||||||
= "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
|
= "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
|
||||||
+ " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
|
+ " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
|
||||||
@ -1033,9 +1045,24 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query)) {
|
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query)) {
|
||||||
ResultSet resultSet = results.getResultSet();
|
ResultSet resultSet = results.getResultSet();
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
list.add(new BinResult(Integer.valueOf(resultSet.getString("BIN")), //NON-NLS
|
final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
|
||||||
resultSet.getLong("count"))); //NON-NLS
|
long count = resultSet.getLong("count");
|
||||||
|
|
||||||
|
IINRange iinRange = (IINRange) getIINInfo(bin);
|
||||||
|
BinResult previousResult = ranges.get(bin);
|
||||||
|
|
||||||
|
if (previousResult != null) {
|
||||||
|
ranges.remove(Range.closed(previousResult.getIINStart(), previousResult.getIINEnd()));
|
||||||
|
count += previousResult.getCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iinRange != null) {
|
||||||
|
ranges.put(Range.closed(iinRange.getIINstart(), iinRange.getIINend()), new BinResult(count, iinRange));
|
||||||
|
} else {
|
||||||
|
ranges.put(Range.closed(bin, bin), new BinResult(count, bin, bin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ranges.asMapOfRanges().values().forEach(list::add);
|
||||||
} catch (TskCoreException | SQLException ex) {
|
} catch (TskCoreException | SQLException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
|
LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
|
||||||
return false;
|
return false;
|
||||||
@ -1053,76 +1080,92 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
* Data model item to back the BINNodes in the tree. Has the number of
|
* Data model item to back the BINNodes in the tree. Has the number of
|
||||||
* accounts found with the BIN.
|
* accounts found with the BIN.
|
||||||
*/
|
*/
|
||||||
private class BinResult implements IINInfo {
|
@Immutable
|
||||||
|
static private class BinResult implements IINInfo {
|
||||||
|
|
||||||
private final Integer bin;
|
|
||||||
/**
|
/**
|
||||||
* The number of accounts with this BIN
|
* The number of accounts with this BIN
|
||||||
*/
|
*/
|
||||||
private final Long count;
|
private final long count;
|
||||||
private final IINInfo iinInfo;
|
|
||||||
|
|
||||||
private BinResult(Integer bin, Long count) {
|
private final IINRange iinRange;
|
||||||
this.bin = bin;
|
private final int iinEnd;
|
||||||
|
private final int iinStart;
|
||||||
|
|
||||||
|
private BinResult(long count, @Nonnull IINRange iinRange) {
|
||||||
this.count = count;
|
this.count = count;
|
||||||
iinInfo = getIINInfo(bin);
|
this.iinRange = iinRange;
|
||||||
|
iinStart = iinRange.getIINstart();
|
||||||
|
iinEnd = iinRange.getIINend();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getBIN() {
|
private BinResult(long count, int start, int end) {
|
||||||
return bin;
|
this.count = count;
|
||||||
|
this.iinRange = null;
|
||||||
|
iinStart = start;
|
||||||
|
iinEnd = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getCount() {
|
int getIINStart() {
|
||||||
|
return iinStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getIINEnd() {
|
||||||
|
return iinEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public long getCount() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasDetails() {
|
boolean hasDetails() {
|
||||||
return iinInfo != null;
|
return iinRange != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Integer> getNumberLength() {
|
public Optional<Integer> getNumberLength() {
|
||||||
return iinInfo.getNumberLength();
|
return iinRange.getNumberLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getBankCity() {
|
public Optional<String> getBankCity() {
|
||||||
return iinInfo.getBankCity();
|
return iinRange.getBankCity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getBankName() {
|
public Optional<String> getBankName() {
|
||||||
return iinInfo.getBankName();
|
return iinRange.getBankName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getBankPhoneNumber() {
|
public Optional<String> getBankPhoneNumber() {
|
||||||
return iinInfo.getBankPhoneNumber();
|
return iinRange.getBankPhoneNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getBankURL() {
|
public Optional<String> getBankURL() {
|
||||||
return iinInfo.getBankURL();
|
return iinRange.getBankURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getBrand() {
|
public Optional<String> getBrand() {
|
||||||
return iinInfo.getBrand();
|
return iinRange.getBrand();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getCardType() {
|
public Optional<String> getCardType() {
|
||||||
return iinInfo.getCardType();
|
return iinRange.getCardType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getCountry() {
|
public Optional<String> getCountry() {
|
||||||
return iinInfo.getCountry();
|
return iinRange.getCountry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getScheme() {
|
public Optional<String> getScheme() {
|
||||||
return iinInfo.getScheme();
|
return iinRange.getScheme();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1139,13 +1182,14 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<Long> list) {
|
protected boolean createKeys(List<Long> list) {
|
||||||
|
|
||||||
String query
|
String query
|
||||||
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
||||||
+ " FROM blackboard_artifacts " //NON-NLS
|
+ " FROM blackboard_artifacts " //NON-NLS
|
||||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT.getTypeID() //NON-NLS
|
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT.getTypeID() //NON-NLS
|
||||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER.getTypeID() //NON-NLS
|
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER.getTypeID() //NON-NLS
|
||||||
+ " AND blackboard_attributes.value_text LIKE \"" + bin.getBIN() + "%\" " //NON-NLS
|
+ " AND blackboard_attributes.value_text >= \"" + bin.getIINStart() + "\" AND blackboard_attributes.value_text < \"" + (bin.getIINEnd() + 1) + "\"" //NON-NLS
|
||||||
+ getRejectedArtifactFilterClause()
|
+ getRejectedArtifactFilterClause()
|
||||||
+ " ORDER BY blackboard_attributes.value_text"; //NON-NLS
|
+ " ORDER BY blackboard_attributes.value_text"; //NON-NLS
|
||||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||||
@ -1253,7 +1297,6 @@ public class Accounts extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
RejectAccounts(Collection<? extends BlackboardArtifact> artifacts) {
|
RejectAccounts(Collection<? extends BlackboardArtifact> artifacts) {
|
||||||
super(Bundle.RejectAccountsAction_name());
|
super(Bundle.RejectAccountsAction_name());
|
||||||
|
|
||||||
this.artifacts = artifacts;
|
this.artifacts = artifacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,21 +182,30 @@ final class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
@Override
|
@Override
|
||||||
public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(String termHit, KeywordHit hit, String snippet, String listName) {
|
public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(String termHit, KeywordHit hit, String snippet, String listName) {
|
||||||
BlackboardArtifact newArtifact;
|
BlackboardArtifact newArtifact;
|
||||||
|
|
||||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
//if the keyword hit matched the credit card number keyword/regex...
|
//if the keyword hit matched the credit card number keyword/regex...
|
||||||
if (keyword.getType() == ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER) {
|
if (keyword.getType() == ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER) {
|
||||||
|
termHit = CharMatcher.inRange('0', '9').negate().trimFrom(termHit);
|
||||||
newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT);
|
newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT);
|
||||||
// make account artifact
|
// make account artifact
|
||||||
//try to match it against the track 1 regex
|
//try to match it against the track 1 regex
|
||||||
Matcher matcher = TRACK1_PATTERN.matcher(hit.getSnippet());
|
Matcher matcher = TRACK1_PATTERN.matcher(hit.getSnippet());
|
||||||
if (matcher.find()) {
|
while (matcher.find()) {
|
||||||
|
if (termHit.equals(matcher.group("accountNumber"))) {
|
||||||
parseTrack1Data(newArtifact, matcher);
|
parseTrack1Data(newArtifact, matcher);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//then try to match it against the track 2 regex
|
//then try to match it against the track 2 regex
|
||||||
matcher = TRACK2_PATTERN.matcher(hit.getSnippet());
|
matcher = TRACK2_PATTERN.matcher(hit.getSnippet());
|
||||||
if (matcher.find()) {
|
while (matcher.find()) {
|
||||||
|
final String group = matcher.group("accountNumber");
|
||||||
|
if (termHit.equals(group)) {
|
||||||
parseTrack2Data(newArtifact, matcher);
|
parseTrack2Data(newArtifact, matcher);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (hit.getContent() instanceof AbstractFile) {
|
if (hit.getContent() instanceof AbstractFile) {
|
||||||
AbstractFile file = (AbstractFile) hit.getContent();
|
AbstractFile file = (AbstractFile) hit.getContent();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user