Skip to content

Commit

Permalink
Remove obsolete template options
Browse files Browse the repository at this point in the history
The option --template allows users to provide either a remediation
system URN or a custome XSLT template path. Instead of using the
--template with URN users should use --fix-type. A custom XSLT path have
never really worked. Users can apply custom XSLTs on their XML files
using xsltsproc tool or other third party tools, they don't need oscap
for that. We advise users to use --fix-type to specify the remediation
type.

The --oval-template and --sce-template options are also useless
because the result file names are defined by oscap itself, therefore
there is no other sensible value for these options than the default
value (hardcoded in oscap).
  • Loading branch information
jan-cerny committed Jul 4, 2024
1 parent 22140fc commit 6258a3d
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 81 deletions.
6 changes: 3 additions & 3 deletions dist/bash_completion.d/oscap
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ function _oscap {
opts[oscap:xccdf:remediate]="--result-id --skip-validation --fetch-remote-resources --local-files --results --results-arf --report --oval-results --export-variables --cpe --check-engine-results --progress --progress-full"
opts[oscap:xccdf:resolve]="-o --output -f --force"
opts[oscap:xccdf:generate]="--profile"
opts[oscap:xccdf:generate:report]="-o --output --result-id --profile --oval-template --sce-template"
opts[oscap:xccdf:generate:report]="-o --output --result-id --profile"
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 --template --profile --result-id --profile --fix-type --xccdf-id --benchmark-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"

Expand Down Expand Up @@ -66,7 +66,7 @@ function _oscap {
local cmd=${modpath##*:}

case "$prev" in
--results|-o|--output|--template|--oval-template) _filedir 'xml.bz2|xml' ;;
--results|-o|--output) _filedir 'xml.bz2|xml' ;;
--report) _filedir 'html' ;;
esac

Expand Down
8 changes: 4 additions & 4 deletions tests/API/XCCDF/applicability/test_report_anaconda_fixes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ line1='^\W*part /tmp$'
line2='^\W*part /tmp --mountoptions=nodev$'
line3='^\W*passwd --minlen=14$'

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--output $result $srcdir/${name}.xccdf.xml 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]
grep "$line1" $result
grep "$line2" $result
grep "$line3" $result && false
:> $result

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--profile xccdf_moc.elpmaxe.www_profile_1 \
--output $result $srcdir/${name}.xccdf.xml 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]
Expand All @@ -31,7 +31,7 @@ grep "$line2" $result
grep "$line3" $result
:> $result

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--cpe $srcdir/cpe-dict.xml \
--output $result $srcdir/${name}.xccdf.xml 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]
Expand All @@ -46,7 +46,7 @@ rm $result
result=./${name}.out
[ -f $result ] && rm $result

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--cpe $srcdir/cpe-dict.xml \
--profile xccdf_moc.elpmaxe.www_profile_1 \
--output $result $srcdir/${name}.xccdf.xml 2>&1 > $stderr
Expand Down
5 changes: 2 additions & 3 deletions tests/API/XCCDF/unittests/test_fix_arf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ profile="xccdf_moc.elpmaxe.www_profile_standard"
result_id="xccdf_org.open-scap_testresult_xccdf_moc.elpmaxe.www_profile_standard"
bash_line1="echo this_is_ok"
bash_line2="echo fix_me_please"
ansible_template="urn:xccdf:fix:script:ansible"
ansible_task1a="\- name: ensure everything passes"
ansible_task1b="shell: /bin/true"
ansible_task2a="\- name: correct the failing case"
Expand Down Expand Up @@ -40,7 +39,7 @@ grep -q "$bash_line1" $script
grep -q "$bash_line2" $script

# Generate an Ansible playbook from a profile in ARF file
$OSCAP xccdf generate fix --profile $profile --template $ansible_template $results_arf | grep -Ev $regex >$playbook 2>$stderr
$OSCAP xccdf generate fix --profile $profile --fix-type ansible $results_arf | grep -Ev $regex >$playbook 2>$stderr
diff -B $playbook $srcdir/$name.playbook1.yml
[ -f $stderr ]; [ ! -s $stderr ]; rm $stderr
grep -q "$ansible_task1a" $playbook
Expand All @@ -56,7 +55,7 @@ grep -q -v "$bash_line1" $script
grep -q "$bash_line2" $script

