diff --git a/backend/application/core/api/serializers.py b/backend/application/core/api/serializers.py index e17026157..a5553f718 100644 --- a/backend/application/core/api/serializers.py +++ b/backend/application/core/api/serializers.py @@ -730,6 +730,7 @@ class Meta: "recommendation", "parser_severity", "parser_status", + "parser_vex_justification", "origin_component_name_version", "origin_component_name", "origin_component_version", diff --git a/backend/application/core/services/observation.py b/backend/application/core/services/observation.py index 0f49fd9b6..d54eaf41f 100644 --- a/backend/application/core/services/observation.py +++ b/backend/application/core/services/observation.py @@ -123,6 +123,7 @@ def normalize_observation_fields(observation) -> None: normalize_severity(observation) normalize_status(observation) + normalize_vex_justification(observation) normalize_description(observation) @@ -356,6 +357,19 @@ def normalize_status(observation): observation.current_status = get_current_status(observation) +def normalize_vex_justification(observation): + if observation.current_vex_justification is None: + observation.current_vex_justification = "" + if observation.assessment_vex_justification is None: + observation.assessment_vex_justification = "" + if observation.rule_vex_justification is None: + observation.rule_vex_justification = "" + if observation.parser_vex_justification is None: + observation.parser_vex_justification = "" + + observation.current_vex_justification = get_current_vex_justification(observation) + + def clip_fields(model: str, my_object) -> None: Model = apps.get_model("core", model) for field in Model._meta.get_fields(): diff --git a/frontend/src/core/observations/ObservationAssessment.tsx b/frontend/src/core/observations/ObservationAssessment.tsx index 340f5f918..0264eaf6f 100644 --- a/frontend/src/core/observations/ObservationAssessment.tsx +++ b/frontend/src/core/observations/ObservationAssessment.tsx @@ -11,6 +11,9 @@ import { httpClient } from "../../commons/ra-data-django-rest-framework"; import { OBSERVATION_SEVERITY_CHOICES, OBSERVATION_STATUS_CHOICES, + OBSERVATION_STATUS_FALSE_POSITIVE, + OBSERVATION_STATUS_NOT_AFFECTED, + OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_OPEN, OBSERVATION_VEX_JUSTIFICATION_CHOICES, } from "../types"; @@ -18,13 +21,18 @@ import { const ObservationAssessment = () => { const [open, setOpen] = useState(false); const [status, setStatus] = useState(OBSERVATION_STATUS_OPEN); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; const refresh = useRefresh(); const notify = useNotify(); const observationUpdate = async (data: any) => { const patch = { severity: data.current_severity, status: data.current_status, - vex_justification: feature_vex_enabled() ? data.current_vex_justification : "", + vex_justification: justificationEnabled ? data.current_vex_justification : "", comment: data.comment, }; @@ -104,7 +112,7 @@ const ObservationAssessment = () => { validate={validate_required} onChange={(e) => setStatus(e)} /> - {feature_vex_enabled() && status === "Not affected" && ( + {justificationEnabled && ( { const [open, setOpen] = useState(false); const [status, setStatus] = useState(OBSERVATION_STATUS_OPEN); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; const refresh = useRefresh(); const [loading, setLoading] = useState(false); const notify = useNotify(); @@ -36,7 +44,7 @@ const ObservationBulkAssessment = (props: ObservationBulkAssessmentButtonProps) severity: data.current_severity, status: data.current_status, comment: data.comment, - vex_justification: feature_vex_enabled() ? data.current_vex_justification : "", + vex_justification: justificationEnabled ? data.current_vex_justification : "", observations: selectedIds, }; @@ -121,7 +129,7 @@ const ObservationBulkAssessment = (props: ObservationBulkAssessmentButtonProps) choices={OBSERVATION_STATUS_CHOICES} onChange={(e) => setStatus(e)} /> - {feature_vex_enabled() && status === "Not affected" && ( + {justificationEnabled && ( { const [open, setOpen] = useState(false); + const [status, setStatus] = useState(OBSERVATION_STATUS_OPEN); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; const refresh = useRefresh(); const notify = useNotify(); const [create] = useCreate(); @@ -73,6 +82,9 @@ const ObservationCreate = ({ id }: ObservationCreateProps) => { const create_observation = (data: any) => { data.product = id; + if (!justificationEnabled) { + data.parser_vex_justification = ""; + } create( "observations", @@ -120,9 +132,10 @@ const ObservationCreate = ({ id }: ObservationCreateProps) => { label="Status" choices={OBSERVATION_STATUS_CHOICES} defaultValue={OBSERVATION_STATUS_OPEN} + onChange={(e) => setStatus(e)} validate={validate_required} /> - {feature_vex_enabled() && ( + {justificationEnabled && ( { }; const ObservationEdit = () => { + const observation = useRecordContext(); + const [status, setStatus] = useState(observation.parser_status); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; const transform = (data: any) => { if (!data.description) { data.description = ""; @@ -85,6 +97,9 @@ const ObservationEdit = () => { if (!data.origin_cloud_provider) { data.origin_cloud_provider = ""; } + if (!justificationEnabled || !data.parser_vex_justification) { + data.parser_vex_justification = ""; + } data.origin_component_name_version = ""; data.origin_docker_image_name_tag = ""; return data; @@ -110,8 +125,9 @@ const ObservationEdit = () => { label="Status" choices={OBSERVATION_STATUS_CHOICES} validate={validate_required} + onChange={(e) => setStatus(e)} /> - {feature_vex_enabled() && ( + {justificationEnabled && ( { - if (data.description == null) { - data.description = ""; - } - if (data.scanner_prefix == null) { - data.scanner_prefix = ""; - } - if (data.title == null) { - data.title = ""; - } - if (data.description_observation == null) { - data.description_observation = ""; - } - if (data.origin_component_name_version == null) { - data.origin_component_name_version = ""; - } - if (data.origin_docker_image_name_tag == null) { - data.origin_docker_image_name_tag = ""; - } - if (data.origin_endpoint_url == null) { - data.origin_endpoint_url = ""; - } - if (data.origin_service_name == null) { - data.origin_service_name = ""; - } - if (data.origin_source_file == null) { - data.origin_source_file = ""; - } - if (data.origin_cloud_qualified_resource == null) { - data.origin_cloud_qualified_resource = ""; - } - if (data.new_severity == null) { - data.new_severity = ""; - } - if (data.new_status == null) { - data.new_status = ""; - } - if (data.new_vex_justification == null) { - data.new_vex_justification = ""; - } - return data; -}; - const GeneralRuleCreate = () => { + const [status, setStatus] = useState(OBSERVATION_STATUS_OPEN); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; + + const transform = (data: any) => { + if (data.description == null) { + data.description = ""; + } + if (data.scanner_prefix == null) { + data.scanner_prefix = ""; + } + if (data.title == null) { + data.title = ""; + } + if (data.description_observation == null) { + data.description_observation = ""; + } + if (data.origin_component_name_version == null) { + data.origin_component_name_version = ""; + } + if (data.origin_docker_image_name_tag == null) { + data.origin_docker_image_name_tag = ""; + } + if (data.origin_endpoint_url == null) { + data.origin_endpoint_url = ""; + } + if (data.origin_service_name == null) { + data.origin_service_name = ""; + } + if (data.origin_source_file == null) { + data.origin_source_file = ""; + } + if (data.origin_cloud_qualified_resource == null) { + data.origin_cloud_qualified_resource = ""; + } + if (data.new_severity == null) { + data.new_severity = ""; + } + if (data.new_status == null) { + data.new_status = ""; + } + if (!justificationEnabled || data.new_vex_justification == null) { + data.new_vex_justification = ""; + } + return data; + }; + return ( @@ -66,8 +78,12 @@ const GeneralRuleCreate = () => { - - {feature_vex_enabled() && ( + setStatus(e)} + /> + {justificationEnabled && ( { ); }; +const GeneralRuleEdit = () => ( + + + +); +const MyForm = () => { + const generalRule = useRecordContext(); + const [status, setStatus] = useState(generalRule.new_status); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; -const transform = (data: any) => { - if (data.description == null) { - data.description = ""; - } - if (data.scanner_prefix == null) { - data.scanner_prefix = ""; - } - if (data.title == null) { - data.title = ""; - } - if (data.description_observation == null) { - data.description_observation = ""; - } - if (data.origin_component_name_version == null) { - data.origin_component_name_version = ""; - } - if (data.origin_docker_image_name_tag == null) { - data.origin_docker_image_name_tag = ""; - } - if (data.origin_endpoint_url == null) { - data.origin_endpoint_url = ""; - } - if (data.origin_service_name == null) { - data.origin_service_name = ""; - } - if (data.origin_source_file == null) { - data.origin_source_file = ""; - } - if (data.origin_cloud_qualified_resource == null) { - data.origin_cloud_qualified_resource = ""; - } - if (data.new_severity == null) { - data.new_severity = ""; - } - if (data.new_status == null) { - data.new_status = ""; - } - if (data.new_vex_justification == null) { - data.new_vex_justification = ""; - } - return data; -}; + const transform = (data: any) => { + if (data.description == null) { + data.description = ""; + } + if (data.scanner_prefix == null) { + data.scanner_prefix = ""; + } + if (data.title == null) { + data.title = ""; + } + if (data.description_observation == null) { + data.description_observation = ""; + } + if (data.origin_component_name_version == null) { + data.origin_component_name_version = ""; + } + if (data.origin_docker_image_name_tag == null) { + data.origin_docker_image_name_tag = ""; + } + if (data.origin_endpoint_url == null) { + data.origin_endpoint_url = ""; + } + if (data.origin_service_name == null) { + data.origin_service_name = ""; + } + if (data.origin_source_file == null) { + data.origin_source_file = ""; + } + if (data.origin_cloud_qualified_resource == null) { + data.origin_cloud_qualified_resource = ""; + } + if (data.new_severity == null) { + data.new_severity = ""; + } + if (data.new_status == null) { + data.new_status = ""; + } + if (!justificationEnabled || !data.new_vex_justification) { + data.new_vex_justification = ""; + } + return data; + }; -const GeneralRuleEdit = () => { return ( } validate={validateRuleForm}> @@ -75,8 +105,12 @@ const GeneralRuleEdit = () => { - - {feature_vex_enabled() && ( + setStatus(e)} + /> + {justificationEnabled && ( { - {feature_vex_enabled() && } diff --git a/frontend/src/rules/product_rules/ProductRuleCreate.tsx b/frontend/src/rules/product_rules/ProductRuleCreate.tsx index 81180714c..81de9f0b8 100644 --- a/frontend/src/rules/product_rules/ProductRuleCreate.tsx +++ b/frontend/src/rules/product_rules/ProductRuleCreate.tsx @@ -20,6 +20,10 @@ import { AutocompleteInputMedium, AutocompleteInputWide, TextInputWide } from ". import { OBSERVATION_SEVERITY_CHOICES, OBSERVATION_STATUS_CHOICES, + OBSERVATION_STATUS_FALSE_POSITIVE, + OBSERVATION_STATUS_NOT_AFFECTED, + OBSERVATION_STATUS_NOT_SECURITY, + OBSERVATION_STATUS_OPEN, OBSERVATION_VEX_JUSTIFICATION_CHOICES, } from "../../core/types"; import { validateRuleForm } from "../functions"; @@ -33,6 +37,13 @@ const ProductRuleCreate = ({ id }: ProductRuleCreateProps) => { const refresh = useRefresh(); const notify = useNotify(); const [create] = useCreate(); + const [status, setStatus] = useState(OBSERVATION_STATUS_OPEN); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; + const handleOpen = () => setOpen(true); const handleCancel = () => setOpen(false); const handleClose = (event: object, reason: string) => { @@ -103,7 +114,7 @@ const ProductRuleCreate = ({ id }: ProductRuleCreateProps) => { if (data.new_status == null) { data.new_status = ""; } - if (data.new_vex_justification == null) { + if (!justificationEnabled || !data.new_vex_justification) { data.new_vex_justification = ""; } @@ -146,8 +157,12 @@ const ProductRuleCreate = ({ id }: ProductRuleCreateProps) => { - - {feature_vex_enabled() && ( + setStatus(e)} + /> + {justificationEnabled && ( { const [update] = useUpdate(); const refresh = useRefresh(); const notify = useNotify(); + const productRule = useRecordContext(); + const [status, setStatus] = useState(productRule.new_status); + const justificationEnabled = + feature_vex_enabled() && + [OBSERVATION_STATUS_NOT_AFFECTED, OBSERVATION_STATUS_NOT_SECURITY, OBSERVATION_STATUS_FALSE_POSITIVE].indexOf( + status + ) >= 0; + const handleOpen = () => setOpen(true); const handleCancel = () => setOpen(false); const handleClose = (event: object, reason: string) => { @@ -72,7 +85,7 @@ const ProductRuleEdit = () => { if (data.new_status == null) { data.new_status = ""; } - if (data.new_vex_justification == null) { + if (!justificationEnabled || data.new_vex_justification == null) { data.new_vex_justification = ""; } @@ -159,8 +172,12 @@ const ProductRuleEdit = () => { - - {feature_vex_enabled() && ( + setStatus(e)} + /> + {justificationEnabled && ( { - {feature_vex_enabled() && }