Skip to content

Commit

Permalink
Merge branch 'features/th4-alert-similarity' into develop-th4
Browse files Browse the repository at this point in the history
  • Loading branch information
nadouani committed Oct 22, 2020
2 parents 711bfb4 + ce44fef commit ada0c8d
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 24 deletions.
122 changes: 118 additions & 4 deletions frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,35 @@

self.CaseResolutionStatus = CaseResolutionStatus;

self.similarityFilters = {
fTitle: undefined
};

self.matchFilters = {
fMatches: []
};

self.rateFilters = {
fObservables: undefined,
fIocs: undefined
};

self.sortField = '-sCreatedAt';
self.matches = [];
self.filteredCases = [];

self.pagination = {
pageSize: 10,
currentPage: 1
};

self.$onInit = function() {
this.filtering = new FilteringSrv('case', 'alert.dialog.similar-cases', {
version: 'v1',
defaults: {
showFilters: true,
showStats: false,
pageSize: 15,
pageSize: 2,
sort: ['-startDate']
},
defaultFilter: []
Expand All @@ -40,12 +62,25 @@
skipStream: true,
version: 'v1',
loadAll: true,
pageSize: 15,
//pageSize: self.filtering.context.pageSize,
operations: [
{'_name': 'getAlert', 'idOrName': this.alertId},
{'_name': 'similarCases'}
{'_name': 'similarCases', 'caseFilter': this.filtering.buildQuery()}
],
onUpdate: function() {}
onUpdate: function(data) {
_.each(data, function(item) {
item.fTitle = item.case.title;
item.fMatches = _.keys(item.observableTypes);
item.fObservables = Math.floor((item.similarObservableCount / item.observableCount) * 100);
item.fIocs = Math.floor((item.similarIocCount / item.iocCount) * 100) || 0;

item.sCreatedAt = item.case._createdAt;
});

self.matches = _.uniq(_.flatten(_.map(data, function(item){
return _.keys(item.observableTypes);
}))).sort();
}
});
};

Expand All @@ -55,6 +90,85 @@
});
};

// Frontend filter methods
this.clearLocalFilters = function() {
self.similarityFilters = {
fTitle: undefined
};

self.matchFilters = {
fMatches: []
};

self.rateFilters = {
fObservables: undefined,
fIocs: undefined
};
};

this.greaterThan = function(prop){
return function(item){
return !self.rateFilters[prop] || item[prop] >= self.rateFilters[prop];
};
};

this.matchFilter = function() {
return function(item){
return !self.matchFilters.fMatches || self.matchFilters.fMatches.length === 0 ||
_.intersection(self.matchFilters.fMatches, item.fMatches).length > 0;
};
};

// Filtering methods
this.toggleFilters = function () {
this.filtering.toggleFilters();
};

this.search = function () {
self.load();
self.filtering.storeContext();
};

this.addFilterValue = function (field, value) {
self.filtering.addFilterValue(field, value);
self.search();
};

/// Clear all filters
this.clearFilters = function () {
self.filtering.clearFilters()
.then(self.search);
};

// Remove a filter
this.removeFilter = function (index) {
self.filtering.removeFilter(index)
.then(self.search);
};

this.filterBy = function(field, value) {
self.filtering.clearFilters()
.then(function(){
self.addFilterValue(field, value);
});
};

this.filterSimilarities = function(data) {
return data;
};

this.sortByField = function(field) {
var sort = null;

if(this.sortField.substr(1) !== field) {
sort = '+' + field;
} else {
sort = (this.sortField === '+' + field) ? '-'+field : '+'+field;
}

this.sortField = sort;
};


},
controllerAs: '$cmp',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
});

