|
14 | 14 | @option("--email", required=True, help="Author email") |
15 | 15 | @option("--github", required=True, help="GitHub username") |
16 | 16 | def main(name: str, description: str, author: str, email: str, github: str): |
17 | | - # Validate name to prevent directory traversal or other injection |
| 17 | + # Validate inputs to prevent configuration injection |
| 18 | + for label, value in [ |
| 19 | + ("name", name), |
| 20 | + ("description", description), |
| 21 | + ("author", author), |
| 22 | + ("email", email), |
| 23 | + ("github", github), |
| 24 | + ]: |
| 25 | + if "\n" in value or "\r" in value: |
| 26 | + raise UsageError(f"Invalid {label}: newlines are not allowed.") |
| 27 | + if label != "description" and '"' in value: |
| 28 | + raise UsageError(f"Invalid {label}: double quotes are not allowed.") |
| 29 | + |
18 | 30 | if not re.match(r"^[a-zA-Z0-9_-]+$", name): |
19 | 31 | raise UsageError( |
20 | 32 | f"Invalid project name '{name}'. Only alphanumeric characters, dashes, and underscores are allowed." |
21 | 33 | ) |
22 | 34 |
|
| 35 | + # Sanitize description for TOML double-quoted strings |
| 36 | + description = description.replace('"', '\\"') |
| 37 | + |
23 | 38 | source = name.replace("-", "_").lower() |
24 | 39 |
|
25 | 40 | echo(f"Initializing project '{name}' (source: '{source}')...") |
@@ -59,7 +74,7 @@ def main(name: str, description: str, author: str, email: str, github: str): |
59 | 74 | content = path.read_text() |
60 | 75 | new_content = content |
61 | 76 | for pattern, replacement in patterns: |
62 | | - new_content = re.sub(pattern, replacement, new_content, flags=re.MULTILINE) |
| 77 | + new_content = re.sub(pattern, lambda _: replacement, content, flags=re.MULTILINE) |
63 | 78 |
|
64 | 79 | if new_content != content: |
65 | 80 | path.write_text(new_content) |
|
0 commit comments