Skip to content

Commit 995c217

Browse files
committed
Add --list-vars option to oscap info
The new option `--list-vars` lists all XCCDF values used by the given profile, including their values. Resolves: https://issues.redhat.com/browse/RHEL-143569
1 parent 4342ab7 commit 995c217

7 files changed

Lines changed: 125 additions & 1 deletion

File tree

docs/manual/manual.adoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,22 @@ xccdf_org.ssgproject.content_rule_partition_for_var
234234
The `--list-rules` option requires `--profile`. Running `--list-rules` without
235235
`--profile` will produce an error.
236236

237+
=== Listing variables set by a profile
238+
239+
To list the XCCDF Values (variables) and their resolved values for a given
240+
profile, use the `--list-vars` option together with `--profile`. Each line
241+
contains a Value ID and its resolved value, separated by a tab character.
242+
243+
----
244+
$ oscap info --profile ospp --list-vars /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml
245+
xccdf_org.ssgproject.content_value_var_password_minlen 15
246+
xccdf_org.ssgproject.content_value_var_accounts_max_concurrent_login_sessions 10
247+
...
248+
----
249+
250+
The `--list-vars` option requires `--profile`. Running `--list-vars` without
251+
`--profile` will produce an error.
252+
237253
=== Displaying information about SCAP result data streams
238254

239255
The `oscap info` command is also helpful with other SCAP file types such as

