Skip to content

Commit ae120ef

Browse files
committed
SADP tag/button implementation
1 parent 96c8a0a commit ae120ef

5 files changed

Lines changed: 140 additions & 8 deletions

File tree

src/assets/style/bulmaCveCustomizations.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
$family-primary: '"Arial", "Source Sans Pro", "Public Sans Web", sans-serif, "Arial"',
2222
$family-secondary: '"Arial", "Source Sans Pro", "Public Sans Web", sans-serif, "Arial"',
2323
$family-code: sans-serif,
24+
$primary: $theme-color-primary-darker,
2425
$size-small: 0.77rem,
2526
);
2627

src/components/SadpTag.vue

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<!--
2+
This component puts an SADP tag where it's instantiated. If an anchor location
3+
is given, the tag serves as a button to bring the current view to the anchor
4+
point. With no given anchor, the tag serves as a label.
5+
6+
It is instantiated in a Vue template as follows:
7+
8+
<SadpTag :hasSadp="<boolean-value>" :anchor="<anchor-id>"/>
9+
10+
where <boolean-value> indicates whether the CVE Record has an SADP, and
11+
<anchor-id> is the optional anchor point for the SADP section.
12+
-->
13+
14+
<template>
15+
<button v-if="hasSadp" class="tag is-rounded is-primary is-normal"
16+
:style="{cursor: anchor ? 'pointer' : 'default'}"
17+
@click="gotoAnchor()">
18+
SADP
19+
</button>
20+
</template>
21+
22+
<script setup>
23+
24+
const props = defineProps({
25+
anchor: {
26+
type: String
27+
},
28+
hasSadp: {
29+
type: [Boolean, undefined],
30+
required: true
31+
}
32+
});
33+
34+
function gotoAnchor() {
35+
36+
if (props.anchor) {
37+
38+
// This method works better than setting window.location.hash
39+
// because it handles the case when the user has already gone
40+
// to the anchor, but then scrolls up and clicks the button
41+
// again with no effect.
42+
43+
const element = document.getElementById(props.anchor);
44+
45+
if (element) {
46+
element.scrollIntoView({behavior: 'smooth', block: 'start'});
47+
}
48+
}
49+
}
50+
51+
</script>

src/stores/cveListSearch.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,11 @@ export const useCveListSearchStore = defineStore('cveListSearch ', {
285285
this.decrement('totalExecutingRequests');
286286
}
287287
},
288+
hasSadp(cveRecord) {
289+
290+
return cveRecord.adp.some((adp) => (adp.x_adpType === 'supplier'
291+
|| adp.providerMetadata.shortName.endsWith('-SADP')));
292+
},
288293
processSearchResults(results){
289294
const parsedResults = [];
290295

@@ -295,6 +300,7 @@ export const useCveListSearchStore = defineStore('cveListSearch ', {
295300
cna: result?._source?.containers?.cna?.providerMetadata?.shortName || 'No CNA provided',
296301
cnaOrgId: result?._source?.containers?.cna?.providerMetadata?.orgId || '',
297302
descriptions: this.processDescriptionsField(result?._source),
303+
hasSadp: this.hasSadp(result._source.containers),
298304
relevancyScore: result?._score
299305
});
300306
});

