Skip to content

Commit e6d21f9

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 0913348 commit e6d21f9

File tree

4 files changed

+70
-2
lines changed

4 files changed

+70
-2
lines changed

tests/utils/autotailor_integration_test.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,13 @@ assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www
138138
python3 $autotailor --id-namespace "com.example.www" --json-tailoring $json_tailoring --tailored-profile-id=CMDL_P --select R3 $ds $original_profile > $tailoring
139139
$OSCAP xccdf eval --profile CMDL_P --progress --tailoring-file $tailoring --results $result $ds
140140
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3"]/result[text()="pass"]'
141+
142+
# test --local-path option with absolute path (should use basename)
143+
python3 $autotailor --id-namespace "com.example.www" --local-path --select R3 $ds $original_profile > $tailoring
144+
assert_exists 1 '/Tailoring/benchmark[@href="data_stream.xml"]'
145+
$OSCAP xccdf eval --profile P1_customized --progress --tailoring-file $tailoring --results $result $ds
146+
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3"]/result[text()="pass"]'
147+
148+
# test default behavior (should use file:// URI)
149+
python3 $autotailor --id-namespace "com.example.www" --select R3 $ds $original_profile > $tailoring
150+
assert_exists 1 '/Tailoring/benchmark[starts-with(@href, "file://")]'

tests/utils/test_autotailor.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,29 @@ def test_no_id():
108108
with pytest.raises(ValueError) as e:
109109
p.import_json_tailoring_profile(profile_dict)
110110
assert str(e.value) == "You must define a base_profile_id or an id"
111+
112+
def test_get_datastream_uri():
113+
t = autotailor.Tailoring()
114+
115+
# Test default behavior with absolute path (file:// URI)
116+
t.original_ds_filename = "/nonexistent/path/test-ds.xml"
117+
t.use_local_path = False
118+
uri = t._get_datastream_uri()
119+
assert uri.startswith("file://")
120+
assert uri.endswith("/nonexistent/path/test-ds.xml")
121+
122+
# Test local path mode with absolute path (basename only)
123+
t.use_local_path = True
124+
uri = t._get_datastream_uri()
125+
assert uri == "test-ds.xml"
126+
127+
# Test local path mode with relative path (preserved as-is)
128+
t.original_ds_filename = "relative/path/to/ds.xml"
129+
uri = t._get_datastream_uri()
130+
assert uri == "relative/path/to/ds.xml"
131+
132+
# Test default behavior with relative path (file:// URI)
133+
t.use_local_path = False
134+
uri = t._get_datastream_uri()
135+
assert uri.startswith("file://")
136+
assert "relative/path/to/ds.xml" in uri

utils/autotailor

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ class Tailoring:
303303
self.id = "xccdf_auto_tailoring_default"
304304
self.version = 1
305305
self.original_ds_filename = ""
306+
self.use_local_path = False
306307

307308
self.profiles = []
308309

@@ -318,12 +319,31 @@ class Tailoring:
318319
self.profiles.append(profile)
319320
return profile
320321

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

324345
benchmark = ET.SubElement(root, "{%s}benchmark" % NS)
325-
datastream_uri = pathlib.Path(
326-
self.original_ds_filename).absolute().as_uri()
346+
datastream_uri = self._get_datastream_uri()
327347
benchmark.set("href", datastream_uri)
328348

329349
version = ET.SubElement(root, "{%s}version" % NS)
@@ -436,6 +456,10 @@ def get_parser():
436456
"-o", "--output", default="-",
437457
help="Where to save the tailoring file. If not supplied, write to "
438458
"standard output.")
459+
parser.add_argument(
460+
"--local-path", action="store_true",
461+
help="Use local path for the benchmark href instead of absolute file:// URI. "
462+
"Absolute paths are converted to basename, relative paths are preserved.")
439463
return parser
440464

441465

@@ -452,6 +476,7 @@ if __name__ == "__main__":
452476
t = Tailoring()
453477
t.original_ds_filename = args.datastream
454478
t.reverse_dns = args.id_namespace
479+
t.use_local_path = args.local_path
455480

456481
if args.json_tailoring:
457482
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)