diff options
context:
space:
mode:
Diffstat (limited to 'tests/t0200-security.sh')
-rw-r--r--tests/t0200-security.sh113
1 file changed, 113 insertions, 0 deletions
diff --git a/tests/t0200-security.sh b/tests/t0200-security.sh
new file mode 100644
index 0000000..221b56b
--- /dev/null
+++ b/tests/t0200-security.sh
@@ -0,0 +1,113 @@
+#!/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
+'
+
+# --- 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