Skip to content

Commit 419070f

Browse files
authored
Merge pull request #1295 from jan-cerny/small_ansible_fixes
Small fixes in "oscap xccdf generate fix", mostly for Ansible
2 parents 4e869bf + e28dd6d commit 419070f

File tree

5 files changed

+91
-42
lines changed

5 files changed

+91
-42
lines changed

src/XCCDF_POLICY/xccdf_policy_remediate.c

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ static inline int _parse_ansible_fix(const char *fix_text, struct oscap_list *va
674674
memcpy(variable_value, &fix_text[ovector[4]], ovector[5] - ovector[4]);
675675
variable_value[ovector[5] - ovector[4]] = '\0';
676676

677-
char *var_line = oscap_sprintf(" %s: %s\n", variable_name, variable_value);
677+
char *var_line = oscap_sprintf(" %s: %s\n", variable_name, variable_value);
678678

679679
free(variable_name);
680680
free(variable_value);
@@ -802,6 +802,72 @@ static int _xccdf_item_recursive_gather_selected_rules(struct xccdf_policy *poli
802802
return ret;
803803
}
804804

805+
static void _trim_trailing_whitespace(char *str, size_t str_len)
806+
{
807+
char *last_char = str + str_len - 1;
808+
while (isspace(*last_char)) {
809+
*last_char = '\0';
810+
last_char--;
811+
}
812+
}
813+
814+
/* Handles multiline strings in profile title and description.
815+
* Puts a '#' at the beginning of each line.
816+
* Also removes trailing and leading whitespaces on each line.
817+
*/
818+
static char *_comment_multiline_text(char *text)
819+
{
820+
if (text == NULL) {
821+
return oscap_strdup("Not available");
822+
}
823+
const char *filler = "\n# ";
824+
size_t buffer_size = strlen(text) + 1; // +1 for terminating '\0'
825+
char *buffer = malloc(buffer_size);
826+
char *saveptr;
827+
size_t filler_len = strlen(filler);
828+
size_t result_len = 0;
829+
bool first = true;
830+
char *str = text;
831+
while (true) {
832+
char *token = oscap_strtok_r(str, "\n", &saveptr);
833+
if (token == NULL) {
834+
break;
835+
}
836+
/* Strip leading whitespace */
837+
while (isspace(*token)) {
838+
token++;
839+
}
840+
size_t token_len = strlen(token);
841+
if (token_len > 0) {
842+
/* Strip trailing whitespace */
843+
_trim_trailing_whitespace(token, token_len);
844+
token_len = strlen(token);
845+
}
846+
if (token_len > 0) {
847+
/* Copy filler to output buffer */
848+
if (!first) {
849+
if (buffer_size < result_len + filler_len + 1) {
850+
buffer_size += filler_len;
851+
buffer = realloc(buffer, buffer_size);
852+
}
853+
strncpy(buffer + result_len, filler, filler_len + 1);
854+
result_len += filler_len;
855+
}
856+
if (buffer_size < result_len + token_len + 1) {
857+
buffer_size += token_len;
858+
buffer = realloc(buffer, buffer_size);
859+
}
860+
/* Copy token to output buffer */
861+
strncpy(buffer + result_len, token, token_len + 1);
862+
result_len += token_len;
863+
first = false;
864+
}
865+
str = NULL;
866+
}
867+
*(buffer + result_len) = '\0';
868+
return buffer;
869+
}
870+
805871
static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_result *result, const char *sys, int output_fd)
806872
{
807873
if (!(oscap_streq(sys, "") || oscap_streq(sys, "urn:xccdf:fix:script:sh") || oscap_streq(sys, "urn:xccdf:fix:commands") ||
@@ -815,18 +881,21 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
815881
"# $ ansible-playbook -i inventory.ini playbook.yml" :
816882
"# $ sudo ./remediation-script.sh";
817883
const char *oscap_version = oscap_get_version();
818-
const char *format = sys != NULL ? sys : "";
819-
const char *template = sys != NULL ? " --template " : "";
884+
const char *format = ansible_script ? "ansible" : "bash";
820885
const char *remediation_type = ansible_script ? "Ansible Playbook" : "Bash Remediation Script";
821886

822887
char *fix_header;
823888

824889
struct xccdf_profile *profile = xccdf_policy_get_profile(policy);
825890
const char *profile_id = xccdf_profile_get_id(profile);
891+
826892
// Title
827893
struct oscap_text_iterator *title_iterator = xccdf_profile_get_title(profile);
828-
char *profile_title = oscap_textlist_get_preferred_plaintext(title_iterator, NULL);
894+
char *raw_profile_title = oscap_textlist_get_preferred_plaintext(title_iterator, NULL);
829895
oscap_text_iterator_free(title_iterator);
896+
char *profile_title = _comment_multiline_text(raw_profile_title);
897+
free(raw_profile_title);
898+
830899
if (result == NULL) {
831900
// Profile-based remediation fix
832901
struct xccdf_benchmark *benchmark = xccdf_policy_get_benchmark(policy);
@@ -838,34 +907,14 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
838907
char *profile_description = description_iterator != NULL ?
839908
oscap_textlist_get_preferred_plaintext(description_iterator, NULL) : NULL;
840909
oscap_text_iterator_free(description_iterator);
910+
char *commented_profile_description = _comment_multiline_text(profile_description);
911+
free(profile_description);
841912

842913
const char *benchmark_version_info = xccdf_benchmark_get_version(benchmark);
843914
const char *benchmark_id = xccdf_benchmark_get_id(benchmark);
844915
const struct xccdf_version_info *xccdf_version = xccdf_benchmark_get_schema_version(benchmark);
845916
const char *xccdf_version_name = xccdf_version_info_get_version(xccdf_version);
846917

847-
if (NULL != profile_description) {
848-
size_t new_lines = 0;
849-
size_t description_length = 1;
850-
for (const char *c = profile_description; *c != '\0'; ++c, ++description_length)
851-
if (*c == '\n')
852-
++new_lines;
853-
854-
if (new_lines > 0) {
855-
const char filler[] = "# ";
856-
char *commented_description = malloc(description_length + new_lines * (sizeof filler - 1));
857-
for (size_t i = 0, j = 0; j < description_length; ++i, ++j) {
858-
commented_description[i] = profile_description[j];
859-
if (profile_description[j] == '\n') {
860-
for (size_t k = 0; k < (sizeof filler - 1); ++k)
861-
commented_description[++i] = filler[k];
862-
}
863-
}
864-
free(profile_description);
865-
profile_description = commented_description;
866-
}
867-
}
868-
869918
fix_header = oscap_sprintf(
870919
"###############################################################################\n"
871920
"#\n"
@@ -880,7 +929,7 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
880929
"# XCCDF Version: %s\n"
881930
"#\n"
882931
"# This file was generated by OpenSCAP %s using:\n"
883-
"# $ oscap xccdf generate fix --profile %s%s%s xccdf-file.xml\n"
932+
"# $ oscap xccdf generate fix --profile %s --fix-type %s xccdf-file.xml\n"
884933
"#\n"
885934
"# This %s is generated from an OpenSCAP profile without preliminary evaluation.\n"
886935
"# It attempts to fix every selected rule, even if the system is already compliant.\n"
@@ -890,13 +939,13 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
890939
"#\n"
891940
"###############################################################################\n\n",
892941
remediation_type, profile_title,
893-
profile_description != NULL ? profile_description : "Not available",
942+
commented_profile_description,
894943
profile_id, benchmark_id, benchmark_version_info, xccdf_version_name,
895-
oscap_version, profile_id, template, format, remediation_type,
944+
oscap_version, profile_id, format, remediation_type,
896945
remediation_type, how_to_apply
897946
);
898947

899-
free(profile_description);
948+
free(commented_profile_description);
900949

901950
} else {
902951
// Results-based remediation fix
@@ -916,7 +965,7 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
916965
"# Evaluation Start Time: %s\n"
917966
"# Evaluation End Time: %s\n#\n"
918967
"# This file was generated by OpenSCAP %s using:\n"
919-
"# $ oscap xccdf generate fix --result-id %s%s%s xccdf-results.xml\n"
968+
"# $ oscap xccdf generate fix --result-id %s --fix-type %s xccdf-results.xml\n"
920969
"#\n"
921970
"# This %s is generated from the results of a profile evaluation.\n"
922971
"# It attempts to remediate all issues from the selected rules that failed the test.\n"
@@ -927,7 +976,7 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_
927976
"###############################################################################\n\n",
928977
remediation_type, profile_title, profile_id, xccdf_version_name,
929978
start_time != NULL ? start_time : "Unknown", end_time, oscap_version,
930-
result_id, template, format, remediation_type, remediation_type, how_to_apply
979+
result_id, format, remediation_type, remediation_type, how_to_apply
931980
);
932981
}
933982
free(profile_title);

tests/API/XCCDF/unittests/test_fix_script_header.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ profile_header6="# XCCDF Version: $xccdf_version"
3232
result_header1a="# Bash Remediation Script generated from evaluation of $title"
3333
result_header1b="# Ansible Playbook generated from evaluation of $title"
3434
result_header2="# XCCDF Version: $xccdf_version"
35-
result_header3a="# $ oscap xccdf generate fix --result-id $result_id --template $bash_template xccdf-results.xml"
36-
result_header3b="# $ oscap xccdf generate fix --result-id $result_id --template $ansible_template xccdf-results.xml"
35+
result_header3a="# $ oscap xccdf generate fix --result-id $result_id --fix-type bash xccdf-results.xml"
36+
result_header3b="# $ oscap xccdf generate fix --result-id $result_id --fix-type ansible xccdf-results.xml"
3737

3838

3939
# Create an ARF

tests/API/XCCDF/unittests/test_generate_fix_ansible_vars_golden.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
---
22
- hosts: all
33
vars:
4-
www_value_val1: 100
5-
www_value_val3: "dummy value"
6-
www_value_val2: 50
4+
www_value_val1: 100
5+
www_value_val3: "dummy value"
6+
www_value_val2: 50
77
tasks:
88
- name: default1
99
lineinfile:

tests/API/XCCDF/unittests/test_generate_fix_ansible_vars_golden_altered.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
---
22
- hosts: all
33
vars:
4-
www_value_val1: 0
5-
www_value_val3: "dummy value"
6-
www_value_val2: 50
4+
www_value_val1: 0
5+
www_value_val3: "dummy value"
6+
www_value_val2: 50
77
tasks:
88
- name: default1
99
lineinfile:

tests/API/XCCDF/unittests/test_generate_fix_ansible_vars_golden_tailoring.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22
- hosts: all
33
vars:
4-
www_value_val1: 200
5-
www_value_val3: "dummy value"
4+
www_value_val1: 200
5+
www_value_val3: "dummy value"
66
tasks:
77
- name: default1
88
lineinfile:

0 commit comments

Comments
 (0)