Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- "3.13"
- "3.12"
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
- uses: jdx/mise-action@v4
with:
tool_versions: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
- uses: jdx/mise-action@v4
- name: Configure Git Credentials
run: |
Expand Down
3 changes: 3 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sentinel Security Journal

This journal contains critical security learnings specific to this project.
5 changes: 5 additions & 0 deletions project/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ def main(name: str = "World"):
Args:
name: the name to be greeted
"""
# Trim leading and trailing whitespace to sanitize input
name = name.strip()
if not name:
name = "World"

if len(name) > 100:
raise UsageError("Invalid name: maximum length is 100 characters.")
if not name.isprintable():
Expand Down
37 changes: 30 additions & 7 deletions tests/test_app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from click.testing import CliRunner
from pytest import main

from project.app import main
from project.app import main as app_main


def test_help():
runner = CliRunner()
result = runner.invoke(main, ["--help"])
result = runner.invoke(app_main, ["--help"])
assert result.exit_code == 0
assert "--name <name>" in result.output
assert "-V, --version" in result.output
Expand All @@ -14,31 +15,53 @@ def test_help():

def test_version():
runner = CliRunner()
result = runner.invoke(main, ["-V"])
result = runner.invoke(app_main, ["-V"])
assert result.exit_code == 0
assert "app, version 0.1.0" in result.output


def test_greet():
runner = CliRunner()
result = runner.invoke(main, ["--name", "Jules"])
result = runner.invoke(app_main, ["--name", "Jules"])
assert result.exit_code == 0
assert "Hello Jules! 👋" in result.output


def test_name_too_long():
runner = CliRunner()
result = runner.invoke(main, ["--name", "A" * 101])
result = runner.invoke(app_main, ["--name", "A" * 101])
assert result.exit_code != 0
assert "maximum length is 100 characters" in result.output


def test_name_control_characters():
runner = CliRunner()
result = runner.invoke(main, ["--name", "Injected\x1b[31mRed\x1b[0m"])
result = runner.invoke(app_main, ["--name", "Injected\x1b[31mRed\x1b[0m"])
assert result.exit_code != 0
assert "control characters are not allowed" in result.output

result = runner.invoke(main, ["--name", "test\x7f"])
result = runner.invoke(app_main, ["--name", "test\x7f"])
assert result.exit_code != 0
assert "control characters are not allowed" in result.output


def test_greet_trimming():
runner = CliRunner()
result = runner.invoke(app_main, ["--name", " Jules "])
assert result.exit_code == 0
assert "Hello Jules! 👋" in result.output


def test_greet_empty_fallback():
runner = CliRunner()
result = runner.invoke(app_main, ["--name", ""])
assert result.exit_code == 0
assert "Hello World! 👋" in result.output

result = runner.invoke(app_main, ["--name", " "])
assert result.exit_code == 0
assert "Hello World! 👋" in result.output


if __name__ == "__main__":
main()
Loading