blob: d3ac472f5275059c284aa23140ef681f358e0b75 (plain)
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
118
119
120
121
122
123
124
125
126
127
#!/bin/sh

test_description='Check security fixes and fork-specific behavior'
. ./setup.sh

# A repo with an oversized blob, readmes that carry markup, and a directory,
# plus a config that pins a tiny blob limit and groups directories.
test_expect_success 'set up security fixtures' '
	mkrepo repos/sec 1 &&
	(
		cd repos/sec &&
		dd if=/dev/zero bs=1024 count=4 2>/dev/null | tr "\0" "X" >big.txt &&
		printf "# Title\n<script>alert(1)</script>\n" >README.md &&
		printf "<script>alert(2)</script>\n" >readme.txt &&
		printf "top\n" >afile &&
		mkdir zsub &&
		printf "inner\n" >zsub/inner &&
		git add -A &&
		git commit -m fixtures
	) &&
	{
		echo "virtual-root=/" &&
		echo "cache-size=0" &&
		echo "max-blob-size=1" &&
		echo "enable-blame=1" &&
		echo "enable-tree-group-dirs=1" &&
		echo "repo.url=sec" &&
		echo "repo.path=$PWD/repos/sec/.git"
	} >seccgitrc
'

secq() { CGIT_CONFIG="$PWD/seccgitrc" QUERY_STRING="$1" cgit; }

# --- Argument injection through the log id= parameter -----------------------
# A tip beginning with a dash would be parsed as a git option, and
# id=--output=<path> would create or truncate an arbitrary file.
test_expect_success 'log id=--output does not write a file' '
	rm -f pwned &&
	cgit_query "url=foo/log&id=--output=$PWD/pwned" >tmp 2>&1 &&
	! test -e pwned
'

test_expect_success 'log id=--output is rejected as an invalid revision' '
	grep -i "invalid revision" tmp
'

test_expect_success 'a normal log still renders' '
	cgit_query "url=foo/log" >tmp &&
	grep -i "commit 5" tmp
'

test_expect_success 'a valid id= still renders the log' '
	sha=$(git -C repos/foo rev-parse HEAD) &&
	cgit_query "url=foo/log&id=$sha" >tmp &&
	grep -i "commit 5" tmp
'

# --- max-blob-size is enforced before the object is read --------------------
test_expect_success 'tree view refuses an oversized blob' '
	secq "url=sec/tree/big.txt" | grep -iE "exceeds|too large"
'

test_expect_success 'plain view refuses an oversized blob' '
	secq "url=sec/plain/big.txt" | grep -iE "exceeds|too large|413"
'

test_expect_success 'blame view refuses an oversized blob' '
	secq "url=sec/blame/big.txt" | grep -iE "exceeds|too large"
'

test_expect_success 'a small blob is still served' '
	secq "url=sec/plain/afile" | grep -F "top"
'

# --- Readme rendering escapes untrusted repository content ------------------
test_expect_success 'markdown readme is escaped and marked for the client' '
	{
		echo "virtual-root=/" &&
		echo "cache-size=0" &&
		echo "repo.url=md" &&
		echo "repo.path=$PWD/repos/sec/.git" &&
		echo "repo.readme=master:README.md"
	} >secmdrc &&
	CGIT_CONFIG="$PWD/secmdrc" QUERY_STRING="url=md/about/" cgit >tmp &&
	grep "data-markdown" tmp &&
	grep "&lt;script&gt;" tmp &&
	! grep "<script>alert(1)</script>" tmp
'

test_expect_success 'non-markdown readme without a filter is escaped' '
	{
		echo "virtual-root=/" &&
		echo "cache-size=0" &&
		echo "repo.url=txt" &&
		echo "repo.path=$PWD/repos/sec/.git" &&
		echo "repo.readme=master:readme.txt"
	} >sectxtrc &&
	CGIT_CONFIG="$PWD/sectxtrc" QUERY_STRING="url=txt/about/" cgit >tmp &&
	grep "&lt;script&gt;" tmp &&
	! grep "<script>alert(2)</script>" tmp
'

test_expect_success 'non-markdown readme keeps its line structure' '
	grep "pre class=.plaintext." tmp
'

# --- Auto-submitting selects carry no inline handlers ------------------------
# A Content-Security-Policy without unsafe-inline blocks inline onchange
# handlers, so the forms mark their selects and cgit.js wires them up.
test_expect_success 'diff option selects use the autosubmit marker' '
	sha=$(git -C repos/foo rev-parse HEAD) &&
	cgit_query "url=foo/commit&id=$sha" >tmp &&
	grep "data-autosubmit" tmp &&
	! grep "onchange" tmp
'

# --- Fork feature: directories are grouped before files in the tree ---------
test_expect_success 'tree groups directories before files' '
	secq "url=sec/tree/" >tmp &&
	dirline=$(grep -n "tree/zsub" tmp | head -1 | cut -d: -f1) &&
	fileline=$(grep -n "tree/afile" tmp | head -1 | cut -d: -f1) &&
	test -n "$dirline" &&
	test -n "$fileline" &&
	test "$dirline" -lt "$fileline"
'

test_done