Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cf0ab57
fix: imports order and path
iTzGooDLife Nov 12, 2024
075bc01
chore: refactor fetch languages and vuln types
iTzGooDLife Nov 12, 2024
4233abf
chore: refactor fetch languages and vuln types, again
iTzGooDLife Nov 12, 2024
a057ef2
fix: update vulnerability type list at language onChange
iTzGooDLife Nov 12, 2024
b3f4ade
feat: update vuln type list at add new type
iTzGooDLife Nov 12, 2024
3cacb33
fix: add fallback to fix eslint error
iTzGooDLife Nov 12, 2024
ba17ba2
fix: update success toast message upon successful vuln type addition
iTzGooDLife Nov 12, 2024
618ccb3
chore: add TODO item
iTzGooDLife Nov 12, 2024
c2cd8ed
feature: add loading icon while fetching vulnTypes
iTzGooDLife Nov 12, 2024
3de9d8d
fix: remove useless error state - fix eslint issue
iTzGooDLife Nov 12, 2024
6f6c418
feat: refetch vulnType list after adding a new item
iTzGooDLife Nov 12, 2024
a3eb0c9
feat: execute Language loading logic only on page load
iTzGooDLife Nov 12, 2024
92815f6
fix: language selected issues
iTzGooDLife Nov 12, 2024
c5976c1
fix: issue at change language after add new vuln type
iTzGooDLife Nov 12, 2024
d223ced
fix: issue with language dropdown after add new vuln type
iTzGooDLife Nov 13, 2024
d8f60be
feat: back DraggableList to the initial state on click cancel
iTzGooDLife Nov 13, 2024
8cc9a0b
feat: update draggable list items after on save click
iTzGooDLife Nov 13, 2024
f31f245
fix: TODO resolved: issue with the last item added being removed
iTzGooDLife Nov 13, 2024
35466dd
feat: add i18n translations for vulnerability types
iTzGooDLife Nov 13, 2024
d8aae7f
feat: add i18n translations to fields
iTzGooDLife Nov 13, 2024
80a8c05
fix: eslint alert
iTzGooDLife Nov 13, 2024
f08d76d
feat: add i18n translations for vulnerability types errors
iTzGooDLife Nov 13, 2024
29b907c
fix: remove import introduced in last commit
iTzGooDLife Nov 13, 2024
fad3e24
feat: add vuln type already exists error
iTzGooDLife Nov 13, 2024
ff63eee
feat: add toast message if vuln Type already exists
iTzGooDLife Nov 13, 2024
de0fb37
fix: remove useless dependency in useEffect
iTzGooDLife Nov 13, 2024
0105d22
chore: remove TODO already implemented
iTzGooDLife Nov 13, 2024
50f3c74
fix: replace inline prop types with VulnerabilityType
iTzGooDLife Nov 13, 2024
c2b1d4e
fix: revert relative path to absolute path
iTzGooDLife Nov 13, 2024
de10ecc
fix: remove dupe console error
iTzGooDLife Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions frontend/src/i18n/en-US/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ export default {
passwordsDoNotMatch: 'Passwords do not match',
auditCustomSectionsSaveSuccess:
'The custom sections have been saved successfully',
vulnerabilityTypeCreatedOk: 'Vulnerability Type Created Successfully!',
vulnerabilityTypesUpdatedOk: 'Vulnerability Types Updated Successfully!',
},
err: {
notDefinedLanguage: 'Not defined for this language',
Expand Down Expand Up @@ -202,6 +204,9 @@ export default {
passwordsDontMatch: 'Passwords do not match',
errorGeneratingPdf: 'Failed exporting audit to pdf',
errorSavingAuditCustomSections: 'Failed saving the audit custom sections',
failedCreatingVulnerabilityType: 'Failed to create vulnerability type',
failedUpdatingVulnerabilityTypes: 'Failed to update vulnerability types',
vulnerabilityTypeAlreadyExists: 'Vulnerability Type already exists',
},
cvss: {
title: 'CVSS v3.1 Base Score',
Expand Down Expand Up @@ -733,4 +738,5 @@ export default {
recommendCVSS: 'Recommend CVSS',
cleanRecommendations: 'Clean Recommendations',
errorRecommendingCVSS: 'Error generating recommendation',
vulnerabilityType: 'Vulnerability Type',
};
12 changes: 5 additions & 7 deletions frontend/src/routes/data/CustomData/VulnerabilityTypeList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Bars2Icon } from '@heroicons/react/24/outline';
import { t } from 'i18next';
import React, { useEffect, useState } from 'react';

import PrimaryButton from '../../../components/button/PrimaryButton';
Expand Down Expand Up @@ -58,7 +59,7 @@ const VulnerabilityTypeList: React.FC<VulnerabilityTypeListProps> = ({
id="name"
name="name"
onChange={e => handleInputChange(row.id, e)}
placeholder="Vulnerability Type"
placeholder={t('vulnerabilityType')}
type="text"
value={row.name}
/>
Expand All @@ -78,13 +79,10 @@ const VulnerabilityTypeList: React.FC<VulnerabilityTypeListProps> = ({
}, [data]);

useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const updatedData = rows.map(({ id, ...rest }) => rest);
const dataHasChanged = JSON.stringify(data) !== JSON.stringify(updatedData);

if (dataHasChanged) {
onUpdateList(updatedData);
}
}, [onUpdateList, rows, data]);
onUpdateList(updatedData);
}, [onUpdateList, rows]);

