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
diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc
index 8ae1685900..ef03bb7fb8 100644
--- a/docs/manual/manual.adoc
+++ b/docs/manual/manual.adoc
@@ -217,6 +217,39 @@ 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.
+
+=== 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/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 @@
+ 42
+
+
+ Value V1
+ 10
+ 20
+
+
+ Value V2
+ default_val
+ custom_val
+
Rule R1
Description
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/oscap_info_profiles/test_list_rules.sh b/tests/oscap_info_profiles/test_list_rules.sh
new file mode 100755
index 0000000000..382938cbb0
--- /dev/null
+++ b/tests/oscap_info_profiles/test_list_rules.sh
@@ -0,0 +1,54 @@
+#!/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
+
+# 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/oscap_info_profiles/test_list_vars.sh b/tests/oscap_info_profiles/test_list_vars.sh
new file mode 100755
index 0000000000..575aaa9283
--- /dev/null
+++ b/tests/oscap_info_profiles/test_list_vars.sh
@@ -0,0 +1,54 @@
+#!/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
+
+# 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
+
+# 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/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/oscap_info_profiles/test_reference_ds_with_tailoring.xml b/tests/oscap_info_profiles/test_reference_ds_with_tailoring.xml
new file mode 100644
index 0000000000..2a2063c6be
--- /dev/null
+++ b/tests/oscap_info_profiles/test_reference_ds_with_tailoring.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+
+
+
+
+
+
+ 1.0
+
+ Tailored P1
+
+
+ 99
+
+
+
+
diff --git a/tests/oscap_info_profiles/test_tailoring_benchmark.xml b/tests/oscap_info_profiles/test_tailoring_benchmark.xml
new file mode 100644
index 0000000000..61a9b303f1
--- /dev/null
+++ b/tests/oscap_info_profiles/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/oscap_info_profiles/test_tailoring_file.xml b/tests/oscap_info_profiles/test_tailoring_file.xml
new file mode 100644
index 0000000000..5b0cb04682
--- /dev/null
+++ b/tests/oscap_info_profiles/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 a707ee6e4e..ace82f3f1c 100644
--- a/utils/oscap-info.c
+++ b/utils/oscap-info.c
@@ -39,6 +39,7 @@
#include
#include
+#include
#include "oscap_source.h"
#include
#include
@@ -63,6 +64,8 @@ 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"
+ " --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"
@@ -400,62 +403,259 @@ 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_policy_model *policy_model, const char *profile_id)
{
- 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 xccdf_policy *policy = xccdf_policy_model_get_policy_by_id(policy_model, profile_id);
+ if (policy == NULL) {
+ return;
+ }
+ struct xccdf_select_iterator *sel_it = xccdf_policy_get_selected_rules(policy);
+ while (xccdf_select_iterator_has_more(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);
+}
- 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;
+static void _print_vars_for_profile(struct xccdf_policy_model *policy_model, const char *profile_id)
+{
+ struct xccdf_policy *policy = xccdf_policy_model_get_policy_by_id(policy_model, profile_id);
+ if (policy == NULL) {
+ return;
+ }
+ const struct xccdf_profile *profile = xccdf_policy_get_profile(policy);
+ if (profile == NULL) {
+ return;
+ }
- while (oscap_string_iterator_has_more(checklist_it) && profile_not_found) {
- const char * id = oscap_string_iterator_next(checklist_it);
+ 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)) {
+ 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) {
+ 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)) {
+ 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) {
+ 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);
+}
+
+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 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;
+ 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_found = false;
+
+ 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) {
- _print_single_benchmark_one_profile(bench, profile_id);
- profile_not_found = false;
+ if (!bench) {
+ ret = OSCAP_ERROR;
+ goto cleanup;
}
- xccdf_benchmark_free(bench);
+ 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) {
- struct xccdf_profile *profile = xccdf_tailoring_get_profile_by_id(tailoring, profile_id);
- _print_xccdf_profile_with_id(profile, prefix);
- profile_not_found = false;
- }
-
- 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)
@@ -509,9 +709,21 @@ 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) {
+ 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);
+ }
+ } 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 +739,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);
}
@@ -735,9 +947,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");
@@ -768,6 +1007,8 @@ 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,
@@ -778,6 +1019,8 @@ 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},
+ {"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},
@@ -792,6 +1035,14 @@ 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_LIST_VARS:
+ action->list_vars = 1;
+ action->provide_machine_readable_output = 1;
+ break;
case OSCAP_INFO_OPT_PROFILE:
action->profile = optarg;
break;
@@ -815,6 +1066,21 @@ 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 (action->list_vars && action->profile == NULL) {
+ oscap_module_usage(action->module, stderr, "The --list-vars option requires --profile.\n");
+ 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;
diff --git a/utils/oscap-tool.h b/utils/oscap-tool.h
index ff4880dddc..6d8cdab2eb 100644
--- a/utils/oscap-tool.h
+++ b/utils/oscap-tool.h
@@ -163,6 +163,8 @@ struct oscap_action {
int references;
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 4faffcb403..d3482ba208 100644
--- a/utils/oscap.8
+++ b/utils/oscap.8
@@ -63,6 +63,16 @@ 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\-\-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.