Skip to content

Commit cb0f519

Browse files
authored
Merge pull request #2268 from jan-cerny/RHEL-104651
Show rule details in output
2 parents 8bc18a7 + 5fceded commit cb0f519

File tree

19 files changed

+444
-21
lines changed

19 files changed

+444
-21
lines changed

docs/manual/manual.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ TIP: Instead of the complete profile ID you can provide only a suffix of the
294294
profile ID. For example, instead of `--profile
295295
xccdf_org.ssgproject.content_profile_ospp` you can use just `--profile ospp`.
296296

297+
Rule description, rule rationale and rule warnings will be displayed in the output if the `--show-rule-details` option is provided.
298+
299+
----
300+
# oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_stig --results-arf results.xml --show-rule-details /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml
301+
----
302+
297303
=== Selecting SCAP source data stream components
298304

299305
To evaluate a specific XCCDF benchmark that is part of a specific SCAP source

src/XCCDF/public/xccdf_benchmark.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,10 @@ OSCAP_API struct oscap_text_iterator *xccdf_rule_get_question(const struct xccdf
20742074
* @memberof xccdf_rule
20752075
*/
20762076
OSCAP_API struct xccdf_warning_iterator *xccdf_rule_get_warnings(const struct xccdf_rule *rule);
2077+
/**
2078+
* @memberof xccdf_rule
2079+
*/
2080+
OSCAP_API struct oscap_stringlist *xccdf_rule_get_warnings_strings(struct xccdf_rule *rule);
20772081
/**
20782082
* @memberof xccdf_rule
20792083
*/

src/XCCDF/public/xccdf_session.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,14 @@ OSCAP_API int xccdf_session_export_all(struct xccdf_session *session);
653653
* @param reference_filter a string in a form "key:identifier"
654654
*/
655655
OSCAP_API void xccdf_session_set_reference_filter(struct xccdf_session *session, const char *reference_filter);
656+
657+
/**
658+
* Enable or disable showing rule details such as description or rationale in command line output.
659+
* @param session XCCDF session
660+
* @param show_rule_details whether to show rule details in command line output
661+
*/
662+
OSCAP_API void xccdf_session_set_show_rule_details(struct xccdf_session *session, bool show_rule_details);
663+
656664
/// @}
657665
/// @}
658666
#endif

src/XCCDF/rule.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,23 @@ void xccdf_group_to_dom(struct xccdf_group *group, xmlNode *group_node, xmlDoc *
11151115

11161116
}
11171117

1118+
struct oscap_stringlist *xccdf_rule_get_warnings_strings(struct xccdf_rule *rule)
1119+
{
1120+
struct oscap_stringlist *warnings_list = oscap_stringlist_new();
1121+
/* Handle generic item child nodes */
1122+
struct xccdf_warning_iterator *warnings = xccdf_rule_get_warnings(rule);
1123+
while (xccdf_warning_iterator_has_more(warnings)) {
1124+
struct xccdf_warning *warning = xccdf_warning_iterator_next(warnings);
1125+
struct oscap_text *warning_text = xccdf_warning_get_text(warning);
1126+
const char *warning_cstr = oscap_text_get_text(warning_text);
1127+
char *warning_plaintext = _xhtml_to_plaintext(warning_cstr);
1128+
oscap_stringlist_add_string(warnings_list, warning_plaintext);
1129+
free(warning_plaintext);
1130+
}
1131+
xccdf_warning_iterator_free(warnings);
1132+
return warnings_list;
1133+
}
1134+
11181135
XCCDF_STATUS_CURRENT(rule)
11191136
XCCDF_STATUS_CURRENT(group)
11201137
XCCDF_GROUP_IGETTER(item, content)

src/XCCDF/xccdf_session.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ struct xccdf_session {
121121
bool full_validation; ///< True value indicates that every possible step will be validated by XSD.
122122
bool validate_signature; ///< False value indicates to skip XML signature validation.
123123
bool enforce_signature; ///< True value forces session to treat all XMLs without signature as invalid.
124+
bool show_rule_details; ///< True value indicates that rule details will be shown.
124125
struct oscap_signature_ctx *signature_ctx; ///< Paths to public keys, certificates, signature related info
125126

126127
struct oscap_list *check_engine_plugins; ///< Extra non-OVAL check engines that may or may not have been loaded
@@ -160,6 +161,7 @@ struct xccdf_session *xccdf_session_new_from_source(struct oscap_source *source)
160161
session->validate = true;
161162
session->validate_signature = true;
162163
session->enforce_signature = false;
164+
session->show_rule_details = false;
163165
session->signature_ctx = oscap_signature_ctx_new();
164166
session->xccdf.base_score = 0;
165167
session->oval.progress = download_progress_empty_calllback;
@@ -797,6 +799,7 @@ static inline int _xccdf_session_load_xccdf_benchmark(struct xccdf_session *sess
797799
xccdf_benchmark_free(benchmark);
798800
return 1;
799801
}
802+
xccdf_policy_model_set_show_rule_details(session->xccdf.policy_model, session->show_rule_details);
800803
return 0;
801804
}
802805

@@ -2073,3 +2076,8 @@ void xccdf_session_set_reference_filter(struct xccdf_session *session, const cha
20732076
{
20742077
session->reference_parameter = reference_filter;
20752078
}
2079+
2080+
void xccdf_session_set_show_rule_details(struct xccdf_session *session, bool show_rule_details)
2081+
{
2082+
session->show_rule_details = show_rule_details;
2083+
}

src/XCCDF_POLICY/public/xccdf_policy.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,20 @@ OSCAP_API bool xccdf_policy_model_set_tailoring(struct xccdf_policy_model *model
171171
*/
172172
OSCAP_API struct xccdf_tailoring *xccdf_policy_model_get_tailoring(struct xccdf_policy_model *model);
173173

174+
/**
175+
* Get whether rule details such as description or rationale should be shown in command line output.
176+
* @param policy XCCDF policy
177+
* @returns true if rule details should be shown, false otherwise
178+
*/
179+
OSCAP_API bool xccdf_policy_get_show_rule_details(struct xccdf_policy *policy);
180+
181+
/**
182+
* Set whether rule details such as description or rationale should be shown in command line output.
183+
* @param policy_model XCCDF policy model
184+
* @param show_rule_details true if rule details should be shown, false otherwise
185+
*/
186+
OSCAP_API void xccdf_policy_model_set_show_rule_details(struct xccdf_policy_model *policy_model, bool show_rule_details);
187+
174188
/**
175189
* Get human readable title of given XCCDF Item. This finds title with best matching language
176190
* and resolves <xccdf:sub> substitution in accordance with the given XCCDF Policy.
@@ -182,6 +196,18 @@ OSCAP_API struct xccdf_tailoring *xccdf_policy_model_get_tailoring(struct xccdf_
182196
*/
183197
OSCAP_API char *xccdf_policy_get_readable_item_title(struct xccdf_policy *policy, struct xccdf_item *item, const char *preferred_lang);
184198

199+
/**
200+
* Get human readable rationale of given XCCDF Item. This function searches for description
201+
* with the best matching language and resolves any inner <xccdf:sub> substitution (in accordance
202+
* with the given XCCDF Policy.
203+
* @memberof xccdf_policy
204+
* @param policy XCCDF Policy
205+
* @param item XCCDF Item to query rationale from
206+
* @param preferred_lang Language of your choice, Null value for the default.
207+
* @returns plaintext C string which must be freed by caller
208+
*/
209+
OSCAP_API char *xccdf_policy_get_readable_item_rationale(struct xccdf_policy *policy, struct xccdf_item *item, const char *preferred_lang);
210+
185211
/**
186212
* Get human readable description of given XCCDF Item. This function searches for description
187213
* with the best matching language and resolves any inner <xccdf:sub> substitution (in accordance

src/XCCDF_POLICY/xccdf_policy.c

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -132,33 +132,53 @@ static struct oscap_iterator *_xccdf_policy_get_engines_by_sysname(struct xccdf_
132132
return oscap_iterator_new_filter(policy->model->engines, (oscap_filter_func) xccdf_policy_engine_filter, (void *) sysname);
133133
}
134134

135+
static char *_xccdf_policy_get_readable_item_text(struct xccdf_policy *policy, struct xccdf_item *item, const char *preferred_lang, struct oscap_text_iterator *it)
136+
{
137+
struct oscap_text *unresolved_text = oscap_textlist_get_preferred_text(it, preferred_lang);
138+
if (!unresolved_text)
139+
return oscap_strdup("");
140+
const char *unresolved = oscap_text_get_text(unresolved_text);
141+
/* Resolve <xccdf:sub> elements */
142+
char *resolved = xccdf_policy_substitute(unresolved, policy);
143+
/* Get rid of xhtml elements */
144+
char *plaintext = _xhtml_to_plaintext(resolved);
145+
free(resolved);
146+
char *filtered_plaintext = oscap_remove_excess_whitespace(plaintext);
147+
free(plaintext);
148+
return filtered_plaintext;
149+
}
150+
135151
char *xccdf_policy_get_readable_item_title(struct xccdf_policy *policy, struct xccdf_item *item, const char *preferred_lang)
136152
{
137153
struct oscap_text_iterator *title_it = xccdf_item_get_title(item);
138-
char *unresolved = oscap_textlist_get_preferred_plaintext(title_it, preferred_lang);
154+
char *readable_text = _xccdf_policy_get_readable_item_text(policy, item, preferred_lang, title_it);
139155
oscap_text_iterator_free(title_it);
140-
if (!unresolved)
141-
return oscap_strdup("");
142-
char *resolved = xccdf_policy_substitute(unresolved, policy);
143-
free(unresolved);
144-
return resolved;
156+
return readable_text;
157+
}
158+
159+
bool xccdf_policy_get_show_rule_details(struct xccdf_policy *policy)
160+
{
161+
if (policy == NULL)
162+
return false;
163+
return policy->show_rule_details;
145164
}
146165

147166
char *xccdf_policy_get_readable_item_description(struct xccdf_policy *policy, struct xccdf_item *item, const char *preferred_lang)
148167
{
149-
/* Get description in prefered language */
168+
/* Get description in preferred language */
150169
struct oscap_text_iterator *description_it = xccdf_item_get_description(item);
151-
struct oscap_text *unresolved_text = oscap_textlist_get_preferred_text(description_it, preferred_lang);
170+
char *readable_text = _xccdf_policy_get_readable_item_text(policy, item, preferred_lang, description_it);
152171
oscap_text_iterator_free(description_it);
153-
if (!unresolved_text)
154-
return oscap_strdup("");
155-
const char *unresolved = oscap_text_get_text(unresolved_text);
156-
/* Resolve <xccdf:sub> elements */
157-
char *resolved = xccdf_policy_substitute(unresolved, policy);
158-
/* Get a rid of xhtml elements */
159-
char *plaintext = _xhtml_to_plaintext(resolved);
160-
free(resolved);
161-
return plaintext;
172+
return readable_text;
173+
}
174+
175+
char *xccdf_policy_get_readable_item_rationale(struct xccdf_policy *policy, struct xccdf_item *item, const char *preferred_lang)
176+
{
177+
/* Get rationale in preferred language */
178+
struct oscap_text_iterator *rat_it = xccdf_item_get_rationale(item);
179+
char *readable_text = _xccdf_policy_get_readable_item_text(policy, item, preferred_lang, rat_it);
180+
oscap_text_iterator_free(rat_it);
181+
return readable_text;
162182
}
163183

164184
/**
@@ -1934,6 +1954,7 @@ struct xccdf_policy * xccdf_policy_new(struct xccdf_policy_model * model, struct
19341954
xccdf_policy_resolve_item(policy, item, true);
19351955
}
19361956
xccdf_item_iterator_free(item_it);
1957+
policy->show_rule_details = model->show_rule_details;
19371958
return policy;
19381959
}
19391960

@@ -2371,4 +2392,7 @@ void xccdf_value_binding_free(struct xccdf_value_binding * binding) {
23712392
free(binding);
23722393
}
23732394

2374-
2395+
void xccdf_policy_model_set_show_rule_details(struct xccdf_policy_model *policy_model, bool show_rule_details)
2396+
{
2397+
policy_model->show_rule_details = show_rule_details;
2398+
}

src/XCCDF_POLICY/xccdf_policy_priv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct xccdf_policy_model {
4444
struct oscap_list * engines; ///< Callbacks for checking engines (see xccdf_policy_engine)
4545

4646
struct cpe_session *cpe;
47+
bool show_rule_details;
4748
};
4849

4950
/**
@@ -77,6 +78,7 @@ struct xccdf_policy {
7778
char *href;
7879
char *title;
7980
} reference_filter;
81+
bool show_rule_details;
8082
};
8183

8284

src/common/public/oscap_helpers.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,12 @@ OSCAP_API char *oscap_dirname(char *path);
6969
*/
7070
OSCAP_API char *oscap_strtok_r(char *str, const char *delim, char **saveptr);
7171

72+
/**
73+
* Indent a string
74+
* @param str string to indent
75+
* @param indent number of spaces to indent
76+
* @return indented string
77+
*/
78+
OSCAP_API char *oscap_indent(const char *str, int indent);
79+
7280
#endif /* OSCAP_HELPERS_H */

src/common/util.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,94 @@ char *oscap_concat(char *str1, char *str2)
504504
strncat(str1, str2, str2_len);
505505
return str1;
506506
}
507+
508+
char *oscap_indent(const char *str, int indent)
509+
{
510+
if (str == NULL) {
511+
return NULL;
512+
}
513+
if (indent <= 0) {
514+
return oscap_strdup(str);
515+
}
516+
517+
size_t line_count = 1;
518+
for (const char *p = str; *p; p++) {
519+
if (*p == '\n' && *(p + 1) != '\0') {
520+
line_count++;
521+
}
522+
}
523+
524+
size_t str_len = strlen(str);
525+
size_t result_len = str_len + (indent * line_count);
526+
char *result = malloc(result_len + 1);
527+
if (result == NULL) {
528+
return NULL;
529+
}
530+
531+
char *dst = result;
532+
const char *src = str;
533+
534+
if (*src != '\n') {
535+
for (int i = 0; i < indent; i++) {
536+
*dst++ = ' ';
537+
}
538+
}
539+
540+
while (*src) {
541+
*dst++ = *src;
542+
if (*src == '\n' && *(src + 1) != '\0' && *(src + 1) != '\n') {
543+
// Add indent after newline (unless it's the last character)
544+
for (int i = 0; i < indent; i++) {
545+
*dst++ = ' ';
546+
}
547+
}
548+
src++;
549+
}
550+
551+
*dst = '\0';
552+
return result;
553+
}
554+
555+
char *oscap_remove_excess_whitespace(const char *str)
556+
{
557+
if (str == NULL) {
558+
return NULL;
559+
}
560+
561+
size_t str_len = strlen(str);
562+
char *result = malloc(str_len + 1);
563+
if (result == NULL) {
564+
return NULL;
565+
}
566+
567+
const char *src = str;
568+
char *dst = result;
569+
bool at_line_start = true;
570+
int trailing_spaces = 0;
571+
572+
while (*src) {
573+
if (*src == '\n') {
574+
trailing_spaces = 0;
575+
if (!at_line_start) {
576+
*dst++ = '\n';
577+
}
578+
at_line_start = true;
579+
src++;
580+
} else if (at_line_start && isspace(*src)) {
581+
src++;
582+
} else if (isspace(*src)) {
583+
trailing_spaces++;
584+
src++;
585+
} else {
586+
at_line_start = false;
587+
for (int i = 0; i < trailing_spaces; i++) {
588+
*dst++ = ' ';
589+
}
590+
trailing_spaces = 0;
591+
*dst++ = *src++;
592+
}
593+
}
594+
595+
*dst = '\0';
596+
return result;
597+
}

0 commit comments

Comments
 (0)