# Generate an Ansible playbook based on scan results stored in ARF file
$OSCAP xccdf generate fix --result-id $result_id --template $ansible_template $results_arf | grep -Ev $regex >$playbook 2>$stderr
$OSCAP xccdf generate fix --result-id $result_id --fix-type ansible $results_arf | grep -Ev $regex >$playbook 2>$stderr
diff -B $playbook $srcdir/$name.playbook2.yml
[ -f $stderr ]; [ ! -s $stderr ]; rm $stderr
grep -q -v "$ansible_task1a" $playbook
Expand Down
2 changes: 1 addition & 1 deletion tests/API/XCCDF/unittests/test_fix_filtering.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ echo "Result file = $result"

line1='^\W*part /var$'

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--output $result $srcdir/${name}.xccdf.xml 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr
grep "$line1" $result
Expand Down
8 changes: 3 additions & 5 deletions tests/API/XCCDF/unittests/test_fix_script_header.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
set -e
set -o pipefail

ansible_template="urn:xccdf:fix:script:ansible"
bash_template="urn:xccdf:fix:script:sh"
profile="xccdf_moc.elpmaxe.www_profile_standard"
result_id="xccdf_org.open-scap_testresult_xccdf_moc.elpmaxe.www_profile_standard"
title="Standard System Security Profile"
Expand Down Expand Up @@ -56,7 +54,7 @@ grep "$profile_header5" $script
grep "$profile_header6" $script

# Generate a bash script based on scan results
$OSCAP xccdf generate fix --result-id $result_id --template $bash_template --output $script $results_arf >$stdout 2>$stderr
$OSCAP xccdf generate fix --result-id $result_id --fix-type bash --output $script $results_arf >$stdout 2>$stderr
[ -f $stdout ]; [ ! -s $stdout ]; rm $stdout
[ -f $stderr ]; [ ! -s $stderr ]; rm $stderr
grep "$result_header1a" $script
Expand All @@ -66,7 +64,7 @@ grep "$result_header5" $script


# Generate an Ansible playbook from an OpenSCAP profile
$OSCAP xccdf generate fix --profile $profile --template $ansible_template --output $playbook $srcdir/$name.xccdf.xml >$stdout 2>$stderr
$OSCAP xccdf generate fix --profile $profile --fix-type ansible --output $playbook $srcdir/$name.xccdf.xml >$stdout 2>$stderr
[ -f $stdout ]; [ ! -s $stdout ]; rm $stdout
[ -f $stderr ]; [ ! -s $stderr ]; rm $stderr
grep "$profile_header1b" $playbook
Expand All @@ -77,7 +75,7 @@ grep "$profile_header5" $playbook
grep "$profile_header6" $playbook

# Generate an Ansible playbook based on scan results stored in ARF file
$OSCAP xccdf generate fix --result-id $result_id --template $ansible_template --output $playbook $results_arf >$stdout 2>$stderr
$OSCAP xccdf generate fix --result-id $result_id --fix-type ansible --output $playbook $results_arf >$stdout 2>$stderr
[ -f $stdout ]; [ ! -s $stdout ]; rm $stdout
[ -f $stderr ]; [ ! -s $stderr ]; rm $stderr
grep "$result_header1b" $playbook
Expand Down
5 changes: 2 additions & 3 deletions tests/API/XCCDF/unittests/test_generate_fix_ansible_vars.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ set -o pipefail

profile="xccdf_com.example.www_profile_test_ansible_vars"
profile_tailored="xccdf_com.example.www_profile_test_ansible_vars_tailored"
ansible_template="urn:xccdf:fix:script:ansible"
ds="test_generate_fix_ansible_vars_ds.xml"
tailoring_file="test_generate_fix_ansible_vars_ds-tailoring.xml"
golden="test_generate_fix_ansible_vars_golden.yml"
Expand All @@ -18,7 +17,7 @@ name=$(basename $0 .sh)
playbook=$(make_temp_file /tmp ${name}.yml)
out=$(make_temp_file /tmp ${name}.out)

