Skip to content

Commit 1331f26

Browse files
committed
autotailor: Add --local-path option for layered product compatibility
Add a new --local-path flag that generates benchmark references using local paths instead of absolute file:// URIs. This enables compatibility with Red Hat Satellite and other layered products that cannot resolve local filesystem paths. When --local-path is specified: - Absolute paths are converted to basename only - Relative paths are preserved as provided by the user The default behavior remains unchanged for backward compatibility. Changes based on code review feedback: - Renamed option from --relative-path to --local-path for clarity (the name better reflects that absolute paths become basenames) - Extracted URI determination logic to _get_datastream_uri() method to improve code organization and readability - Added comprehensive documentation to autotailor.8 man page - Added unit tests for _get_datastream_uri() method - Added integration tests for --local-path functionality Resolves: RHEL-143568
1 parent 7373845 commit 1331f26

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

tests/utils/autotailor_integration_test.sh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,14 @@ assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www
136136
# use JSON tailoring (P11) with a new profile from the command line
137137
python3 $autotailor --id-namespace "com.example.www" --json-tailoring $json_tailoring --tailored-profile-id=CMDL_P --select R3 $ds $original_profile > $tailoring
138138
$OSCAP xccdf eval --profile CMDL_P --progress --tailoring-file $tailoring --results $result $ds
139-
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3"]/result[text()="pass"]'
139+
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3"]/result[text()="pass"]'
140+
141+
# test --local-path option with absolute path (should use basename)
142+
python3 $autotailor --id-namespace "com.example.www" --local-path --select R3 $ds $original_profile > $tailoring
143+
assert_exists 1 '/Tailoring/benchmark[@href="data_stream.xml"]'
144+
$OSCAP xccdf eval --profile P1_customized --progress --tailoring-file $tailoring --results $result $ds
145+
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3"]/result[text()="pass"]'
146+
147+
# test default behavior (should use file:// URI)
148+
python3 $autotailor --id-namespace "com.example.www" --select R3 $ds $original_profile > $tailoring
149+
assert_exists 1 '/Tailoring/benchmark[starts-with(@href, "file://")]'

tests/utils/test_autotailor.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,30 @@ def test_refine_rule():
9494
"'high'.")
9595
assert p.rule_refinements(fav, "severity") == "high"
9696
assert p.rule_refinements(fav, "role") == "full"
97+
98+
99+
def test_get_datastream_uri():
100+
t = autotailor.Tailoring()
101+
102+
# Test default behavior with absolute path (file:// URI)
103+
t.original_ds_filename = "/tmp/test-ds.xml"
104+
t.use_local_path = False
105+
uri = t._get_datastream_uri()
106+
assert uri.startswith("file://")
107+
assert uri.endswith("/tmp/test-ds.xml")
108+
109+
# Test local path mode with absolute path (basename only)
110+
t.use_local_path = True
111+
uri = t._get_datastream_uri()
112+
assert uri == "test-ds.xml"
113+
114+
# Test local path mode with relative path (preserved as-is)
115+
t.original_ds_filename = "relative/path/to/ds.xml"
116+
uri = t._get_datastream_uri()
117+
assert uri == "relative/path/to/ds.xml"
118+
119+
# Test default behavior with relative path (file:// URI)
120+
t.use_local_path = False
121+
uri = t._get_datastream_uri()
122+
assert uri.startswith("file://")
123+
assert "relative/path/to/ds.xml" in uri

utils/autotailor

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ class Tailoring:
300300
self.id = "xccdf_auto_tailoring_default"
301301
self.version = 1
302302
self.original_ds_filename = ""
303+
self.use_local_path = False
303304

304305
self.profiles = []
305306

@@ -315,12 +316,31 @@ class Tailoring:
315316
self.profiles.append(profile)
316317
return profile
317318

319+
def _get_datastream_uri(self):
320+
"""
321+
Determine the datastream URI based on the use_local_path setting.
322+
323+
Returns:
324+
str: The URI/path to use in the benchmark href attribute
325+
"""
326+
if self.use_local_path:
327+
# Preserve relative paths as-is, convert absolute paths to basename
328+
ds_path = pathlib.Path(self.original_ds_filename)
329+
if ds_path.is_absolute():
330+
# Convert absolute path to basename for compatibility with layered products
331+
return ds_path.name
332+
else:
333+
# Keep relative paths as provided by the user
334+
return self.original_ds_filename
335+
else:
336+
# Use absolute URI (default behavior for backward compatibility)
337+
return pathlib.Path(self.original_ds_filename).absolute().as_uri()
338+
318339
def to_xml(self, root):
319340
root.set("id", self.id)
320341

321342
benchmark = ET.SubElement(root, "{%s}benchmark" % NS)
322-
datastream_uri = pathlib.Path(
323-
self.original_ds_filename).absolute().as_uri()
343+
datastream_uri = self._get_datastream_uri()
324344
benchmark.set("href", datastream_uri)
325345

326346
version = ET.SubElement(root, "{%s}version" % NS)
@@ -433,6 +453,10 @@ def get_parser():
433453
"-o", "--output", default="-",
434454
help="Where to save the tailoring file. If not supplied, write to "
435455
"standard output.")
456+
parser.add_argument(
457+
"--local-path", action="store_true",
458+
help="Use local path for the benchmark href instead of absolute file:// URI. "
459+
"Absolute paths are converted to basename, relative paths are preserved.")
436460
return parser
437461

438462

@@ -447,6 +471,7 @@ if __name__ == "__main__":
447471
t = Tailoring()
448472
t.original_ds_filename = args.datastream
449473
t.reverse_dns = args.id_namespace
474+
t.use_local_path = args.local_path
450475

451476
if args.json_tailoring:
452477
t.import_json_tailoring(args.json_tailoring)

utils/autotailor.8

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ Import tailoring from a JSON file (https://github.com/ComplianceAsCode/schemas/t
7474
However, data passed in the command line options takes precedence over JSON contents, including the BASE_PROFILE_ID argument.
7575
JSON tailoring can be accompanied with additional command-line options to either override contents of an existing profile (along with --tailored-profile-id identifier) or to create an extra profile (BASE_PROFILE_ID is a mandatory argument in this case and --tailored-profile-id is optional) in the resulting XCCDF tailoring file.
7676
.RE
77+
.TP
78+
\fB--local-path\fR
79+
.RS
80+
Use local path for the benchmark href instead of absolute file:// URI. This option is useful for compatibility with layered products like Red Hat Satellite that cannot resolve local filesystem paths.
81+
When this option is specified, absolute paths are converted to basename only, while relative paths are preserved as provided.
82+
By default, the tool uses absolute file:// URIs for backward compatibility.
83+
.RE
7784

7885
.SH USAGE
7986
.SS Modify a variable value

0 commit comments

Comments
 (0)