Skip to content

Commit 03bba71

Browse files
mnriemCopilot
andauthored
fix: reject trailing newlines in preset IDs (#5188)
Require the entire preset ID to match the allowed character set and cover valid and malformed IDs with regression tests. Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent a283c9d commit 03bba71

2 files changed

Lines changed: 18 additions & 4 deletions

File tree

‎src/specify_cli/presets/_manifest.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def _validate(self):
9797
# Validate preset metadata
9898
pack = self.data["preset"]
9999
# Check presence AND type: the format/version checks below feed these
100-
# values straight to ``re.match`` and ``packaging.Version``, both of
100+
# values straight to ``re.fullmatch`` and ``packaging.Version``, both of
101101
# which raise a bare TypeError on a non-string. YAML makes that an easy
102102
# authoring slip -- unquoted ``version: 1.0`` parses as a float and
103103
# ``id: 2`` as an int -- and TypeError is not a PresetValidationError,
@@ -117,7 +117,7 @@ def _validate(self):
117117
)
118118

119119
# Validate pack ID format
120-
if not re.match(r'^[a-z0-9-]+$', pack["id"]):
120+
if not re.fullmatch(r'[a-z0-9-]+', pack["id"]):
121121
raise PresetValidationError(
122122
f"Invalid preset ID '{pack['id']}': "
123123
"must be lowercase alphanumeric with hyphens only"

‎tests/specify_cli/presets/test_manifest.py‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,15 +199,29 @@ def test_missing_pack_id(self, temp_dir, valid_pack_data):
199199
with pytest.raises(PresetValidationError, match="Missing preset.id"):
200200
PresetManifest(manifest_path)
201201

202-
def test_invalid_pack_id_format(self, temp_dir, valid_pack_data):
202+
@pytest.mark.parametrize(
203+
"pack_id",
204+
["Invalid_ID", "", "test-pack\n", "test-pack\r\n", "test\npack",
205+
" test-pack", "test-pack ", "../test-pack", "test/pack"],
206+
)
207+
def test_invalid_pack_id_format(self, temp_dir, valid_pack_data, pack_id):
203208
"""Test invalid pack ID format."""
204-
valid_pack_data["preset"]["id"] = "Invalid_ID"
209+
valid_pack_data["preset"]["id"] = pack_id
205210
manifest_path = temp_dir / "preset.yml"
206211
with open(manifest_path, 'w') as f:
207212
yaml.dump(valid_pack_data, f)
208213
with pytest.raises(PresetValidationError, match="Invalid preset ID"):
209214
PresetManifest(manifest_path)
210215

216+
@pytest.mark.parametrize("pack_id", ["test-pack", "preset123", "123", "a"])
217+
def test_valid_pack_id_format(self, temp_dir, valid_pack_data, pack_id):
218+
"""Accept IDs containing only lowercase letters, digits, and hyphens."""
219+
valid_pack_data["preset"]["id"] = pack_id
220+
manifest_path = temp_dir / "preset.yml"
221+
manifest_path.write_text(yaml.safe_dump(valid_pack_data), encoding="utf-8")
222+
223+
assert PresetManifest(manifest_path).id == pack_id
224+
211225
def test_invalid_version(self, temp_dir, valid_pack_data):
212226
"""Test invalid semantic version."""
213227
valid_pack_data["preset"]["version"] = "not-a-version"

0 commit comments

Comments
 (0)