Skip to content
Draft
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
54 changes: 33 additions & 21 deletions scripts/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
]:
if len(value) > 100:
raise UsageError(f"Invalid {label}: maximum length is 100 characters.")
if any(not c.isprintable() for c in value):
if not value.isprintable():
raise UsageError(f"Invalid {label}: control characters are not allowed.")
if label != "description" and '"' in value:
raise UsageError(f"Invalid {label}: double quotes are not allowed.")
Expand All @@ -73,31 +73,43 @@
escaped_author = toml_escape(author)
escaped_email = toml_escape(email)

replacements = [
("docs/reference/app.md", r"^::: project\.app", f"::: {source}.app"),
("mkdocs.yml", r"^repo_name: .*", f"repo_name: {github}/{name}"),
("mkdocs.yml", r"^repo_url: .*", f"repo_url: https://github.com/{github}/{name}"),
("pyproject.toml", r"^source = \[.*\]", f'source = ["{source}"]'),
("pyproject.toml", r'^app = "project\.app:main"', f'app = "{source}.app:main"'),
("pyproject.toml", r'^name = ".*"', f'name = "{source}"'),
("pyproject.toml", r'^description = ".*"', f'description = "{escaped_description}"'),
("pyproject.toml", r"^authors = \[.*\]", f'authors = ["{escaped_author} <{escaped_email}>"]'),
("docs/README.md", r"^# .*", f"# {description}"),
(".github/CODEOWNERS", r"@.*", f"@{github}"),
(".github/FUNDING.yml", r"^github: \[.*\]", f"github: [{github}]"),
]

for filepath, pattern, replacement in replacements:
def update_file(filepath: str, file_replacements: list[tuple[str, str]]):
path = Path(filepath)
if not path.exists():
secho(f" Warning: File {filepath} not found, skipping. ⚠️", fg="yellow")
continue
return

content = path.read_text()
# Use a lambda for replacement to avoid regex backreference injection
new_content = re.sub(pattern, lambda _, r=replacement: r, content, flags=re.MULTILINE)
path.write_text(new_content)
secho(f" Updated {filepath} ✅", fg="blue")
new_content = content
for pattern, replacement in file_replacements:
# Use a lambda for replacement to avoid regex backreference injection
new_content = re.sub(pattern, lambda _, r=replacement: r, new_content, flags=re.MULTILINE)

if new_content != content:
path.write_text(new_content)

Check failure on line 89 in scripts/init.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Change this code to not construct the path from user-controlled data.

See more on https://sonarcloud.io/project/issues?id=cur8d_python&issues=AZ9KT25Mx7oKr4XqLUfE&open=AZ9KT25Mx7oKr4XqLUfE&pullRequest=114

Check failure

Code scanning / SonarCloud

I/O function calls should not be vulnerable to path injection attacks High

Change this code to not construct the path from user-controlled data. See more on SonarQube Cloud
secho(f" Updated {filepath} ✅", fg="blue")

update_file("docs/reference/app.md", [(r"^::: project\.app", f"::: {source}.app")])
update_file(
"mkdocs.yml",
[
(r"^repo_name: .*", f"repo_name: {github}/{name}"),
(r"^repo_url: .*", f"repo_url: https://github.com/{github}/{name}"),
],
)
update_file(
"pyproject.toml",
[
(r"^source = \[.*\]", f'source = ["{source}"]'),
(r'^app = "project\.app:main"', f'app = "{source}.app:main"'),
(r'^name = ".*"', f'name = "{source}"'),
(r'^description = ".*"', f'description = "{escaped_description}"'),
(r"^authors = \[.*\]", f'authors = ["{escaped_author} <{escaped_email}>"]'),
],
)
update_file("docs/README.md", [(r"^# .*", f"# {description}")])
update_file(".github/CODEOWNERS", [(r"@.*", f"@{github}")])
update_file(".github/FUNDING.yml", [(r"^github: \[.*\]", f"github: [{github}]")])


@command(context_settings={"help_option_names": ["-h", "--help"]})
Expand Down