From 96651a9308adb32b03cc46458813dbe9288debae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 4 Jul 2024 11:50:06 +0200 Subject: [PATCH 1/7] Add function add_kv_entry This function adds a new row to the given table body. The row contains 2 columns, the first column is a bold text and the second column is a normal text. A HTML element can be put instead of a text into the second column. The purpose of the function is to facilitate displaying key-value data pairs in HTML tables. --- .../js/oval_graph_generation_script.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js index 85935627..6919ede8 100644 --- a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js +++ b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js @@ -362,6 +362,26 @@ function render_OVAL_test(node_data) { return test_node; } +function add_kv_entry(tbody, key, value) { + const row = ROW.cloneNode(); + row.setAttribute("role", "row"); + tbody.appendChild(row); + const first_col = COL.cloneNode(); + first_col.setAttribute("role", "cell"); + first_col.className = "pf-m-truncate pf-m-fit-content"; + first_col.appendChild(get_bold_text(key + ":")); + row.appendChild(first_col); + const second_col = COL.cloneNode(); + second_col.setAttribute("role", "cell"); + second_col.className = "pf-m-truncate pf-m-fit-content"; + if (typeof value === "string") { + second_col.textContent = value; + } else { + second_col.appendChild(value); + } + row.appendChild(second_col); +} + function get_icon_as_html(icon) { const html_icon = SPAN.cloneNode(); html_icon.className = "pf-c-label__icon"; From 8f62de393a2d6acfa7ae876922fb85a5c4806db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 4 Jul 2024 11:53:20 +0200 Subject: [PATCH 2/7] Add function get_header --- .../html_templates/js/oval_graph_generation_script.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js index 6919ede8..e9f8de1c 100644 --- a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js +++ b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js @@ -405,6 +405,13 @@ function get_bold_text(text) { return b; } +function get_header(title) { + const h1 = H1.cloneNode(); + h1.textContent = title; + h1.className = "pf-c-title pf-m-2xl"; + return h1; +} + function get_tooltip(text) { const div = DIV.cloneNode(); div.className = "tooltip-wrapper"; From 49c9aeb1d722731bc3731fba742d3be708589207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 4 Jul 2024 11:55:14 +0200 Subject: [PATCH 3/7] Improve layout of test details window - make the layout more compact to show all related data at a single place - For OVAL objects, create a table where each child element is represented as a table row - OVAL object child elements tag name is shown as first column in the row - OVAL object child elements text data shown as second column in the row - OVAL object child elements attributes are shown as labels - OVAL states and OVAL variables are presented in a similar way as OVAL objects as described above - make sure the test details window is standalone --- .../js/oval_graph_generation_script.js | 205 +++++++++--------- 1 file changed, 98 insertions(+), 107 deletions(-) diff --git a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js index e9f8de1c..cad8f5c6 100644 --- a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js +++ b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js @@ -609,55 +609,44 @@ function get_table_body(objects) { return tbody; } -function generate_property_elements(table_div, endpoint, data) { - for (const [key, value] of Object.entries(data)) { // eslint-disable-line array-element-newline - if(Object.values(value).every(v => typeof v === "object")) { - const h1 = H1.cloneNode(); - h1.textContent = `Element ${remove_uuid(key)} contains this elements:`; - h1.className = "pf-c-title pf-m-md"; - table_div.appendChild(h1); - - generate_property_elements(table_div, endpoint, value); - } else { - const h1 = H1.cloneNode(); - h1.textContent = `Element ${remove_uuid(key)}:`; - h1.className = "pf-c-title pf-m-md"; - table_div.appendChild(h1); - - const table = TABLE.cloneNode(); - table.className = "pf-c-table pf-m-compact pf-m-grid-md"; - table.setAttribute("role", "grid"); - table_div.appendChild(table); - - const objects = []; - objects.push(filter_object(value, endpoint)); +function generate_endpoint_element(tbody, element_id, element_dict) { + const row = ROW.cloneNode(); + row.setAttribute("role", "row"); + tbody.appendChild(row); - table.appendChild(get_table_header(objects)); - table.appendChild(get_table_body(objects)); + const first_col = COL.cloneNode(); + first_col.setAttribute("role", "cell"); + first_col.className = "pf-m-truncate pf-m-fit-content"; + row.appendChild(first_col); + first_col.appendChild(get_bold_text(element_id + ":")); + + for (const [key, value] of Object.entries(element_dict)) { + if (key.endsWith("@text")) { + const col = COL.cloneNode(); + col.setAttribute("role", "cell"); + col.className = "pf-m-truncate pf-m-fit-content"; + col.textContent = value; + row.appendChild(col); + } else { + label_key = remove_uuid(key); + first_col.appendChild(get_label("pf-m-blue", `${label_key}: ${value}`)); } } } -function get_OVAL_object_info_heading(oval_object) { - const div = DIV.cloneNode(); - const h1 = H1.cloneNode(); - h1.textContent ='OVAL Object definition: '; - h1.className = "pf-c-title pf-m-lg"; - div.appendChild(BR.cloneNode()); - div.appendChild(h1); - - div.appendChild(get_label("pf-m-blue", `OVAL Object ID: ${oval_object.object_id}\u00A0`, undefined, "", "", oval_object.comment)); - div.appendChild(get_label("pf-m-blue", `OVAL Object type: ${oval_object.object_type}\u00A0`)); - div.appendChild(get_label("pf-m-blue", `Flag: ${oval_object.flag}\u00A0`)); - - return div; +function generate_endpoint_elements(tbody, data) { + for (const [element_id, element_dict] of Object.entries(data)) { + if (Object.values(element_dict).every(v => typeof v === "object")) { + generate_endpoint_elements(tbody, element_dict); + } else { + generate_endpoint_element(tbody, element_id, element_dict); + } + } } function generate_collected_items(collected_items, div) { - const h1 = H1.cloneNode(); - h1.textContent = "Collected Items:"; - h1.className = "pf-c-title pf-m-lg"; - div.appendChild(h1); + div.appendChild(get_header("Collected Items")); + div.appendChild(BR.cloneNode()); if (collected_items == null) { const explanation = DIV.cloneNode(); @@ -723,87 +712,68 @@ function generate_collected_items(collected_items, div) { } } -function generate_OVAL_object(test_info, oval_object, div) { - if (oval_object === undefined) { - // eslint-disable-next-line no-console - console.error("Error: The test information has no OVAL Objects."); - return; - } - div.appendChild(get_OVAL_object_info_heading(oval_object)); +function generate_OVAL_endpoint(div, title, kv_entries, data, referenced_id, test_info) { + div.appendChild(get_header(title)); + div.appendChild(BR.cloneNode()); const table_div = DIV.cloneNode(); table_div.className = "pf-c-scroll-inner-wrapper oval-test-detail-table"; div.appendChild(table_div); - generate_property_elements(table_div, oval_object, oval_object.object_data); + const table = TABLE.cloneNode(); + table.className = "pf-c-table pf-m-compact pf-m-grid-md"; + table.setAttribute("role", "grid"); + table_div.appendChild(table); - if (oval_object.object_id in test_info.map_referenced_oval_endpoints) { - generate_referenced_endpoints(test_info, oval_object.object_id, table_div); + const tbody = TBODY.cloneNode(); + tbody.setAttribute("role", "rowgroup"); + table.appendChild(tbody); + + for (const [key, value] of Object.entries(kv_entries)) { + add_kv_entry(tbody, key, value); } -} -function get_OVAL_state_heading() { - const div = DIV.cloneNode(); - const h1 = H1.cloneNode(); - h1.textContent ='OVAL State definitions: '; - h1.className = "pf-c-title pf-m-lg"; - div.appendChild(h1); - return div; -} + generate_endpoint_elements(tbody, data); -function get_OVAL_state_info(oval_state, off_heading=false) { - const div = DIV.cloneNode(); - if(off_heading) { - const h1 = H1.cloneNode(); - h1.textContent ='OVAL State definition: '; - h1.className = "pf-c-title pf-m-lg"; - div.appendChild(h1); + if (referenced_id in test_info.map_referenced_oval_endpoints) { + generate_referenced_endpoints(test_info, referenced_id, table_div); } - div.appendChild(get_label("pf-m-blue", `OVAL State ID: ${oval_state.state_id}\u00A0`, undefined, "", "", oval_state.comment)); - return div; } -// eslint-disable-next-line max-params -function generate_OVAL_state(test_info, oval_state, div, off_heading=false) { - if (oval_state === null) { +function generate_OVAL_object(test_info, oval_object, div) { + if (oval_object === undefined) { + // eslint-disable-next-line no-console + console.error("Error: The test information has no OVAL Objects."); return; } - div.appendChild(get_OVAL_state_info(oval_state, off_heading)); - const table_div = DIV.cloneNode(); - table_div.className = "pf-c-scroll-inner-wrapper oval-test-detail-table"; - div.appendChild(table_div); - generate_property_elements(table_div, oval_state, oval_state.state_data); - if (oval_state.state_id in test_info.map_referenced_oval_endpoints) { - generate_referenced_endpoints(test_info, oval_state.state_id, table_div); - } + const kv_entries = { + "OVAL Object ID": oval_object.object_id, + "OVAL Object Type": oval_object.object_type, + "Flag": oval_object.flag, + }; + generate_OVAL_endpoint(div, "OVAL Object", kv_entries, oval_object.object_data, oval_object.object_id, test_info); } -function get_OVAL_variable_info_heading(oval_variable) { - const div = DIV.cloneNode(); - const h1 = H1.cloneNode(); - h1.textContent ='OVAL Variable definition: '; - h1.className = "pf-c-title pf-m-lg"; - div.appendChild(BR.cloneNode()); - div.appendChild(h1); - - div.appendChild(get_label("pf-m-blue", `OVAL Variable ID: ${oval_variable.variable_id}\u00A0`, undefined, "", "", oval_variable.comment)); - div.appendChild(get_label("pf-m-blue", `OVAL Variable type: ${oval_variable.variable_type}\u00A0`)); - return div; +function generate_OVAL_state(test_info, oval_state, div) { + if (oval_state === null) { + return; + } + const kv_entries = { + "OVAL State ID": oval_state.state_id, + "OVAL State Type": oval_state.state_type + }; + generate_OVAL_endpoint(div, "OVAL State", kv_entries, oval_state.state_data, oval_state.state_id, test_info); } function generate_OVAL_variable(test_info, oval_variable, div) { if (oval_variable === null) { return; } - div.appendChild(get_OVAL_variable_info_heading(oval_variable)); - const table_div = DIV.cloneNode(); - table_div.className = "pf-c-scroll-inner-wrapper oval-test-detail-table"; - div.appendChild(table_div); - - generate_property_elements(table_div, oval_variable, oval_variable.variable_data); - if (oval_variable.variable_id in test_info.map_referenced_oval_endpoints) { - generate_referenced_endpoints(test_info, oval_variable.variable_id, table_div); - } + const kv_entries = { + "OVAL Variable ID": oval_variable.variable_id, + "OVAL Variable Type": oval_variable.variable_type + }; + generate_OVAL_endpoint(div, "OVAL Variable", kv_entries, oval_variable.variable_data, oval_variable.variable_id, test_info); } function generate_OVAL_error_message(test_info, div) { @@ -886,7 +856,7 @@ function generate_endpoint(id, test_info, body) { } else if(id.includes(":obj:")) { generate_OVAL_object(test_info, endpoint, body); } else if(id.includes(":ste:")) { - generate_OVAL_state(test_info, endpoint, body, true); + generate_OVAL_state(test_info, endpoint, body); } else { // eslint-disable-next-line no-console console.error("Not implemented endpoint type!"); @@ -917,27 +887,48 @@ function get_spacer() { function get_OVAL_test_info(test_info) { const div = DIV.cloneNode(); div.className = "pf-c-accordion__expanded-content-body"; - div.appendChild(get_label("", `${test_info.test_id}\u00A0`, undefined, "", "", test_info.comment)); + div.appendChild(get_header("OVAL Test")); + div.appendChild(BR.cloneNode()); + + const table_div = DIV.cloneNode(); + table_div.className = "pf-c-scroll-inner-wrapper oval-test-detail-table"; + div.appendChild(table_div); + + const table = TABLE.cloneNode(); + table.className = "pf-c-table pf-m-compact pf-m-grid-md"; + table.setAttribute("role", "grid"); + table_div.appendChild(table); + + const tbody = TBODY.cloneNode(); + tbody.setAttribute("role", "rowgroup"); + table.appendChild(tbody); + + add_kv_entry(tbody, "Test ID", test_info.test_id); + add_kv_entry(tbody, "Test Type", test_info.test_type); + add_kv_entry(tbody, "Comment", test_info.comment); + if (test_info.check && test_info.check in CHECK_ATTRIBUTE_TO_TEXT) { - div.appendChild(get_label("pf-m-cyan", `Check atribute: ${test_info.check}\u00A0`, undefined, "", "", CHECK_ATTRIBUTE_TO_TEXT[test_info.check])); + add_kv_entry(tbody, "Check", get_label("pf-m-blue", test_info.check, undefined, "", "", CHECK_ATTRIBUTE_TO_TEXT[test_info.check])); } if (test_info.check_existence && test_info.check_existence in CHECK_EXISTENCE_ATTRIBUTE_TO_TEXT) { - div.appendChild(get_label("pf-m-cyan", `Check existence atribute: ${test_info.check_existence}\u00A0`, undefined, "", "", CHECK_EXISTENCE_ATTRIBUTE_TO_TEXT[test_info.check_existence])); + add_kv_entry(tbody, "Check existence", get_label("pf-m-blue", test_info.check_existence, undefined, "", "", CHECK_EXISTENCE_ATTRIBUTE_TO_TEXT[test_info.check_existence])); } + div.appendChild(get_spacer()); + + + generate_OVAL_object(test_info, test_info.oval_object, div); + if (test_info.oval_object.message !== null) { generate_OVAL_error_message(test_info, div); } - generate_OVAL_object(test_info, test_info.oval_object, div); - div.appendChild(get_spacer()); generate_collected_items(test_info.oval_object.collected_items, div); if (test_info.oval_states.length > 0) { div.appendChild(get_spacer()); - div.appendChild(get_OVAL_state_heading()); } for (const oval_state of test_info.oval_states) { From d47251dc770fe1db9be2ccf84a5c07bb13d1577b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 4 Jul 2024 14:15:11 +0200 Subject: [PATCH 4/7] Fix a missing declaration --- .../html_templates/js/oval_graph_generation_script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js index cad8f5c6..c1c24774 100644 --- a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js +++ b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js @@ -628,7 +628,7 @@ function generate_endpoint_element(tbody, element_id, element_dict) { col.textContent = value; row.appendChild(col); } else { - label_key = remove_uuid(key); + const label_key = remove_uuid(key); first_col.appendChild(get_label("pf-m-blue", `${label_key}: ${value}`)); } } From 5321e20079983fbb898bc2a354086a0b2095d9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 11 Jul 2024 15:40:55 +0200 Subject: [PATCH 5/7] Prevent showing UUID in the report The UUID is important to distinguish between two items of the same name stored in an internal directory. But it shouldn't be disaplayed to the user. --- .../html_templates/js/oval_graph_generation_script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js index c1c24774..0e023a1e 100644 --- a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js +++ b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js @@ -618,7 +618,7 @@ function generate_endpoint_element(tbody, element_id, element_dict) { first_col.setAttribute("role", "cell"); first_col.className = "pf-m-truncate pf-m-fit-content"; row.appendChild(first_col); - first_col.appendChild(get_bold_text(element_id + ":")); + first_col.appendChild(get_bold_text(remove_uuid(element_id) + ":")); for (const [key, value] of Object.entries(element_dict)) { if (key.endsWith("@text")) { From 8676dccb97391bd6e1dca2a3005040c4a650796d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 11 Jul 2024 15:45:48 +0200 Subject: [PATCH 6/7] Show direct child elements This change will cause that elements like `` and OVAL variable child elements (``, ``, ``) will be displayed in the HTML report. --- .../html_templates/js/oval_graph_generation_script.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js index 0e023a1e..8dc1e804 100644 --- a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js +++ b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js @@ -637,7 +637,16 @@ function generate_endpoint_element(tbody, element_id, element_dict) { function generate_endpoint_elements(tbody, data) { for (const [element_id, element_dict] of Object.entries(data)) { if (Object.values(element_dict).every(v => typeof v === "object")) { - generate_endpoint_elements(tbody, element_dict); + const row = ROW.cloneNode(); + row.setAttribute("role", "row"); + tbody.appendChild(row); + const col = COL.cloneNode(); + col.appendChild(get_bold_text(element_id + ":")); + row.appendChild(col); + const col2 = COL.cloneNode(); + row.appendChild(col2); + // recursion + generate_endpoint_elements(col2, element_dict); } else { generate_endpoint_element(tbody, element_id, element_dict); } From 377f0a7eed707c8805dfb3edb4b781ff7795817b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 11 Jul 2024 16:28:47 +0200 Subject: [PATCH 7/7] Show attributes of child elements of OVAL variables It's important to add attributes of these elements because they are significant for understanding the operation performed. For example, the arithmetic element has the arithmetic operation in the arithmetic_operation attribute. This commit also simplifies the generator code. --- .../js/oval_graph_generation_script.js | 31 ++++++------------- .../oval_endpoint_information_parser.py | 3 +- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js index 8dc1e804..7380b492 100644 --- a/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js +++ b/openscap_report/report_generators/html_templates/js/oval_graph_generation_script.js @@ -619,14 +619,16 @@ function generate_endpoint_element(tbody, element_id, element_dict) { first_col.className = "pf-m-truncate pf-m-fit-content"; row.appendChild(first_col); first_col.appendChild(get_bold_text(remove_uuid(element_id) + ":")); - + const second_col = COL.cloneNode(); + second_col.setAttribute("role", "cell"); + second_col.className = "pf-m-truncate pf-m-fit-content"; + row.appendChild(second_col); for (const [key, value] of Object.entries(element_dict)) { - if (key.endsWith("@text")) { - const col = COL.cloneNode(); - col.setAttribute("role", "cell"); - col.className = "pf-m-truncate pf-m-fit-content"; - col.textContent = value; - row.appendChild(col); + if (key.endsWith("@text") || key.startsWith("var_ref@") || key.startsWith("object_ref@")) { + second_col.textContent = value; + } else if (typeof value == "object") { + // recursion + generate_endpoint_element(second_col, key, value); } else { const label_key = remove_uuid(key); first_col.appendChild(get_label("pf-m-blue", `${label_key}: ${value}`)); @@ -636,20 +638,7 @@ function generate_endpoint_element(tbody, element_id, element_dict) { function generate_endpoint_elements(tbody, data) { for (const [element_id, element_dict] of Object.entries(data)) { - if (Object.values(element_dict).every(v => typeof v === "object")) { - const row = ROW.cloneNode(); - row.setAttribute("role", "row"); - tbody.appendChild(row); - const col = COL.cloneNode(); - col.appendChild(get_bold_text(element_id + ":")); - row.appendChild(col); - const col2 = COL.cloneNode(); - row.appendChild(col2); - // recursion - generate_endpoint_elements(col2, element_dict); - } else { - generate_endpoint_element(tbody, element_id, element_dict); - } + generate_endpoint_element(tbody, element_id, element_dict); } } diff --git a/openscap_report/scap_results_parser/parsers/oval_endpoint_information_parser.py b/openscap_report/scap_results_parser/parsers/oval_endpoint_information_parser.py index 21e342fb..50d84019 100644 --- a/openscap_report/scap_results_parser/parsers/oval_endpoint_information_parser.py +++ b/openscap_report/scap_results_parser/parsers/oval_endpoint_information_parser.py @@ -32,7 +32,8 @@ def _get_items(self, xml_test_property): element_dict ) if len(element): - element_dict = self._get_items(element) + # parse children like set, regex_capture, concat + element_dict.update(self._get_items(element)) items_of_test_property[id_in_items_of_test_property] = element_dict return items_of_test_property