if (angular.isFunction(this.onUpdate)) {
this.onUpdate();
this.onUpdate(this.allValues);
}
} else {
this.update();
Expand Down
5 changes: 4 additions & 1 deletion frontend/app/styles/case-item.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ div.case-item>div.case-observables-list {
width: 450px;
}
div.case-item>div.case-similarity {
width: 200px;
width: 140px;
}
div.case-item>div.case-similarity-match {
width: 150px;
}
div.case-item>div.case-similarity-merge {
width: 150px;
Expand Down
20 changes: 20 additions & 0 deletions frontend/app/styles/filters.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,23 @@
background: #f5f5f5;
}
*/


.similar-case-list .progress {
height: 2px;
opacity: 1;
}

.similar-case-list .has-feedback .form-control {
padding-right: 25px;
}

.similar-case-list .case-item.filter-panel {
padding: 5px 0;
margin-bottom: 5px !important;
border: none !important;
}

.similar-case-list .case-item.filter-panel .form-group {
margin-bottom: 0 !important;
}
119 changes: 101 additions & 18 deletions frontend/app/views/components/alert/similar-case-list.component.html
Original file line number Diff line number Diff line change
@@ -1,32 +1,110 @@
<div class="similar-case-list">

<div ng-include="'views/components/alert/similarity/toolbar.html'"></div>

<div class="mt-s filter-panel" ng-include="'views/components/alert/similarity/filters.html'" ng-show="$cmp.filtering.context.showFilters"></div>

<div class="row mt-s">
<div class="col-md-12 clearfix">

<filters-preview filters="$cmp.filtering.context.filters"
on-clear-item="$cmp.removeFilter(field)"
on-clear-all="$cmp.clearFilters()"></filters-preview>
</div>
</div>

<div class="row">

<div class="col-md-12" ng-show="$cmp.list.total === 0">
<div class="empty-message">No records</div>
</div>

<div class="col-md-12" ng-show="$cmp.list.total > 0">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group pull-right" role="group">
<page-sizer collection="$cmp.list" sizes="[10, 15, 30, 100]"></page-sizer>
</div>
<!-- Client side pagination -->
<div class="text-center" ng-show="$cmp.filteredCases.length > $cmp.pagination.pageSize">
<ul uib-pagination class="pagination-sm" boundary-links="true" max-size="5" rotate="false"
total-items="$cmp.filteredCases.length"
items-per-page="$cmp.pagination.pageSize"
ng-model="$cmp.pagination.currentPage"></ul>
</div>

<psearch control="$cmp.list"></psearch>

<!-- Table header -->
<div class="case-item">
<div class="case-details text-bold">Title</div>
<div class="case-date text-bold">Date</div>
<div class="case-similarity text-bold">Observables</div>
<div class="case-similarity text-bold">IOCs</div>
<div class="case-details text-bold">
<a href class="text-default" ng-click="$cmp.sortByField('fTitle')">
Title
<i ng-show="$cmp.sortField !== '+fTitle' && $cmp.sortField !== '-fTitle'" class="fa fa-sort"></i>
<i ng-show="$cmp.sortField === '+fTitle'" class="fa fa-caret-up"></i>
<i ng-show="$cmp.sortField === '-fTitle'" class="fa fa-caret-down"></i>
</a>
</div>
<div class="case-date text-bold">
<a href class="text-default" ng-click="$cmp.sortByField('sCreatedAt')">
Created At
<i ng-show="$cmp.sortField !== '+sCreatedAt' && $cmp.sortField !== '-sCreatedAt'" class="fa fa-sort"></i>
<i ng-show="$cmp.sortField === '+sCreatedAt'" class="fa fa-caret-up"></i>
<i ng-show="$cmp.sortField === '-sCreatedAt'" class="fa fa-caret-down"></i>
</a>
</div>
<div class="case-similarity text-bold">
<a href class="text-default" ng-click="$cmp.sortByField('fObservables')">
Observables
<i ng-show="$cmp.sortField !== '+fObservables' && $cmp.sortField !== '-fObservables'" class="fa fa-sort"></i>
<i ng-show="$cmp.sortField === '+fObservables'" class="fa fa-caret-up"></i>
<i ng-show="$cmp.sortField === '-fObservables'" class="fa fa-caret-down"></i>
</a>
</div>
<div class="case-similarity text-bold">
<a href class="text-default" ng-click="$cmp.sortByField('fIocs')">
IOCs
<i ng-show="$cmp.sortField !== '+fIocs' && $cmp.sortField !== '-fIocs'" class="fa fa-sort"></i>
<i ng-show="$cmp.sortField === '+fIocs'" class="fa fa-caret-up"></i>
<i ng-show="$cmp.sortField === '-fIocs'" class="fa fa-caret-down"></i>
</a>
</div>
<div class="case-similarity-match text-bold">Matches</div>
<div class="case-similarity-merge text-bold">Action</div>
</div>

<!-- Table filter -->
<div class="case-item filter-panel">
<div class="case-details text-bold">
<div class="form-group has-feedback has-clear">
<input type="text" class="form-control input-sm" ng-model="$cmp.similarityFilters.fTitle" placeholder="Filter by title">
<a class="fa fa-times-circle form-control-feedback form-control-clear text-muted" ng-click="$cmp.similarityFilters.fTitle = undefined" style="pointer-events: auto;cursor: pointer;"></a>
</div>
</div>
<div class="case-date text-bold"></div>
<div class="case-similarity text-bold">
<div class="form-group has-feedback has-clear">
<input type="number" class="form-control input-sm" ng-model="$cmp.rateFilters.fObservables" placeholder="% min.">
<a class="fa fa-times-circle form-control-feedback form-control-clear text-muted" ng-click="$cmp.rateFilters.fObservables = undefined" style="pointer-events: auto;cursor: pointer;"></a>
</div>
</div>
<div class="case-similarity text-bold">
<div class="form-group has-feedback has-clear">
<input type="number" class="form-control input-sm" ng-model="$cmp.rateFilters.fIocs" placeholder="% min." step="1" min="0">
<a class="fa fa-times-circle form-control-feedback form-control-clear text-muted" ng-click="$cmp.rateFilters.fIocs = undefined" style="pointer-events: auto;cursor: pointer;"></a>
</div>
</div>
<div class="case-similarity-match text-bold">
<div>
<multiselect ng-model="$cmp.matchFilters.fMatches" options="$cmp.matches" show-select-all="true" show-unselect-all="true"
classes-btn="'btn-default btn-block btn-sm'"></multiselect>
</div>
</div>
<div class="case-similarity-merge text-bold">
<div class="text-center">
<a class="btn btn-link btn-clear btn-sm" ng-click="$cmp.clearLocalFilters()"><i class="fa fa-times"></i> Clear Filters</a>
</div>
</div>
</div>

<div class="case-collection" ng-repeat="item in filteredLinks = ($cmp.list.values | filter:$cmp.similarityFilters)">
<!-- Table data -->
<div class="case-collection" ng-repeat="item in ($cmp.filteredCases = ($cmp.list.allValues | filter:$cmp.similarityFilters | filter:$cmp.matchFilter() | filter:$cmp.greaterThan('fObservables') | filter:$cmp.greaterThan('fIocs')) | orderBy:$cmp.sortField | limitTo:$cmp.pagination.pageSize:($cmp.pagination.currentPage-1)*$cmp.pagination.pageSize)">
<div class="case-item" >
<!-- case severity -->
<div class="case-tlp bg-tlp-{{item.case.tlp}}"></div>
<!-- case tlp -->
<div class="case-tlp bg-tlp-{{item.case.tlp}} clickable" ng-click="$cmp.addFilterValue('tlp', item.case.tlp)"></div>

<!-- case title and main details -->
<div class="case-details">
Expand All @@ -36,7 +114,7 @@
<div class="case-tags flexwrap mt-xxs">
<span class="mr-xxxs text-muted"><i class="fa fa-tags"></i></span>
<strong class="text-muted mr-xxxs" ng-if="!item.case.tags || item.case.tags.length === 0">None</strong>
<span ng-repeat="tag in item.case.tags track by $index" class="label label-primary mb-xxxs mr-xxxs pointer">{{tag}}</span>
<span ng-repeat="tag in item.case.tags track by $index" ng-click="$cmp.addFilterValue('tags', tag)" class="label label-primary mb-xxxs mr-xxxs pointer">{{tag}}</span>
</div>
<div class="text-success" ng-show="item.case.status !== 'Open'">
<small>
Expand All @@ -52,30 +130,35 @@
</div>

<div class="case-severity">
<div class="clickable">
<div class="clickable" ng-click="$cmp.addFilterValue('severity', item.case.severity)">
<severity active="false" value="item.case.severity"></severity>
</div>
</div>

<div class="case-date">
<span uib-tooltip="{{item.case.startDate | showDate}}" tooltip-popup-delay="500" tooltip-placement="bottom">{{item.case.startDate | shortDate}}</span>
<span uib-tooltip="{{item.case._createdAt | showDate}}" tooltip-popup-delay="500" tooltip-placement="bottom">{{item.case._createdAt | shortDate}}</span>
</div>

<div class="case-similarity">
<div>
<strong>{{(item.similarObservableCount / item.observableCount) | percentage:0}}</strong> ({{item.similarObservableCount}} / {{item.observableCount}})
<a href ng-click="$cmp.rateFilters.fObservables = item.fObservables"><strong>{{item.fObservables | number:0}} %</strong></a> ({{item.similarObservableCount}} / {{item.observableCount}})
<uib-progressbar class="progress progress-sm" max="item.observableCount" value="item.similarObservableCount" type="primary"></uib-progressbar>
</div>
</div>
<div class="case-similarity">
<div ng-if="item.iocCount > 0">
<strong>{{(item.similarIocCount / item.iocCount) | percentage:0}}</strong> ({{item.similarIocCount}} / {{item.iocCount}})
<a href ng-click="$cmp.rateFilters.fIocs = item.fIocs"><strong>{{item.fIocs | number:0}} %</strong></a> ({{item.similarIocCount}} / {{item.iocCount}})
<uib-progressbar class="progress progress-sm" max="item.iocCount" value="item.similarIocCount" type="danger"></uib-progressbar>
</div>
<div ng-if="item.iocCount === 0">
<em>N/A</em>
</div>
</div>
<div class="case-similarity-match">
<div>
<div ng-repeat="(match, count) in item.observableTypes track by $index">{{match}} ({{count}})</div>
</div>
</div>

<div class="case-similarity-merge text-center">
<button class="btn btn-sm btn-primary" ng-click="$cmp.merge(item.case._id)">Merge in this case</button>
Expand Down
Loading

0 comments on commit ada0c8d

Please sign in to comment.