src/views/CVERecord/PublishedRecord.vue

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
cve-state-tag">{{cveFieldList.state}}
1515
</span>
1616
</div>
17+
<div class="level-item">
18+
<SadpTag :hasSadp="hasSadp()" :anchor="sadpContainerAnchor()"/>
19+
</div>
1720
</div>
1821
<div class="level-right ml-1">
1922
<div class="level-item">
@@ -61,7 +64,7 @@
6164
<div class="mt-2">
6265
<div id="cve-cna-cve-program-containers">
6366
<h2 class="title">Required CVE Record Information</h2>
64-
<AdpVulnerabilityEnrichment v-if="Object.keys(cnaContainer).length > 0" role="cna" :selectedCnaData="cveFieldList"
67+
<AdpVulnerabilityEnrichment v-if="!isEmptyObject(cnaContainer)" role="cna" :selectedCnaData="cveFieldList"
6568
:containerObject="cnaContainer" :orgId="`cna-${cnaContainer.providerMetadata.orgId}`" :anchorId="cnaContainer.onPageMenu.anchorId">
6669
<h1 class="mb-1 has-text-white">
6770
<span v-if="!isLoading">{{ cnaContainer.onPageMenu.label }} </span>
@@ -74,14 +77,14 @@
7477
</h1>
7578
</AdpVulnerabilityEnrichment>
7679
<!-- <AdpVulnerabilityEnrichment v-if="!usecveRecordStore.hasHistoricalReferences" role="cna" :containerObject="cnaContainer"></AdpVulnerabilityEnrichment> -->
77-
<AdpVulnerabilityEnrichment v-if="Object.keys(cveProgramContainer).length > 0" role='cveProgram'
80+
<AdpVulnerabilityEnrichment v-if="!isEmptyObject(cveProgramContainer)" role='cveProgram'
7881
:selectedCnaData="{}" :containerObject="cveProgramContainer" :orgId="`cve-program-${cveProgramContainer.providerMetadata.orgId}`"
7982
:anchorId="cveProgramContainer.onPageMenu.anchorId"
8083
>
8184
<h1 class="mb-1 has-text-white cve-capitalize-first-letter">{{ cveProgramContainer.onPageMenu.label }}</h1>
8285
</AdpVulnerabilityEnrichment>
8386
</div>
84-
<div v-if="Object.keys(adpContainers).length > 0" id="cve-adp-containers" class="mt-6">
87+
<div v-if="!isEmptyObject(adpContainers)" id="cve-adp-containers" class="mt-6">
8588
<h2 class="title mb-0">Authorized Data Publishers</h2>
8689
<div class="cve-learn-more mb-5">
8790
<router-link to="/ProgramOrganization/ADPs" class="cve-learn-more-link">Learn more</router-link>
@@ -107,6 +110,29 @@
107110
</div> -->
108111
</div>
109112
</div>
113+
<template v-if="sadpContainer">
114+
<div class="mt-6" id="cve-sadp-container" >
115+
<h2 class="title mb-0">Supplier Authorized Data Publisher</h2>
116+
<div class="cve-learn-more mb-5">
117+
<router-link to="/ProgramOrganization/ADPs" class="cve-learn-more-link">Learn more</router-link>
118+
</div>
119+
</div>
120+
<div class="cve-sadp-container">
121+
<AdpVulnerabilityEnrichment role="adp" :selectedCnaData="{}"
122+
:containerObject="sadpContainer" :orgId="`sadp-${sadpContainer.providerMetadata.orgId}`"
123+
:anchorId="sadpContainerAnchor()">
124+
<h1 class="mb-1 has-text-white cve-capitalize-first-letter">
125+
<span v-if="!isLoading">{{ sadpContainer.onPageMenu.label }}</span>
126+
<span v-else>
127+
<span class="icon">
128+
<span class="loader is-loading"/>
129+
</span>
130+
<span class="ml-2">Loading...</span>
131+
</span>
132+
</h1>
133+
</AdpVulnerabilityEnrichment>
134+
</div>
135+
</template>
110136
</div>
111137
</div>
112138
</main>
@@ -122,16 +148,17 @@
122148
<script>
123149
import _ from 'lodash';
124150
import AdpVulnerabilityEnrichment from '@/components/AdpVulnerabilityEnrichment.vue';
125-
import CNAData from '@/assets/data/CNAsList.json';
126151
import OnPageSidebar from '@/components/OnPageSidebar.vue';
152+
import SadpTag from '@/components/SadpTag.vue';
127153
import { usecveRecordStore } from '@/stores/cveRecord.ts';
128154
import { usePartnerStore } from '@/stores/partners.js';
129155
130156
export default {
131157
name: 'PublishedRecord',
132158
components: {
133159
AdpVulnerabilityEnrichment,
134-
OnPageSidebar
160+
OnPageSidebar,
161+
SadpTag
135162
},
136163
data() {
137164
return {
@@ -163,10 +190,21 @@ export default {
163190
cnaContainer: {},
164191
cveProgramContainer: {},
165192
adpContainers: {},
193+
sadpContainer: {},
166194
partnerStore: usePartnerStore(),
167195
};
168196
},
169197
methods: {
198+
isEmptyObject(obj) {
199+
200+
return Object.keys(obj).length === 0;
201+
},
202+
hasSadp() {
203+
return !this.isEmptyObject(this.sadpContainer);
204+
},
205+
sadpContainerAnchor() {
206+
return this.hasSadp() ? this.sadpContainer.onPageMenu.anchorId : '';
207+
},
170208
toggleAccordion() {
171209
const newPanelState = !this.cveRecordStore.accordionState.collapseAll;
172210
Object.keys(this.cveRecordStore.accordionState).forEach( (panelName) => {
@@ -185,7 +223,7 @@ export default {
185223
"items": {}
186224
}
187225
188-
if (Object.keys(this.cnaContainer).length > 0) {
226+
if (!this.isEmptyObject(this.cnaContainer)) {
189227
190228
// This section determines the name displayed for the CNA.
191229
@@ -204,7 +242,7 @@ export default {
204242
this.cnaContainer.onPageMenu = onPageMenu[headingLabel].items['CNA'];
205243
}
206244
207-
if (Object.keys(this.cveProgramContainer).length > 0) {
245+
if (!this.isEmptyObject(this.cveProgramContainer)) {
208246
onPageMenu[headingLabel].items['CVE Program'] = {
209247
label: this.partnerStore.getOrganizationName(this.cveProgramContainer.providerMetadata?.shortName,
210248
this.cveProgramContainer.providerMetadata.orgId),
@@ -224,7 +262,7 @@ export default {
224262
this.adpContainers[containerName].onPageMenu = adpContainersMenu[`ADP-${this.adpContainers[containerName].providerMetadata.orgId}`];
225263
});
226264
227-
if (Object.keys(adpContainersMenu).length > 0) {
265+
if (!this.isEmptyObject(adpContainersMenu)) {
228266
onPageMenu['Authorized Data Publishers'] = {
229267
"id": "100.0.1",
230268
"anchorId": "cve-adp-containers",
@@ -234,6 +272,29 @@ export default {
234272
}
235273
}
236274
275+
if (!this.isEmptyObject(this.sadpContainer)) {
276+
277+
const orgId = this.sadpContainer.providerMetadata.orgId;
278+
const shortName = this.sadpContainer.providerMetadata.shortName;
279+
const orgName = this.partnerStore.getOrganizationName(shortName, orgId);
280+
281+
const sadpContainersMenu = {
282+
label: orgName,
283+
anchorId: `sadp-${orgId}`
284+
};
285+
286+
this.sadpContainer.onPageMenu = sadpContainersMenu;
287+
288+
onPageMenu['Supplier Authorized Data Publisher'] = {
289+
290+
"id": "100.0.2",
291+
"anchorId": "cve-sadp-container",
292+
"label": "Supplier Authorized Data Publisher",
293+
"path": `CVERecord?id=${this.cveRecordStore.cveId}`,
294+
"items": {[orgName]: sadpContainersMenu}
295+
}
296+
}
297+
237298
this.cveRecordStore.onPageMenuItems.submenu = onPageMenu;
238299
},
239300
setupContainersAccordionStateOnPageMenu() {
@@ -355,12 +416,20 @@ export default {
355416
}
356417
return false;
357418
},
419+
isSadp(adpContainer) {
420+
421+
return adpContainer.providerMetadata.shortName.endsWith('-SADP');
422+
},
358423
// getProductsStatus() is adapted from https://github.com/Vulnogram/seaview/blob/main/script.js#L140-L253
359424
getProductsStatus(containerName, containerAffectedList, adpContainer) {
360425
const affectedList = containerAffectedList;
426+
361427
if (this.hasOnlyCisaAdpContainer(containerName)) {
362428
this.adpContainers[containerName] = adpContainer;
363429
}
430+
else if (!this.isEmptyObject(adpContainer) && this.isSadp(adpContainer))
431+
this.sadpContainer = adpContainer;
432+
364433
if (this.hasData(affectedList)) {
365434
const prodStatusTemplate = {
366435
vendor: '',

src/views/CVERecord/SearchResults.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@
171171
{{ partnerStore.getOrganizationName(result.cna, result.cnaOrgId) }}
172172
</p>
173173
</div>
174+
<div class="column cve-column">
175+
<SadpTag :has-sadp="result.hasSadp"/>
176+
</div>
174177
</div>
175178
<div class="columns cve-columns">
176179
<div class="column cve-column">
@@ -289,6 +292,7 @@ import { computed, createApp, ref, watch } from 'vue';
289292
import { useHead } from '@unhead/vue';
290293
import { useRouter } from 'vue-router';
291294
import ServiceUnavailable from '@/components/ServiceUnavailable.vue'
295+
import SadpTag from '@/components/SadpTag.vue';
292296
293297
const cveListSearchStore = useCveListSearchStore();
294298
const genericGlobalsStore = useGenericGlobalsStore();
@@ -298,6 +302,7 @@ const app = createApp({});
298302
const resultUrl = ref(genericGlobalsStore.cveServicesBaseUrl);
299303
300304
app.component('ServiceUnavailable', ServiceUnavailable);
305+
app.component('SadpTag', SadpTag);
301306
302307
// Keeping track of the current settings for the results per page and
303308
// sort option prevents the watches from doing searches just because the

0 commit comments

Comments
 (0)