#!/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\n" >README.md && printf "\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= 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 "<script>" tmp && ! grep "" 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 "<script>" tmp && ! grep "" 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