tests/API/XCCDF/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,6 @@ add_oscap_test("test_no_newline_between_select_elements.sh")
114114
add_oscap_test("test_single_line_tailoring.sh")
115115
add_oscap_test("test_reference.sh")
116116
add_oscap_test("test_list_rules.sh")
117+
add_oscap_test("test_list_vars.sh")
117118
add_oscap_test("test_remediation_bootc.sh")
118119
add_oscap_test("openscap_2289_regression.sh")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
. $builddir/tests/test_common.sh
3+
4+
set -e
5+
set -o pipefail
6+
7+
stderr=$(mktemp -t ${name}.err.XXXXXX)
8+
stdout=$(mktemp -t ${name}.out.XXXXXX)
9+
10+
ds="$srcdir/test_reference_ds.xml"
11+
p1="xccdf_com.example.www_profile_P1"
12+
13+
# Test 1: --list-vars with --profile prints value IDs and resolved values
14+
$OSCAP info --profile $p1 --list-vars $ds > $stdout 2> $stderr
15+
[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr
16+
grep -q "xccdf_com.example.www_value_V1 42" $stdout
17+
grep -q "xccdf_com.example.www_value_V2 custom_val" $stdout
18+
# Verify output contains exactly 2 lines
19+
[ "$(wc -l < $stdout)" -eq 2 ]
20+
:> $stdout
21+
22+
# Test 2: --list-vars without --profile produces an error
23+
$OSCAP info --list-vars $ds > $stdout 2> $stderr && exit 1 || true
24+
grep -q "\-\-list-vars option requires \-\-profile" $stderr
25+
:> $stdout
26+
:> $stderr
27+
28+
rm -f $stdout $stderr

tests/API/XCCDF/unittests/test_reference_ds.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,19 @@
6767
<select idref="xccdf_com.example.www_rule_R2" selected="true"/>
6868
<select idref="xccdf_com.example.www_rule_R3" selected="true"/>
6969
<select idref="xccdf_com.example.www_rule_R4" selected="true"/>
70+
<set-value idref="xccdf_com.example.www_value_V1">42</set-value>
71+
<refine-value idref="xccdf_com.example.www_value_V2" selector="custom"/>
7072
</Profile>
73+
<Value id="xccdf_com.example.www_value_V1" type="number">
74+
<title>Value V1</title>
75+
<value>10</value>
76+
<value selector="twenty">20</value>
77+
</Value>
78+
<Value id="xccdf_com.example.www_value_V2" type="string">
79+
<title>Value V2</title>
80+
<value>default_val</value>
81+
<value selector="custom">custom_val</value>
82+
</Value>
7183
<Rule selected="true" id="xccdf_com.example.www_rule_R1">
7284
<title>Rule R1</title>
7385
<description>Description</description>

utils/oscap-info.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct oscap_module OSCAP_INFO_MODULE = {
6464
.help = "Options:\n"
6565
" --fetch-remote-resources - Download remote content referenced by data stream.\n"
6666
" --list-rules - Print selected rule IDs for the given profile (requires --profile).\n"
67+
" --list-vars - Print XCCDF Value IDs and their resolved values for the given profile (requires --profile).\n"
6768
" --local-files <dir> - Use locally downloaded copies of remote resources stored in the given directory.\n"
6869
" --profile <id> - Show info of the profile with the given ID.\n"
6970
" --profiles - Show profiles from the input file in the <id>:<title> format, one line per profile.\n"
@@ -418,6 +419,49 @@ static void _print_rules_for_profile(struct xccdf_benchmark *bench, const char *
418419
xccdf_policy_model_free(policy_model);
419420
}
420421

422+
static void _print_vars_for_profile(struct xccdf_benchmark *bench, const char *profile_id)
423+
{
424+
struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench);
425+
struct xccdf_policy *policy = xccdf_policy_model_get_policy_by_id(policy_model, profile_id);
426+
if (policy == NULL) {
427+
xccdf_policy_model_free(policy_model);
428+
return;
429+
}
430+
struct xccdf_profile *profile = xccdf_policy_get_profile(policy);
431+
if (profile == NULL) {
432+
xccdf_policy_model_free(policy_model);
433+
return;
434+
}
435+
436+
struct xccdf_setvalue_iterator *sv_it = xccdf_profile_get_setvalues(profile);
437+
while (xccdf_setvalue_iterator_has_more(sv_it)) {
438+
struct xccdf_setvalue *sv = xccdf_setvalue_iterator_next(sv_it);
439+
const char *value_id = xccdf_setvalue_get_item(sv);
440+
struct xccdf_item *item = xccdf_benchmark_get_item(bench, value_id);
441+
if (item != NULL) {
442+
const char *resolved = xccdf_policy_get_value_of_item(policy, item);
443+
if (resolved != NULL)
444+
printf("%s\t%s\n", value_id, resolved);
445+
}
446+
}
447+
xccdf_setvalue_iterator_free(sv_it);
448+
449+
struct xccdf_refine_value_iterator *rv_it = xccdf_profile_get_refine_values(profile);
450+
while (xccdf_refine_value_iterator_has_more(rv_it)) {
451+
struct xccdf_refine_value *rv = xccdf_refine_value_iterator_next(rv_it);
452+
const char *value_id = xccdf_refine_value_get_item(rv);
453+
struct xccdf_item *item = xccdf_benchmark_get_item(bench, value_id);
454+
if (item != NULL) {
455+
const char *resolved = xccdf_policy_get_value_of_item(policy, item);
456+
if (resolved != NULL)
457+
printf("%s\t%s\n", value_id, resolved);
458+
}
459+
}
460+
xccdf_refine_value_iterator_free(rv_it);
461+
462+
xccdf_policy_model_free(policy_model);
463+
}
464+
421465
static int app_info_single_ds_one_profile(struct ds_stream_index_iterator* sds_it, struct ds_sds_session *session, const struct oscap_action *action)
422466
{
423467
const char *profile_suffix = action->profile;
@@ -426,7 +470,7 @@ static int app_info_single_ds_one_profile(struct ds_stream_index_iterator* sds_i
426470
struct ds_stream_index * stream = ds_stream_index_iterator_next(sds_it);
427471
struct oscap_string_iterator* checklist_it = ds_stream_index_get_checklists(stream);
428472

429-
if (!action->list_rules) {
473+
if (!action->list_rules && !action->list_vars) {
430474
printf("\nStream: %s\n", ds_stream_index_get_id(stream));
431475
printf("Generated: %s\n", ds_stream_index_get_timestamp(stream));
432476
printf("Version: %s\n", ds_stream_index_get_version(stream));
@@ -458,6 +502,9 @@ static int app_info_single_ds_one_profile(struct ds_stream_index_iterator* sds_i
458502
if (action->list_rules) {
459503
_print_rules_for_profile(bench, profile_id);
460504
// bench is freed by policy_model inside _print_rules_for_profile
505+
} else if (action->list_vars) {
506+
_print_vars_for_profile(bench, profile_id);
507+
// bench is freed by policy_model inside _print_vars_for_profile
461508
} else {
462509
_print_single_benchmark_one_profile(bench, profile_id);
463510
xccdf_benchmark_free(bench);
@@ -541,6 +588,9 @@ static void app_info_single_benchmark(struct xccdf_benchmark *bench, const struc
541588
if (action->list_rules) {
542589
_print_rules_for_profile(bench, profile_id);
543590
// bench is freed by policy_model inside _print_rules_for_profile
591+
} else if (action->list_vars) {
592+
_print_vars_for_profile(bench, profile_id);
593+
// bench is freed by policy_model inside _print_vars_for_profile
544594
} else {
545595
_print_single_benchmark_one_profile(bench, profile_id);
546596
xccdf_benchmark_free(bench);
@@ -805,6 +855,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action)
805855
enum oscap_info_opts {
806856
OSCAP_INFO_OPT_REMOTE_RESOURCES,
807857
OSCAP_INFO_OPT_LIST_RULES,
858+
OSCAP_INFO_OPT_LIST_VARS,
808859
OSCAP_INFO_OPT_LOCAL_FILES,
809860
OSCAP_INFO_OPT_PROFILE,
810861
OSCAP_INFO_OPT_PROFILES,
@@ -816,6 +867,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action)
816867
const struct option long_options[] = {
817868
{"fetch-remote-resources", no_argument, &action->remote_resources, 1},
818869
{"list-rules", no_argument, 0, OSCAP_INFO_OPT_LIST_RULES},
870+
{"list-vars", no_argument, 0, OSCAP_INFO_OPT_LIST_VARS},
819871
{"local-files", required_argument, NULL, OSCAP_INFO_OPT_LOCAL_FILES},
820872
{"profile", required_argument, 0, OSCAP_INFO_OPT_PROFILE},
821873
{"profiles", no_argument, 0, OSCAP_INFO_OPT_PROFILES},
@@ -834,6 +886,10 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action)
834886
action->list_rules = 1;
835887
action->provide_machine_readable_output = 1;
836888
break;
889+
case OSCAP_INFO_OPT_LIST_VARS:
890+
action->list_vars = 1;
891+
action->provide_machine_readable_output = 1;
892+
break;
837893
case OSCAP_INFO_OPT_PROFILE:
838894
action->profile = optarg;
839895
break;
@@ -862,6 +918,11 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action)
862918
return false;
863919
}
864920

921+
if (action->list_vars && action->profile == NULL) {
922+
oscap_module_usage(action->module, stderr, "The --list-vars option requires --profile.\n");
923+
return false;
924+
}
925+
865926
if (optind >= argc) {
866927
oscap_module_usage(action->module, stderr, "SCAP file needs to be specified!\n");
867928
return false;

utils/oscap-tool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ struct oscap_action {
164164
int raw;
165165
int show_rule_details;
166166
int list_rules;
167+
int list_vars;
167168
};
168169

169170
int app_xslt(const char *infile, const char *xsltfile, const char *outfile, const char **params);

utils/oscap.8

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ Allow download of remote components referenced from data stream.
6868
Print IDs of XCCDF rules that are selected by the given profile, one per line. This option must be used together with \fB\-\-profile\fR. The output is machine-readable and contains only rule IDs with no additional decoration.
6969
.RE
7070
.TP
71+
\fB\-\-list-vars\fR
72+
.RS
73+
Print XCCDF Value IDs and their resolved values for the given profile, one per line, tab-separated. This option must be used together with \fB\-\-profile\fR. The output is machine-readable.
74+
.RE
75+
.TP
7176
\fB\-\-local-files DIRECTORY\fR
7277
.RS
7378
Instead of downloading remote data stream components from the network, use data stream components stored locally as files in the given directory. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.

0 commit comments

Comments
 (0)