Skip to content

Commit a2b53cf

Browse files
authored
Updated filtering for vulnerabilities table (#2848)
### What's done: - removed useless filter `isOwner` in `VulnerabilityFilter`. - added filtering by `Summary` to field for `Identifier` filtering, field renamed to `Identifier or Summary`. - added field for filtering by `Status`. - for better understanding, the displayed status description changed for `AUTO_APPROVED` to `Approved` (as for `APPROVED`) to display `AUTO_APPROVED` and `APPROVED` statuses together, and for `PENDING_REVIEW` to `Edited`.
1 parent cc7a66a commit a2b53cf

File tree

7 files changed

+59
-44
lines changed

7 files changed

+59
-44
lines changed

save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/VulnerabilityController.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class VulnerabilityController(
6464
authentication: Authentication?,
6565
): Mono<VulnerabilityMetadataDtoList> = blockingToMono {
6666
checkPermission(filter, authentication)
67-
vulnerabilityService.getFilteredWithUserInfos(filter, authentication, page, size)
67+
vulnerabilityService.getFilteredWithUserInfos(filter, page, size)
6868
}
6969

7070
@PostMapping("/count/by-filter")
@@ -79,7 +79,7 @@ class VulnerabilityController(
7979
authentication: Authentication?,
8080
): Mono<Long> = blockingToMono {
8181
checkPermission(filter, authentication)
82-
vulnerabilityService.getCountVulnerabilitiesByFilter(filter, authentication)
82+
vulnerabilityService.getCountVulnerabilitiesByFilter(filter)
8383
}
8484

8585
private fun checkPermission(

save-backend/src/main/kotlin/com/saveourtool/save/backend/service/vulnerability/VulnerabilityService.kt

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ class VulnerabilityService(
8484

8585
/**
8686
* @param filter filters for vulnerability
87-
* @param authentication [Authentication] describing an authenticated request
8887
* @param page
8988
* @param size
9089
* @return list of vulnerabilities with that match [filter]
@@ -96,12 +95,11 @@ class VulnerabilityService(
9695
)
9796
fun getFilteredWithUserInfos(
9897
filter: VulnerabilityFilter,
99-
authentication: Authentication?,
10098
page: Int,
10199
size: Int,
102100
): List<VulnerabilityMetadataDto> {
103101
val metadataList = vulnerabilityMetadataRepository.kFindAll(PageRequest.of(page, size)) { root, cq, cb ->
104-
getFilterPredicate(root, cq, cb, filter, authentication)
102+
getFilterPredicate(root, cq, cb, filter)
105103
}
106104

107105
val tagMap = metadataList.content.toTagMap()
@@ -115,39 +113,26 @@ class VulnerabilityService(
115113

116114
/**
117115
* @param filter
118-
* @param authentication
119116
* @return count of vulnerability
120117
*/
121118
fun getCountVulnerabilitiesByFilter(
122119
filter: VulnerabilityFilter,
123-
authentication: Authentication?,
124-
) = vulnerabilityMetadataRepository.count { root, cq, cb -> getFilterPredicate(root, cq, cb, filter, authentication) }
120+
) = vulnerabilityMetadataRepository.count { root, cq, cb -> getFilterPredicate(root, cq, cb, filter) }
125121

126122
private fun getFilterPredicate(
127123
root: Root<VulnerabilityMetadata>,
128124
cq: CriteriaQuery<*>,
129125
cb: CriteriaBuilder,
130126
filter: VulnerabilityFilter,
131-
authentication: Authentication?,
132127
): Predicate {
133128
with(filter) {
134-
val namePredicate = if (identifierPrefix.isBlank()) {
135-
cb.and()
129+
val namePredicate = if (freeText.isNotBlank()) {
130+
cb.or(cb.like(root.get("identifier"), "%$freeText%"), cb.like(root.get("summary"), "%$freeText%"))
136131
} else {
137-
cb.like(root.get("identifier"), "%$identifierPrefix%")
132+
cb.and()
138133
}
139134

140-
val ownerPredicate = authentication?.let {
141-
val userId = authentication.userId()
142-
143-
if (isOwner) {
144-
cb.equal(root.get<User>("user").get<Long>("id"), userId)
145-
} else {
146-
cb.and()
147-
}
148-
} ?: cb.and()
149-
150-
val statusPredicate = statuses?.let { statuses ->
135+
val statusesPredicate = statuses?.let { statuses ->
151136
cb.and(root.get<VulnerabilityStatus>("status").`in`(statuses))
152137
} ?: cb.and()
153138

@@ -158,6 +143,10 @@ class VulnerabilityService(
158143
cb.equal(root.get<VulnerabilityLanguage>("language"), language)
159144
} ?: cb.and()
160145

146+
val chosenStatusesPredicate = chosenStatuses?.let { statuses ->
147+
cb.and(root.get<VulnerabilityStatus>("status").`in`(statuses))
148+
} ?: cb.and()
149+
161150
val organizationPredicate = organizationName?.let { organization ->
162151
cb.equal(root.get<Organization>("organization").get<String>("name"), organization)
163152
} ?: cb.and()
@@ -168,10 +157,10 @@ class VulnerabilityService(
168157

169158
return cb.and(
170159
namePredicate,
171-
ownerPredicate,
172-
statusPredicate,
160+
statusesPredicate,
173161
criticalityPredicate,
174162
languagePredicate,
163+
chosenStatusesPredicate,
175164
authorPredicate,
176165
organizationPredicate,
177166
getPredicateForTags(root, cq, cb, tags),

save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/entities/vulnerability/VulnerabilityStatus.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ enum class VulnerabilityStatus(val value: String) {
2020
/**
2121
* Automatically approved by batch-upload
2222
*/
23-
AUTO_APPROVED("Auto-approved"),
23+
AUTO_APPROVED("Approved"),
2424

2525
/**
2626
* Created status
@@ -30,7 +30,7 @@ enum class VulnerabilityStatus(val value: String) {
3030
/**
3131
* Review status
3232
*/
33-
PENDING_REVIEW("Pending review"),
33+
PENDING_REVIEW("Edited"),
3434

3535
/**
3636
* Reject status

save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/filters/VulnerabilityFilter.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@ import com.saveourtool.save.entities.vulnerability.VulnerabilityStatus
55
import kotlinx.serialization.Serializable
66

77
/**
8-
* @property identifierPrefix
8+
* @property freeText
99
* @property statuses
10-
* @property isOwner
1110
* @property tags
1211
* @property minCriticality
1312
* @property maxCriticality
1413
* @property language
14+
* @property chosenStatuses
1515
* @property authorName
1616
* @property organizationName
1717
*/
1818
@Serializable
1919
data class VulnerabilityFilter(
20-
val identifierPrefix: String = "",
20+
val freeText: String = "",
2121
val statuses: List<VulnerabilityStatus>? = null,
22-
val isOwner: Boolean = false,
2322
val tags: Set<String> = emptySet(),
2423
val minCriticality: Float = 0.0f,
2524
val maxCriticality: Float = 10.0f,
2625
val language: VulnerabilityLanguage? = null,
26+
val chosenStatuses: List<VulnerabilityStatus>? = null,
2727
val authorName: String? = null,
2828
val organizationName: String? = null,
2929
) {

save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/table/filters/VulnerabilitiesFiltersRow.kt

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package com.saveourtool.save.frontend.components.basic.table.filters
44

55
import com.saveourtool.save.entities.OrganizationDto
66
import com.saveourtool.save.entities.vulnerability.VulnerabilityLanguage
7+
import com.saveourtool.save.entities.vulnerability.VulnerabilityStatus
78
import com.saveourtool.save.filters.VulnerabilityFilter
89
import com.saveourtool.save.frontend.components.inputform.*
910
import com.saveourtool.save.frontend.components.inputform.renderUserWithAvatar
@@ -36,6 +37,7 @@ val vulnerabilitiesFiltersRow: FC<VulnerabilitiesFiltersProps> = FC { props ->
3637
val (filter, setFilter) = useStateFromProps(props.filter)
3738

3839
val languagePlaceholder = "Language".t()
40+
val statusPlaceholder = "Status".t()
3941
div {
4042
className = ClassName("px-0 container-fluid")
4143
div {
@@ -59,12 +61,12 @@ val vulnerabilitiesFiltersRow: FC<VulnerabilitiesFiltersProps> = FC { props ->
5961
input {
6062
type = InputType.text
6163
className = ClassName("form-control")
62-
value = filter.identifierPrefix
63-
placeholder = "${"Identifier".t()}..."
64+
value = filter.freeText
65+
placeholder = "${"Identifier or Summary".t()}..."
6466
required = false
6567
onChange = { event ->
6668
setFilter { oldFilter ->
67-
oldFilter.copy(identifierPrefix = event.target.value)
69+
oldFilter.copy(freeText = event.target.value)
6870
}
6971
}
7072
}
@@ -161,17 +163,35 @@ val vulnerabilitiesFiltersRow: FC<VulnerabilitiesFiltersProps> = FC { props ->
161163
}
162164

163165
div {
164-
className = ClassName("col-4 px-1")
166+
className = ClassName("col-${if (props.isNeedToFilterStatus) "2" else "4"} px-1")
165167
selectorBuilder(
166168
filter.language?.value ?: languagePlaceholder,
167-
VulnerabilityLanguage.values().map { it.value }.plus(languagePlaceholder),
169+
listOf(languagePlaceholder).plus(VulnerabilityLanguage.values().map { it.value }),
168170
"form-control custom-select",
169171
) { event ->
170172
val newLanguage = VulnerabilityLanguage.values().find { it.value == event.target.value }
171173
setFilter { oldFilter -> oldFilter.copy(language = newLanguage) }
172174
}
173175
}
174176

177+
if (props.isNeedToFilterStatus) {
178+
div {
179+
className = ClassName("col-2 px-1")
180+
selectorBuilder(
181+
filter.chosenStatuses?.firstOrNull()?.value ?: statusPlaceholder,
182+
listOf(statusPlaceholder).plus(
183+
(filter.statuses ?: VulnerabilityStatus.values().toList())
184+
.map { it.value }
185+
.distinct()
186+
),
187+
"form-control custom-select",
188+
) { event ->
189+
val newStatuses = VulnerabilityStatus.values().filter { it.value == event.target.value }.takeIf { it.isNotEmpty() }
190+
setFilter { oldFilter -> oldFilter.copy(chosenStatuses = newStatuses) }
191+
}
192+
}
193+
}
194+
175195
div {
176196
className = ClassName("col-4 px-1")
177197
inputWithDebounceForOrganizationDto {
@@ -236,6 +256,11 @@ external interface VulnerabilitiesFiltersProps : Props {
236256
*/
237257
var filter: VulnerabilityFilter
238258

259+
/**
260+
* Flag that defines is it needed to add status filtering
261+
*/
262+
var isNeedToFilterStatus: Boolean
263+
239264
/**
240265
* [StateSetter] for [VulnerabilityFilter]
241266
*/

save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/views/OrganizationView.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ class OrganizationView : AbstractView<OrganizationProps, OrganizationViewState>(
575575

576576
nav {
577577
className = ClassName("nav nav-tabs")
578-
OrganizationMenuBar.entries
578+
OrganizationMenuBar.values()
579579
.filter {
580580
it != OrganizationMenuBar.SETTINGS || state.selfRole.isHigherOrEqualThan(Role.ADMIN)
581581
}

save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/views/vuln/VulnerabilityTableComponent.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ val vulnerabilityTableComponent: FC<VulnerabilityTableComponentProps> = FC { pro
143143
td {
144144
val severityNum = cellContext.row.original.severityNum
145145
val severityNumColor = when (severityNum) {
146-
in 0.0f..4.9f -> "text-success"
147-
in 5.0f..7.5f -> "text-warning"
148-
in 7.6f..10.0f -> "text-danger"
146+
in 0.0f..3.9f -> "text-success"
147+
in 3.9f..6.9f -> "text-warning"
148+
in 6.9f..10.0f -> "text-danger"
149149
else -> ""
150150
}
151151
className = ClassName("align-middle $severityNumColor text-center")
@@ -198,7 +198,7 @@ val vulnerabilityTableComponent: FC<VulnerabilityTableComponentProps> = FC { pro
198198
val status = cellContext.row.original.status.value
199199
val statusColor = when (status) {
200200
"Approved" -> "text-success"
201-
"Created", "Pending review" -> "text-warning"
201+
"Created", "Edited" -> "text-warning"
202202
"Rejected" -> "text-danger"
203203
else -> ""
204204
}
@@ -224,9 +224,9 @@ val vulnerabilityTableComponent: FC<VulnerabilityTableComponentProps> = FC { pro
224224
if (isVulnerabilityCollectionPage) {
225225
props.currentUserInfo?.globalRole?.let { role ->
226226
val tabList = if (role.isSuperAdmin()) {
227-
VulnerabilityListTab.entries.map { it.name }
227+
VulnerabilityListTab.values().map { it.name }
228228
} else {
229-
VulnerabilityListTab.entries.filter { it != VulnerabilityListTab.ADMIN }
229+
VulnerabilityListTab.values().filter { it != VulnerabilityListTab.ADMIN }
230230
.map { it.name }
231231
}
232232
tab(selectedMenu.name, tabList, "nav nav-tabs mt-3") { value ->
@@ -265,6 +265,7 @@ val vulnerabilityTableComponent: FC<VulnerabilityTableComponentProps> = FC { pro
265265
colSpan = tableInstance.visibleColumnsCount()
266266
vulnerabilitiesFiltersRow {
267267
filter = vulnerabilityFilter
268+
isNeedToFilterStatus = isNeedToShowStatus
268269
onChangeFilter = { filterValue ->
269270
filterValue?.let {
270271
setVulnerabilityFilter(filterValue)
@@ -366,7 +367,7 @@ private fun getFiltersByTab(
366367
) = when (selectedMenu) {
367368
VulnerabilityListTab.PUBLIC -> VulnerabilityFilter.approved
368369
VulnerabilityListTab.ADMIN -> VulnerabilityFilter.pendingApproval
369-
VulnerabilityListTab.OWNER -> VulnerabilityFilter(isOwner = true, authorName = currentUserInfo?.name) // TODO: delete isOwner?
370+
VulnerabilityListTab.OWNER -> VulnerabilityFilter(authorName = currentUserInfo?.name)
370371
}
371372

372373
/**

0 commit comments

Comments
 (0)