return (
<div>
Expand Down
175 changes: 125 additions & 50 deletions frontend/src/routes/data/CustomData/VulnerabilityTypes.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ArrowPathIcon } from '@heroicons/react/24/outline';
import { t } from 'i18next';
import { useCallback, useEffect, useState } from 'react';
import { toast } from 'sonner';
Expand All @@ -22,105 +23,175 @@ type ListItem = {
label?: string;
};

type VulnerabilityType = {
name: string;
locale: string;
};
Comment thread
iTzGooDLife marked this conversation as resolved.

export const VulnerabilityTypes = () => {
const [languages, setLanguages] = useState<ListItem[]>([]);
const [currentLanguage, setCurrentLanguage] = useState<ListItem | null>(null);
const [loadingLanguages, setLoadingLanguages] = useState<boolean>(true);

const [vulnerabilityTypes, setVulnerabilityTypes] = useState<
{ name: string; locale: string }[]
VulnerabilityType[]
>([]);

const [filteredVulnerabilityTypes, setFilteredVulnerabilityTypes] = useState<
{ name: string; locale: string }[]
VulnerabilityType[]
>([]);

const [newVulnerabilityType, setNewVulnerabilityType] = useState('');

const [isEditing, setIsEditing] = useState<boolean>(false);
const [loadingVulnerabilityTypes, setLoadingVulnerabilityTypes] =
useState<boolean>(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const fetchData = async () => {
setLoadingLanguages(true);
setLoadingVulnerabilityTypes(true);
const fetchLanguages = useCallback(async () => {
setLoadingLanguages(true);

try {
const dataLanguage = await getLanguages();
const languagesConverted = dataLanguage.datas.map(
(item: Language, index: number) => ({
id: index,
value: item.locale,
label: item.language,
}),
);
setLanguages(languagesConverted);

if (languagesConverted.length > 0) {
setCurrentLanguage(languagesConverted[0]);
setLoadingLanguages(false);
return languagesConverted[0];
}
} catch (err) {
console.error(err);
}
Comment thread
iTzGooDLife marked this conversation as resolved.
setLoadingLanguages(false);
return null;
}, []);

const fetchVulnerabilityTypes = useCallback(
async (languageSelected: ListItem | null) => {
try {
const dataLanguage = await getLanguages();
const languages = dataLanguage.datas.map(
(item: Language, index: number) => ({
id: index,
value: item.locale,
label: item.language,
const dataVulnerabilityType = await getVulnerabilityTypes();
const types = dataVulnerabilityType.datas.map(
(item: VulnerabilityType) => ({
name: item.name,
locale: item.locale,
}),
);
setLanguages(languages);

if (!currentLanguage && languages.length > 0) {
setCurrentLanguage(languages[0]);
}

const dataVulnerabilityType = await getVulnerabilityTypes();
setVulnerabilityTypes(dataVulnerabilityType.datas);

setVulnerabilityTypes(types);
setFilteredVulnerabilityTypes(
dataVulnerabilityType.datas.filter(
types.filter(
(type: { locale: string }) =>
type.locale === currentLanguage?.value,
type.locale === languageSelected?.value,
),
);
} catch (err) {
setError('Error fetching data');
} finally {
setLoadingLanguages(false);
setLoadingVulnerabilityTypes(false);
console.error(err);
}
Comment thread
iTzGooDLife marked this conversation as resolved.
};
void fetchData();
}, [currentLanguage]);
},
[],
);
Comment thread
iTzGooDLife marked this conversation as resolved.

const fetchVulnTypesAndLanguages = useCallback(async () => {
try {
const languageSelected = await fetchLanguages();
setLoadingVulnerabilityTypes(true);
await fetchVulnerabilityTypes(languageSelected);
setLoadingVulnerabilityTypes(false);
} catch (error) {
console.error(error);
}
Comment thread
iTzGooDLife marked this conversation as resolved.
}, [fetchLanguages, fetchVulnerabilityTypes]);

useEffect(() => {
void fetchVulnTypesAndLanguages();
}, [fetchVulnTypesAndLanguages]);

const handleAddVulnerabilityType = async () => {
if (!newVulnerabilityType.trim()) {
setError(`${t('err.createEmptyField')}: ${t('name')}`);
toast.error(`${t('err.createEmptyField')}: ${t('name')}`);
return;
}

try {
await createVulnerabilityType({
locale: currentLanguage.value,
const addVulnType = await createVulnerabilityType({
locale: currentLanguage?.value ?? '',
name: newVulnerabilityType,
});
if (addVulnType.status === 'success') {
await fetchVulnerabilityTypes(currentLanguage);

toast.success(t('msg.vulnerabilityTypeCreatedOk'));
setNewVulnerabilityType('');
}
} catch (error) {
setError('Error creating vulnerability type');
toast.error('Error creating vulnerability type');
if (
error instanceof Error &&
error.message === 'Vulnerability Type already exists'
) {
toast.error(t('err.vulnerabilityTypeAlreadyExists'));
} else {
toast.error(t('err.failedCreatingVulnerabilityType'));
}
console.error('Error:', error);
return;
}
toast.success('Vulnerability Type Created Successfully!');
setNewVulnerabilityType('');
};

const onChangeLanguage = (value: ListItem) => {
setFilteredVulnerabilityTypes(
vulnerabilityTypes.filter(type => type.locale === value.value),
);
setCurrentLanguage(value);
};

const [newVulnerabilityTypeList, setNewVulnerabilityTypeList] = useState<
VulnerabilityType[]
>([]);

const handleUpdateVulnerabilityType = useCallback(
(data: VulnerabilityType[]) => {
setNewVulnerabilityTypeList(data);
},
[setNewVulnerabilityTypeList],
);

const onClickSave = async () => {
try {
await updateVulnerabilityTypes(vulnerabilityTypes);
toast.success('Vulnerability Type Updated Successfully!');
const differentLanguageTypes = vulnerabilityTypes.filter(
type => type.locale !== currentLanguage?.value,
);

const vulnTypeMerged = [
...newVulnerabilityTypeList,
...differentLanguageTypes,
];

await updateVulnerabilityTypes(vulnTypeMerged);
await fetchVulnerabilityTypes(currentLanguage);
toast.success(t('msg.vulnerabilityTypesUpdatedOk'));
setIsEditing(false);
} catch (error) {
setError('Error updating vulnerability types');
toast.error('Error updating vulnerability types');
console.error(error);

toast.error(t('err.failedUpdatingVulnerabilityTypes'));
setIsEditing(false);
return;
}
};

const handleUpdateVulnerabilityType = useCallback(
(data: { name: string; locale: string }[]) => {
setVulnerabilityTypes(data);
},
[setVulnerabilityTypes],
);
const onClickCancel = () => {
setFilteredVulnerabilityTypes(
vulnerabilityTypes.filter(type => type.locale === currentLanguage?.value),
);
setIsEditing(false);
};

return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
Expand All @@ -129,7 +200,7 @@ export const VulnerabilityTypes = () => {
{!loadingLanguages ? (
<SelectDropdown
items={languages}
onChange={setCurrentLanguage}
onChange={value => onChangeLanguage(value)}
selected={currentLanguage}
title={t('language')}
/>
Expand All @@ -153,13 +224,17 @@ export const VulnerabilityTypes = () => {
<EditCard
editTitle={t('editVulnerabilityTypes')}
isEditing={isEditing}
onClickCancel={() => setIsEditing(false)}
onClickCancel={onClickCancel}
onClickEdit={() => setIsEditing(true)}
onClickSave={onClickSave}
title={t('editVulnerabilityTypes')}
>
{loadingVulnerabilityTypes ? (
<p>{t('loading')}</p>
<div className="flex items-center justify-center h-min">
<span className="flex">
<ArrowPathIcon className="h-8 w-8 animate-spin text-blue-500" />
</span>
</div>
) : (
<VulnerabilityTypeList
data={filteredVulnerabilityTypes}
Expand Down
12 changes: 9 additions & 3 deletions frontend/src/services/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,9 +716,15 @@ export const createVulnerabilityType = async (
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(vulnerabilityType),
}); // Incluir token
});
if (!response.ok) {
throw new Error(networkErrorMsg);
const errorText = await response.text();
const errorData = JSON.parse(errorText).datas;
if (errorData === 'Vulnerability Type already exists') {
throw new Error(errorData);
} else {
throw new Error(networkErrorMsg);
}
Comment thread
iTzGooDLife marked this conversation as resolved.
}
return await response.json();
} catch (error) {
Expand All @@ -740,7 +746,7 @@ export const updateVulnerabilityTypes = async (
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(vulnerabilityType),
}); // Incluir token
});
if (!response.ok) {
throw new Error(networkErrorMsg);
}
Expand Down