1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from pygitweb.actions import blog_link_spec, project_board_link
from pygitweb.templates_env import env, html_format, html_join, html_link, render_page, trusted_html
def test_table_escapes_plain_text() -> None:
html = env.get_template("table.html").render(
cols=["Name"],
rows=[["<script>alert(1)</script>"]],
)
assert "<script>" not in html
assert "<script>alert(1)</script>" in html
def test_table_renders_link_without_double_escaping() -> None:
cell = html_link("/p?a=commit&h=abc", "a<b", id="commit-link")
html = env.get_template("table.html").render(cols=["Commit"], rows=[[cell]])
assert 'href="/p?a=commit&h=abc"' in html
assert 'id="commit-link"' in html
assert ">a<b</a>" in html
assert "&amp;" not in html
def test_html_join_escapes_text_and_keeps_markup() -> None:
joined = html_join([html_link("/a", "x"), " <", "a&b", ">"])
assert str(joined) == '<a href="/a">x</a> <a&b>'
def test_trusted_html_is_not_escaped_in_table() -> None:
html = env.get_template("table.html").render(
cols=["V"],
rows=[[trusted_html("<em>ok</em>")]],
)
assert "<em>ok</em>" in html
def test_html_format_escapes_values() -> None:
assert str(html_format("<pre>{}</pre>", "<hi>")) == "<pre><hi></pre>"
def test_render_page_uses_base_template_and_escapes_text() -> None:
resp = render_page("message.html", title="Hello <x>", paragraphs=["Hi <there>"])
text = resp.body.decode()
assert text.startswith("<!DOCTYPE html>")
assert "<title>Hello <x></title>" in text
assert "Hi <there>" in text
assert text.count('<div class="page">') == 1
assert "</body></html>" in text
assert text.index('<div class="container-xl">') < text.index("Hi <there>")
assert text.index("Hi <there>") < text.index("</body></html>")
def test_project_list_template_emits_links() -> None:
resp = render_page(
"project_list.html",
title="Projects",
notice=None,
projects=[
{
"href": "/project/a<b",
"path": "a<b",
"descr": "d&d",
"board_href": "/board/create?project=a%3Cb",
"board_text": "create board",
"board_style": None,
}
],
)
text = resp.body.decode()
assert 'href="/project/a<b"' in text
assert ">a<b</a>" in text
assert "d&d" in text
assert ">create board</a>" in text
assert 'href="/board/create?project=a%3Cb"' in text
assert "bg-secondary-lt" in text
def test_project_board_link_points_at_existing_board(monkeypatch) -> None:
monkeypatch.setattr(
"pygitweb.actions.git_get_references",
lambda _project, _prefix: [("Tasks", "refs/tags/boards/Tasks")],
)
assert str(project_board_link("demo")) == '<a href="/project/demo/board/">project board</a>'
def test_project_board_link_offers_create_when_missing(monkeypatch) -> None:
monkeypatch.setattr("pygitweb.actions.git_get_references", lambda _project, _prefix: [])
monkeypatch.setattr("pygitweb.actions.settings.AUTH", True)
html = str(project_board_link("my proj"))
assert 'href="/board/create?project=my%20proj"' in html
assert 'style="color: #999; cursor: not-allowed;"' in html
assert ">create board</a>" in html
monkeypatch.setattr("pygitweb.actions.settings.AUTH", False)
plain = str(project_board_link("my proj"))
assert "style=" not in plain
assert ">create board</a>" in plain
def test_blog_link_spec_points_at_existing_blog(monkeypatch) -> None:
monkeypatch.setattr("pygitweb.actions.project_has_blog", lambda _project: True)
spec = blog_link_spec("demo")
assert spec == {
"href": "/project/demo/blog/",
"text": "view blog",
"style": None,
}
def test_blog_link_spec_muted_when_missing(monkeypatch) -> None:
monkeypatch.setattr("pygitweb.actions.project_has_blog", lambda _project: False)
spec = blog_link_spec("my proj")
assert spec == {
"href": "#",
"text": "no blog",
"style": "color: #999; cursor: not-allowed;",
}