$OSCAP xccdf generate fix --profile $profile --template $ansible_template \
$OSCAP xccdf generate fix --profile $profile --fix-type ansible \
$srcdir/$ds >$playbook 2>$out
[ -f $out ]; [ ! -s $out ]; :> $out
[ -f $playbook ]; [ -s $playbook ]
Expand All @@ -40,7 +39,7 @@ golden_altered_var=$(grep "$var:" $srcdir/$golden_altered | xsed "s|.*$var:[^0-9
[ "$generated_var" != "$golden_altered_var" ]

# Generates Ansible playbook using tailoring file.
$OSCAP xccdf generate fix --template $ansible_template \
$OSCAP xccdf generate fix --fix-type ansible \
--profile $profile_tailored --tailoring-file $srcdir/$tailoring_file \
$srcdir/$ds >$playbook 2>$out
[ -f $out ]; [ ! -s $out ]; :> $out
Expand Down
8 changes: 4 additions & 4 deletions tests/API/XCCDF/unittests/test_report_anaconda_fixes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ line1='^\W*part /tmp$'
line2='^\W*part /tmp --mountoptions=nodev$'
line3='^\W*passwd --minlen=14$'

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--output $result $srcdir/${name}.xccdf.xml 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr
grep "$line1" $result
Expand All @@ -38,7 +38,7 @@ grep -v "$line1" $result | grep -v "$line2" | grep -v "$line3"

:> $result

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--profile xccdf_moc.elpmaxe.www_profile_1 \
--output $result $srcdir/${name}.xccdf.xml 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr
Expand All @@ -53,7 +53,7 @@ rm $result


# And Now For Something Completely Different -- Tailoring:
$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--tailoring-file $srcdir/${name}.tailoring.xml \
--profile xccdf_org.open-scap_profile_unselecting \
--output $result \
Expand All @@ -65,7 +65,7 @@ $OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
rm $result

line4='^\W*passwd --minlen=8$'
$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--tailoring-file $srcdir/${name}.tailoring.xml \
--profile xccdf_org.open-scap_profile_override \
--output $result \
Expand Down
4 changes: 2 additions & 2 deletions tests/API/XCCDF/unittests/test_report_anaconda_fixes_ds.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ component_id=scap_org.open-scap_cref_test_report_anaconda_fixes.xccdf.xml
$OSCAP info $sds | grep $datastream_id
$OSCAP info $sds | grep $component_id

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--datastream-id $datastream_id --xccdf-id $component_id \
--output $result $sds 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr
Expand All @@ -40,7 +40,7 @@ grep -v "$line1" $result | grep -v "$line2" | grep -v "$line3"

:> $result

$OSCAP xccdf generate fix --template urn:redhat:anaconda:pre \
$OSCAP xccdf generate fix --fix-type anaconda \
--profile xccdf_moc.elpmaxe.www_profile_1 \
--output $result $sds 2>&1 > $stderr
[ -f $stderr ]; [ ! -s $stderr ]; :> $stderr
Expand Down
3 changes: 0 additions & 3 deletions utils/oscap-tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ struct oscap_action {
struct oscap_stringlist *rules;
struct oscap_stringlist *skip_rules;
char *format;
const char *tmpl;
char *id;
char *oval_template;
int hide_profile_info;
char *stylesheet;
char *tailoring_file;
Expand Down Expand Up @@ -155,7 +153,6 @@ struct oscap_action {
int without_sys_chars;
int thin_results;
int remediate;
char *sce_template;
int check_engine_results;
int export_variables;
int list_dynamic;
Expand Down
63 changes: 20 additions & 43 deletions utils/oscap-xccdf.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,7 @@ static struct oscap_module XCCDF_GEN_REPORT = {
.help = GEN_OPTS
"\nReport Options:\n"
" --result-id <id> - TestResult ID to be processed. Default is the most recent one.\n"
" --output <file> - Write the document into file.\n"
" --oval-template <template-string> - Template which will be used to obtain OVAL result files.\n",
" --output <file> - Write the document into file.\n",
.opt_parser = getopt_xccdf,
.user = "xccdf-report.xsl",
.func = app_xccdf_xslt
Expand Down Expand Up @@ -285,7 +284,6 @@ static struct oscap_module XCCDF_GEN_FIX = {
" blueprint (default: bash).\n"
" --output <file> - Write the script into file.\n"
" --result-id <id> - Fixes will be generated for failed rule-results of the specified TestResult.\n"
" --template <id|filename> - Fix template. (default: bash)\n"
" --benchmark-id <id> - ID of XCCDF Benchmark in some component in the data stream that should be used.\n"
" (only applicable for source data streams)\n"
" --xccdf-id <id> - ID of component-ref with XCCDF in the data stream that should be evaluated.\n"
Expand Down Expand Up @@ -943,43 +941,32 @@ int app_generate_fix(const struct oscap_action *action)
{
struct xccdf_session *session = NULL;
struct ds_rds_session *arf_session = NULL;
const char *template = NULL;

if (action->fix_type != NULL && action->tmpl != NULL) {
/* Avoid undefined situations, eg.:
* oscap xccdf generate fix --fix-type ansible --template urn:xccdf:fix:scipt:sh
*/
fprintf(stderr,
"Option '--fix-type' is mutually exclusive with '--template'.\n"
"Please provide only one of them.\n");
return OSCAP_ERROR;
} else if (action->fix_type != NULL) {
const char *remediation_system = NULL;

if (action->fix_type != NULL) {
if (strcmp(action->fix_type, "bash") == 0) {
template = "urn:xccdf:fix:script:sh";
remediation_system = "urn:xccdf:fix:script:sh";
} else if (strcmp(action->fix_type, "ansible") == 0) {
template = "urn:xccdf:fix:script:ansible";
remediation_system = "urn:xccdf:fix:script:ansible";
} else if (strcmp(action->fix_type, "puppet") == 0) {
template = "urn:xccdf:fix:script:puppet";
remediation_system = "urn:xccdf:fix:script:puppet";
} else if (strcmp(action->fix_type, "anaconda") == 0) {
template = "urn:redhat:anaconda:pre";
remediation_system = "urn:redhat:anaconda:pre";
} else if (strcmp(action->fix_type, "ignition") == 0) {
template = "urn:xccdf:fix:script:ignition";
remediation_system = "urn:xccdf:fix:script:ignition";
} else if (strcmp(action->fix_type, "kubernetes") == 0) {
template = "urn:xccdf:fix:script:kubernetes";
remediation_system = "urn:xccdf:fix:script:kubernetes";
} else if (strcmp(action->fix_type, "blueprint") == 0) {
template = "urn:redhat:osbuild:blueprint";
remediation_system = "urn:redhat:osbuild:blueprint";
} else {
fprintf(stderr,
"Unknown fix type '%s'.\n"
"Please provide one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint.\n"
"Or provide a custom template using '--template' instead.\n",
"Please provide one of: bash, ansible, puppet, anaconda, ignition, kubernetes, blueprint.\n",
action->fix_type);
return OSCAP_ERROR;
}
} else if (action->tmpl != NULL) {
template = action->tmpl;
} else {
template = "urn:xccdf:fix:script:sh";
remediation_system = "urn:xccdf:fix:script:sh";
}

int ret = OSCAP_ERROR;
Expand Down Expand Up @@ -1046,7 +1033,7 @@ int app_generate_fix(const struct oscap_action *action)

struct xccdf_policy *policy = xccdf_session_get_xccdf_policy(session);
struct xccdf_result *result = xccdf_policy_get_result_by_id(policy, xccdf_session_get_result_id(session));
if (xccdf_policy_generate_fix(policy, result, template, output_fd) == 0)
if (xccdf_policy_generate_fix(policy, result, remediation_system, output_fd) == 0)
ret = OSCAP_OK;
} else { // Fallback to profile if result id is missing
/* Profile-oriented fixes */
Expand All @@ -1060,7 +1047,7 @@ int app_generate_fix(const struct oscap_action *action)
}
}
struct xccdf_policy *policy = xccdf_session_get_xccdf_policy(session);
if (xccdf_policy_generate_fix(policy, NULL, template, output_fd) == 0)
if (xccdf_policy_generate_fix(policy, NULL, remediation_system, output_fd) == 0)
ret = OSCAP_OK;
}
cleanup2:
Expand Down Expand Up @@ -1125,20 +1112,20 @@ int app_generate_guide(const struct oscap_action *action)

int app_xccdf_xslt(const struct oscap_action *action)
{
const char *oval_template = action->oval_template;
const char *sce_template = action->sce_template;
const char *oval_template = NULL;
const char *sce_template = NULL;

if (action->module == &XCCDF_GEN_REPORT && (oval_template == NULL || sce_template == NULL)) {
if (action->module == &XCCDF_GEN_REPORT) {
/* If generating the report and the option is missing -> use defaults */
struct oscap_source *xccdf_source = oscap_source_new_from_file(action->f_xccdf);
/* We want to define default template because we strive to serve user the
* best. However, we must not offer a template, if there is a risk it might
* be incorrect. Otherwise, libxml2 will throw a lot of misleading messages
* to stderr. */
if (oval_template == NULL && _some_result_exists(xccdf_source, "http://oval.mitre.org/XMLSchema/oval-definitions-5")) {
if (_some_result_exists(xccdf_source, "http://oval.mitre.org/XMLSchema/oval-definitions-5")) {
oval_template = "%.result.xml";
}
if (sce_template == NULL && _some_result_exists(xccdf_source, "http://open-scap.org/page/SCE")) {
if (_some_result_exists(xccdf_source, "http://open-scap.org/page/SCE")) {
sce_template = "%.result.xml";
}
oscap_source_free(xccdf_source);
Expand All @@ -1152,7 +1139,6 @@ int app_xccdf_xslt(const struct oscap_action *action)
"result-id", action->id,
"benchmark_id", action->f_benchmark_id,
"profile_id", action->profile,
"template", action->tmpl,
"oval-template", oval_template,
"sce-template", sce_template,
"verbosity", "",
Expand Down Expand Up @@ -1192,11 +1178,8 @@ enum oval_opt {
XCCDF_OPT_RULE,
XCCDF_OPT_SKIP_RULE,
XCCDF_OPT_REPORT_FILE,
XCCDF_OPT_TEMPLATE,
XCCDF_OPT_FORMAT,
XCCDF_OPT_OVAL_TEMPLATE,
XCCDF_OPT_STYLESHEET_FILE,
XCCDF_OPT_SCE_TEMPLATE,
XCCDF_OPT_FILE_VERSION,
XCCDF_OPT_TAILORING_FILE,
XCCDF_OPT_TAILORING_ID,
Expand Down Expand Up @@ -1230,14 +1213,11 @@ bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
{"skip-rule", required_argument, NULL, XCCDF_OPT_SKIP_RULE},
{"result-id", required_argument, NULL, XCCDF_OPT_RESULT_ID},
{"report", required_argument, NULL, XCCDF_OPT_REPORT_FILE},
{"template", required_argument, NULL, XCCDF_OPT_TEMPLATE},
{"oval-template", required_argument, NULL, XCCDF_OPT_OVAL_TEMPLATE},
{"stylesheet", required_argument, NULL, XCCDF_OPT_STYLESHEET_FILE},
{"tailoring-file", required_argument, NULL, XCCDF_OPT_TAILORING_FILE},
{"tailoring-id", required_argument, NULL, XCCDF_OPT_TAILORING_ID},
{"cpe", required_argument, NULL, XCCDF_OPT_CPE},
{"cpe-dict", required_argument, NULL, XCCDF_OPT_CPE_DICT}, // DEPRECATED!
{"sce-template", required_argument, NULL, XCCDF_OPT_SCE_TEMPLATE},
{"fix-type", required_argument, NULL, XCCDF_OPT_FIX_TYPE},
{"local-files", required_argument, NULL, XCCDF_OPT_LOCAL_FILES},
{"reference", required_argument, NULL, XCCDF_OPT_REFERENCE},
Expand Down Expand Up @@ -1281,8 +1261,6 @@ bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
break;
case XCCDF_OPT_RESULT_ID: action->id = optarg; break;
case XCCDF_OPT_REPORT_FILE: action->f_report = optarg; break;
case XCCDF_OPT_TEMPLATE: action->tmpl = optarg; break;
case XCCDF_OPT_OVAL_TEMPLATE: action->oval_template = optarg; break;
/* we use realpath to get an absolute path to given XSLT to prevent openscap from looking
into /usr/share/openscap/xsl instead of CWD */
case XCCDF_OPT_STYLESHEET_FILE: oscap_realpath(optarg, custom_stylesheet_path); action->stylesheet = custom_stylesheet_path; break;
Expand All @@ -1294,7 +1272,6 @@ bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
fprintf(stdout, "Warning: --cpe-dict is a deprecated option. Please use --cpe instead!\n\n");
action->cpe = optarg; break;
}
case XCCDF_OPT_SCE_TEMPLATE: action->sce_template = optarg; break;
case XCCDF_OPT_FIX_TYPE:
action->fix_type = optarg;
break;
Expand Down
Loading

0 comments on commit 6258a3d

Please sign in to comment.