Skip to content

Commit f6d8e3e

Browse files
Added component search by identity
1 parent 64382c5 commit f6d8e3e

File tree

2 files changed

+194
-1
lines changed

2 files changed

+194
-1
lines changed

src/i18n/locales/en.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"components": "Components",
2626
"component_search": "Component Search",
2727
"component": "Component",
28+
"search": "Search",
2829
"dependencies": "Dependencies",
2930
"vulnerabilities": "Vulnerabilities",
3031
"licenses": "Licenses",
@@ -162,16 +163,24 @@
162163
"coordinates": "Coordinates",
163164
"package_url_full": "Package URL (PURL)",
164165
"swid_tagid": "SWID Tag ID",
166+
"cpe": "CPE",
165167
"cpe_full": "Common Platform Enumeration (CPE)",
166168
"classifier": "Classifier",
167169
"filename": "Filename",
168170
"copyright": "Copyright",
169171
"md5": "MD5",
170172
"sha1": "SHA1",
171173
"sha256": "SHA 256",
174+
"sha384": "SHA 384",
172175
"sha512": "SHA 512",
173176
"sha3_256": "SHA3 256",
177+
"sha3_384": "SHA3 384",
174178
"sha3_512": "SHA3 512",
179+
"blake2b_256": "BLAKE2b 256",
180+
"blake2b_384": "BLAKE2b 384",
181+
"blake2b_512": "BLAKE2b 512",
182+
"blake3": "BLAKE3",
183+
"hashes_short_desc": "Hash (MD5, SHA, SHA3, Blake2b, Blake3)",
175184
"component_name_desc": "The name of the component as provided by the supplier",
176185
"component_version_desc": "The version of the component as provided by the supplier",
177186
"component_group_desc": "The suppliers higher-level namespace, group, or vendor identifier",

src/views/portfolio/components/ComponentSearch.vue

Lines changed: 185 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,209 @@
11
<template>
22
<div class="animated fadeIn" v-permission="'VIEW_PORTFOLIO'">
33
<portfolio-widget-row :fetch="true" />
4+
<b-row>
5+
<b-col md="4" lg="3">
6+
<b-input-group-form-select id="input-subject" required="true"
7+
v-model="subject" :options="subjects" v-on:keyup.enter="performSearch" />
8+
</b-col>
9+
<b-col md="7" lg="6">
10+
<b-input-group-form-input v-if="subject !== 'COORDINATES'" id="input-value" required="true" type="text" v-model="value" lazy="true" />
11+
<b-input-group v-else-if="subject === 'COORDINATES'">
12+
<b-form-input id="input-value-coordinates-group" :placeholder="$t('message.group')" type="text" v-model="coordinatesGroup" v-on:keyup.enter="performSearch"></b-form-input>
13+
<b-form-input id="input-value-coordinates-name" :placeholder="$t('message.name')" type="text" v-model="coordinatesName" v-on:keyup.enter="performSearch"></b-form-input>
14+
<b-form-input id="input-value-coordinates-version" :placeholder="$t('message.version')" type="text" v-model="coordinatesVersion" v-on:keyup.enter="performSearch"></b-form-input>
15+
</b-input-group>
16+
</b-col>
17+
<b-col md="1" lg="1">
18+
<b-button variant="outline-primary" v-on:click="performSearch">{{ $t('message.search') }}</b-button>
19+
</b-col>
20+
</b-row>
21+
<bootstrap-table
22+
ref="table"
23+
:columns="columns"
24+
:data="data"
25+
:options="options">
26+
</bootstrap-table>
427
</div>
528
</template>
629

