Diff options
Diffstat (limited to '')
| -rwxr-xr-x | tests/t0301-security.sh | 233 |
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 "<script>" 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 "<script>" 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 "<script>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 && |
