Skip to content

Commit

Permalink
feat: Add VEX justification
Browse files Browse the repository at this point in the history
  • Loading branch information
dervoeti committed Feb 28, 2024
1 parent 73b7f4a commit ef520f4
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 105 deletions.
1 change: 1 addition & 0 deletions backend/application/core/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ class Meta:
"recommendation",
"parser_severity",
"parser_status",
"parser_vex_justification",
"origin_component_name_version",
"origin_component_name",
"origin_component_version",
Expand Down
14 changes: 14 additions & 0 deletions backend/application/core/services/observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def normalize_observation_fields(observation) -> None:

normalize_severity(observation)
normalize_status(observation)
normalize_vex_justification(observation)

normalize_description(observation)

Expand Down Expand Up @@ -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():
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/core/observations/ObservationAssessment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,28 @@ 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";

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,
};

Expand Down Expand Up @@ -104,7 +112,7 @@ const ObservationAssessment = () => {
validate={validate_required}
onChange={(e) => setStatus(e)}
/>
{feature_vex_enabled() && status === "Not affected" && (
{justificationEnabled && (
<AutocompleteInputMedium
source="current_vex_justification"
label="Current VEX justification"
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/core/observations/ObservationBulkAssessment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -22,6 +25,11 @@ type ObservationBulkAssessmentButtonProps = {
const ObservationBulkAssessment = (props: ObservationBulkAssessmentButtonProps) => {
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();
Expand All @@ -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,
};

Expand Down Expand Up @@ -121,7 +129,7 @@ const ObservationBulkAssessment = (props: ObservationBulkAssessmentButtonProps)
choices={OBSERVATION_STATUS_CHOICES}
onChange={(e) => setStatus(e)}
/>
{feature_vex_enabled() && status === "Not affected" && (
{justificationEnabled && (
<AutocompleteInputMedium
source="current_vex_justification"
label="Current VEX justification"
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/core/observations/ObservationCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ 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";
Expand All @@ -37,6 +40,12 @@ export type ObservationCreateProps = {

const ObservationCreate = ({ id }: ObservationCreateProps) => {
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();
Expand Down Expand Up @@ -73,6 +82,9 @@ const ObservationCreate = ({ id }: ObservationCreateProps) => {

const create_observation = (data: any) => {
data.product = id;
if (!justificationEnabled) {
data.parser_vex_justification = "";
}

create(
"observations",
Expand Down Expand Up @@ -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 && (
<AutocompleteInputMedium
source="parser_vex_justification"
label="VEX Justification"
Expand Down
18 changes: 17 additions & 1 deletion frontend/src/core/observations/ObservationEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Divider, Stack, Typography } from "@mui/material";
import { useState } from "react";
import {
DeleteButton,
Edit,
Expand All @@ -25,6 +26,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,

Check failure on line 32 in frontend/src/core/observations/ObservationEdit.tsx

View workflow job for this annotation

GitHub Actions / check_frontend

'OBSERVATION_STATUS_OPEN' is defined but never used
OBSERVATION_VEX_JUSTIFICATION_CHOICES,
} from "../../core/types";

Expand All @@ -42,6 +47,13 @@ const CustomToolbar = () => {
};

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 = "";
Expand Down Expand Up @@ -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;
Expand All @@ -110,8 +125,9 @@ const ObservationEdit = () => {
label="Status"
choices={OBSERVATION_STATUS_CHOICES}
validate={validate_required}
onChange={(e) => setStatus(e)}
/>
{feature_vex_enabled() && (
{justificationEnabled && (
<AutocompleteInputMedium
source="parser_vex_justification"
label="VEX justification"
Expand Down
106 changes: 61 additions & 45 deletions frontend/src/rules/general_rules/GeneralRuleCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Divider, Stack, Typography } from "@mui/material";
import { RichTextInput } from "ra-input-rich-text";
import { useState } from "react";
import { BooleanInput, Create, ReferenceInput, SimpleForm } from "react-admin";

import { validate_255, validate_513, validate_2048, validate_required_255 } from "../../commons/custom_validators";
Expand All @@ -8,54 +9,65 @@ 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";

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 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 (
<Create redirect="show" transform={transform}>
<SimpleForm warnWhenUnsavedChanges validate={validateRuleForm}>
Expand All @@ -66,8 +78,12 @@ const GeneralRuleCreate = () => {
<TextInputWide autoFocus source="name" validate={validate_required_255} />
<RichTextInput source="description" validate={validate_2048} />
<AutocompleteInputMedium source="new_severity" choices={OBSERVATION_SEVERITY_CHOICES} />
<AutocompleteInputMedium source="new_status" choices={OBSERVATION_STATUS_CHOICES} />
{feature_vex_enabled() && (
<AutocompleteInputMedium
source="new_status"
choices={OBSERVATION_STATUS_CHOICES}
onChange={(e) => setStatus(e)}
/>
{justificationEnabled && (
<AutocompleteInputMedium
label="New VEX justification"
source="new_vex_justification"
Expand Down
Loading

0 comments on commit ef520f4

Please sign in to comment.