mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
fix: replace xargs trim with sed to handle quotes in descriptions (#2351)
xargs re-parses stdin as shell tokens, causing 'unterminated quote' errors when feature descriptions contain apostrophes, double quotes, or backslashes. Replace with sed-based whitespace trim that preserves input verbatim. Add regression tests for special characters in descriptions (core and extension scripts), plus a negative test for whitespace-only input. Fixes #2339
This commit is contained in:
@@ -95,7 +95,7 @@ if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||
fi
|
||||
|
||||
# Trim whitespace and validate description is not empty
|
||||
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | xargs)
|
||||
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')
|
||||
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||
echo "Error: Feature description cannot be empty or contain only whitespace" >&2
|
||||
exit 1
|
||||
|
||||
@@ -84,7 +84,7 @@ if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||
fi
|
||||
|
||||
# Trim whitespace and validate description is not empty (e.g., user passed only whitespace)
|
||||
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | xargs)
|
||||
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')
|
||||
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||
echo "Error: Feature description cannot be empty or contain only whitespace" >&2
|
||||
exit 1
|
||||
|
||||
@@ -1257,3 +1257,67 @@ class TestFeatureDirectoryResolution:
|
||||
break
|
||||
else:
|
||||
pytest.fail("FEATURE_DIR not found in PowerShell output")
|
||||
|
||||
|
||||
# ── Description Quoting Tests (issue #2339) ──────────────────────────────────
|
||||
|
||||
|
||||
@requires_bash
|
||||
class TestDescriptionQuoting:
|
||||
"""Descriptions with quotes, apostrophes, and backslashes must not break the script.
|
||||
|
||||
Regression tests for https://github.com/github/spec-kit/issues/2339
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"description",
|
||||
[
|
||||
"Add user's profile page",
|
||||
"Fix the \"login\" bug",
|
||||
"Handle path\\with\\backslashes",
|
||||
"It's a \"complex\" feature\\here",
|
||||
],
|
||||
ids=["apostrophe", "double-quotes", "backslashes", "mixed"],
|
||||
)
|
||||
def test_core_script_handles_special_chars(self, git_repo: Path, description: str):
|
||||
"""Core create-new-feature.sh succeeds with special characters in description."""
|
||||
result = run_script(git_repo, "--dry-run", "--short-name", "feat", description)
|
||||
assert result.returncode == 0, (
|
||||
f"Script failed for description {description!r}: {result.stderr}"
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"description",
|
||||
[
|
||||
"Add user's profile page",
|
||||
"Fix the \"login\" bug",
|
||||
"Handle path\\with\\backslashes",
|
||||
"It's a \"complex\" feature\\here",
|
||||
],
|
||||
ids=["apostrophe", "double-quotes", "backslashes", "mixed"],
|
||||
)
|
||||
def test_ext_script_handles_special_chars(self, ext_git_repo: Path, description: str):
|
||||
"""Extension create-new-feature.sh succeeds with special characters in description."""
|
||||
script = (
|
||||
ext_git_repo / ".specify" / "extensions" / "git" / "scripts" / "bash" / "create-new-feature.sh"
|
||||
)
|
||||
result = subprocess.run(
|
||||
["bash", str(script), "--dry-run", "--short-name", "feat", description],
|
||||
cwd=ext_git_repo,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
assert result.returncode == 0, (
|
||||
f"Script failed for description {description!r}: {result.stderr}"
|
||||
)
|
||||
|
||||
def test_whitespace_only_still_rejected(self, git_repo: Path):
|
||||
"""Whitespace-only descriptions must still be rejected after trimming."""
|
||||
result = run_script(git_repo, "--dry-run", "--short-name", "feat", " ")
|
||||
assert result.returncode != 0
|
||||
assert "empty" in result.stderr.lower() or "whitespace" in result.stderr.lower()
|
||||
|
||||
def test_plain_description_still_works(self, git_repo: Path):
|
||||
"""Plain description without special characters continues to work."""
|
||||
result = run_script(git_repo, "--dry-run", "--short-name", "feat", "Add login feature")
|
||||
assert result.returncode == 0, result.stderr
|
||||
|
||||
Reference in New Issue
Block a user