Diff options
Diffstat (limited to '')
-rwxr-xr-xtests/t0301-security.sh233
1 file changed, 62 insertions, 171 deletions
diff --git a/tests/t0301-security.sh b/tests/t0301-security.sh
index d9ec265..b62fc62 100755
--- a/tests/t0301-security.sh
+++ b/tests/t0301-security.sh
@@ -8,15 +8,13 @@
test_description='Check security fixes and fork-specific behaviour'
. ./setup.sh
-# Most of what follows shares one repository and one config, so the fixture
-# carries everything they need at once, a blob over the size limit, readmes
-# holding markup that must not reach the page as markup, and a subdirectory
-# to sort ahead of the files.
+# Some cases below share one repository and one config, so the fixture
+# carries everything they need at once, readmes holding markup that must not
+# reach the page as markup and a subdirectory to sort ahead of the files.
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 &&
@@ -25,25 +23,14 @@ test_expect_success 'set up security fixtures' '
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
+ cgitrc_repo seccgitrc sec max-blob-size=1 enable-tree-group-dirs=1
'
-secq() { CGIT_CONFIG="$PWD/seccgitrc" QUERY_STRING="$1" cgit; }
-
# A revision beginning with a dash reaches git as an option rather than as a
# tip, so a request for id=--output=<path> could create or truncate any file
# the server is able to write.
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 &&
+ cgit_url "foo/log&id=--output=$PWD/pwned" >tmp 2>&1 &&
! test -e pwned
'
@@ -51,34 +38,15 @@ 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 &&
+ cgit_url "foo/log&id=$sha" >tmp &&
grep -i "commit 5" tmp
'
-# max-blob-size is enforced before the object is read, so every view that
-# would otherwise inline a file has to turn the same one away rather than
-# inflate it first and think better of it afterwards.
-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"
+ cgit_rc seccgitrc "url=sec/plain/afile" >tmp &&
+ grep -F top tmp
'
# A readme is repository content, so with no about filter configured it has
@@ -90,31 +58,24 @@ test_expect_success 'markdown readme without a filter is escaped as plain text'
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 &&
+ echo "repo.readme=master:README.md" &&
+ echo "repo.url=txt" &&
+ echo "repo.path=$PWD/repos/sec/.git" &&
+ echo "repo.readme=master:readme.txt"
+ } >secreadmerc &&
+ cgit_rc secreadmerc "url=md/about/" >tmp &&
grep "pre class=.plaintext." 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 &&
+test_expect_success 'non-markdown readme without a filter is escaped as plain text' '
+ cgit_rc secreadmerc "url=txt/about/" >tmp &&
+ grep "pre class=.plaintext." 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
-'
-
# root-readme is the site's own file rather than repository content, so it
# goes out as written, on an about page of its own.
test_expect_success 'root-readme without a filter is its own page, as written' '
@@ -124,10 +85,10 @@ test_expect_success 'root-readme without a filter is its own page, as written' '
echo "cache-size=0" &&
echo "root-readme=$PWD/root.html"
} >secrootrc &&
- CGIT_CONFIG="$PWD/secrootrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc secrootrc "url=" >tmp &&
grep "<li><a title=.About this site. href=./?p=about.>about</a>" tmp &&
! grep "<b>site</b>" tmp &&
- CGIT_CONFIG="$PWD/secrootrc" QUERY_STRING="p=about" cgit >tmp &&
+ cgit_rc secrootrc "p=about" >tmp &&
grep "^<b>site</b>$" tmp
'
@@ -136,7 +97,7 @@ test_expect_success 'root-readme without a filter is its own page, as written' '
# button and its selects carry no handlers at all.
test_expect_success 'diff options submit through a button' '
sha=$(git -C repos/foo rev-parse HEAD) &&
- cgit_query "url=foo/commit&id=$sha" >tmp &&
+ cgit_url "foo/commit&id=$sha" >tmp &&
grep "type=.submit. value=.reload." tmp &&
! grep "onchange" tmp
'
@@ -144,7 +105,7 @@ test_expect_success 'diff options submit through a button' '
# Grouping directories ahead of files is behaviour this fork adds, so nothing
# upstream covers it.
test_expect_success 'tree groups directories before files' '
- secq "url=sec/tree/" >tmp &&
+ cgit_rc seccgitrc "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" &&
@@ -153,7 +114,7 @@ test_expect_success 'tree groups directories before files' '
'
# A file name is repository content and may hold a quote, which would break
-# out of the href attribute on the line number links of a side by side diff,
+# out of the href attribute on the line number links of a side-by-side diff,
# so the path is percent-encoded on its way into them.
test_expect_success 'ssdiff percent-encodes a quoted file path' '
mkrepo repos/xss 1 &&
@@ -166,14 +127,9 @@ test_expect_success 'ssdiff percent-encodes a quoted file path' '
printf "a\nc\n" >"$name" &&
git commit -am change
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=xss" &&
- echo "repo.path=$PWD/repos/xss/.git"
- } >xssrc &&
+ cgitrc_repo xssrc xss &&
sha=$(git -C repos/xss rev-parse HEAD) &&
- CGIT_CONFIG="$PWD/xssrc" QUERY_STRING="url=xss/diff/&id=$sha&ss=1" cgit >tmp &&
+ cgit_rc xssrc "url=xss/diff/&id=$sha&ss=1" >tmp &&
grep "tree/x%27y.txt" tmp &&
! grep "href=.[^>]*x.y.txt.[^>]*>" tmp
'
@@ -189,15 +145,10 @@ test_expect_success 'a message-less commit renders without crashing' '
cid=$(git hash-object -t commit -w raw) &&
git update-ref refs/heads/master "$cid"
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=nomsg" &&
- echo "repo.path=$PWD/repos/nomsg/.git"
- } >nomsgrc &&
- CGIT_CONFIG="$PWD/nomsgrc" QUERY_STRING="url=nomsg/log/&showmsg=1" cgit >tmp &&
+ cgitrc_repo nomsgrc nomsg &&
+ cgit_rc nomsgrc "url=nomsg/log/&showmsg=1" >tmp &&
grep "no commit message" tmp &&
- CGIT_CONFIG="$PWD/nomsgrc" QUERY_STRING="url=nomsg/" cgit >tmp &&
+ cgit_rc nomsgrc "url=nomsg/" >tmp &&
grep "no commit message" tmp
'
@@ -218,42 +169,21 @@ test_expect_success 'set up a repo whose branches point at odd objects' '
grep -q "tree.refs/heads/points-at-tree" refs.out &&
grep -q "tag.refs/heads/points-at-tag" refs.out
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "branch-sort=age" &&
- echo "repo.url=oddref" &&
- echo "repo.path=$PWD/repos/oddref/.git"
- } >oddrefrc
-'
-
-test_expect_success 'refs page sorts such branches without crashing' '
- CGIT_CONFIG="$PWD/oddrefrc" QUERY_STRING="url=oddref/refs/" cgit >tmp &&
- grep "points-at-tree" tmp &&
- grep "</html>" tmp
-'
-
-test_expect_success 'the branch page sorts them without crashing' '
- CGIT_CONFIG="$PWD/oddrefrc" QUERY_STRING="url=oddref/refs/heads/" cgit >tmp &&
- grep "points-at-tree" tmp &&
- grep "</html>" tmp
+ cgitrc_repo oddrefrc oddref branch-sort=age
'
-test_expect_success 'the summary page sorts them without crashing' '
- CGIT_CONFIG="$PWD/oddrefrc" QUERY_STRING="url=oddref/" cgit >tmp &&
- grep "points-at-tree" tmp &&
- grep "</html>" tmp
+test_expect_success 'the refs, branch and summary pages sort such branches' '
+ for page in refs/ refs/heads/ ""
+ do
+ cgit_rc oddrefrc "url=oddref/$page" >tmp &&
+ grep "points-at-tree" tmp &&
+ grep "</html>" tmp || return 1
+ done
'
test_expect_success 'name-sorted branches are unaffected' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "branch-sort=name" &&
- echo "repo.url=oddref" &&
- echo "repo.path=$PWD/repos/oddref/.git"
- } >oddrefnamerc &&
- CGIT_CONFIG="$PWD/oddrefnamerc" QUERY_STRING="url=oddref/refs/heads/" cgit >tmp &&
+ sed "s/^branch-sort=age$/branch-sort=name/" oddrefrc >oddrefnamerc &&
+ cgit_rc oddrefnamerc "url=oddref/refs/heads/" >tmp &&
grep "points-at-tree" tmp &&
grep "</html>" tmp
'
@@ -266,9 +196,9 @@ test_expect_success 'name-sorted branches are unaffected' '
# working.
test_expect_success 'set up a submodule fixture with a hostile module-link' '
mkrepo repos/modlink 1 &&
+ sub=$(git -C repos/modlink rev-parse HEAD) &&
(
cd repos/modlink &&
- sub=$(git rev-parse HEAD) &&
git update-index --add --cacheinfo 160000,$sub,submod &&
git commit -m gitlink
) &&
@@ -284,7 +214,7 @@ test_expect_success 'set up a submodule fixture with a hostile module-link' '
'
test_expect_success 'a surplus conversion does not crash the tree view' '
- CGIT_CONFIG="$PWD/modlinkrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgit_rc modlinkrc "url=modlink/tree/" >tmp &&
grep "ls-mod" tmp
'
@@ -293,41 +223,20 @@ test_expect_success 'a surplus conversion is shown literally, not filled' '
'
test_expect_success 'a well-formed module-link still takes name and sha1' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "module-link=/mod/%s/commit/?id=%s" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git"
- } >modlinkokrc &&
- sub=$(git -C repos/modlink rev-parse HEAD~1) &&
- CGIT_CONFIG="$PWD/modlinkokrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgitrc_repo modlinkokrc modlink "module-link=/mod/%s/commit/?id=%s" &&
+ cgit_rc modlinkokrc "url=modlink/tree/" >tmp &&
grep "href=./mod/submod/commit/?id=$sub." tmp
'
test_expect_success 'a per-path module-link takes only the sha1' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git" &&
- echo "repo.module-link.submod=https://example.com/s/?id=%s"
- } >modlinkpathrc &&
- sub=$(git -C repos/modlink rev-parse HEAD~1) &&
- CGIT_CONFIG="$PWD/modlinkpathrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgitrc_repo modlinkpathrc modlink "repo.module-link.submod=https://example.com/s/?id=%s" &&
+ cgit_rc modlinkpathrc "url=modlink/tree/" >tmp &&
grep "href=.https://example.com/s/?id=$sub." tmp
'
test_expect_success 'a doubled percent in a module-link renders as one' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "module-link=/m/%s/%%/%s" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git"
- } >modlinkpctrc &&
- sub=$(git -C repos/modlink rev-parse HEAD~1) &&
- CGIT_CONFIG="$PWD/modlinkpctrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgitrc_repo modlinkpctrc modlink "module-link=/m/%s/%%/%s" &&
+ cgit_rc modlinkpctrc "url=modlink/tree/" >tmp &&
grep "href=./m/submod/%/$sub." tmp
'
@@ -345,7 +254,7 @@ test_expect_success 'a clone endpoint with http clone off answers the same way'
echo "enable-http-clone=0" &&
cat cgitrc
} >noclonerc &&
- CGIT_CONFIG="$PWD/noclonerc" QUERY_STRING="url=foo/info/refs" cgit >tmp &&
+ cgit_rc noclonerc "url=foo/info/refs" >tmp &&
grep "^Status: 404 Not Found" tmp &&
grep "<select name=.h. " tmp
'
@@ -369,17 +278,11 @@ test_expect_success 'set up a hostile .gitmodules' '
git add .gitmodules &&
git commit -m hostile
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "enable-gitmodules-links=1" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git"
- } >modlinkgmrc
+ cgitrc_repo modlinkgmrc modlink enable-gitmodules-links=1
'
test_expect_success 'a javascript url never reaches an href' '
- CGIT_CONFIG="$PWD/modlinkgmrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgit_rc modlinkgmrc "url=modlink/tree/" >tmp &&
! grep "href=.javascript:" tmp &&
grep "class=.ls-mod. title=.javascript:alert(1).>submod</span>" tmp
'
@@ -412,8 +315,7 @@ test_expect_success 'set up the advisory fixtures' '
) &&
mkdir -p readme &&
printf "readme text\n" >readme/README &&
- cat >auth.sh <<-\EOF &&
- #!/bin/sh
+ write_script auth.sh <<-\EOF &&
case "$1" in
authenticate-post)
cat >/dev/null
@@ -422,22 +324,11 @@ test_expect_success 'set up the advisory fixtures' '
esac
exit 1
EOF
- chmod +x auth.sh &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "auth-filter=exec:$PWD/auth.sh" &&
- echo "repo.url=cve" &&
- echo "repo.path=$PWD/repos/cve/.git" &&
- echo "repo.readme=$PWD/readme/README" &&
- echo "repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh"
- } >cverc
+ cgitrc_repo cverc cve "auth-filter=exec:$PWD/auth.sh" "repo.readme=$PWD/readme/README" "repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh"
'
-cveq() { CGIT_CONFIG="$PWD/cverc" QUERY_STRING="$1" cgit; }
-
test_expect_success 'a newline in a file name cannot split the headers' '
- cveq "url=cve/plain/crlf%0d%0aX-Injected:%201.txt" >tmp &&
+ cgit_rc cverc "url=cve/plain/crlf%0d%0aX-Injected:%201.txt" >tmp &&
grep "^Status: 200" tmp &&
! grep "^X-Injected" tmp &&
grep "^Content-Disposition: inline; filename=.crlf\\\\r\\\\nX-Injected: 1.txt.$" tmp
@@ -451,48 +342,48 @@ test_expect_success 'a posted length past any buffer is clamped' '
'
test_expect_success 'the dumb transport refuses a path that climbs out' '
- cveq "url=cve/objects/../config" >tmp &&
+ cgit_rc cverc "url=cve/objects/../config" >tmp &&
grep "^Status: 404" tmp &&
! grep "repositoryformatversion" tmp &&
- cveq "url=cve/objects/info/../../config" >tmp &&
+ cgit_rc cverc "url=cve/objects/info/../../config" >tmp &&
grep "^Status: 404" tmp &&
! grep "repositoryformatversion" tmp
'
test_expect_success 'the about page serves nothing above the readme directory' '
- cveq "url=cve/about/../cverc" >tmp &&
+ cgit_rc cverc "url=cve/about/../cverc" >tmp &&
grep "^Status: 404" tmp &&
! grep "virtual-root" tmp &&
- cveq "url=cve/about/" >tmp &&
+ cgit_rc cverc "url=cve/about/" >tmp &&
grep "readme text" tmp
'
test_expect_success 'a commit with an empty author renders everywhere' '
- cveq "url=cve/log/&h=noname" >tmp &&
+ cgit_rc cverc "url=cve/log/&h=noname" >tmp &&
grep ">empty author</a>" tmp &&
- cveq "url=cve/commit/&h=noname" >tmp &&
+ cgit_rc cverc "url=cve/commit/&h=noname" >tmp &&
grep "<div class=.commit-subject.>empty author<" tmp &&
- cveq "url=cve/atom/&h=noname" >tmp &&
+ cgit_rc cverc "url=cve/atom/&h=noname" >tmp &&
grep "<name>unknown</name>" tmp &&
! grep "<email></email>" tmp
'
test_expect_success 'a percent sign without digits behind it is read as itself' '
- cveq "url=cve/log/&q=%" >tmp &&
+ cgit_rc cverc "url=cve/log/&q=%" >tmp &&
grep "^Status: 200" tmp &&
- cveq "url=cve/log/&q=%zz%2" >tmp &&
+ cgit_rc cverc "url=cve/log/&q=%zz%2" >tmp &&
grep "^Status: 200" tmp &&
grep "value=.%zz%2." tmp
'
test_expect_success 'script in a file name is escaped on the diff page' '
- cveq "url=cve/diff/" >tmp &&
+ cgit_rc cverc "url=cve/diff/" >tmp &&
! grep "<script>alert" tmp &&
grep "&lt;script&gt;alert(1)x.txt" tmp
'
test_expect_success 'shell syntax in a file name reaches a filter as a plain argument' '
- cveq "url=cve/tree/%24(touch%20pwned).c" >tmp &&
+ cgit_rc cverc "url=cve/tree/%24(touch%20pwned).c" >tmp &&
grep "^Status: 200" tmp &&
grep "pwned).c" tmp &&
! test -e pwned &&