Skip to content

Commit d9e2d01

Browse files
committed
CVERecord & cveRecordSearchModule: consolidate error message handling
1 parent 99d5e80 commit d9e2d01

File tree

4 files changed

+52
-63
lines changed

4 files changed

+52
-63
lines changed

src/components/ProductStatus.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export default {
121121
const isDefaultStausNa = this.productStatusList[0].defaultStatus.length === 0;
122122
let isVersionNa = false;
123123
124-
if (this.productStatusList[0].versionsColumns.twoDTable.affected.length === 1) {
124+
if (this.productStatusList[0].versionsColumns.twoDTable.affected?.length === 1) {
125125
isVersionNa = /n\/a/.test(this.productStatusList[0].versionsColumns.twoDTable.affected[0].parentVersionRange.toString().toLowerCase());
126126
}
127127

src/components/cveRecordSearchModule.vue

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@
4141
</button>
4242
</p>
4343
</div>
44-
<div class="notification is-warning is-light" role="alert" v-if="errorMessage.length > 0">
44+
<div class="notification is-warning is-light" role="alert" v-if="errorMessageStore.showErrorMessage">
4545
<div class="is-flex is-align-content-flex-start">
4646
<p id="alertIcon" class="is-hidden">alert</p>
4747
<font-awesome-icon style="flex: 0 0 40px; margin-top:3px" size="lg" icon="exclamation-triangle" role="alert"
4848
aria-labelledby="alertIcon" aria-hidden="false" />
4949
<p class="cve-help-text">
50-
{{ errorMessage }}
50+
{{ errorMessageStore.errorMessage }}
5151
</p>
5252
</div>
5353
</div>
@@ -59,7 +59,7 @@ import { useCveListSearchStore } from '@/stores/cveListSearch';
5959
import { computed, ref, watch } from 'vue';
6060
import { useRoute, useRouter } from 'vue-router';
6161
// Legacy Search Import
62-
import { usecveRecordStore } from '@/stores/cveRecord';
62+
import { usecveRecordStore, useErrorMessageStore } from '@/stores/cveRecord';
6363
import { useGenericGlobalsStore } from '@/stores/genericGlobals';
6464
6565
const cveIdRegex = /^CVE\p{Pd}(?<year>\d{4})\p{Pd}(?<id>\d{4,})$/iu;
@@ -74,6 +74,8 @@ const isProductionWebsite = import.meta.env.VITE_WEBSITE_ENVIRONMENT === 'prd';
7474
const maxCveIdSuffix = 7;
7575
7676
let cveListSearchStore = useCveListSearchStore();
77+
const errorMessageStore = useErrorMessageStore();
78+
7779
const route = useRoute();
7880
const router = useRouter();
7981
@@ -127,22 +129,11 @@ function resetStates() {
127129
cveListSearchStore.searchType = cveGenericGlobalsStore.useSearch = searchTypeBoolean.value;
128130
cveId = '';
129131
queryString.value = cveListSearchStore.query = '';
130-
showHelpMessage('');
132+
errorMessageStore.$reset();
131133
cveListSearchStore.isSearchButtonDisabled = true;
132134
resetSearch();
133135
}
134136
135-
function showHelpMessage(helpText) {
136-
137-
if (helpText.length) {
138-
errorMessage.value = helpText;
139-
cveListSearchStore.showHelpText = true;
140-
} else {
141-
errorMessage.value = '';
142-
cveListSearchStore.showHelpText = false;
143-
}
144-
}
145-
146137
function startSearch() {
147138
// We only want to flip the search item _When we actually do a search_
148139
// otherwise we should default back to what we were on a page refresh
@@ -182,7 +173,7 @@ function validateQueryString() {
182173
183174
prevSearchValue.value = searchValue;
184175
cveListSearchStore.isSearchButtonDisabled = true;
185-
showHelpMessage('');
176+
errorMessageStore.$reset();
186177
187178
if (!searchValue)
188179
return !cveListSearchStore.isSearchButtonDisabled;
@@ -205,7 +196,7 @@ function validateQueryString() {
205196
else
206197
cveId = normalizedCveId;
207198
} else {
208-
showHelpMessage(`Invalid CVE ID "${normalizedCveId}" - identifier out of range`);
199+
errorMessageStore.setErrorMessage(`Invalid CVE ID "${normalizedCveId}" - identifier out of range`);
209200
}
210201
}
211202
@@ -215,7 +206,7 @@ function validateQueryString() {
215206
// of "typical" words (alphanumeric phrases), then it's an error.
216207
217208
if (!cveIdMatch && !wordRegex.test(searchValue)) {
218-
showHelpMessage(contentMessage);
209+
errorMessageStore.setErrorMessage(contentMessage);
219210
} else if (!errorMessage.value) {
220211
221212
// The provided search string is good.
@@ -229,7 +220,7 @@ function validateQueryString() {
229220
} else if (!errorMessage.value) {
230221
231222
// Legacy Find by CVE ID but the query string is not a CVE ID.
232-
showHelpMessage('Required CVE ID format: CVE-YYYY-NNNN');
223+
errorMessageStore.cveIdFormatMessage();
233224
}
234225
235226
return !cveListSearchStore.isSearchButtonDisabled;
@@ -252,7 +243,7 @@ function onInputChange() {
252243
// input field.
253244
254245
resetSearch();
255-
showHelpMessage('');
246+
errorMessageStore.$reset();
256247
cveListSearchStore.isSearchButtonDisabled = true;
257248
258249
} else if (cveListSearchStore.isSearchButtonDisabled

src/stores/cveRecord.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const usecveRecordStore = defineStore('cveRecord', {
66
state: () => {
77
return {
88
cveId: '',
9+
cveIdRegex: /^CVE-\d{4}-\d{4,7}$/i,
910
idData: {},
1011
isArecord: false,
1112
isIdOrRecordFound: false,
@@ -62,5 +63,38 @@ export const usecveRecordStore = defineStore('cveRecord', {
6263

6364
return false;
6465
},
66+
isValidCveId(cveId: string) {
67+
return this.cveIdRegex.test(cveId);
68+
},
69+
}
70+
});
71+
72+
73+
// The "error message" store handles the error message displayed to the user
74+
// just below the search input field. A store is used because error messages
75+
// triggered by the user's CVE ID input occur in both the CVERecord module and
76+
// the cveRecordSearch module.
77+
78+
export const useErrorMessageStore = defineStore('errorMessage', {
79+
state: () => {
80+
return {
81+
errorMessage: '',
82+
showErrorMessage: false
83+
}
84+
},
85+
actions: {
86+
cveIdFormatMessage() {
87+
const formatMessage = 'Required CVE ID format: CVE-YYYY-NNNN';
88+
this.setErrorMessage(formatMessage);
89+
},
90+
setErrorMessage(message: string) {
91+
if (message.length) {
92+
this.errorMessage = message;
93+
this.showErrorMessage = true;
94+
} else {
95+
this.errorMessage = '';
96+
this.showErrorMessage = false;
97+
}
98+
}
6599
}
66100
});

src/views/CVERecord/CVERecord.vue

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
import PublishedRecord from './PublishedRecord.vue';
8080
import RejectedRecordOrId from './RejectedRecordOrId.vue';
8181
import ReservedId from './ReservedId.vue';
82-
import { usecveRecordStore } from '@/stores/cveRecord.ts';
82+
import { usecveRecordStore, useErrorMessageStore } from '@/stores/cveRecord.ts';
8383
import { useGenericGlobalsStore } from '@/stores/genericGlobals';
8484
import ServiceUnavailable from '@/components/ServiceUnavailable.vue';
8585
import axios from 'axios';
@@ -99,8 +99,8 @@ export default {
9999
resultUrl: this.GenericGlobalsStore.useSearch ? `https://${import.meta.env.VITE_CVE_SERVICES_BASE_URL}` : this.GenericGlobalsStore.cveServiceTestBaseUrl,
100100
usecveRecordStore: usecveRecordStore(),
101101
cveId: usecveRecordStore().cveId,
102+
errorMessageStore: useErrorMessageStore(),
102103
showHelpText: false,
103-
errorMessage: [],
104104
disabled: true,
105105
getIdStatusCode: undefined,
106106
legacyCveWebsiteLink: this.GenericGlobalsStore.legacyCveWebsiteLink
@@ -150,46 +150,15 @@ export default {
150150
},
151151
},
152152
methods: {
153-
removeHelpText() {
154-
if (this.cveId.length === 0) {
155-
this.errorMessage = [];
156-
}
157-
},
158153
cveIdToUpperCase(cveId) {
159154
return cveId.toUpperCase();
160155
},
161156
validateCveId() {
162-
this.errorMessage = [];
163-
164-
const [cve, year, digits] = this.cveId.split('-');
165-
const firstElContainsCve = new RegExp(/^(cve)/, 'i').test(cve);
166-
const idStartsWithCve = new RegExp(/^(cve-)/, 'i').test(this.cveId);
167-
const isElFilledIn = cve && year && digits;
168-
169-
if (!firstElContainsCve) {
170-
this.errorMessage.push('Required CVE ID format: <b>CVE-YYYY-NNNN</b>');
171-
this.errorMessage.push('<b>YYYY</b> must be a year starting from 1999');
172-
this.errorMessage.push('<b>NNNN</b> must be 4 digits or greater');
173-
} else if (!idStartsWithCve && !new RegExp(/^(cve-)\d{4}(-)/, 'i').test(this.cveId)) {
174-
if (!idStartsWithCve) this.errorMessage.push('CVE ID must start with <b>CVE-</b>');
175-
}
176-
177-
if (year && year.length > 0 && (parseInt(year, 10) < 1999 || !new RegExp(/^\d{4}$/, 'i').test(year))) {
178-
this.errorMessage.push('CVE-<b>YYYY</b>-NNNN must be a year starting from 1999');
179-
} else if (year && year.length > 0 && (!new RegExp(/^(cve-)\d{4}(-)/, 'i').test(this.cveId) && isElFilledIn === undefined)) {
180-
this.errorMessage.push('Hyphen (-) must follow the year <b>(YYYY)</b>');
181-
this.errorMessage.push('Required CVE ID format: <b>CVE-YYYY-NNNN</b>');
182-
}
183-
184-
if (digits && digits.length > 0 && !new RegExp(/^\d{4,}$/).test(digits)) {
185-
this.errorMessage.push('CVE-YYYY-<b>NNNN</b> must be 4 digits or greater');
186-
}
187-
188-
if (this.errorMessage.length === 0 && new RegExp(/^(cve)-\d{4}-\d{4,}$/, 'i').test(this.cveId)) {
189-
this.disabled = false;
190-
} else {
157+
if (!this.usecveRecordStore.isValidCveId(this.cveId)) {
191158
this.disabled = true;
192-
}
159+
this.errorMessageStore.cveIdFormatMessage();
160+
} else
161+
this.disabled = false;
193162
},
194163
resetStates() {
195164
usecveRecordStore().$reset();
@@ -318,11 +287,6 @@ export default {
318287
usecveRecordStore().isSearching = newState;
319288
this.disabled = false;
320289
},
321-
onKeyUpEnter() {
322-
this.validateCveId();
323-
324-
if (!this.disabled) this.startLookup();
325-
},
326290
isJson(value) {
327291
if (typeof value === 'object') {
328292
return true;

0 commit comments

Comments
 (0)