Skip to content

Commit d70eb7d

Browse files
committed
cveRecordSearchModule: allow subsequent searches with same search string
1 parent 61131ad commit d70eb7d

2 files changed

Lines changed: 106 additions & 60 deletions

File tree

src/components/cveRecordSearchModule.vue

Lines changed: 105 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@
2626
</span>
2727
</p>
2828
<p class="control is-expanded">
29-
<input v-if="searchTypeBoolean" v-model.trim="queryString" @keyup.enter="onKeyUpEnter"
30-
@keyup="validateQueryString" type="text" class="input cve-id-input is-expanded"
29+
<input v-if="searchTypeBoolean" v-model.trim="queryString" @keyup="onKeyUp"
30+
@keyup.enter="validate" type="text" class="input cve-id-input is-expanded"
3131
placeholder="Enter keywords (e.g.: CVE ID, sql injection, etc.)" />
32-
<input v-else v-model.trim="cveId" @keyup.enter="onKeyUpEnter" @keyup="validateQueryString"
32+
<input v-else v-model.trim="cveId" @keyup="onKeyUp" @keyup.enter="validate"
3333
type="text" class="input cve-id-input" placeholder="Enter CVE ID (CVE-YYYY-NNNN)" />
3434
</p>
3535
<p class="control">
3636
<button @click="validate" class="button cve-button cve-button-accent-warm"
37-
:class="{ 'is-loading': cveListSearchStore.isSearching, 'disabled': cveListSearchStore.isSeachButtonDisabled }"
38-
:aria-disabled="cveListSearchStore.isSeachButtonDisabled">
37+
:class="{ 'is-loading': cveListSearchStore.isSearching, 'disabled': cveListSearchStore.isSearchButtonDisabled }"
38+
:aria-disabled="cveListSearchStore.isSearchButtonDisabled"
39+
:disabled="cveListSearchStore.isSearchButtonDisabled">
3940
{{ searchTypeBoolean ? 'Search' : 'Find'}}
4041
</button>
4142
</p>
@@ -61,10 +62,16 @@ import { useRoute, useRouter } from 'vue-router';
6162
import { usecveRecordStore } from '@/stores/cveRecord';
6263
import { useGenericGlobalsStore } from '@/stores/genericGlobals';
6364
65+
const cveIdRegex = /^CVE\p{Pd}(?<year>\d{4})\p{Pd}(?<id>\d{4,})$/iu;
66+
const wordRegex = /^[a-z0-9 ]+$/i;
67+
68+
const cveStartYear = 1999;
69+
6470
let cveListSearchStore = useCveListSearchStore();
6571
const route = useRoute();
6672
const router = useRouter();
6773
74+
let prevSearchValue = ref('');
6875
let queryString = ref('');
6976
let errorMessage = ref('');
7077
@@ -86,12 +93,9 @@ watch(searchType, () => {
8693
watch(
8794
() => route.query,
8895
() => {
89-
if (route.query?.query) {
90-
queryString.value = route.query.query.trim();
91-
validate();
92-
} else {
93-
queryString.value = cveListSearchStore.query = '';
94-
errorMessage.value = '';
96+
if (searchTypeBoolean.value && route.query?.query) {
97+
queryString.value = route.query.query.trim();
98+
validate();
9599
}
96100
}
97101
)
@@ -100,9 +104,19 @@ function resetStates() {
100104
cveListSearchStore.searchType = cveGenericGlobalsStore.useSearch = searchTypeBoolean.value;
101105
cveId = '';
102106
queryString.value = cveListSearchStore.query = '';
103-
errorMessage.value = '';
104-
cveListSearchStore.showHelpText = false;
105-
cveListSearchStore.isSeachButtonDisabled = true;
107+
showHelpMessage('');
108+
cveListSearchStore.isSearchButtonDisabled = true;
109+
}
110+
111+
function showHelpMessage(helpText) {
112+
113+
if (helpText.length) {
114+
errorMessage.value = helpText;
115+
cveListSearchStore.showHelpText = true;
116+
} else {
117+
errorMessage.value = '';
118+
cveListSearchStore.showHelpText = false;
119+
}
106120
}
107121
108122
function startSearch() {
@@ -116,15 +130,15 @@ function startSearch() {
116130
cveGenericGlobalsStore.setCurrentServicesUrl(cveGenericGlobalsStore.cveServiceTestBaseUrl)
117131
}
118132
if (cveGenericGlobalsStore.useSearch) {
119-
if (queryString.value !== cveListSearchStore.query) {
120133
cveListSearchStore.$reset();
121134
cveListSearchStore.query = queryString.value;
122-
router.push({
123-
name: 'SearchResults',
124-
query: { query: cveListSearchStore.query }
125-
});
126-
cveListSearchStore.search();
127-
}
135+
if (route.name != 'SearchResults' || !route.query?.query
136+
|| (route.query.query != cveListSearchStore.query)) {
137+
138+
router.push({name: 'SearchResults',
139+
query: {query: cveListSearchStore.query}});
140+
} else
141+
cveListSearchStore.search();
128142
} else {
129143
const lookupPath = `/CVERecord?id=${cveId}`;
130144
@@ -133,51 +147,83 @@ function startSearch() {
133147
}
134148
135149
function validateQueryString() {
136-
if (searchTypeBoolean.value) {
137-
const alphaNumericDashPattern = new RegExp(/^[a-zA-Z0-9 ]+$/, 'i').test(queryString.value);
138-
const cveIdPattern = new RegExp(/^CVE-\d{4}-\d{4,7}$/, 'i').test(queryString.value);
139-
140-
if (queryString.value.length > 0 && !alphaNumericDashPattern && !cveIdPattern) {
141-
cveListSearchStore.isSeachButtonDisabled = true;
142-
errorMessage.value = 'Only letters, numbers, and CVE IDs (CVE-YYYY-NNNN) are allowed.';
143-
cveListSearchStore.showHelpText = true;
144-
} else if (queryString.value.length === 0) {
145-
cveListSearchStore.isSeachButtonDisabled = true;
146-
errorMessage.value = '';
147-
cveListSearchStore.showHelpText = false;
150+
151+
const contentMessage = 'Enter words of alphanumeric characters OR a single CVD ID (CVE-YYYY-NNNN).';
152+
const isSearch = searchTypeBoolean.value;
153+
const searchValue = isSearch ? queryString.value : cveId;
154+
155+
prevSearchValue.value = searchValue;
156+
cveListSearchStore.isSearchButtonDisabled = true;
157+
showHelpMessage('');
158+
159+
if (!searchValue)
160+
return !cveListSearchStore.isSearchButtonDisabled;
161+
162+
const cveIdMatch = cveIdRegex.exec(searchValue)
163+
164+
if (cveIdMatch) {
165+
const year = parseInt(cveIdMatch.groups.year);
166+
const id = cveIdMatch.groups.id;
167+
const currentYear = new Date().getFullYear();
168+
169+
if (cveStartYear <= year && year <= currentYear) {
170+
const normalizedCveId = `CVE-${year}-${id}`;
171+
if (isSearch)
172+
queryString.value = normalizedCveId;
173+
else
174+
cveId = normalizedCveId;
148175
} else {
149-
cveListSearchStore.isSeachButtonDisabled = false;
150-
errorMessage.value = '';
151-
cveListSearchStore.showHelpText = false;
176+
showHelpMessage(`CVE ID year must be within ${cveStartYear}-${currentYear}`);
152177
}
153-
} else {
154-
//Basic Checking
155-
const cveIdPattern = new RegExp(/^CVE-\d{4}-\d{4,7}$/, 'i').test(cveId);
156-
if (cveId.length > 0 && !cveIdPattern) {
157-
cveListSearchStore.isSeachButtonDisabled = true;
158-
errorMessage.value = 'Required CVE ID format: CVE-YYYY-NNNN';
159-
cveListSearchStore.showHelpText = true;
160-
} else if (cveId.length === 0) {
161-
cveListSearchStore.isSeachButtonDisabled = true;
162-
errorMessage.value = '';
163-
cveListSearchStore.showHelpText = false;
164-
} else {
165-
cveListSearchStore.isSeachButtonDisabled = false;
166-
errorMessage.value = '';
167-
cveListSearchStore.showHelpText = false;
178+
}
179+
180+
if (isSearch) {
181+
182+
// Search: if the query string isn't a CVE ID and it doesn't just consist
183+
// of "typical" words (alphanumeric phrases), then it's an error.
184+
185+
if (!cveIdMatch && !wordRegex.test(searchValue)) {
186+
showHelpMessage(contentMessage);
187+
} else if (!errorMessage.value) {
188+
189+
// The provided search string is good.
190+
cveListSearchStore.isSearchButtonDisabled = false;
168191
}
192+
} else if (cveIdMatch) {
193+
194+
// Legacy Find by CVE ID and it's in the correct format.
195+
cveListSearchStore.isSearchButtonDisabled = false;
196+
197+
} else if (!errorMessage.value) {
198+
199+
// Legacy Find by CVE ID but the query string is not a CVE ID.
200+
showHelpMessage('Required CVE ID format: CVE-YYYY-NNNN');
169201
}
202+
203+
return !cveListSearchStore.isSearchButtonDisabled;
170204
}
171205
172-
function onKeyUpEnter() {
173-
validate()
206+
function onKeyUp() {
207+
if (cveListSearchStore.isSearchButtonDisabled) {
208+
const isSearch = searchTypeBoolean.value;
209+
const searchValue = isSearch ? queryString.value : cveId;
210+
211+
if (prevSearchValue.value !== searchValue)
212+
cveListSearchStore.isSearchButtonDisabled = false;
213+
}
174214
}
175215
176216
function validate() {
177217
178-
validateQueryString();
179-
if (!cveListSearchStore.isSeachButtonDisabled) {
180-
startSearch();
218+
if (validateQueryString()) {
219+
try {
220+
cveListSearchStore.isSearchButtonDisabled = true;
221+
startSearch();
222+
} finally {
223+
cveListSearchStore.isSearchButtonDisabled = false;
224+
}
225+
} else if (route.name != 'home' || route?.query) {
226+
router.push({name: 'home', query: {}});
181227
}
182228
}
183229
@@ -189,8 +235,8 @@ const websiteEnv = computed(() => {
189235
<style scoped lang="scss">
190236
.disabled {
191237
opacity: 0.7 !important;
192-
background-color: #3d4551;
193-
color: white;
238+
background-color: #3d4551 !important;
239+
color: white !important;
194240
border-color: #dbdbdb;
195241
box-shadow: none;
196242
}
@@ -216,4 +262,4 @@ const websiteEnv = computed(() => {
216262
cursor: pointer;
217263
text-decoration: none !important;
218264
}
219-
</style>
265+
</style>

src/stores/cveListSearch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const useCveListSearchStore = defineStore('cveListSearch ', {
99
from: 0,
1010
isArecord: undefined,
1111
isAnId: undefined,
12-
isSeachButtonDisabled: true,
12+
isSearchButtonDisabled: true,
1313
isIdOrRecordFound: true,
1414
isPublished: false,
1515
isReserved: false,

0 commit comments

Comments
 (0)