#!/bin/sh # Checks the ceilings a config can put on a page, the caps on refs listed, # on the lines and files a diff renders inline, on the size of a blob any # view will inflate, and the clamp on index query length. Crossing one has # to trim the page or point somewhere better suited, never drop the request. test_description='Check the ref listing and diff size limits' . ./setup.sh # The limits only show themselves against content that crosses them, so the # fixture carries more branches and tags than max-ref-count allows and a # commit whose oversized file diff sits beside a small one, which is what # tells a per file limit apart from a whole page one. test_expect_success 'set up limit fixtures' ' mkrepo repos/limits 1 && ( cd repos/limits && test_seq 1 60 >big.c && printf "int x;\n" >small.c && git add -A && git commit -m sources && test_seq 301 360 >big.c && printf "int y;\n" >small.c && git commit -am change && for i in 1 2 3 do git branch branch-$i && git tag tag-$i || exit 1 done ) && cgitrc_repo limitrc limits max-ref-count=2 max-diff-lines=20 max-diff-files=0 && cgitrc_repo limitfilesrc limits max-diff-files=1 ' # An index query is compared against every repository the listing holds, so # an enormous one multiplies out across the whole index. Clamping it rather # than refusing it keeps the cost bounded without making an ordinary search # behave any differently. test_expect_success 'an enormous query is clamped, not rejected' ' long=$(awk "BEGIN{s=\"\";for(i=0;i<4000;i++)s=s \"a\"; print s}") && cgit_query "q=$long" >tmp && grep "" tmp && longest=$(awk "{ while (match(\$0, /aa*/)) { if (RLENGTH > n) n = RLENGTH \$0 = substr(\$0, RSTART + RLENGTH) } } END { print n }" tmp) && test "$longest" -eq 512 ' test_expect_success 'an ordinary query still filters the index' ' cgit_query "q=foo" >tmp && grep ">foo" tmp && ! grep ">bar<" tmp ' # The combined refs page caps branches and tags separately, and the pages it # hands the overflow to page on their own, so a limit that leaked between the # two sections would show up as the wrong link or the wrong count here. test_expect_success 'refs page lists max-ref-count branches and tags' ' cgit_rc limitrc "url=limits/refs/" >tmp && test $(grep -c "log/?h=" tmp) -eq 2 && test $(grep -c "/tag/?h=tag-" tmp) -eq 2 ' test_expect_success 'refs page links each overflow to its own category' ' cgit_ok limitrc "url=limits/refs/" >tmp && test_grep "href=./limits/refs/heads.>\[\.\.\.\]" tmp && test_grep "href=./limits/refs/tags.>\[\.\.\.\]" tmp ' # The branches sort by name, so the second page holds branch-3 and master. test_expect_success 'branch page paginates independently' ' cgit_ok limitrc "url=limits/refs/heads/" >tmp && test $(grep -c "log/?h=" tmp) -eq 2 && test_grep "log/?h=branch-1." tmp && test_grep "\[next\]" tmp && cgit_ok limitrc "url=limits/refs/heads/&ofs=2" >tmp && test_grep "\[prev\] 3 - 4 of 4" tmp && test_grep "log/?h=branch-3." tmp && test_grep "href=./limits/log/.>master" tmp && test_grep ! "log/?h=branch-1." tmp && test_grep ! "/tag/?h=tag-" tmp ' test_expect_success 'tag page paginates independently' ' cgit_ok limitrc "url=limits/refs/tags/" >tmp && test_grep "/tag/?h=tag-1." tmp && test_grep "1 - 2 of 3" tmp && cgit_ok limitrc "url=limits/refs/tags/&ofs=2" >tmp && test_grep "\[prev\] 3 - 3 of 3" tmp && test_grep "/tag/?h=tag-3." tmp && test_grep ! "/tag/?h=tag-1." tmp && test_grep ! "log/?h=" tmp ' # A count of 0 lists every ref, and a negative one has to mean the same # rather than reach the sort as a size. test_expect_success 'a negative ref count lists every ref' ' for n in 0 -1 do { echo "summary-branches=$n" && echo "summary-tags=$n" && sed "s/^max-ref-count=2$/max-ref-count=$n/" limitrc } >count$n.rc && cgit_rc count$n.rc "url=limits/refs/" >refs$n && cgit_rc count$n.rc "url=limits/" >summary$n || return 1 done && for page in refs0 summary0 do test $(grep -c "" $page) = 4 && test_grep "href=./limits/log/.>master" $page && test $(grep -c "" $page) = 3 && for n in 1 2 3 do test_grep "href=./limits/log/?h=branch-$n.>" $page && test_grep "href=./limits/tag/?h=tag-$n.>" $page || return 1 done || return 1 done && test_cmp refs0 refs-1 && test_cmp summary0 summary-1 ' # A file that busts max-diff-lines is replaced by a link to its own page, # where the reader asked for that one file and the limit no longer applies. # The rest of the commit still renders, so one huge file cannot hide the # small change beside it. test_expect_success 'oversized file diff is replaced by a link' ' cgit_ok limitrc "url=limits/commit/" >tmp && test_grep "
This diff is too large to be rendered inline. View it on its own page.
" tmp ' test_expect_success 'small file in the same commit still renders inline' ' cgit_ok limitrc "url=limits/commit/" >tmp && test_grep "class=.add.>+int y;" tmp ' test_expect_success 'the single-file diff page always renders in full' ' cgit_ok limitrc "url=limits/diff/big.c" >tmp && test_grep "class=.hunk." tmp && test_grep ! "too large to be rendered inline" tmp ' # Following renames empties the prefix so the diff machinery sees the whole # commit, yet the page still shows the one file it names, so the cap has to # come from the path asked for. The links keeping follow=1 show that the page # took that path. test_expect_success 'a followed single-file diff page renders in full' ' { echo "enable-follow-links=1" && cat limitrc } >followrc && cgit_ok followrc "url=limits/diff/big.c&follow=1" >tmp && test_grep "href=./limits/diff/big.c?follow=1." tmp && test_grep "class=.hunk." tmp && test_grep ! "too large to be rendered inline" tmp ' # max-diff-files counts the files in a commit rather than the lines in one, # so it drops the whole body back to the diffstat while a request naming a # single file stays unaffected. test_expect_success 'a commit changing too many files shows stat only' ' cgit_ok limitfilesrc "url=limits/commit/" >tmp && test_grep "This diff is too large to be rendered inline. Follow a file above" tmp && test_grep ! "class=.hunk." tmp ' test_expect_success 'the single-file page is not limited by max-diff-files' ' cgit_ok limitfilesrc "url=limits/diff/small.c" >tmp && test_grep "class=.hunk." tmp ' # A path naming a directory still covers every file below it, so the caps # go on applying there. test_expect_success 'the diff caps hold when the path names a directory' ' test_create_repo repos/capdir && mkdir repos/capdir/dir && for n in 1 2 3 do echo $n >repos/capdir/dir/f$n || return 1 done && git -C repos/capdir add dir && git -C repos/capdir commit -q -m dir && cgitrc_repo capdirrc capdir max-diff-files=2 && cgit_ok capdirrc "url=capdir/diff/dir" >tmp && test_grep "This diff is too large to be rendered inline. Follow a file above" tmp && cgit_ok capdirrc "url=capdir/diff/dir/f1" >tmp && test_grep "class=.hunk." tmp && test_grep ! "too large to be rendered inline" tmp ' # The patch the notice links to has to hold the change the page compared, # both ends of a range and a branch other than the default. test_expect_success 'the stat only notice links the patch of the same change' ' new=$(git -C repos/limits rev-parse HEAD) && old=$(git -C repos/limits rev-parse HEAD~2) && cgit_rc limitfilesrc "url=limits/diff/&id=$new&id2=$old" >tmp && grep "href=./limits/patch/?id=$new&id2=$old.>patch link" tmp && cgit_rc limitfilesrc "url=limits/diff/&h=branch-1" >tmp && grep "href=./limits/patch/?h=branch-1.>patch link" tmp ' # The diff views must not inflate a blob past max-blob-size merely to render # it, which is the same defence the tree and plain views make and is easy to # miss on this path. Such a file is reported as binary instead. test_expect_success 'a diff of an oversized blob is not inlined' ' mkrepo repos/blobdiff 1 && ( cd repos/blobdiff && awk "BEGIN{for(i=0;i<400;i++)print \"aaaaaaaa\"}" >big.txt && git add -A && git commit -m add && awk "BEGIN{for(i=0;i<401;i++)print \"aaaaaaaa\"}" >big.txt && git commit -am change ) && cgitrc_repo blobdiffrc blobdiff max-blob-size=1 && sha=$(git -C repos/blobdiff rev-parse HEAD) && cgit_rc blobdiffrc "url=blobdiff/commit/&id=$sha" >tmp && grep "Binary files differ" tmp && ! grep "aaaaaaaa" tmp ' test_expect_success 'every page showing a blob refuses an oversized one alike' ' { echo "enable-blame=1" && cat blobdiffrc } >blobpagesrc && for page in tree plain blob blame do cgit_rc blobpagesrc "url=blobdiff/$page/big.txt" >tmp && grep "^Status: 413 Content Too Large" tmp && grep "Blob size (3 KB) exceeds the limit (1 KB)" tmp || return 1 done ' # The limit is in whole kilobytes, so a blob of exactly that size is still # within it. test_expect_success 'a blob of exactly max-blob-size is shown' ' printf "%01023d\n" 0 >repos/blobdiff/exact.txt && test $(wc -c tmp && test_grep "00000000" tmp || return 1 done ' # The patch and rawdiff pages hand the work to git, whose own limit on an # inlined blob is far past max-blob-size, so the cap is passed through to it. test_expect_success 'patch and rawdiff show an oversized blob as binary' ' test_seq 1 2000 >repos/limits/big.txt && git -C repos/limits add big.txt && git -C repos/limits commit -q -m "big blob" && { echo "max-blob-size=1" && cat limitrc } >blobrc && for p in patch rawdiff do cgit_rc blobrc "url=limits/$p/" >tmp && grep "^Binary files" tmp && ! grep "^+1999$" tmp || return 1 done ' # The diff options offer up to 40 lines of context, and context lines are # not counted against max-diff-lines, so a request asking for more is held # at 40. One asking for less than none falls back to the default. test_expect_success 'the context a request asks for is clamped' ' test_create_repo repos/context && test_seq 1 100 >repos/context/f && git -C repos/context add f && git -C repos/context commit -q -m one && test_seq 1 100 | sed "s/^50$/fifty/" >repos/context/f && git -C repos/context commit -q -a -m two && cgitrc_repo contextrc context && cgit_ok contextrc "url=context/commit/&context=100000" >tmp && test_grep "class=.hunk.>@@ -10,81 +10,81 @@" tmp && test_grep "