From e2c0bba429254cb4bfdb0cc52b2a5312d49cda85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Mon, 16 Feb 2026 13:28:34 +0100 Subject: [PATCH 1/9] Add --list-rules option to oscap info Add a new --list-rules option to the oscap info module that, when combined with --profile, prints the IDs of all XCCDF rules selected by the given profile. The output is machine-readable (one rule ID per line with no decoration), making it suitable for CI/CD automation, auditing, and tailoring validation workflows. Resolves: https://issues.redhat.com/browse/RHEL-143569 --- docs/manual/manual.adoc | 17 +++++ tests/API/XCCDF/unittests/CMakeLists.txt | 1 + tests/API/XCCDF/unittests/test_list_rules.sh | 30 +++++++++ utils/oscap-info.c | 65 +++++++++++++++++--- utils/oscap-tool.h | 1 + utils/oscap.8 | 5 ++ 6 files changed, 110 insertions(+), 9 deletions(-) create mode 100755 tests/API/XCCDF/unittests/test_list_rules.sh diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc index 8ae1685900..b32f125dfb 100644 --- a/docs/manual/manual.adoc +++ b/docs/manual/manual.adoc @@ -217,6 +217,23 @@ description, use the `--profile` option followed by the profile ID. $ oscap info --profile xccdf_org.ssgproject.content_profile_ospp /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml ---- +=== Listing rules selected by a profile + +To list the IDs of all XCCDF rules that are selected by a given profile, use +the `--list-rules` option together with `--profile`. The output contains one +rule ID per line and is machine-readable, which makes it suitable for scripting, +CI/CD pipelines, and tailoring validation workflows. + +---- +$ oscap info --profile ospp --list-rules /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml +xccdf_org.ssgproject.content_rule_partition_for_tmp +xccdf_org.ssgproject.content_rule_partition_for_var +... +---- + +The `--list-rules` option requires `--profile`. Running `--list-rules` without +`--profile` will produce an error. + === Displaying information about SCAP result data streams The `oscap info` command is also helpful with other SCAP file types such as diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt index 674e2b29b1..d4dfd5a8ce 100644 --- a/tests/API/XCCDF/unittests/CMakeLists.txt +++ b/tests/API/XCCDF/unittests/CMakeLists.txt @@ -113,5 +113,6 @@ add_oscap_test("test_skip_rule.sh") add_oscap_test("test_no_newline_between_select_elements.sh") add_oscap_test("test_single_line_tailoring.sh") add_oscap_test("test_reference.sh") +add_oscap_test("test_list_rules.sh") add_oscap_test("test_remediation_bootc.sh") add_oscap_test("openscap_2289_regression.sh") diff --git a/tests/API/XCCDF/unittests/test_list_rules.sh b/tests/API/XCCDF/unittests/test_list_rules.sh new file mode 100755 index 0000000000..1056bfac69 --- /dev/null +++ b/tests/API/XCCDF/unittests/test_list_rules.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +. $builddir/tests/test_common.sh + +set -e +set -o pipefail + +stderr=$(mktemp -t ${name}.err.XXXXXX) +stdout=$(mktemp -t ${name}.out.XXXXXX) + +ds="$srcdir/test_reference_ds.xml" +p1="xccdf_com.example.www_profile_P1" + +# Test 1: --list-rules with --profile prints selected rule IDs +$OSCAP info --profile $p1 --list-rules $ds > $stdout 2> $stderr +[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +grep -q "xccdf_com.example.www_rule_R1" $stdout +grep -q "xccdf_com.example.www_rule_R2" $stdout +grep -q "xccdf_com.example.www_rule_R3" $stdout +grep -q "xccdf_com.example.www_rule_R4" $stdout +# Verify output contains only rule IDs, one per line (4 rules = 4 lines) +[ "$(wc -l < $stdout)" -eq 4 ] +:> $stdout + +# Test 2: --list-rules without --profile produces an error +$OSCAP info --list-rules $ds > $stdout 2> $stderr && exit 1 || true +grep -q "\-\-list-rules option requires \-\-profile" $stderr +:> $stdout +:> $stderr + +rm -f $stdout $stderr diff --git a/utils/oscap-info.c b/utils/oscap-info.c index a707ee6e4e..e30a1f1717 100644 --- a/utils/oscap-info.c +++ b/utils/oscap-info.c @@ -63,6 +63,7 @@ struct oscap_module OSCAP_INFO_MODULE = { .usage = "some-file.xml", .help = "Options:\n" " --fetch-remote-resources - Download remote content referenced by data stream.\n" + " --list-rules - Print selected rule IDs for the given profile (requires --profile).\n" " --local-files - Use locally downloaded copies of remote resources stored in the given directory.\n" " --profile - Show info of the profile with the given ID.\n" " --profiles - Show profiles from the input file in the : format, one line per profile.\n" @@ -400,15 +401,36 @@ static const char *benchmark_get_profile_or_report_id_issues(struct xccdf_benchm return result; } -static int app_info_single_ds_one_profile(struct ds_stream_index_iterator* sds_it, struct ds_sds_session *session, const char *profile_suffix, const char *filename) +static void _print_rules_for_profile(struct xccdf_benchmark *bench, const char *profile_id) { + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + struct xccdf_policy *policy = xccdf_policy_model_get_policy_by_id(policy_model, profile_id); + if (policy == NULL) { + xccdf_policy_model_free(policy_model); + return; + } + struct xccdf_select_iterator *sel_it = xccdf_policy_get_selected_rules(policy); + while (xccdf_select_iterator_has_more(sel_it)) { + struct xccdf_select *sel = xccdf_select_iterator_next(sel_it); + printf("%s\n", xccdf_select_get_item(sel)); + } + xccdf_select_iterator_free(sel_it); + xccdf_policy_model_free(policy_model); +} + +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) +{ + const char *profile_suffix = action->profile; + const char *filename = action->file; const char *prefix = ""; struct ds_stream_index * stream = ds_stream_index_iterator_next(sds_it); struct oscap_string_iterator* checklist_it = ds_stream_index_get_checklists(stream); - printf("\nStream: %s\n", ds_stream_index_get_id(stream)); - printf("Generated: %s\n", ds_stream_index_get_timestamp(stream)); - printf("Version: %s\n", ds_stream_index_get_version(stream)); + if (!action->list_rules) { + printf("\nStream: %s\n", ds_stream_index_get_id(stream)); + printf("Generated: %s\n", ds_stream_index_get_timestamp(stream)); + printf("Version: %s\n", ds_stream_index_get_version(stream)); + } bool profile_not_found = true; while (oscap_string_iterator_has_more(checklist_it) && profile_not_found) { @@ -433,10 +455,17 @@ static int app_info_single_ds_one_profile(struct ds_stream_index_iterator* sds_i } const char *profile_id = benchmark_get_profile_or_report_multiple_ids(bench, profile_suffix, filename); if (profile_id != NULL) { - _print_single_benchmark_one_profile(bench, profile_id); + if (action->list_rules) { + _print_rules_for_profile(bench, profile_id); + // bench is freed by policy_model inside _print_rules_for_profile + } else { + _print_single_benchmark_one_profile(bench, profile_id); + xccdf_benchmark_free(bench); + } profile_not_found = false; + } else { + xccdf_benchmark_free(bench); } - xccdf_benchmark_free(bench); } else if (oscap_source_get_scap_type(xccdf_source) == OSCAP_DOCUMENT_XCCDF_TAILORING) { struct xccdf_tailoring *tailoring = xccdf_tailoring_import_source(xccdf_source, NULL); @@ -509,9 +538,16 @@ static void app_info_single_benchmark(struct xccdf_benchmark *bench, const struc } else if (action->profile) { const char *profile_id = benchmark_get_profile_or_report_id_issues(bench, action->profile, action->file); if (profile_id != NULL) { - _print_single_benchmark_one_profile(bench, profile_id); + if (action->list_rules) { + _print_rules_for_profile(bench, profile_id); + // bench is freed by policy_model inside _print_rules_for_profile + } else { + _print_single_benchmark_one_profile(bench, profile_id); + xccdf_benchmark_free(bench); + } + } else { + xccdf_benchmark_free(bench); } - xccdf_benchmark_free(bench); } else { printf("Checklist version: %s\n", oscap_source_get_schema_version(source)); print_time(action->file); @@ -527,7 +563,7 @@ static int app_info_single_ds(struct ds_stream_index_iterator* sds_it, struct ds if (action->show_profiles_only) { return_value = app_info_single_ds_profiles_only(sds_it, session, action); } else if (action->profile) { - return_value = app_info_single_ds_one_profile(sds_it, session, action->profile, action->file); + return_value = app_info_single_ds_one_profile(sds_it, session, action); } else { return_value = app_info_single_ds_all(sds_it, session, action); } @@ -768,6 +804,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) enum oscap_info_opts { OSCAP_INFO_OPT_REMOTE_RESOURCES, + OSCAP_INFO_OPT_LIST_RULES, OSCAP_INFO_OPT_LOCAL_FILES, OSCAP_INFO_OPT_PROFILE, OSCAP_INFO_OPT_PROFILES, @@ -778,6 +815,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) /* Command-options */ const struct option long_options[] = { {"fetch-remote-resources", no_argument, &action->remote_resources, 1}, + {"list-rules", no_argument, 0, OSCAP_INFO_OPT_LIST_RULES}, {"local-files", required_argument, NULL, OSCAP_INFO_OPT_LOCAL_FILES}, {"profile", required_argument, 0, OSCAP_INFO_OPT_PROFILE}, {"profiles", no_argument, 0, OSCAP_INFO_OPT_PROFILES}, @@ -792,6 +830,10 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) { switch(c) { case 0: break; + case OSCAP_INFO_OPT_LIST_RULES: + action->list_rules = 1; + action->provide_machine_readable_output = 1; + break; case OSCAP_INFO_OPT_PROFILE: action->profile = optarg; break; @@ -815,6 +857,11 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) } } + if (action->list_rules && action->profile == NULL) { + oscap_module_usage(action->module, stderr, "The --list-rules option requires --profile.\n"); + return false; + } + if (optind >= argc) { oscap_module_usage(action->module, stderr, "SCAP file needs to be specified!\n"); return false; diff --git a/utils/oscap-tool.h b/utils/oscap-tool.h index ff4880dddc..766fecd857 100644 --- a/utils/oscap-tool.h +++ b/utils/oscap-tool.h @@ -163,6 +163,7 @@ struct oscap_action { int references; int raw; int show_rule_details; + int list_rules; }; int app_xslt(const char *infile, const char *xsltfile, const char *outfile, const char **params); diff --git a/utils/oscap.8 b/utils/oscap.8 index 4faffcb403..57136fd8a9 100644 --- a/utils/oscap.8 +++ b/utils/oscap.8 @@ -63,6 +63,11 @@ For XCCDF or SCAP source data stream files, the info module prints out IDs of in Allow download of remote components referenced from data stream. .RE .TP +\fB\-\-list-rules\fR +.RS +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. +.RE +.TP \fB\-\-local-files DIRECTORY\fR .RS 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. From 90228c38f672c6cd84db628e678e37ad55240fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com> Date: Mon, 16 Feb 2026 14:10:20 +0100 Subject: [PATCH 2/9] 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 --- docs/manual/manual.adoc | 16 +++++ tests/API/XCCDF/unittests/CMakeLists.txt | 1 + tests/API/XCCDF/unittests/test_list_vars.sh | 28 +++++++++ .../API/XCCDF/unittests/test_reference_ds.xml | 12 ++++ utils/oscap-info.c | 63 ++++++++++++++++++- utils/oscap-tool.h | 1 + utils/oscap.8 | 5 ++ 7 files changed, 125 insertions(+), 1 deletion(-) create mode 100755 tests/API/XCCDF/unittests/test_list_vars.sh diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc index b32f125dfb..ef03bb7fb8 100644 --- a/docs/manual/manual.adoc +++ b/docs/manual/manual.adoc @@ -234,6 +234,22 @@ xccdf_org.ssgproject.content_rule_partition_for_var The `--list-rules` option requires `--profile`. Running `--list-rules` without `--profile` will produce an error. +=== Listing variables set by a profile + +To list the XCCDF Values (variables) and their resolved values for a given +profile, use the `--list-vars` option together with `--profile`. Each line +contains a Value ID and its resolved value, separated by a tab character. + +---- +$ oscap info --profile ospp --list-vars /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml +xccdf_org.ssgproject.content_value_var_password_minlen 15 +xccdf_org.ssgproject.content_value_var_accounts_max_concurrent_login_sessions 10 +... +---- + +The `--list-vars` option requires `--profile`. Running `--list-vars` without +`--profile` will produce an error. + === Displaying information about SCAP result data streams The `oscap info` command is also helpful with other SCAP file types such as diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt index d4dfd5a8ce..f5a3829ba6 100644 --- a/tests/API/XCCDF/unittests/CMakeLists.txt +++ b/tests/API/XCCDF/unittests/CMakeLists.txt @@ -114,5 +114,6 @@ add_oscap_test("test_no_newline_between_select_elements.sh") add_oscap_test("test_single_line_tailoring.sh") add_oscap_test("test_reference.sh") add_oscap_test("test_list_rules.sh") +add_oscap_test("test_list_vars.sh") add_oscap_test("test_remediation_bootc.sh") add_oscap_test("openscap_2289_regression.sh") diff --git a/tests/API/XCCDF/unittests/test_list_vars.sh b/tests/API/XCCDF/unittests/test_list_vars.sh new file mode 100755 index 0000000000..516786fa95 --- /dev/null +++ b/tests/API/XCCDF/unittests/test_list_vars.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +. $builddir/tests/test_common.sh + +set -e +set -o pipefail + +stderr=$(mktemp -t ${name}.err.XXXXXX) +stdout=$(mktemp -t ${name}.out.XXXXXX) + +ds="$srcdir/test_reference_ds.xml" +p1="xccdf_com.example.www_profile_P1" + +# Test 1: --list-vars with --profile prints value IDs and resolved values +$OSCAP info --profile $p1 --list-vars $ds > $stdout 2> $stderr +[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +grep -q "xccdf_com.example.www_value_V1 42" $stdout +grep -q "xccdf_com.example.www_value_V2 custom_val" $stdout +# Verify output contains exactly 2 lines +[ "$(wc -l < $stdout)" -eq 2 ] +:> $stdout + +# Test 2: --list-vars without --profile produces an error +$OSCAP info --list-vars $ds > $stdout 2> $stderr && exit 1 || true +grep -q "\-\-list-vars option requires \-\-profile" $stderr +:> $stdout +:> $stderr + +rm -f $stdout $stderr diff --git a/tests/API/XCCDF/unittests/test_reference_ds.xml b/tests/API/XCCDF/unittests/test_reference_ds.xml index c2bacf0704..863494bbb9 100644 --- a/tests/API/XCCDF/unittests/test_reference_ds.xml +++ b/tests/API/XCCDF/unittests/test_reference_ds.xml @@ -67,7 +67,19 @@ <select idref="xccdf_com.example.www_rule_R2" selected="true"/> <select idref="xccdf_com.example.www_rule_R3" selected="true"/> <select idref="xccdf_com.example.www_rule_R4" selected="true"/> + <set-value idref="xccdf_com.example.www_value_V1">42</set-value> + <refine-value idref="xccdf_com.example.www_value_V2" selector="custom"/> </Profile> + <Value id="xccdf_com.example.www_value_V1" type="number"> + <title>Value V1 + 10 + 20 + + + Value V2 + default_val + custom_val + Rule R1 Description diff --git a/utils/oscap-info.c b/utils/oscap-info.c index e30a1f1717..6fc46b4f9b 100644 --- a/utils/oscap-info.c +++ b/utils/oscap-info.c @@ -64,6 +64,7 @@ struct oscap_module OSCAP_INFO_MODULE = { .help = "Options:\n" " --fetch-remote-resources - Download remote content referenced by data stream.\n" " --list-rules - Print selected rule IDs for the given profile (requires --profile).\n" + " --list-vars - Print XCCDF Value IDs and their resolved values for the given profile (requires --profile).\n" " --local-files - Use locally downloaded copies of remote resources stored in the given directory.\n" " --profile - Show info of the profile with the given ID.\n" " --profiles - Show profiles from the input file in the : format, one line per profile.\n" @@ -418,6 +419,49 @@ static void _print_rules_for_profile(struct xccdf_benchmark *bench, const char * xccdf_policy_model_free(policy_model); } +static void _print_vars_for_profile(struct xccdf_benchmark *bench, const char *profile_id) +{ + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + struct xccdf_policy *policy = xccdf_policy_model_get_policy_by_id(policy_model, profile_id); + if (policy == NULL) { + xccdf_policy_model_free(policy_model); + return; + } + struct xccdf_profile *profile = xccdf_policy_get_profile(policy); + if (profile == NULL) { + xccdf_policy_model_free(policy_model); + return; + } + + struct xccdf_setvalue_iterator *sv_it = xccdf_profile_get_setvalues(profile); + while (xccdf_setvalue_iterator_has_more(sv_it)) { + struct xccdf_setvalue *sv = xccdf_setvalue_iterator_next(sv_it); + const char *value_id = xccdf_setvalue_get_item(sv); + struct xccdf_item *item = xccdf_benchmark_get_item(bench, value_id); + if (item != NULL) { + const char *resolved = xccdf_policy_get_value_of_item(policy, item); + if (resolved != NULL) + printf("%s\t%s\n", value_id, resolved); + } + } + xccdf_setvalue_iterator_free(sv_it); + + struct xccdf_refine_value_iterator *rv_it = xccdf_profile_get_refine_values(profile); + while (xccdf_refine_value_iterator_has_more(rv_it)) { + struct xccdf_refine_value *rv = xccdf_refine_value_iterator_next(rv_it); + const char *value_id = xccdf_refine_value_get_item(rv); + struct xccdf_item *item = xccdf_benchmark_get_item(bench, value_id); + if (item != NULL) { + const char *resolved = xccdf_policy_get_value_of_item(policy, item); + if (resolved != NULL) + printf("%s\t%s\n", value_id, resolved); + } + } + xccdf_refine_value_iterator_free(rv_it); + + xccdf_policy_model_free(policy_model); +} + 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) { 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 struct ds_stream_index * stream = ds_stream_index_iterator_next(sds_it); struct oscap_string_iterator* checklist_it = ds_stream_index_get_checklists(stream); - if (!action->list_rules) { + if (!action->list_rules && !action->list_vars) { printf("\nStream: %s\n", ds_stream_index_get_id(stream)); printf("Generated: %s\n", ds_stream_index_get_timestamp(stream)); 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 if (action->list_rules) { _print_rules_for_profile(bench, profile_id); // bench is freed by policy_model inside _print_rules_for_profile + } else if (action->list_vars) { + _print_vars_for_profile(bench, profile_id); + // bench is freed by policy_model inside _print_vars_for_profile } else { _print_single_benchmark_one_profile(bench, profile_id); xccdf_benchmark_free(bench); @@ -541,6 +588,9 @@ static void app_info_single_benchmark(struct xccdf_benchmark *bench, const struc if (action->list_rules) { _print_rules_for_profile(bench, profile_id); // bench is freed by policy_model inside _print_rules_for_profile + } else if (action->list_vars) { + _print_vars_for_profile(bench, profile_id); + // bench is freed by policy_model inside _print_vars_for_profile } else { _print_single_benchmark_one_profile(bench, profile_id); xccdf_benchmark_free(bench); @@ -805,6 +855,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) enum oscap_info_opts { OSCAP_INFO_OPT_REMOTE_RESOURCES, OSCAP_INFO_OPT_LIST_RULES, + OSCAP_INFO_OPT_LIST_VARS, OSCAP_INFO_OPT_LOCAL_FILES, OSCAP_INFO_OPT_PROFILE, OSCAP_INFO_OPT_PROFILES, @@ -816,6 +867,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) const struct option long_options[] = { {"fetch-remote-resources", no_argument, &action->remote_resources, 1}, {"list-rules", no_argument, 0, OSCAP_INFO_OPT_LIST_RULES}, + {"list-vars", no_argument, 0, OSCAP_INFO_OPT_LIST_VARS}, {"local-files", required_argument, NULL, OSCAP_INFO_OPT_LOCAL_FILES}, {"profile", required_argument, 0, OSCAP_INFO_OPT_PROFILE}, {"profiles", no_argument, 0, OSCAP_INFO_OPT_PROFILES}, @@ -834,6 +886,10 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) action->list_rules = 1; action->provide_machine_readable_output = 1; break; + case OSCAP_INFO_OPT_LIST_VARS: + action->list_vars = 1; + action->provide_machine_readable_output = 1; + break; case OSCAP_INFO_OPT_PROFILE: action->profile = optarg; break; @@ -862,6 +918,11 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) return false; } + if (action->list_vars && action->profile == NULL) { + oscap_module_usage(action->module, stderr, "The --list-vars option requires --profile.\n"); + return false; + } + if (optind >= argc) { oscap_module_usage(action->module, stderr, "SCAP file needs to be specified!\n"); return false; diff --git a/utils/oscap-tool.h b/utils/oscap-tool.h index 766fecd857..6d8cdab2eb 100644 --- a/utils/oscap-tool.h +++ b/utils/oscap-tool.h @@ -164,6 +164,7 @@ struct oscap_action { int raw; int show_rule_details; int list_rules; + int list_vars; }; int app_xslt(const char *infile, const char *xsltfile, const char *outfile, const char **params); diff --git a/utils/oscap.8 b/utils/oscap.8 index 57136fd8a9..d3482ba208 100644 --- a/utils/oscap.8 +++ b/utils/oscap.8 @@ -68,6 +68,11 @@ Allow download of remote components referenced from data stream. 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. .RE .TP +\fB\-\-list-vars\fR +.RS +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. +.RE +.TP \fB\-\-local-files DIRECTORY\fR .RS 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. From 2d9993a701a0001eed0b7cbd9443c86285ef1c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com> Date: Mon, 16 Feb 2026 14:35:51 +0100 Subject: [PATCH 3/9] Prevent using --list-rules and --list-vars together There's no check preventing `--list-rules` and `--list-vars` from being passed simultaneously. If both are set, `--list_rules` wins silently because of the `if/else if` chain. We will print an error message in this situation. --- tests/API/XCCDF/unittests/test_list_vars.sh | 6 ++++++ utils/oscap-info.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/tests/API/XCCDF/unittests/test_list_vars.sh b/tests/API/XCCDF/unittests/test_list_vars.sh index 516786fa95..9c0e38a1e0 100755 --- a/tests/API/XCCDF/unittests/test_list_vars.sh +++ b/tests/API/XCCDF/unittests/test_list_vars.sh @@ -25,4 +25,10 @@ grep -q "\-\-list-vars option requires \-\-profile" $stderr :> $stdout :> $stderr +# Test 3: --list-vars with --list-rules produces an error +$OSCAP info --profile $p1 --list-vars --list-rules $ds > $stdout 2> $stderr && exit 1 || true +grep -q "The \-\-list-rules and \-\-list-vars options can't be used at the same time." $stderr +:> $stdout +:> $stderr + rm -f $stdout $stderr diff --git a/utils/oscap-info.c b/utils/oscap-info.c index 6fc46b4f9b..6017fb7eb0 100644 --- a/utils/oscap-info.c +++ b/utils/oscap-info.c @@ -923,6 +923,11 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action) return false; } + if (action->list_rules && action->list_vars) { + oscap_module_usage(action->module, stderr, "The --list-rules and --list-vars options can't be used at the same time.\n"); + return false; + } + if (optind >= argc) { oscap_module_usage(action->module, stderr, "SCAP file needs to be specified!\n"); return false; From da6c717974bc1fc2cddcbed5ff9da0a8534ff45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com> Date: Mon, 16 Feb 2026 15:35:37 +0100 Subject: [PATCH 4/9] Support XCCDF Tailoring files in oscap info --list-rules and --list-vars Refactor _print_rules_for_profile and _print_vars_for_profile to accept a pre-configured xccdf_policy_model instead of a raw benchmark. This enables callers to set tailoring on the policy model before printing, so that profile inheritance and overrides are properly resolved. Add support for two tailoring scenarios: - Standalone XCCDF Tailoring files: resolve the referenced benchmark from the tailoring file's benchmark href, load it, create a policy model with tailoring set, then print the resolved profile's rules or variables. - Source data streams with tailoring components: find the XCCDF benchmark component in the stream, re-import the tailoring with benchmark context, then print via the policy model. --- tests/API/XCCDF/unittests/test_list_rules.sh | 24 +++ tests/API/XCCDF/unittests/test_list_vars.sh | 20 +++ .../test_reference_ds_with_tailoring.xml | 132 ++++++++++++++ .../unittests/test_tailoring_benchmark.xml | 55 ++++++ .../XCCDF/unittests/test_tailoring_file.xml | 11 ++ utils/oscap-info.c | 168 +++++++++++++++--- 6 files changed, 386 insertions(+), 24 deletions(-) create mode 100644 tests/API/XCCDF/unittests/test_reference_ds_with_tailoring.xml create mode 100644 tests/API/XCCDF/unittests/test_tailoring_benchmark.xml create mode 100644 tests/API/XCCDF/unittests/test_tailoring_file.xml diff --git a/tests/API/XCCDF/unittests/test_list_rules.sh b/tests/API/XCCDF/unittests/test_list_rules.sh index 1056bfac69..93d513a24d 100755 --- a/tests/API/XCCDF/unittests/test_list_rules.sh +++ b/tests/API/XCCDF/unittests/test_list_rules.sh @@ -27,4 +27,28 @@ grep -q "\-\-list-rules option requires \-\-profile" $stderr :> $stdout :> $stderr +# Test 3: --list-rules with standalone XCCDF tailoring file +tailoring="$srcdir/test_tailoring_file.xml" +tp="xccdf_com.example.www_profile_P1_tailored" +$OSCAP info --profile $tp --list-rules $tailoring > $stdout 2> $stderr +[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +grep -q "xccdf_com.example.www_rule_R1" $stdout +grep -q "xccdf_com.example.www_rule_R2" $stdout +# R3 and R4 are deselected by tailoring +! grep -q "xccdf_com.example.www_rule_R3" $stdout +! grep -q "xccdf_com.example.www_rule_R4" $stdout +[ "$(wc -l < $stdout)" -eq 2 ] +:> $stdout + +# Test 4: --list-rules with SDS containing tailoring +ds_tailoring="$srcdir/test_reference_ds_with_tailoring.xml" +$OSCAP info --profile $tp --list-rules $ds_tailoring > $stdout 2> $stderr +[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +grep -q "xccdf_com.example.www_rule_R1" $stdout +grep -q "xccdf_com.example.www_rule_R2" $stdout +! grep -q "xccdf_com.example.www_rule_R3" $stdout +! grep -q "xccdf_com.example.www_rule_R4" $stdout +[ "$(wc -l < $stdout)" -eq 2 ] +:> $stdout + rm -f $stdout $stderr diff --git a/tests/API/XCCDF/unittests/test_list_vars.sh b/tests/API/XCCDF/unittests/test_list_vars.sh index 9c0e38a1e0..ae63d6404c 100755 --- a/tests/API/XCCDF/unittests/test_list_vars.sh +++ b/tests/API/XCCDF/unittests/test_list_vars.sh @@ -31,4 +31,24 @@ grep -q "The \-\-list-rules and \-\-list-vars options can't be used at the same :> $stdout :> $stderr +# Test 4: --list-vars with standalone XCCDF tailoring file +tailoring="$srcdir/test_tailoring_file.xml" +tp="xccdf_com.example.www_profile_P1_tailored" +$OSCAP info --profile $tp --list-vars $tailoring > $stdout 2> $stderr +[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +# V1 is overridden to 99 by tailoring, V2 is inherited from base profile +grep -q "xccdf_com.example.www_value_V1 99" $stdout +grep -q "xccdf_com.example.www_value_V2 custom_val" $stdout +[ "$(wc -l < $stdout)" -eq 2 ] +:> $stdout + +# Test 5: --list-vars with SDS containing tailoring +ds_tailoring="$srcdir/test_reference_ds_with_tailoring.xml" +$OSCAP info --profile $tp --list-vars $ds_tailoring > $stdout 2> $stderr +[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +grep -q "xccdf_com.example.www_value_V1 99" $stdout +grep -q "xccdf_com.example.www_value_V2 custom_val" $stdout +[ "$(wc -l < $stdout)" -eq 2 ] +:> $stdout + rm -f $stdout $stderr diff --git a/tests/API/XCCDF/unittests/test_reference_ds_with_tailoring.xml b/tests/API/XCCDF/unittests/test_reference_ds_with_tailoring.xml new file mode 100644 index 0000000000..2a2063c6be --- /dev/null +++ b/tests/API/XCCDF/unittests/test_reference_ds_with_tailoring.xml @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="utf-8"?> +<ds:data-stream-collection xmlns:ds="http://scap.nist.gov/schema/scap/source/1.2" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:cat="urn:oasis:names:tc:entity:xmlns:xml:catalog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="scap_org.open-scap_collection_from_xccdf_test_single_rule.xccdf.xml" schematron-version="1.3" xsi:schemaLocation="http://scap.nist.gov/schema/scap/source/1.2 https://scap.nist.gov/schema/scap/1.3/scap-source-data-stream_1.3.xsd"> + <ds:data-stream id="scap_org.open-scap_datastream_simple" scap-version="1.3" use-case="OTHER"> + <ds:checklists> + <ds:component-ref id="scap_org.open-scap_cref_test_single_rule.xccdf.xml" xlink:href="#scap_org.open-scap_comp_test_single_rule.xccdf.xml"> + <cat:catalog> + <cat:uri name="test_single_rule.oval.xml" uri="#scap_org.open-scap_cref_test_single_rule.oval.xml"/> + </cat:catalog> + </ds:component-ref> + <ds:component-ref id="scap_org.open-scap_cref_tailoring.xml" xlink:href="#scap_org.open-scap_comp_tailoring.xml"/> + </ds:checklists> + <ds:checks> + <ds:component-ref id="scap_org.open-scap_cref_test_single_rule.oval.xml" xlink:href="#scap_org.open-scap_comp_test_single_rule.oval.xml"/> + </ds:checks> + </ds:data-stream> + <ds:component id="scap_org.open-scap_comp_test_single_rule.oval.xml" timestamp="2021-02-01T08:07:06+01:00"> + <oval_definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:ind-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5" xmlns:win-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#windows" xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#independent independent-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#windows windows-definitions-schema.xsd"> + <generator> + <oval:schema_version>5.11.2</oval:schema_version> + <oval:timestamp>2021-02-01T08:07:06+01:00</oval:timestamp> + </generator> + <definitions> + <definition class="compliance" id="oval:x:def:1" version="1"> + <metadata> + <title>PASS + pass + + + + + + + + + + + + + + oval:x:var:1 + + + + + 100 + + + + + + + accepted + Test Benchmark + Description + animals + fruit + 1.0 + + OpenSCAP + OpenSCAP + OpenSCAP + http://scap.nist.gov + + + xccdf_test_profile + This profile is for testing. + + + 42 + + + + Value V1 + 10 + 20 + + + Value V2 + default_val + custom_val + + + Rule R1 + Description + 3.14 + 42.42 + + + + + + Rule R2 + Description + 17.71.777 + 88888888 + + + + + + Rule R3 + Description + 17.71.777 + 666 + + + + + + Rule R4 + Description + + + + + + + + + + 1.0 + + Tailored P1 + + 99 + + + + diff --git a/tests/API/XCCDF/unittests/test_tailoring_benchmark.xml b/tests/API/XCCDF/unittests/test_tailoring_benchmark.xml new file mode 100644 index 0000000000..61a9b303f1 --- /dev/null +++ b/tests/API/XCCDF/unittests/test_tailoring_benchmark.xml @@ -0,0 +1,55 @@ + + + accepted + Test Benchmark + Description + 1.0 + + xccdf_test_profile + This profile is for testing. + + + 42 + + + + Value V1 + 10 + 20 + + + Value V2 + default_val + custom_val + + + Rule R1 + Description + + + + + + Rule R2 + Description + + + + + + Rule R3 + Description + + + + + + Rule R4 + Description + + + + + diff --git a/tests/API/XCCDF/unittests/test_tailoring_file.xml b/tests/API/XCCDF/unittests/test_tailoring_file.xml new file mode 100644 index 0000000000..5b0cb04682 --- /dev/null +++ b/tests/API/XCCDF/unittests/test_tailoring_file.xml @@ -0,0 +1,11 @@ + + + + 1.0 + + Tailored P1 + + 99 + + diff --git a/utils/oscap-info.c b/utils/oscap-info.c index 6017fb7eb0..882f42e62b 100644 --- a/utils/oscap-info.c +++ b/utils/oscap-info.c @@ -39,6 +39,7 @@ #include #include +#include #include "oscap_source.h" #include #include @@ -402,12 +403,10 @@ static const char *benchmark_get_profile_or_report_id_issues(struct xccdf_benchm return result; } -static void _print_rules_for_profile(struct xccdf_benchmark *bench, const char *profile_id) +static void _print_rules_for_profile(struct xccdf_policy_model *policy_model, const char *profile_id) { - struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); struct xccdf_policy *policy = xccdf_policy_model_get_policy_by_id(policy_model, profile_id); if (policy == NULL) { - xccdf_policy_model_free(policy_model); return; } struct xccdf_select_iterator *sel_it = xccdf_policy_get_selected_rules(policy); @@ -416,23 +415,21 @@ static void _print_rules_for_profile(struct xccdf_benchmark *bench, const char * printf("%s\n", xccdf_select_get_item(sel)); } xccdf_select_iterator_free(sel_it); - xccdf_policy_model_free(policy_model); } -static void _print_vars_for_profile(struct xccdf_benchmark *bench, const char *profile_id) +static void _print_vars_for_profile(struct xccdf_policy_model *policy_model, const char *profile_id) { - struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); struct xccdf_policy *policy = xccdf_policy_model_get_policy_by_id(policy_model, profile_id); if (policy == NULL) { - xccdf_policy_model_free(policy_model); return; } struct xccdf_profile *profile = xccdf_policy_get_profile(policy); if (profile == NULL) { - xccdf_policy_model_free(policy_model); return; } + struct xccdf_benchmark *bench = xccdf_policy_model_get_benchmark(policy_model); + struct xccdf_setvalue_iterator *sv_it = xccdf_profile_get_setvalues(profile); while (xccdf_setvalue_iterator_has_more(sv_it)) { struct xccdf_setvalue *sv = xccdf_setvalue_iterator_next(sv_it); @@ -458,8 +455,51 @@ static void _print_vars_for_profile(struct xccdf_benchmark *bench, const char *p } } xccdf_refine_value_iterator_free(rv_it); +} - xccdf_policy_model_free(policy_model); +static struct xccdf_benchmark *_resolve_benchmark_for_tailoring( + struct oscap_source *tailoring_source, + const char *tailoring_filepath, + struct oscap_source **out_bench_source) +{ + *out_bench_source = NULL; + + struct xccdf_tailoring *tailoring = xccdf_tailoring_import_source(tailoring_source, NULL); + if (tailoring == NULL) { + return NULL; + } + + const char *ref = xccdf_tailoring_get_benchmark_ref(tailoring); + char *benchmark_ref = ref ? strdup(ref) : NULL; + xccdf_tailoring_free(tailoring); + + if (benchmark_ref == NULL) { + fprintf(stderr, "The tailoring file doesn't contain a benchmark reference.\n"); + return NULL; + } + + char *filepath_cpy = strdup(tailoring_filepath); + char *dir = oscap_dirname(filepath_cpy); + char *benchmark_path = benchmark_ref[0] == '/' ? + strdup(benchmark_ref) : oscap_sprintf("%s/%s", dir, benchmark_ref); + free(dir); + free(filepath_cpy); + free(benchmark_ref); + + struct oscap_source *bench_source = oscap_source_new_from_file(benchmark_path); + free(benchmark_path); + if (bench_source == NULL) { + return NULL; + } + + struct xccdf_benchmark *bench = xccdf_benchmark_import_source(bench_source); + if (bench == NULL) { + oscap_source_free(bench_source); + return NULL; + } + + *out_bench_source = bench_source; + return bench; } 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) @@ -500,11 +540,13 @@ static int app_info_single_ds_one_profile(struct ds_stream_index_iterator* sds_i const char *profile_id = benchmark_get_profile_or_report_multiple_ids(bench, profile_suffix, filename); if (profile_id != NULL) { if (action->list_rules) { - _print_rules_for_profile(bench, profile_id); - // bench is freed by policy_model inside _print_rules_for_profile + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + _print_rules_for_profile(policy_model, profile_id); + xccdf_policy_model_free(policy_model); } else if (action->list_vars) { - _print_vars_for_profile(bench, profile_id); - // bench is freed by policy_model inside _print_vars_for_profile + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + _print_vars_for_profile(policy_model, profile_id); + xccdf_policy_model_free(policy_model); } else { _print_single_benchmark_one_profile(bench, profile_id); xccdf_benchmark_free(bench); @@ -518,12 +560,61 @@ static int app_info_single_ds_one_profile(struct ds_stream_index_iterator* sds_i const char *profile_id = tailoring_get_profile_or_report_multiple_ids(tailoring, profile_suffix, filename); if (profile_id != NULL) { - struct xccdf_profile *profile = xccdf_tailoring_get_profile_by_id(tailoring, profile_id); - _print_xccdf_profile_with_id(profile, prefix); + if (action->list_rules || action->list_vars) { + char *profile_id_dup = strdup(profile_id); + xccdf_tailoring_free(tailoring); + tailoring = NULL; + + const char *stream_id = ds_stream_index_get_id(stream); + + /* Find the XCCDF benchmark component in the stream */ + ds_sds_session_reset(session); + struct oscap_string_iterator *bench_it = ds_stream_index_get_checklists(stream); + struct xccdf_benchmark *bench = NULL; + while (oscap_string_iterator_has_more(bench_it)) { + const char *cl_id = oscap_string_iterator_next(bench_it); + struct oscap_source *src = ds_sds_session_select_checklist(session, stream_id, cl_id, NULL); + if (src != NULL && oscap_source_get_scap_type(src) == OSCAP_DOCUMENT_XCCDF) { + bench = xccdf_benchmark_import_source(src); + break; + } + ds_sds_session_reset(session); + } + oscap_string_iterator_free(bench_it); + + if (bench == NULL) { + fprintf(stderr, "Could not find a benchmark in the datastream.\n"); + free(profile_id_dup); + oscap_string_iterator_free(checklist_it); + ds_stream_index_iterator_free(sds_it); + ds_sds_session_free(session); + return OSCAP_ERROR; + } + + /* Re-select the tailoring component and import with benchmark */ + ds_sds_session_reset(session); + struct oscap_source *tail_source = ds_sds_session_select_checklist(session, stream_id, id, NULL); + struct xccdf_tailoring *resolved_tailoring = xccdf_tailoring_import_source(tail_source, bench); + + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + xccdf_policy_model_set_tailoring(policy_model, resolved_tailoring); + + if (action->list_rules) { + _print_rules_for_profile(policy_model, profile_id_dup); + } else { + _print_vars_for_profile(policy_model, profile_id_dup); + } + xccdf_policy_model_free(policy_model); + free(profile_id_dup); + } else { + struct xccdf_profile *profile = xccdf_tailoring_get_profile_by_id(tailoring, profile_id); + _print_xccdf_profile_with_id(profile, prefix); + xccdf_tailoring_free(tailoring); + } profile_not_found = false; + } else { + xccdf_tailoring_free(tailoring); } - - xccdf_tailoring_free(tailoring); } ds_sds_session_reset(session); } @@ -586,11 +677,13 @@ static void app_info_single_benchmark(struct xccdf_benchmark *bench, const struc const char *profile_id = benchmark_get_profile_or_report_id_issues(bench, action->profile, action->file); if (profile_id != NULL) { if (action->list_rules) { - _print_rules_for_profile(bench, profile_id); - // bench is freed by policy_model inside _print_rules_for_profile + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + _print_rules_for_profile(policy_model, profile_id); + xccdf_policy_model_free(policy_model); } else if (action->list_vars) { - _print_vars_for_profile(bench, profile_id); - // bench is freed by policy_model inside _print_vars_for_profile + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + _print_vars_for_profile(policy_model, profile_id); + xccdf_policy_model_free(policy_model); } else { _print_single_benchmark_one_profile(bench, profile_id); xccdf_benchmark_free(bench); @@ -821,9 +914,36 @@ static int app_info(const struct oscap_action *action) } break; case OSCAP_DOCUMENT_XCCDF_TAILORING: - printf("Document type: XCCDF Tailoring\n"); - print_time(action->file); - _print_xccdf_tailoring(source, "", 0); + if (action->profile && (action->list_rules || action->list_vars)) { + struct oscap_source *bench_source = NULL; + struct xccdf_benchmark *bench = _resolve_benchmark_for_tailoring(source, action->file, &bench_source); + if (bench == NULL) { + goto cleanup; + } + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + /* Re-import tailoring with benchmark for proper profile resolution */ + struct xccdf_tailoring *tailoring = xccdf_tailoring_import_source(source, bench); + if (tailoring == NULL) { + xccdf_policy_model_free(policy_model); + oscap_source_free(bench_source); + goto cleanup; + } + xccdf_policy_model_set_tailoring(policy_model, tailoring); + const char *profile_id = tailoring_get_profile_or_report_multiple_ids(tailoring, action->profile, action->file); + if (profile_id != NULL) { + if (action->list_rules) { + _print_rules_for_profile(policy_model, profile_id); + } else { + _print_vars_for_profile(policy_model, profile_id); + } + } + xccdf_policy_model_free(policy_model); + oscap_source_free(bench_source); + } else { + printf("Document type: XCCDF Tailoring\n"); + print_time(action->file); + _print_xccdf_tailoring(source, "", 0); + } break; case OSCAP_DOCUMENT_SCE_RESULT: printf("Document type: SCE Result File\n"); From 0fb1b77060cf48bc79150a769913aa16b532a547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Mon, 16 Feb 2026 15:54:14 +0100 Subject: [PATCH 5/9] Move list_rules/list_vars tests to tests/oscap_info_profiles These tests are for oscap info --list-rules and --list-vars options, not XCCDF unit tests, so they belong in their own test directory. Data files used only by these tests are moved; test_reference_ds.xml is copied since it is also used by test_reference.sh in unittests. --- tests/API/XCCDF/unittests/CMakeLists.txt | 2 - tests/CMakeLists.txt | 1 + tests/oscap_info_profiles/CMakeLists.txt | 2 + .../test_list_rules.sh | 0 .../test_list_vars.sh | 0 .../oscap_info_profiles/test_reference_ds.xml | 119 ++++++++++++++++++ .../test_reference_ds_with_tailoring.xml | 0 .../test_tailoring_benchmark.xml | 0 .../test_tailoring_file.xml | 0 9 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 tests/oscap_info_profiles/CMakeLists.txt rename tests/{API/XCCDF/unittests => oscap_info_profiles}/test_list_rules.sh (100%) rename tests/{API/XCCDF/unittests => oscap_info_profiles}/test_list_vars.sh (100%) create mode 100644 tests/oscap_info_profiles/test_reference_ds.xml rename tests/{API/XCCDF/unittests => oscap_info_profiles}/test_reference_ds_with_tailoring.xml (100%) rename tests/{API/XCCDF/unittests => oscap_info_profiles}/test_tailoring_benchmark.xml (100%) rename tests/{API/XCCDF/unittests => oscap_info_profiles}/test_tailoring_file.xml (100%) diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt index f5a3829ba6..674e2b29b1 100644 --- a/tests/API/XCCDF/unittests/CMakeLists.txt +++ b/tests/API/XCCDF/unittests/CMakeLists.txt @@ -113,7 +113,5 @@ add_oscap_test("test_skip_rule.sh") add_oscap_test("test_no_newline_between_select_elements.sh") add_oscap_test("test_single_line_tailoring.sh") add_oscap_test("test_reference.sh") -add_oscap_test("test_list_rules.sh") -add_oscap_test("test_list_vars.sh") add_oscap_test("test_remediation_bootc.sh") add_oscap_test("openscap_2289_regression.sh") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fdd228d0c5..2eb0a550ce 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory("CPE") add_subdirectory("DS") add_subdirectory("mitre") add_subdirectory("nist") +add_subdirectory("oscap_info_profiles") add_subdirectory("oscap_string") add_subdirectory("oval_details") add_subdirectory("probe_behavior") diff --git a/tests/oscap_info_profiles/CMakeLists.txt b/tests/oscap_info_profiles/CMakeLists.txt new file mode 100644 index 0000000000..7ced793b2d --- /dev/null +++ b/tests/oscap_info_profiles/CMakeLists.txt @@ -0,0 +1,2 @@ +add_oscap_test("test_list_rules.sh") +add_oscap_test("test_list_vars.sh") diff --git a/tests/API/XCCDF/unittests/test_list_rules.sh b/tests/oscap_info_profiles/test_list_rules.sh similarity index 100% rename from tests/API/XCCDF/unittests/test_list_rules.sh rename to tests/oscap_info_profiles/test_list_rules.sh diff --git a/tests/API/XCCDF/unittests/test_list_vars.sh b/tests/oscap_info_profiles/test_list_vars.sh similarity index 100% rename from tests/API/XCCDF/unittests/test_list_vars.sh rename to tests/oscap_info_profiles/test_list_vars.sh diff --git a/tests/oscap_info_profiles/test_reference_ds.xml b/tests/oscap_info_profiles/test_reference_ds.xml new file mode 100644 index 0000000000..863494bbb9 --- /dev/null +++ b/tests/oscap_info_profiles/test_reference_ds.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + 5.11.2 + 2021-02-01T08:07:06+01:00 + + + + + PASS + pass + + + + + + + + + + + + + + oval:x:var:1 + + + + + 100 + + + + + + + accepted + Test Benchmark + Description + animals + fruit + 1.0 + + OpenSCAP + OpenSCAP + OpenSCAP + http://scap.nist.gov + + + xccdf_test_profile + This profile is for testing. + + + 42 + + + + Value V1 + 10 + 20 + + + Value V2 + default_val + custom_val + + + Rule R1 + Description + 3.14 + 42.42 + + + + + + Rule R2 + Description + 17.71.777 + 88888888 + + + + + + Rule R3 + Description + 17.71.777 + 666 + + + + + + Rule R4 + Description + + + + + + + diff --git a/tests/API/XCCDF/unittests/test_reference_ds_with_tailoring.xml b/tests/oscap_info_profiles/test_reference_ds_with_tailoring.xml similarity index 100% rename from tests/API/XCCDF/unittests/test_reference_ds_with_tailoring.xml rename to tests/oscap_info_profiles/test_reference_ds_with_tailoring.xml diff --git a/tests/API/XCCDF/unittests/test_tailoring_benchmark.xml b/tests/oscap_info_profiles/test_tailoring_benchmark.xml similarity index 100% rename from tests/API/XCCDF/unittests/test_tailoring_benchmark.xml rename to tests/oscap_info_profiles/test_tailoring_benchmark.xml diff --git a/tests/API/XCCDF/unittests/test_tailoring_file.xml b/tests/oscap_info_profiles/test_tailoring_file.xml similarity index 100% rename from tests/API/XCCDF/unittests/test_tailoring_file.xml rename to tests/oscap_info_profiles/test_tailoring_file.xml From addcb464a3493750bc4e62ccfb6e23f924469c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 17 Feb 2026 09:00:13 +0100 Subject: [PATCH 6/9] Fix CodeClimate problem Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. --- tests/oscap_info_profiles/test_list_rules.sh | 12 ++++++------ tests/oscap_info_profiles/test_list_vars.sh | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/oscap_info_profiles/test_list_rules.sh b/tests/oscap_info_profiles/test_list_rules.sh index 93d513a24d..382938cbb0 100755 --- a/tests/oscap_info_profiles/test_list_rules.sh +++ b/tests/oscap_info_profiles/test_list_rules.sh @@ -12,13 +12,13 @@ p1="xccdf_com.example.www_profile_P1" # Test 1: --list-rules with --profile prints selected rule IDs $OSCAP info --profile $p1 --list-rules $ds > $stdout 2> $stderr -[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +[[ -f $stderr ]]; [[ ! -s $stderr ]]; :> $stderr grep -q "xccdf_com.example.www_rule_R1" $stdout grep -q "xccdf_com.example.www_rule_R2" $stdout grep -q "xccdf_com.example.www_rule_R3" $stdout grep -q "xccdf_com.example.www_rule_R4" $stdout # Verify output contains only rule IDs, one per line (4 rules = 4 lines) -[ "$(wc -l < $stdout)" -eq 4 ] +[[ "$(wc -l < $stdout)" -eq 4 ]] :> $stdout # Test 2: --list-rules without --profile produces an error @@ -31,24 +31,24 @@ grep -q "\-\-list-rules option requires \-\-profile" $stderr tailoring="$srcdir/test_tailoring_file.xml" tp="xccdf_com.example.www_profile_P1_tailored" $OSCAP info --profile $tp --list-rules $tailoring > $stdout 2> $stderr -[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +[[ -f $stderr ]]; [[ ! -s $stderr ]]; :> $stderr grep -q "xccdf_com.example.www_rule_R1" $stdout grep -q "xccdf_com.example.www_rule_R2" $stdout # R3 and R4 are deselected by tailoring ! grep -q "xccdf_com.example.www_rule_R3" $stdout ! grep -q "xccdf_com.example.www_rule_R4" $stdout -[ "$(wc -l < $stdout)" -eq 2 ] +[[ "$(wc -l < $stdout)" -eq 2 ]] :> $stdout # Test 4: --list-rules with SDS containing tailoring ds_tailoring="$srcdir/test_reference_ds_with_tailoring.xml" $OSCAP info --profile $tp --list-rules $ds_tailoring > $stdout 2> $stderr -[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +[[ -f $stderr ]]; [[ ! -s $stderr ]]; :> $stderr grep -q "xccdf_com.example.www_rule_R1" $stdout grep -q "xccdf_com.example.www_rule_R2" $stdout ! grep -q "xccdf_com.example.www_rule_R3" $stdout ! grep -q "xccdf_com.example.www_rule_R4" $stdout -[ "$(wc -l < $stdout)" -eq 2 ] +[[ "$(wc -l < $stdout)" -eq 2 ]] :> $stdout rm -f $stdout $stderr diff --git a/tests/oscap_info_profiles/test_list_vars.sh b/tests/oscap_info_profiles/test_list_vars.sh index ae63d6404c..575aaa9283 100755 --- a/tests/oscap_info_profiles/test_list_vars.sh +++ b/tests/oscap_info_profiles/test_list_vars.sh @@ -12,11 +12,11 @@ p1="xccdf_com.example.www_profile_P1" # Test 1: --list-vars with --profile prints value IDs and resolved values $OSCAP info --profile $p1 --list-vars $ds > $stdout 2> $stderr -[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +[[ -f $stderr ]]; [[ ! -s $stderr ]]; :> $stderr grep -q "xccdf_com.example.www_value_V1 42" $stdout grep -q "xccdf_com.example.www_value_V2 custom_val" $stdout # Verify output contains exactly 2 lines -[ "$(wc -l < $stdout)" -eq 2 ] +[[ "$(wc -l < $stdout)" -eq 2 ]] :> $stdout # Test 2: --list-vars without --profile produces an error @@ -35,20 +35,20 @@ grep -q "The \-\-list-rules and \-\-list-vars options can't be used at the same tailoring="$srcdir/test_tailoring_file.xml" tp="xccdf_com.example.www_profile_P1_tailored" $OSCAP info --profile $tp --list-vars $tailoring > $stdout 2> $stderr -[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +[[ -f $stderr ]]; [[ ! -s $stderr ]]; :> $stderr # V1 is overridden to 99 by tailoring, V2 is inherited from base profile grep -q "xccdf_com.example.www_value_V1 99" $stdout grep -q "xccdf_com.example.www_value_V2 custom_val" $stdout -[ "$(wc -l < $stdout)" -eq 2 ] +[[ "$(wc -l < $stdout)" -eq 2 ]] :> $stdout # Test 5: --list-vars with SDS containing tailoring ds_tailoring="$srcdir/test_reference_ds_with_tailoring.xml" $OSCAP info --profile $tp --list-vars $ds_tailoring > $stdout 2> $stderr -[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr +[[ -f $stderr ]]; [[ ! -s $stderr ]]; :> $stderr grep -q "xccdf_com.example.www_value_V1 99" $stdout grep -q "xccdf_com.example.www_value_V2 custom_val" $stdout -[ "$(wc -l < $stdout)" -eq 2 ] +[[ "$(wc -l < $stdout)" -eq 2 ]] :> $stdout rm -f $stdout $stderr From 96f3b3a26d0f0eb90aaefbe98ea3d53769ae2ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 17 Feb 2026 09:00:57 +0100 Subject: [PATCH 7/9] Fix CodeClimate problem Make the type of this variable a pointer-to-const. The current type of "profile" is "struct xccdf_profile *". --- utils/oscap-info.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/oscap-info.c b/utils/oscap-info.c index 882f42e62b..d489824ddb 100644 --- a/utils/oscap-info.c +++ b/utils/oscap-info.c @@ -411,7 +411,7 @@ static void _print_rules_for_profile(struct xccdf_policy_model *policy_model, co } struct xccdf_select_iterator *sel_it = xccdf_policy_get_selected_rules(policy); while (xccdf_select_iterator_has_more(sel_it)) { - struct xccdf_select *sel = xccdf_select_iterator_next(sel_it); + const struct xccdf_select *sel = xccdf_select_iterator_next(sel_it); printf("%s\n", xccdf_select_get_item(sel)); } xccdf_select_iterator_free(sel_it); @@ -423,16 +423,16 @@ static void _print_vars_for_profile(struct xccdf_policy_model *policy_model, con if (policy == NULL) { return; } - struct xccdf_profile *profile = xccdf_policy_get_profile(policy); + const struct xccdf_profile *profile = xccdf_policy_get_profile(policy); if (profile == NULL) { return; } - struct xccdf_benchmark *bench = xccdf_policy_model_get_benchmark(policy_model); + const struct xccdf_benchmark *bench = xccdf_policy_model_get_benchmark(policy_model); struct xccdf_setvalue_iterator *sv_it = xccdf_profile_get_setvalues(profile); while (xccdf_setvalue_iterator_has_more(sv_it)) { - struct xccdf_setvalue *sv = xccdf_setvalue_iterator_next(sv_it); + const struct xccdf_setvalue *sv = xccdf_setvalue_iterator_next(sv_it); const char *value_id = xccdf_setvalue_get_item(sv); struct xccdf_item *item = xccdf_benchmark_get_item(bench, value_id); if (item != NULL) { @@ -445,7 +445,7 @@ static void _print_vars_for_profile(struct xccdf_policy_model *policy_model, con struct xccdf_refine_value_iterator *rv_it = xccdf_profile_get_refine_values(profile); while (xccdf_refine_value_iterator_has_more(rv_it)) { - struct xccdf_refine_value *rv = xccdf_refine_value_iterator_next(rv_it); + const struct xccdf_refine_value *rv = xccdf_refine_value_iterator_next(rv_it); const char *value_id = xccdf_refine_value_get_item(rv); struct xccdf_item *item = xccdf_benchmark_get_item(bench, value_id); if (item != NULL) { From 2ba6d9dc9c478be1a427df0f7a5fba1bde36601e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 17 Feb 2026 09:29:13 +0100 Subject: [PATCH 8/9] Reduce cognitive complexity of app_info_single_ds_one_profile --- utils/oscap-info.c | 221 ++++++++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 94 deletions(-) diff --git a/utils/oscap-info.c b/utils/oscap-info.c index d489824ddb..ace82f3f1c 100644 --- a/utils/oscap-info.c +++ b/utils/oscap-info.c @@ -502,127 +502,160 @@ static struct xccdf_benchmark *_resolve_benchmark_for_tailoring( return bench; } +static struct xccdf_benchmark *_find_benchmark_in_stream( + struct ds_sds_session *session, + struct ds_stream_index *stream) +{ + const char *stream_id = ds_stream_index_get_id(stream); + struct oscap_string_iterator *bench_it = ds_stream_index_get_checklists(stream); + struct xccdf_benchmark *bench = NULL; + while (oscap_string_iterator_has_more(bench_it)) { + const char *cl_id = oscap_string_iterator_next(bench_it); + struct oscap_source *src = ds_sds_session_select_checklist(session, stream_id, cl_id, NULL); + if (src != NULL && oscap_source_get_scap_type(src) == OSCAP_DOCUMENT_XCCDF) { + bench = xccdf_benchmark_import_source(src); + break; + } + ds_sds_session_reset(session); + } + oscap_string_iterator_free(bench_it); + return bench; +} + +static int _handle_xccdf_benchmark_profile( + struct xccdf_benchmark *bench, + const struct oscap_action *action, + const char *profile_suffix, + const char *filename, + bool *profile_found) +{ + const char *profile_id = benchmark_get_profile_or_report_multiple_ids(bench, profile_suffix, filename); + if (profile_id == NULL) { + xccdf_benchmark_free(bench); + return OSCAP_OK; + } + if (action->list_rules || action->list_vars) { + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + if (action->list_rules) { + _print_rules_for_profile(policy_model, profile_id); + } else { + _print_vars_for_profile(policy_model, profile_id); + } + xccdf_policy_model_free(policy_model); + } else { + _print_single_benchmark_one_profile(bench, profile_id); + xccdf_benchmark_free(bench); + } + *profile_found = true; + return OSCAP_OK; +} + +static int _handle_xccdf_tailoring_profile( + struct xccdf_tailoring *tailoring, + const struct oscap_action *action, + const char *profile_suffix, + const char *filename, + struct ds_sds_session *session, + struct ds_stream_index *stream, + const char *checklist_id, + bool *profile_found) +{ + const char *profile_id = tailoring_get_profile_or_report_multiple_ids(tailoring, profile_suffix, filename); + if (profile_id == NULL) { + xccdf_tailoring_free(tailoring); + return OSCAP_OK; + } + if (!(action->list_rules || action->list_vars)) { + struct xccdf_profile *profile = xccdf_tailoring_get_profile_by_id(tailoring, profile_id); + _print_xccdf_profile_with_id(profile, ""); + xccdf_tailoring_free(tailoring); + *profile_found = true; + return OSCAP_OK; + } + char *profile_id_dup = strdup(profile_id); + xccdf_tailoring_free(tailoring); + + /* Find the XCCDF benchmark component in the stream */ + ds_sds_session_reset(session); + struct xccdf_benchmark *bench = _find_benchmark_in_stream(session, stream); + if (bench == NULL) { + fprintf(stderr, "Could not find a benchmark in the datastream.\n"); + free(profile_id_dup); + return OSCAP_ERROR; + } + + /* Re-select the tailoring component and import with benchmark */ + const char *stream_id = ds_stream_index_get_id(stream); + ds_sds_session_reset(session); + struct oscap_source *tail_source = ds_sds_session_select_checklist(session, stream_id, checklist_id, NULL); + struct xccdf_tailoring *resolved_tailoring = xccdf_tailoring_import_source(tail_source, bench); + + struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); + xccdf_policy_model_set_tailoring(policy_model, resolved_tailoring); + + if (action->list_rules) { + _print_rules_for_profile(policy_model, profile_id_dup); + } else { + _print_vars_for_profile(policy_model, profile_id_dup); + } + xccdf_policy_model_free(policy_model); + free(profile_id_dup); + *profile_found = true; + return OSCAP_OK; +} + 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) { + int ret = OSCAP_OK; const char *profile_suffix = action->profile; const char *filename = action->file; - const char *prefix = ""; - struct ds_stream_index * stream = ds_stream_index_iterator_next(sds_it); - struct oscap_string_iterator* checklist_it = ds_stream_index_get_checklists(stream); + struct ds_stream_index *stream = ds_stream_index_iterator_next(sds_it); + struct oscap_string_iterator *checklist_it = ds_stream_index_get_checklists(stream); if (!action->list_rules && !action->list_vars) { printf("\nStream: %s\n", ds_stream_index_get_id(stream)); printf("Generated: %s\n", ds_stream_index_get_timestamp(stream)); printf("Version: %s\n", ds_stream_index_get_version(stream)); } - bool profile_not_found = true; + bool profile_found = false; - while (oscap_string_iterator_has_more(checklist_it) && profile_not_found) { - const char * id = oscap_string_iterator_next(checklist_it); + while (oscap_string_iterator_has_more(checklist_it) && !profile_found) { + const char *id = oscap_string_iterator_next(checklist_it); /* decompose */ struct oscap_source *xccdf_source = ds_sds_session_select_checklist(session, ds_stream_index_get_id(stream), id, NULL); if (xccdf_source == NULL) { - oscap_string_iterator_free(checklist_it); - ds_stream_index_iterator_free(sds_it); - ds_sds_session_free(session); - return OSCAP_ERROR; + ret = OSCAP_ERROR; + goto cleanup; } if (oscap_source_get_scap_type(xccdf_source) == OSCAP_DOCUMENT_XCCDF) { struct xccdf_benchmark *bench = xccdf_benchmark_import_source(xccdf_source); - if(!bench) { - oscap_string_iterator_free(checklist_it); - ds_stream_index_iterator_free(sds_it); - ds_sds_session_free(session); - return OSCAP_ERROR; - } - const char *profile_id = benchmark_get_profile_or_report_multiple_ids(bench, profile_suffix, filename); - if (profile_id != NULL) { - if (action->list_rules) { - struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); - _print_rules_for_profile(policy_model, profile_id); - xccdf_policy_model_free(policy_model); - } else if (action->list_vars) { - struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); - _print_vars_for_profile(policy_model, profile_id); - xccdf_policy_model_free(policy_model); - } else { - _print_single_benchmark_one_profile(bench, profile_id); - xccdf_benchmark_free(bench); - } - profile_not_found = false; - } else { - xccdf_benchmark_free(bench); + if (!bench) { + ret = OSCAP_ERROR; + goto cleanup; } + ret = _handle_xccdf_benchmark_profile(bench, action, profile_suffix, filename, &profile_found); } else if (oscap_source_get_scap_type(xccdf_source) == OSCAP_DOCUMENT_XCCDF_TAILORING) { struct xccdf_tailoring *tailoring = xccdf_tailoring_import_source(xccdf_source, NULL); - - const char *profile_id = tailoring_get_profile_or_report_multiple_ids(tailoring, profile_suffix, filename); - if (profile_id != NULL) { - if (action->list_rules || action->list_vars) { - char *profile_id_dup = strdup(profile_id); - xccdf_tailoring_free(tailoring); - tailoring = NULL; - - const char *stream_id = ds_stream_index_get_id(stream); - - /* Find the XCCDF benchmark component in the stream */ - ds_sds_session_reset(session); - struct oscap_string_iterator *bench_it = ds_stream_index_get_checklists(stream); - struct xccdf_benchmark *bench = NULL; - while (oscap_string_iterator_has_more(bench_it)) { - const char *cl_id = oscap_string_iterator_next(bench_it); - struct oscap_source *src = ds_sds_session_select_checklist(session, stream_id, cl_id, NULL); - if (src != NULL && oscap_source_get_scap_type(src) == OSCAP_DOCUMENT_XCCDF) { - bench = xccdf_benchmark_import_source(src); - break; - } - ds_sds_session_reset(session); - } - oscap_string_iterator_free(bench_it); - - if (bench == NULL) { - fprintf(stderr, "Could not find a benchmark in the datastream.\n"); - free(profile_id_dup); - oscap_string_iterator_free(checklist_it); - ds_stream_index_iterator_free(sds_it); - ds_sds_session_free(session); - return OSCAP_ERROR; - } - - /* Re-select the tailoring component and import with benchmark */ - ds_sds_session_reset(session); - struct oscap_source *tail_source = ds_sds_session_select_checklist(session, stream_id, id, NULL); - struct xccdf_tailoring *resolved_tailoring = xccdf_tailoring_import_source(tail_source, bench); - - struct xccdf_policy_model *policy_model = xccdf_policy_model_new(bench); - xccdf_policy_model_set_tailoring(policy_model, resolved_tailoring); - - if (action->list_rules) { - _print_rules_for_profile(policy_model, profile_id_dup); - } else { - _print_vars_for_profile(policy_model, profile_id_dup); - } - xccdf_policy_model_free(policy_model); - free(profile_id_dup); - } else { - struct xccdf_profile *profile = xccdf_tailoring_get_profile_by_id(tailoring, profile_id); - _print_xccdf_profile_with_id(profile, prefix); - xccdf_tailoring_free(tailoring); - } - profile_not_found = false; - } else { - xccdf_tailoring_free(tailoring); - } + ret = _handle_xccdf_tailoring_profile(tailoring, action, profile_suffix, filename, session, stream, id, &profile_found); + } + if (ret != OSCAP_OK) { + goto cleanup; } ds_sds_session_reset(session); } - oscap_string_iterator_free(checklist_it); - if (profile_not_found) { + if (!profile_found) { report_missing_profile(profile_suffix, filename); } - return OSCAP_OK; + +cleanup: + oscap_string_iterator_free(checklist_it); + if (ret != OSCAP_OK) { + ds_stream_index_iterator_free(sds_it); + ds_sds_session_free(session); + } + return ret; } static int app_info_single_ds_all(struct ds_stream_index_iterator* sds_it, struct ds_sds_session *session, const struct oscap_action *action) From 529c867b31937ab229cbd837050b187511ea4db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Tue, 17 Feb 2026 09:45:04 +0100 Subject: [PATCH 9/9] Update bash completion --- dist/bash_completion.d/oscap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/bash_completion.d/oscap b/dist/bash_completion.d/oscap index 1b237ffdce..7e86bc5f68 100644 --- a/dist/bash_completion.d/oscap +++ b/dist/bash_completion.d/oscap @@ -39,7 +39,7 @@ function _oscap { opts[oscap:xccdf:generate:guide]="-o --output --hide-profile-info --profile --benchmark-id --xccdf-id --tailoring-file --tailoring-id --skip-signature-validation --enforce-signature" opts[oscap:xccdf:generate:fix]="-o --output --profile --result-id --profile --fix-type --xccdf-id --benchmark-id --tailoring-file --tailoring-id --skip-signature-validation --enforce-signature" opts[oscap:xccdf:generate:custom]="-o --output --stylesheet" - opts[oscap:info]="--fetch-remote-resources --local-files --profile --profiles --references" + opts[oscap:info]="--fetch-remote-resources --local-files --profile --profiles --references --list-rules --list-vars" # local variables local std cmd i prev