730
<script>
831
import { Switch as cSwitch } from '@coreui/vue';
932
import PortfolioWidgetRow from "../../dashboard/PortfolioWidgetRow";
1033
import permissionsMixin from "../../../mixins/permissionsMixin";
34+
import BInputGroupFormSelect from "../../../forms/BInputGroupFormSelect";
35+
import BInputGroupFormInput from "../../../forms/BInputGroupFormInput";
36+
import common from "../../../shared/common";
37+
import xssFilters from "xss-filters";
38+
import SeverityProgressBar from "@/views/components/SeverityProgressBar";
1139
1240
export default {
1341
mixins: [permissionsMixin],
1442
components: {
1543
cSwitch,
16-
PortfolioWidgetRow
44+
PortfolioWidgetRow,
45+
BInputGroupFormSelect,
46+
BInputGroupFormInput
1747
},
1848
methods: {
49+
createQueryParams: function() {
50+
if (this.subject === "COORDINATES") {
51+
let g = common.trimToNull(this.coordinatesGroup);
52+
let n = common.trimToNull(this.coordinatesName);
53+
let v = common.trimToNull(this.coordinatesVersion);
54+
return (g != null)? "group="+encodeURIComponent(g): "" + (n != null)? "name="+encodeURIComponent(n): "" + (v != null)? "version="+encodeURIComponent(v): "";
55+
} else if (this.subject === "PACKAGE_URL") {
56+
let v = common.trimToNull(this.value);
57+
return (v != null)? "purl="+encodeURIComponent(v): "";
58+
} else if (this.subject === "CPE") {
59+
let v = common.trimToNull(this.value);
60+
return (v != null)? "cpe="+encodeURIComponent(v): "";
61+
} else if (this.subject === "SWID_TAGID") {
62+
let v = common.trimToNull(this.value);
63+
return (v != null)? "swidTagId="+encodeURIComponent(v): "";
64+
}
65+
},
66+
performSearch: function() {
67+
if (this.subject === "HASH") {
68+
let hash = encodeURIComponent(common.trimToNull(this.value));
69+
this.options.url = `${this.$api.BASE_URL}/${this.$api.URL_COMPONENT}/hash/${hash}`;
70+
this.$refs.table.refresh({ silent: true });
71+
} else {
72+
let queryParams = this.createQueryParams();
73+
this.options.url = `${this.$api.BASE_URL}/${this.$api.URL_COMPONENT}/identity?${queryParams}`;
74+
this.$refs.table.refresh({ silent: true });
75+
}
76+
}
1977
},
2078
data() {
2179
return {
80+
subject: 'COORDINATES',
81+
value: null,
82+
coordinatesGroup: null,
83+
coordinatesName: null,
84+
coordinatesVersion: null,
85+
subjects: [
86+
{value: 'COORDINATES', text: this.$t('message.coordinates')},
87+
{value: 'PACKAGE_URL', text: this.$t('message.package_url')},
88+
{value: 'CPE', text: this.$t('message.cpe_full')},
89+
{value: 'SWID_TAGID', text: this.$t('message.swid_tagid')},
90+
{value: 'HASH', text: this.$t('message.hashes_short_desc')}
91+
],
92+
columns: [
93+
{
94+
title: this.$t('message.component'),
95+
field: "name",
96+
sortable: true,
97+
formatter(value, row, index) {
98+
let url = xssFilters.uriInUnQuotedAttr("../components/" + row.uuid);
99+
return `<a href="${url}">${xssFilters.inHTMLData(value)}</a>`;
100+
}
101+
},
102+
{
103+
title: this.$t('message.version'),
104+
field: "version",
105+
sortable: true,
106+
formatter(value, row, index) {
107+
return xssFilters.inHTMLData(common.valueWithDefault(value, ""));
108+
}
109+
},
110+
{
111+
title: this.$t('message.group'),
112+
field: "group",
113+
sortable: true,
114+
formatter(value, row, index) {
115+
return xssFilters.inHTMLData(common.valueWithDefault(value, ""));
116+
}
117+
},
118+
{
119+
title: this.$t('message.package_url'),
120+
field: "purl",
121+
sortable: true,
122+
formatter(value, row, index) {
123+
return xssFilters.inHTMLData(common.valueWithDefault(value, ""));
124+
}
125+
},
126+
{
127+
title: this.$t('message.cpe'),
128+
field: "cpe",
129+
sortable: true,
130+
formatter(value, row, index) {
131+
return xssFilters.inHTMLData(common.valueWithDefault(value, ""));
132+
}
133+
},
134+
{
135+
title: this.$t('message.swid_tagid'),
136+
field: "swid",
137+
sortable: true,
138+
formatter(value, row, index) {
139+
return xssFilters.inHTMLData(common.valueWithDefault(value, ""));
140+
}
141+
},
142+
{
143+
title: this.$t('message.project_name'),
144+
field: "project.name",
145+
sortable: false,
146+
formatter(value, row, index) {
147+
let url = xssFilters.uriInUnQuotedAttr("../projects/" + row.project.uuid);
148+
let name = common.concatenateComponentName(null, row.project.name, row.project.version);
149+
return `<a href="${url}">${xssFilters.inHTMLData(name)}</a>`;
150+
}
151+
},
152+
{
153+
title: this.$t('message.risk_score'),
154+
field: "lastInheritedRiskScore",
155+
sortable: true,
156+
visible: false,
157+
class: "tight",
158+
},
159+
{
160+
title: this.$t('message.vulnerabilities'),
161+
field: "metrics",
162+
sortable: false,
163+
visible: false,
164+
formatter(metrics, row, index) {
165+
if (typeof metrics === "undefined") {
166+
return "-"; // No vulnerability info available
167+
}
168+
169+
// Programmatically instantiate SeverityProgressBar Vue component
170+
let ComponentClass = Vue.extend(SeverityProgressBar);
171+
let progressBar = new ComponentClass({
172+
propsData: {
173+
vulnerabilities: metrics.vulnerabilities,
174+
critical: metrics.critical,
175+
high: metrics.high,
176+
medium: metrics.medium,
177+
low: metrics.low,
178+
unassigned: metrics.unassigned }
179+
});
180+
progressBar.$mount();
181+
return progressBar.$el.outerHTML;
182+
}
183+
}
184+
],
22185
data: [],
186+
options: {
187+
onPostBody: this.initializeTooltips,
188+
search: false,
189+
showColumns: false,
190+
showRefresh: false,
191+
pagination: true,
192+
silentSort: false,
193+
sidePagination: 'server',
194+
queryParamsType: 'pageSize',
195+
pageList: '[10, 25, 50, 100]',
196+
pageSize: 10,
197+
icons: {
198+
refresh: 'fa-refresh'
199+
},
200+
//toolbar: '#componentSearchToolbar',
201+
responseHandler: function (res, xhr) {
202+
res.total = xhr.getResponseHeader("X-Total-Count");
203+
return res;
204+
},
205+
url: `${this.$api.BASE_URL}/${this.$api.URL_COMPONENT}/identity`
206+
}
23207
};
24208
}
25209
};

0 commit comments

Comments
 (0)