Add support for Blueprint remediations
Resolves: rhbz#2020052
This commit is contained in:
parent
522d98f271
commit
5ad69e624b
64
openscap-1.3.6-blueprint-fix-pr-1749.patch
Normal file
64
openscap-1.3.6-blueprint-fix-pr-1749.patch
Normal file
@ -0,0 +1,64 @@
|
||||
From 5f0a9033b466d929613a2a55a1524ec75c09b5b0 Mon Sep 17 00:00:00 2001
|
||||
From: Evgeny Kolesnikov <ekolesni@redhat.com>
|
||||
Date: Thu, 6 May 2021 08:14:12 +0200
|
||||
Subject: [PATCH] Introduce OSBuild Blueprint fix type
|
||||
|
||||
---
|
||||
utils/oscap-xccdf.c | 7 +++++--
|
||||
utils/oscap.8 | 2 +-
|
||||
xsl/xccdf-share.xsl | 1 +
|
||||
3 files changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c
|
||||
index 95c1c7658d..801e54fa35 100644
|
||||
--- a/utils/oscap-xccdf.c
|
||||
+++ b/utils/oscap-xccdf.c
|
||||
@@ -275,7 +275,8 @@ static struct oscap_module XCCDF_GEN_FIX = {
|
||||
.usage = "[options] xccdf-file.xml",
|
||||
.help = GEN_OPTS
|
||||
"\nFix Options:\n"
|
||||
- " --fix-type <type> - Fix type. Should be one of: bash, ansible, puppet, anaconda (default: bash).\n"
|
||||
+ " --fix-type <type> - Fix type. Should be one of: bash, ansible, puppet, anaconda, ignition, kubernetes,\n"
|
||||
+ " blueprint (default: bash).\n"
|
||||
" --output <file> - Write the script into file.\n"
|
||||
" --result-id <id> - Fixes will be generated for failed rule-results of the specified TestResult.\n"
|
||||
" --template <id|filename> - Fix template. (default: bash)\n"
|
||||
@@ -887,10 +888,12 @@ int app_generate_fix(const struct oscap_action *action)
|
||||
template = "urn:xccdf:fix:script:ignition";
|
||||
} else if (strcmp(action->fix_type, "kubernetes") == 0) {
|
||||
template = "urn:xccdf:fix:script:kubernetes";
|
||||
+ } else if (strcmp(action->fix_type, "blueprint") == 0) {
|
||||
+ template = "urn:redhat:osbuild:blueprint";
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Unknown fix type '%s'.\n"
|
||||
- "Please provide one of: bash, ansible, puppet, anaconda, ignition, kubernetes.\n"
|
||||
+ "Please provide one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint.\n"
|
||||
"Or provide a custom template using '--template' instead.\n",
|
||||
action->fix_type);
|
||||
return OSCAP_ERROR;
|
||||
diff --git a/utils/oscap.8 b/utils/oscap.8
|
||||
index 240b829d7b..6cae0ffe8a 100644
|
||||
--- a/utils/oscap.8
|
||||
+++ b/utils/oscap.8
|
||||
@@ -395,7 +395,7 @@ Result-oriented fixes are generated using result-id provided to select only the
|
||||
Profile-oriented fixes are generated using all rules within the provided profile. If no result-id/profile are provided, (default) profile will be used to generate fixes.
|
||||
.TP
|
||||
\fB\-\-fix-type TYPE\fR
|
||||
-Specify fix type. There are multiple programming languages in which the fix script can be generated. TYPE should be one of: bash, ansible, puppet, anaconda, ignition, kubernetes. Default is bash. This option is mutually exclusive with --template, because fix type already determines the template URN.
|
||||
+Specify fix type. There are multiple programming languages in which the fix script can be generated. TYPE should be one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint. Default is bash. This option is mutually exclusive with --template, because fix type already determines the template URN.
|
||||
.TP
|
||||
\fB\-\-output FILE\fR
|
||||
Write the report to this file instead of standard output.
|
||||
diff --git a/xsl/xccdf-share.xsl b/xsl/xccdf-share.xsl
|
||||
index 9f8e587676..d7a9f3b7e2 100644
|
||||
--- a/xsl/xccdf-share.xsl
|
||||
+++ b/xsl/xccdf-share.xsl
|
||||
@@ -295,6 +295,7 @@ Authors:
|
||||
<xsl:when test="$fix/@system = 'urn:xccdf:fix:script:puppet'">Puppet snippet</xsl:when>
|
||||
<xsl:when test="$fix/@system = 'urn:redhat:anaconda:pre'">Anaconda snippet</xsl:when>
|
||||
<xsl:when test="$fix/@system = 'urn:xccdf:fix:script:kubernetes'">Kubernetes snippet</xsl:when>
|
||||
+ <xsl:when test="$fix/@system = 'urn:redhat:osbuild:blueprint'">OSBuild Blueprint snippet</xsl:when>
|
||||
<xsl:otherwise>script</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:variable>
|
583
openscap-1.3.6-blueprint-toml-pr-1810.patch
Normal file
583
openscap-1.3.6-blueprint-toml-pr-1810.patch
Normal file
@ -0,0 +1,583 @@
|
||||
From b0b7626dca08acd4563ae42c1c27ccc0777b5357 Mon Sep 17 00:00:00 2001
|
||||
From: Evgeny Kolesnikov <ekolesni@redhat.com>
|
||||
Date: Thu, 23 Sep 2021 00:58:29 +0200
|
||||
Subject: [PATCH] Add proper Blueprint's remediation snippets handling for
|
||||
generation of the final TOML document.
|
||||
|
||||
As the final Blueprint could not be created by just gluing up all
|
||||
the snippets together we have to get a bit more creative.
|
||||
---
|
||||
docs/manual/manual.adoc | 15 ++
|
||||
src/XCCDF_POLICY/xccdf_policy_remediate.c | 216 ++++++++++++++++--
|
||||
src/common/list.c | 19 ++
|
||||
src/common/list.h | 1 +
|
||||
tests/API/XCCDF/unittests/CMakeLists.txt | 1 +
|
||||
.../unittests/test_remediation_blueprint.sh | 27 +++
|
||||
.../unittests/test_remediation_blueprint.toml | 45 ++++
|
||||
.../test_remediation_blueprint.xccdf.xml | 102 +++++++++
|
||||
8 files changed, 405 insertions(+), 21 deletions(-)
|
||||
create mode 100755 tests/API/XCCDF/unittests/test_remediation_blueprint.sh
|
||||
create mode 100644 tests/API/XCCDF/unittests/test_remediation_blueprint.toml
|
||||
create mode 100644 tests/API/XCCDF/unittests/test_remediation_blueprint.xccdf.xml
|
||||
|
||||
diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc
|
||||
index e8664eb920..90e2cc2c63 100644
|
||||
--- a/docs/manual/manual.adoc
|
||||
+++ b/docs/manual/manual.adoc
|
||||
@@ -1084,6 +1084,21 @@ scanned during this command. If you want to generate remediation only for the
|
||||
failed rules based on scan results, refer to <<_reviewing_remediations,Reviewing
|
||||
remediations>>.
|
||||
|
||||
+=== Generating Image Builder Blueprints
|
||||
+
|
||||
+OpenSCAP can also create a remediation in form of Image Builder (OSBuild) Blueprint. This remeditaion
|
||||
+is intendeded to be used as a bootstrap for image creation and usually it will contain only essential
|
||||
+elements of the configuration, elements that would be hard or impossible to change after the image
|
||||
+is created, like partitioning or set of installed packages.
|
||||
+
|
||||
+It is recommended to combine this type of remediation with other types, executed on the running system.
|
||||
+
|
||||
+For example, to generate a blueprint remediation for RHEL 8 OSPP profile, run:
|
||||
+
|
||||
+----
|
||||
+$ oscap xccdf generate fix --profile ospp --fix-type blueprint /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml > blueprint.toml
|
||||
+----
|
||||
+
|
||||
== Details on SCAP conformance
|
||||
|
||||
=== Check Engines
|
||||
diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c
|
||||
index 0b3a037a5f..6033c3b54b 100644
|
||||
--- a/src/XCCDF_POLICY/xccdf_policy_remediate.c
|
||||
+++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c
|
||||
@@ -656,6 +656,78 @@ static int _write_fix_missing_warning_to_fd(const char *sys, int output_fd, stru
|
||||
}
|
||||
}
|
||||
|
||||
+struct blueprint_entries {
|
||||
+ const char *pattern;
|
||||
+ struct oscap_list *list;
|
||||
+ pcre *re;
|
||||
+};
|
||||
+
|
||||
+static inline int _parse_blueprint_fix(const char *fix_text, struct oscap_list *generic, struct oscap_list *services_enable, struct oscap_list *services_disable, struct oscap_list *kernel_append)
|
||||
+{
|
||||
+ const char *err;
|
||||
+ int errofs;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ struct blueprint_entries tab[] = {
|
||||
+ {"\\[customizations\\.services\\]\\s+enabled[=\\s]+\\[([^\\]]+)\\]\\s+", services_enable, NULL},
|
||||
+ {"\\[customizations\\.services\\]\\s+disabled[=\\s]+\\[([^\\]]+)\\]\\s+", services_disable, NULL},
|
||||
+ {"\\[customizations\\.kernel\\]\\s+append[=\\s\"]+([^\"]+)[\\s\"]+", kernel_append, NULL},
|
||||
+ // We do this only to pop the 'distro' entry to the top of the generic list,
|
||||
+ // effectively placing it to the root of the TOML document.
|
||||
+ {"\\s+(distro[=\\s\"]+[^\"]+[\\s\"]+)", generic, NULL},
|
||||
+ {NULL, NULL, NULL}
|
||||
+ };
|
||||
+
|
||||
+ for (int i = 0; tab[i].pattern != NULL; i++) {
|
||||
+ tab[i].re = pcre_compile(tab[i].pattern, PCRE_UTF8, &err, &errofs, NULL);
|
||||
+ if (tab[i].re == NULL) {
|
||||
+ dE("Unable to compile /%s/ regex pattern, pcre_compile() returned error (offset: %d): '%s'.\n", tab[i].pattern, errofs, err);
|
||||
+ ret = 1;
|
||||
+ goto exit;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const size_t fix_text_len = strlen(fix_text);
|
||||
+ size_t start_offset = 0;
|
||||
+ int ovector[6] = {0};
|
||||
+
|
||||
+ for (int i = 0; tab[i].pattern != NULL; i++) {
|
||||
+ while (true) {
|
||||
+ const int match = pcre_exec(tab[i].re, NULL, fix_text, fix_text_len, start_offset,
|
||||
+ 0, ovector, sizeof(ovector) / sizeof(ovector[0]));
|
||||
+ if (match == -1)
|
||||
+ break;
|
||||
+
|
||||
+ if (match != 2) {
|
||||
+ dE("Expected 1 capture group matches per entry. Found %i!", match - 1);
|
||||
+ ret = 1;
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ char *val = malloc((ovector[3] - ovector[2] + 1) * sizeof(char));
|
||||
+ memcpy(val, &fix_text[ovector[2]], ovector[3] - ovector[2]);
|
||||
+ val[ovector[3] - ovector[2]] = '\0';
|
||||
+
|
||||
+ if (!oscap_list_contains(kernel_append, val, (oscap_cmp_func) oscap_streq)) {
|
||||
+ oscap_list_prepend(tab[i].list, val);
|
||||
+ } else {
|
||||
+ free(val);
|
||||
+ }
|
||||
+
|
||||
+ start_offset = ovector[1];
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (start_offset < fix_text_len-1) {
|
||||
+ oscap_list_add(generic, strdup(fix_text + start_offset));
|
||||
+ }
|
||||
+
|
||||
+exit:
|
||||
+ for (int i = 0; tab[i].pattern != NULL; i++)
|
||||
+ pcre_free(tab[i].re);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
|
||||
static inline int _parse_ansible_fix(const char *fix_text, struct oscap_list *variables, struct oscap_list *tasks)
|
||||
{
|
||||
@@ -793,6 +865,18 @@ static int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, struct x
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int _xccdf_policy_rule_generate_blueprint_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, struct oscap_list *generic, struct oscap_list *services_enable, struct oscap_list *services_disable, struct oscap_list *kernel_append)
|
||||
+{
|
||||
+ char *fix_text = NULL;
|
||||
+ int ret = _xccdf_policy_rule_get_fix_text(policy, rule, template, &fix_text);
|
||||
+ if (fix_text == NULL) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = _parse_blueprint_fix(fix_text, generic, services_enable, services_disable, kernel_append);
|
||||
+ free(fix_text);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int _xccdf_policy_rule_generate_ansible_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, struct oscap_list *variables, struct oscap_list *tasks)
|
||||
{
|
||||
char *fix_text = NULL;
|
||||
@@ -914,25 +998,45 @@ static char *_comment_multiline_text(char *text)
|
||||
static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_result *result, const char *sys, int output_fd)
|
||||
{
|
||||
if (!(oscap_streq(sys, "") || oscap_streq(sys, "urn:xccdf:fix:script:sh") || oscap_streq(sys, "urn:xccdf:fix:commands") ||
|
||||
- oscap_streq(sys, "urn:xccdf:fix:script:ansible")))
|
||||
+ oscap_streq(sys, "urn:xccdf:fix:script:ansible") || oscap_streq(sys, "urn:redhat:osbuild:blueprint")))
|
||||
return 0; // no header required
|
||||
|
||||
- const bool ansible_script = strcmp(sys, "urn:xccdf:fix:script:ansible") == 0;
|
||||
- const char *how_to_apply = ansible_script ?
|
||||
- "# $ ansible-playbook -i \"localhost,\" -c local playbook.yml\n"
|
||||
- "# $ ansible-playbook -i \"192.168.1.155,\" playbook.yml\n"
|
||||
- "# $ ansible-playbook -i inventory.ini playbook.yml" :
|
||||
- "# $ sudo ./remediation-script.sh";
|
||||
const char *oscap_version = oscap_get_version();
|
||||
- const char *format = ansible_script ? "ansible" : "bash";
|
||||
- const char *remediation_type = ansible_script ? "Ansible Playbook" : "Bash Remediation Script";
|
||||
- const char *shebang_with_newline = ansible_script ? "" : "#!/usr/bin/env bash\n";
|
||||
+ char *how_to_apply = "";
|
||||
+ char *format = (char *)sys;
|
||||
+ char *remediation_type = "Unknown";
|
||||
+ char *shebang_with_newline = "";
|
||||
+
|
||||
+ if (oscap_streq(sys, "urn:xccdf:fix:script:ansible")) {
|
||||
+ how_to_apply = "# $ ansible-playbook -i \"localhost,\" -c local playbook.yml\n"
|
||||
+ "# $ ansible-playbook -i \"192.168.1.155,\" playbook.yml\n"
|
||||
+ "# $ ansible-playbook -i inventory.ini playbook.yml";
|
||||
+ format = "ansible";
|
||||
+ remediation_type = "Ansible Playbook";
|
||||
+ }
|
||||
+
|
||||
+ if (oscap_streq(sys, "urn:redhat:osbuild:blueprint")) {
|
||||
+ how_to_apply = "# composer-cli blueprints push blueprint.toml";
|
||||
+ format = "blueprint";
|
||||
+ remediation_type = "Blueprint";
|
||||
+ }
|
||||
+
|
||||
+ if (oscap_streq(sys, "") || oscap_streq(sys, "urn:xccdf:fix:script:sh") || oscap_streq(sys, "urn:xccdf:fix:commands")) {
|
||||
+ how_to_apply = "# $ sudo ./remediation-script.sh";
|
||||
+ format = "bash";
|
||||
+ remediation_type = "Bash Remediation Script";
|
||||
+ shebang_with_newline = "#!/usr/bin/env bash\n";
|
||||
+ }
|
||||
|
||||
char *fix_header;
|
||||
|
||||
struct xccdf_profile *profile = xccdf_policy_get_profile(policy);
|
||||
const char *profile_id = xccdf_profile_get_id(profile);
|
||||
|
||||
+ struct xccdf_benchmark *benchmark = xccdf_policy_get_benchmark(policy);
|
||||
+ const char *benchmark_version_info = benchmark ? xccdf_benchmark_get_version(benchmark) : "Unknown";
|
||||
+ const char *benchmark_id = benchmark ? xccdf_benchmark_get_id(benchmark) : "Unknown";
|
||||
+
|
||||
// Title
|
||||
struct oscap_text_iterator *title_iterator = xccdf_profile_get_title(profile);
|
||||
char *raw_profile_title = oscap_textlist_get_preferred_plaintext(title_iterator, NULL);
|
||||
@@ -942,11 +1046,6 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
|
||||
|
||||
if (result == NULL) {
|
||||
// Profile-based remediation fix
|
||||
- struct xccdf_benchmark *benchmark = xccdf_policy_get_benchmark(policy);
|
||||
- if (benchmark == NULL) {
|
||||
- free(profile_title);
|
||||
- return 1;
|
||||
- }
|
||||
// Description
|
||||
struct oscap_text_iterator *description_iterator = xccdf_profile_get_description(profile);
|
||||
char *profile_description = description_iterator != NULL ?
|
||||
@@ -955,10 +1054,8 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
|
||||
char *commented_profile_description = _comment_multiline_text(profile_description);
|
||||
free(profile_description);
|
||||
|
||||
- const char *benchmark_version_info = xccdf_benchmark_get_version(benchmark);
|
||||
- const char *benchmark_id = xccdf_benchmark_get_id(benchmark);
|
||||
- const struct xccdf_version_info *xccdf_version = xccdf_benchmark_get_schema_version(benchmark);
|
||||
- const char *xccdf_version_name = xccdf_version_info_get_version(xccdf_version);
|
||||
+ const struct xccdf_version_info *xccdf_version = benchmark ? xccdf_benchmark_get_schema_version(benchmark) : NULL;
|
||||
+ const char *xccdf_version_name = xccdf_version ? xccdf_version_info_get_version(xccdf_version) : "Unknown";
|
||||
|
||||
fix_header = oscap_sprintf(
|
||||
"%s"
|
||||
@@ -1026,9 +1123,8 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
|
||||
result_id, format, remediation_type, remediation_type, how_to_apply
|
||||
);
|
||||
}
|
||||
- free(profile_title);
|
||||
|
||||
- if (ansible_script) {
|
||||
+ if (oscap_streq(sys, "urn:xccdf:fix:script:ansible")) {
|
||||
char *ansible_fix_header = oscap_sprintf(
|
||||
"---\n"
|
||||
"%s\n"
|
||||
@@ -1036,9 +1132,85 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
|
||||
fix_header);
|
||||
free(fix_header);
|
||||
return _write_text_to_fd_and_free(output_fd, ansible_fix_header);
|
||||
+ } else if (oscap_streq(sys, "urn:redhat:osbuild:blueprint")) {
|
||||
+ char *blueprint_fix_header = oscap_sprintf(
|
||||
+ "%s"
|
||||
+ "name = \"%s\"\n"
|
||||
+ "description = \"%s\"\n"
|
||||
+ "version = \"%s\"\n",
|
||||
+ fix_header, profile_id, profile_title, benchmark_version_info);
|
||||
+ free(fix_header);
|
||||
+ return _write_text_to_fd_and_free(output_fd, blueprint_fix_header);
|
||||
} else {
|
||||
return _write_text_to_fd_and_free(output_fd, fix_header);
|
||||
}
|
||||
+
|
||||
+ free(profile_title);
|
||||
+}
|
||||
+
|
||||
+static int _xccdf_policy_generate_fix_blueprint(struct oscap_list *rules_to_fix, struct xccdf_policy *policy, const char *sys, int output_fd)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ struct oscap_list *generic = oscap_list_new();
|
||||
+ struct oscap_list *services_enable = oscap_list_new();
|
||||
+ struct oscap_list *services_disable = oscap_list_new();
|
||||
+ struct oscap_list *kernel_append = oscap_list_new();
|
||||
+ struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix);
|
||||
+ while (oscap_iterator_has_more(rules_to_fix_it)) {
|
||||
+ struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it);
|
||||
+ ret = _xccdf_policy_rule_generate_blueprint_fix(policy, rule, sys, generic, services_enable, services_disable, kernel_append);
|
||||
+ if (ret != 0)
|
||||
+ break;
|
||||
+ }
|
||||
+ oscap_iterator_free(rules_to_fix_it);
|
||||
+
|
||||
+ struct oscap_iterator *generic_it = oscap_iterator_new(generic);
|
||||
+ while(oscap_iterator_has_more(generic_it)) {
|
||||
+ char *var_line = (char *) oscap_iterator_next(generic_it);
|
||||
+ _write_text_to_fd(output_fd, var_line);
|
||||
+ }
|
||||
+ _write_text_to_fd(output_fd, "\n");
|
||||
+ oscap_iterator_free(generic_it);
|
||||
+ oscap_list_free(generic, free);
|
||||
+
|
||||
+ _write_text_to_fd(output_fd, "[customizations.kernel]\nappend = \"");
|
||||
+ struct oscap_iterator *kernel_append_it = oscap_iterator_new(kernel_append);
|
||||
+ while(oscap_iterator_has_more(kernel_append_it)) {
|
||||
+ char *var_line = (char *) oscap_iterator_next(kernel_append_it);
|
||||
+ _write_text_to_fd(output_fd, var_line);
|
||||
+ if (oscap_iterator_has_more(kernel_append_it))
|
||||
+ _write_text_to_fd(output_fd, " ");
|
||||
+ }
|
||||
+ _write_text_to_fd(output_fd, "\"\n\n");
|
||||
+ oscap_iterator_free(kernel_append_it);
|
||||
+ oscap_list_free(kernel_append, free);
|
||||
+
|
||||
+ _write_text_to_fd(output_fd, "[customizations.services]\n");
|
||||
+ _write_text_to_fd(output_fd, "enabled = [");
|
||||
+ struct oscap_iterator *services_enable_it = oscap_iterator_new(services_enable);
|
||||
+ while(oscap_iterator_has_more(services_enable_it)) {
|
||||
+ char *var_line = (char *) oscap_iterator_next(services_enable_it);
|
||||
+ _write_text_to_fd(output_fd, var_line);
|
||||
+ if (oscap_iterator_has_more(services_enable_it))
|
||||
+ _write_text_to_fd(output_fd, ",");
|
||||
+ }
|
||||
+ _write_text_to_fd(output_fd, "]\n");
|
||||
+ oscap_iterator_free(services_enable_it);
|
||||
+ oscap_list_free(services_enable, free);
|
||||
+
|
||||
+ _write_text_to_fd(output_fd, "disabled = [");
|
||||
+ struct oscap_iterator *services_disable_it = oscap_iterator_new(services_disable);
|
||||
+ while(oscap_iterator_has_more(services_disable_it)) {
|
||||
+ char *var_line = (char *) oscap_iterator_next(services_disable_it);
|
||||
+ _write_text_to_fd(output_fd, var_line);
|
||||
+ if (oscap_iterator_has_more(services_disable_it))
|
||||
+ _write_text_to_fd(output_fd, ",");
|
||||
+ }
|
||||
+ _write_text_to_fd(output_fd, "]\n\n");
|
||||
+ oscap_iterator_free(services_disable_it);
|
||||
+ oscap_list_free(services_disable, free);
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int _xccdf_policy_generate_fix_ansible(struct oscap_list *rules_to_fix, struct xccdf_policy *policy, const char *sys, int output_fd)
|
||||
@@ -1145,6 +1317,8 @@ int xccdf_policy_generate_fix(struct xccdf_policy *policy, struct xccdf_result *
|
||||
// in Ansible we have to generate variables first and then tasks
|
||||
if (strcmp(sys, "urn:xccdf:fix:script:ansible") == 0) {
|
||||
ret = _xccdf_policy_generate_fix_ansible(rules_to_fix, policy, sys, output_fd);
|
||||
+ } else if (strcmp(sys, "urn:redhat:osbuild:blueprint") == 0) {
|
||||
+ ret = _xccdf_policy_generate_fix_blueprint(rules_to_fix, policy, sys, output_fd);
|
||||
} else {
|
||||
ret = _xccdf_policy_generate_fix_other(rules_to_fix, policy, sys, output_fd);
|
||||
}
|
||||
diff --git a/src/common/list.c b/src/common/list.c
|
||||
index 2516d0f2f0..90381069f8 100644
|
||||
--- a/src/common/list.c
|
||||
+++ b/src/common/list.c
|
||||
@@ -66,6 +66,25 @@ bool oscap_list_add(struct oscap_list * list, void *value)
|
||||
return true;
|
||||
}
|
||||
|
||||
+bool oscap_list_prepend(struct oscap_list * list, void *value)
|
||||
+{
|
||||
+ __attribute__nonnull__(list);
|
||||
+ if (value == NULL) return false;
|
||||
+
|
||||
+ struct oscap_list_item *item = malloc(sizeof(struct oscap_list_item));
|
||||
+ item->next = NULL;
|
||||
+ item->data = value;
|
||||
+ ++list->itemcount;
|
||||
+
|
||||
+ if (list->first == NULL) {
|
||||
+ list->last = list->first = item;
|
||||
+ } else {
|
||||
+ item->next = list->first;
|
||||
+ list->first = item;
|
||||
+ }
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
bool oscap_list_push(struct oscap_list *list, void *value)
|
||||
{
|
||||
return oscap_list_add(list,value);
|
||||
diff --git a/src/common/list.h b/src/common/list.h
|
||||
index 7a0694dc8a..3179c514f0 100644
|
||||
--- a/src/common/list.h
|
||||
+++ b/src/common/list.h
|
||||
@@ -62,6 +62,7 @@ struct oscap_list *oscap_list_new(void);
|
||||
void oscap_create_lists(struct oscap_list **first, ...);
|
||||
bool oscap_list_add(struct oscap_list *list, void *value);
|
||||
bool oscap_list_push(struct oscap_list *list, void *value);
|
||||
+bool oscap_list_prepend(struct oscap_list *list, void *value);
|
||||
bool oscap_list_pop(struct oscap_list *list, oscap_destruct_func destructor);
|
||||
bool oscap_list_remove(struct oscap_list *list, void *value, oscap_cmp_func compare, oscap_destruct_func destructor);
|
||||
struct oscap_list *oscap_list_clone(const struct oscap_list * list, oscap_clone_func cloner);
|
||||
diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt
|
||||
index 52645834c4..9c17ebb78a 100644
|
||||
--- a/tests/API/XCCDF/unittests/CMakeLists.txt
|
||||
+++ b/tests/API/XCCDF/unittests/CMakeLists.txt
|
||||
@@ -75,6 +75,7 @@ add_oscap_test("test_single_rule_stigw.sh")
|
||||
add_oscap_test("test_remediation_simple.sh")
|
||||
add_oscap_test("test_remediation_offline.sh")
|
||||
add_oscap_test("test_remediation_metadata.sh")
|
||||
+add_oscap_test("test_remediation_blueprint.sh")
|
||||
add_oscap_test("test_remediation_bad_fix.sh")
|
||||
add_oscap_test("test_remediation_subs_plain_text.sh")
|
||||
add_oscap_test("test_remediation_subs_plain_text_empty.sh")
|
||||
diff --git a/tests/API/XCCDF/unittests/test_remediation_blueprint.sh b/tests/API/XCCDF/unittests/test_remediation_blueprint.sh
|
||||
new file mode 100755
|
||||
index 0000000000..7c79822529
|
||||
--- /dev/null
|
||||
+++ b/tests/API/XCCDF/unittests/test_remediation_blueprint.sh
|
||||
@@ -0,0 +1,27 @@
|
||||
+#!/usr/bin/env bash
|
||||
+. $builddir/tests/test_common.sh
|
||||
+
|
||||
+set -e
|
||||
+set -o pipefail
|
||||
+
|
||||
+name=$(basename $0 .sh)
|
||||
+result=$(make_temp_file /tmp ${name}.out)
|
||||
+stderr=$(make_temp_file /tmp ${name}.out)
|
||||
+
|
||||
+ret=0
|
||||
+
|
||||
+input_xml="$srcdir/${name}.xccdf.xml"
|
||||
+valid_toml="$srcdir/${name}.toml"
|
||||
+
|
||||
+echo "Stderr file = $stderr"
|
||||
+echo "Result file = $result"
|
||||
+[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr
|
||||
+
|
||||
+# The $valid_toml file was generated without ' # This file was generated by OpenSCAP 1.3.5 using:' line
|
||||
+# to make the test independent from the scanner version. We have to filter this line from the output as well.
|
||||
+
|
||||
+$OSCAP xccdf generate fix --fix-type blueprint --profile 'common' "$input_xml" | grep -v "OpenSCAP" > "$result"
|
||||
+
|
||||
+diff $valid_toml $result
|
||||
+
|
||||
+rm "$result"
|
||||
diff --git a/tests/API/XCCDF/unittests/test_remediation_blueprint.toml b/tests/API/XCCDF/unittests/test_remediation_blueprint.toml
|
||||
new file mode 100644
|
||||
index 0000000000..e189adca9d
|
||||
--- /dev/null
|
||||
+++ b/tests/API/XCCDF/unittests/test_remediation_blueprint.toml
|
||||
@@ -0,0 +1,45 @@
|
||||
+###############################################################################
|
||||
+#
|
||||
+# Blueprint for Profile title on one line
|
||||
+#
|
||||
+# Profile Description:
|
||||
+# Profile description
|
||||
+#
|
||||
+# Profile ID: xccdf_moc.elpmaxe.www_profile_common
|
||||
+# Benchmark ID: xccdf_moc.elpmaxe.www_benchmark_test
|
||||
+# Benchmark Version: 1.0
|
||||
+# XCCDF Version: 1.2
|
||||
+#
|
||||
+# $ oscap xccdf generate fix --profile xccdf_moc.elpmaxe.www_profile_common --fix-type blueprint xccdf-file.xml
|
||||
+#
|
||||
+# It attempts to fix every selected rule, even if the system is already compliant.
|
||||
+#
|
||||
+# How to apply this Blueprint:
|
||||
+# composer-cli blueprints push blueprint.toml
|
||||
+#
|
||||
+###############################################################################
|
||||
+
|
||||
+name = "xccdf_moc.elpmaxe.www_profile_common"
|
||||
+description = "Profile title on one line"
|
||||
+version = "1.0"
|
||||
+distro = rhel-80
|
||||
+
|
||||
+[[packages]]
|
||||
+name = "aide"
|
||||
+version = "*"
|
||||
+
|
||||
+[[customizations.filesystem]]
|
||||
+mountpoint = "/home"
|
||||
+size = 1
|
||||
+
|
||||
+[[customizations.filesystem]]
|
||||
+mountpoint = "/tmp"
|
||||
+size = 2
|
||||
+
|
||||
+[customizations.kernel]
|
||||
+append = "foo=bar audit=1"
|
||||
+
|
||||
+[customizations.services]
|
||||
+enabled = ["sshd","usbguard"]
|
||||
+disabled = ["kdump"]
|
||||
+
|
||||
diff --git a/tests/API/XCCDF/unittests/test_remediation_blueprint.xccdf.xml b/tests/API/XCCDF/unittests/test_remediation_blueprint.xccdf.xml
|
||||
new file mode 100644
|
||||
index 0000000000..e685620dac
|
||||
--- /dev/null
|
||||
+++ b/tests/API/XCCDF/unittests/test_remediation_blueprint.xccdf.xml
|
||||
@@ -0,0 +1,102 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<Benchmark xmlns="http://checklists.nist.gov/xccdf/1.2" id="xccdf_moc.elpmaxe.www_benchmark_test">
|
||||
+ <status>accepted</status>
|
||||
+ <version>1.0</version>
|
||||
+ <Profile id="xccdf_moc.elpmaxe.www_profile_common">
|
||||
+ <title>Profile title on one line</title>
|
||||
+ <description>Profile description</description>
|
||||
+ <select idref="xccdf_moc.elpmaxe.www_rule_1" selected="true"/>
|
||||
+ </Profile>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_1">
|
||||
+ <title>Install aide</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[[packages]]
|
||||
+name = "aide"
|
||||
+version = "*"
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_2">
|
||||
+ <title>Define /home</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[[customizations.filesystem]]
|
||||
+mountpoint = "/home"
|
||||
+size = 1
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_3">
|
||||
+ <title>Add audit=1 kernel option</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[customizations.kernel]
|
||||
+append = "audit=1"
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_4">
|
||||
+ <title>Add foo=bar kernel option</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[customizations.kernel]
|
||||
+append = "foo=bar"
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_5">
|
||||
+ <title>Define /tmp</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[[customizations.filesystem]]
|
||||
+mountpoint = "/tmp"
|
||||
+size = 2
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_6">
|
||||
+ <title>Enable usbguard</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[customizations.services]
|
||||
+enabled = ["usbguard"]
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_7">
|
||||
+ <title>Disable kdump</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[customizations.services]
|
||||
+disabled = ["kdump"]
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_8">
|
||||
+ <title>Set distro (RHEL 8.0)</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+distro = rhel-80
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+ <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_9">
|
||||
+ <title>Enable sshd</title>
|
||||
+ <fix system="urn:redhat:osbuild:blueprint">
|
||||
+[customizations.services]
|
||||
+enabled = ["sshd"]
|
||||
+</fix>
|
||||
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
+ <check-content-ref href="test_remediation_simple.oval.xml" name="oval:moc.elpmaxe.www:def:1"/>
|
||||
+ </check>
|
||||
+ </Rule>
|
||||
+</Benchmark>
|
@ -20,6 +20,8 @@ Patch11: openscap-1.3.6-http_error_fix-PR_1805.patch
|
||||
Patch12: openscap-1.3.6-empty-proc-in-offline-pr-1812.patch
|
||||
Patch13: openscap-1.3.6-initialize-crapi-once-pr-1779.patch
|
||||
Patch14: openscap-1.3.6-test-rhbz1959570-pr-1788.patch
|
||||
Patch15: openscap-1.3.6-blueprint-fix-pr-1749.patch
|
||||
Patch16: openscap-1.3.6-blueprint-toml-pr-1810.patch
|
||||
BuildRequires: make
|
||||
BuildRequires: cmake >= 2.6
|
||||
BuildRequires: gcc
|
||||
@ -212,6 +214,7 @@ pathfix.py -i %{__python3} -p -n $RPM_BUILD_ROOT%{_bindir}/scap-as-rpm
|
||||
%changelog
|
||||
* Thu Nov 04 2021 Jan Černý <jcerny@redhat.com> - 1:1.3.5-11
|
||||
- Initialize crypto API only once (rhbz#2020044)
|
||||
- Add support for Blueprint remediations (rhbz#2020052)
|
||||
|
||||
* Mon Nov 01 2021 Evgenii Kolesnikov <ekolesni@redhat.com> - 1:1.3.5-10
|
||||
- Fix process58 probe errors when scanning minimalist filesystem in offline mode (rhbz#2019054)
|
||||
|
Loading…
Reference in New Issue
Block a user