Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/fwl_io/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ def _cmd_list(args: argparse.Namespace) -> int:
'' if ds.registry_path and ds.registry_path.is_file() else ' [NO REGISTRY]'
)
print(f' {ds.key:50s} required_by: {consumers}{registry_note}')
if ds.name != ds.key:
# A name is a single-line label; drop control characters so a
# manifest cannot inject extra lines into the listing.
label = ''.join(c for c in ds.name if c.isprintable())
# A name is a single-line label; drop control characters so a manifest
# cannot inject extra lines, then skip the line when there is nothing
# left to show or it collapses to the key already printed above.
label = ''.join(c for c in ds.name if c.isprintable()).strip()
if label and label != ds.key:
print(f' {label}')
for provider, message in sorted(errors.items()):
print(f'[{provider}] FAILED TO LOAD: {message}', file=sys.stderr)
Expand Down
81 changes: 78 additions & 3 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,17 @@ def load(self):


@pytest.mark.unit
def test_list_strips_control_characters_from_a_declared_name(tmp_path, capsys, monkeypatch):
"""A name carrying a newline cannot inject an extra line into the listing."""
@pytest.mark.parametrize(
'escape',
['\\n', '\\t', '\\r', '\\u2028'],
ids=['newline', 'tab', 'cr', 'line-separator'],
)
def test_list_strips_control_characters_from_a_declared_name(escape, tmp_path, capsys, monkeypatch):
"""A name carrying a control character cannot inject an extra line into the listing."""
manifest = tmp_path / 'manifest.toml'
manifest.write_text(
'[evil]\n'
'name = "harmless\\n forged.key required_by: victim [NO REGISTRY]"\n'
f'name = "harmless{escape} forged.key required_by: victim [NO REGISTRY]"\n'
'zenodo = "10.5281/zenodo.1"\n'
)

Expand Down Expand Up @@ -151,6 +156,76 @@ def load(self):
assert not any(line.startswith(' forged') for line in lines)


@pytest.mark.unit
@pytest.mark.parametrize(
'toml_escaped_name',
['\\u200b\\u200b', '\\u0001 \\u0001'],
ids=['all-non-printable', 'non-printable-padding-around-spaces'],
)
def test_list_omits_the_label_line_for_an_all_non_printable_name(
toml_escaped_name, tmp_path, capsys, monkeypatch
):
"""A name that strips to nothing after filtering must not print a bare indented line."""
manifest = tmp_path / 'manifest.toml'
manifest.write_text(f'[evil]\nname = "{toml_escaped_name}"\nzenodo = "10.5281/zenodo.1"\n')

class _EP:
def __init__(self, name, target):
self.name = name
self._target = target

def load(self):
return self._target

monkeypatch.setattr(
'fwl_io.manifest.entry_points',
lambda group: [_EP('demo', lambda: manifest)],
)

code = main(['list'])
out = capsys.readouterr().out
lines = out.splitlines()
assert code == 0
assert len(lines) == 2
assert lines[0] == '[demo]'
assert lines[1].startswith(' evil')


@pytest.mark.unit
@pytest.mark.parametrize(
'toml_escaped_name',
['evil ', 'evil\\u200b'],
ids=['trailing-space', 'trailing-invisible-char'],
)
def test_list_omits_the_label_line_when_it_collapses_to_the_key(
toml_escaped_name, tmp_path, capsys, monkeypatch
):
"""A name that differs from the key raw but not after filtering must not repeat it."""
manifest = tmp_path / 'manifest.toml'
manifest.write_text(f'[evil]\nname = "{toml_escaped_name}"\nzenodo = "10.5281/zenodo.1"\n')

class _EP:
def __init__(self, name, target):
self.name = name
self._target = target

def load(self):
return self._target

monkeypatch.setattr(
'fwl_io.manifest.entry_points',
lambda group: [_EP('demo', lambda: manifest)],
)

code = main(['list'])
out = capsys.readouterr().out
lines = out.splitlines()
assert code == 0
assert len(lines) == 2
assert lines[0] == '[demo]'
assert lines[1].startswith(' evil')


@pytest.mark.unit
def test_fetch_unknown_module_exits_nonzero(capsys, monkeypatch):
"""Asking for a model no manifest declares is an error, not an empty success."""
Expand Down
Loading