#!/bin/sh # Checks the filters that hand a piece of a page to an outside program or a # Lua script before it is written out, from the arguments and environment # each one gets to what happens when one fails. The loop runs the source, # about, commit and email filters once per kind of filter, the exec kind # always and the lua kind only where cgit was built with lua, because both # have to produce the same page from the same repository. Elsewhere the lua # kind shows as skipped. Most failures go through the auth filter, which # runs before any of the page is out. The Lua tests at the end drive the # calls cgit gives a script, and git tracing before any filter is set up. # The SIGPIPE reset for an exec filter is checked in t0303-robustness.sh. test_description='Check filtered content' . ./setup.sh for prefix in exec lua do filter_prereq= test "$prefix" = exec || filter_prereq=CGIT_LUA # Page output is buffered and has to be flushed as stdout is handed # to a filter and back. A missed flush reorders the page rather than # losing any of it, so the test below watches which side of the # filtered text the surrounding markup lands on. test_expect_success $filter_prereq "the $prefix source filter gets the file name and fills the code cell" ' cgit_ok_url "filter-$prefix/tree/a%2bb" >tmp && grep "a+b HELLO$" tmp && tr -d "\n" flat.out && grep "a+b HELLO" flat.out ' test_expect_success $filter_prereq "the $prefix about filter gets the readme name and the tree url" ' cgit_ok_url "filter-$prefix/about/" >tmp && grep "
a+b /filter-$prefix/tree/ HELLO$" tmp ' test_expect_success $filter_prereq "the $prefix commit filter rewrites the subject" ' cgit_ok_url "filter-$prefix/commit/" >tmp && grep "
ADD A+B" tmp ' test_expect_success $filter_prereq "the $prefix email filter gets the author's address and name" ' cgit_ok_url "filter-$prefix/commit/" >tmp && grep " commit A U THOR <AUTHOR@EXAMPLE.COM>" tmp ' test_expect_success $filter_prereq "the $prefix email filter gets the committer's address and name" ' cgit_ok_url "filter-$prefix/commit/" >tmp && grep " commit C O MITTER <COMMITTER@EXAMPLE.COM>" tmp ' done test_expect_success 'the blame page goes through the source filter too' ' cgitrc_copy blamerc enable-blame=1 && cgit_rc blamerc "url=filter-exec/blame/a%2bb" >tmp && grep "" tmp && grep "a+b HELLO$" tmp ' # A readme read at another branch links into the tree at that branch, which # the filter is told as the query to follow a path with. test_expect_success 'the about filter gets the tree url at the readme branch' ' cgitrc_repo aboutargsrc filtered readme=:a+b "about-filter=exec:$FILTER_DIRECTORY/dump.sh" "repo.path=$PWD/repos/filter/.git" && cgit_rc aboutargsrc "url=filtered/about/&h=1%2b2" >tmp && grep "
a+b /filtered/tree/ ?h=1%2B2 HELLO" tmp ' test_expect_success 'the site readme leaves the tree url empty' ' echo site >root.txt && cgitrc_repo siterc foo "about-filter=exec:$FILTER_DIRECTORY/dump.sh" "root-readme=$PWD/root.txt" && cgit_rc siterc "p=about" >tmp && grep "root.txt SITE" tmp ' test_expect_success 'only a repository page hands filters its variables' ' write_script env.sh <<-\EOF && cat >/dev/null echo "repo url: $CGIT_REPO_URL." EOF echo site >root.txt && cgitrc_repo envrc filtered readme=:a+b "about-filter=exec:$PWD/env.sh" "root-readme=$PWD/root.txt" "repo.path=$PWD/repos/filter/.git" && cgit_rc envrc "url=filtered/about/" >tmp && grep "repo url: filtered\." tmp && cgit_rc envrc "p=about" >tmp && grep "repo url: \." tmp ' # Only the auth filter's exit status means anything, so a content filter that # fails after writing its output still leaves the page whole. test_expect_success 'a content filter that exits non-zero still renders the page' ' write_script failing-filter <<-\EOF && cat exit 3 EOF cgitrc_repo failingrc foo "repo.commit-filter=exec:$PWD/failing-filter" && cgit_ok failingrc "url=foo/commit/" >tmp && test_grep "
commit 5" tmp ' # A filter killed by a signal leaves no exit status to go on, so the request # ends there. The auth filter runs before any of the page is out, which lets # the error page carry its own status. test_expect_success 'a filter killed by a signal ends the request' ' write_script killed-filter <<-\EOF && cat >/dev/null kill -9 $$ EOF cgitrc_repo killedrc foo "auth-filter=exec:$PWD/killed-filter" && cgit_rc killedrc "url=foo/" >tmp 2>err && test $(grep -c "^Status:" tmp) = 1 && test_grep "^Status: 500" tmp && test_grep -F "Subprocess $PWD/killed-filter exited abnormally" err ' test_expect_success 'a filter command with no prefix runs as exec' ' cgitrc_repo bareprefixrc foo "repo.commit-filter=$FILTER_DIRECTORY/dump.sh" && cgit_ok bareprefixrc "url=foo/commit/" >tmp && test_grep "
COMMIT 5" tmp ' # A prefix that names no kind of filter ends the request rather than leave # the page unfiltered. test_expect_success 'a filter command with an unknown prefix ends the request' ' cgitrc_repo badprefixrc foo "repo.commit-filter=perl:$FILTER_DIRECTORY/dump.sh" && cgit_rc badprefixrc "url=foo/commit/" >tmp 2>err && test $(grep -c "^Status:" tmp) = 1 && test_grep "^Status: 500" tmp && test_grep -F "Invalid filter type: perl" err ' # A die inside a lua filter has to reach the visitor as an error page rather # than being fed back into the filter that just failed, and the reason goes to # the log with the script it came from. Each script runs as the auth filter, # so it fails before any of the page is out. check_lua_error() { test $(grep -c "^Status:" tmp) = 1 && test_grep "^Status: 500" tmp && test_grep -F "Lua error in $1: " err } test_expect_success CGIT_LUA 'a failing lua filter still renders an error page' ' cat >broken.lua <<-\EOF && function filter_open(...) error("boom") end function filter_write(str) end function filter_close() return 0 end EOF cgitrc_repo brokenrc foo "auth-filter=lua:$PWD/broken.lua" && cgit_rc brokenrc "url=foo/commit" >tmp 2>err && check_lua_error "$PWD/broken.lua" ' # A script may raise a value that is no string, which the log still names. test_expect_success CGIT_LUA 'a lua filter raising a table is named by type' ' cat >tabled.lua <<-\EOF && function filter_open(...) error({}) end function filter_write(str) end function filter_close() return 0 end EOF cgitrc_repo tabledrc foo "auth-filter=lua:$PWD/tabled.lua" && cgit_rc tabledrc "url=foo/commit" >tmp 2>err && grep "Status: 500" tmp && grep "Lua error in .*tabled.lua: (error object is a table value)" err ' # Lua shortens a long path at the front where it names a chunk, so the line # a syntax error is reported on is found by the file's own name. test_expect_success CGIT_LUA 'a lua filter with a syntax error ends the request' ' cat >syntax.lua <<-\EOF && function filter_open(...) end function filter_write(str) html(str end function filter_close() return 0 end EOF cgitrc_repo syntaxrc foo "auth-filter=lua:$PWD/syntax.lua" && cgit_rc syntaxrc "url=foo/" >tmp 2>err && check_lua_error "$PWD/syntax.lua" && test_grep "syntax\.lua:2: " err ' test_expect_success CGIT_LUA 'a lua filter whose script is missing ends the request' ' cgitrc_repo missingrc foo "auth-filter=lua:$PWD/no-such.lua" && cgit_rc missingrc "url=foo/" >tmp 2>err && check_lua_error "$PWD/no-such.lua" && test_grep -F "cannot open $PWD/no-such.lua" err ' test_expect_success CGIT_LUA 'a lua filter that fails in filter_close ends the request' ' cat >close-error.lua <<-\EOF && function filter_open(...) end function filter_write(str) end function filter_close() error("boom") end EOF cgitrc_repo closeerrorrc foo "auth-filter=lua:$PWD/close-error.lua" && cgit_rc closeerrorrc "url=foo/" >tmp 2>err && check_lua_error "$PWD/close-error.lua" ' # Only a login post writes to the auth filter. test_expect_success CGIT_LUA 'a lua filter that fails in filter_write ends the request' ' cat >write-error.lua <<-\EOF && function filter_open(...) end function filter_write(str) error("boom") end function filter_close() return 0 end EOF cgitrc_repo writeerrorrc foo "auth-filter=lua:$PWD/write-error.lua" && printf "user=a" >post && ( export REQUEST_METHOD=POST CONTENT_LENGTH=6 && cgit_rc writeerrorrc "p=login" tmp 2>err && check_lua_error "$PWD/write-error.lua" ' test_expect_success CGIT_LUA 'a lua filter without filter_write ends the request' ' cat >no-write.lua <<-\EOF && function filter_open(...) end function filter_close() return 0 end EOF cgitrc_repo nowriterc foo "auth-filter=lua:$PWD/no-write.lua" && printf "user=a" >post && ( export REQUEST_METHOD=POST CONTENT_LENGTH=6 && cgit_rc nowriterc "p=login" tmp 2>err && check_lua_error "$PWD/no-write.lua" ' # A script runs its top level before cgit hooks stdout for it, so what it # writes there goes straight onto the page instead of tripping the hook. It # loads as the filter first opens, which puts the text where the filter runs. test_expect_success CGIT_LUA 'a lua filter that writes while loading still renders' ' cat >loud.lua <<-\EOF && html("written while loading") function filter_open(...) end function filter_write(str) html(str) end function filter_close() return 0 end EOF cgitrc_repo loudrc foo "repo.commit-filter=lua:$PWD/loud.lua" && cgit_ok loudrc "url=foo/commit" >tmp && grep "
written while loading" tmp ' # A Lua build replaces write, and git writes its trace before cgit has run a # line of its own. test_expect_success CGIT_LUA 'git can trace before the filters are set up' ' CGIT_CONFIG="$PWD/cgitrc" GIT_TRACE2="$PWD/trace2.out" QUERY_STRING="url=foo/" cgit >tmp && grep "" tmp && test -s trace2.out ' # html_url_arg and html_include are the two calls a Lua filter has beyond # plain html, and nothing else drives them. test_expect_success CGIT_LUA 'a lua filter can encode an argument and include a file' ' echo "included bold" >include.txt && cat >apis.lua <<-EOF && function filter_open(...) end function filter_write(str) html_url_arg("a b&c") html("|") html_include("$PWD/include.txt") end function filter_close() return 0 end EOF cgitrc_repo apisrc foo "repo.commit-filter=lua:$PWD/apis.lua" && cgit_rc apisrc "url=foo/commit" >tmp && grep "a+b%26c|included bold" tmp ' # The print of LuaJIT and Lua 5.1 leaves its line in stdio's buffer, which only # reached the page as cgit exited, behind the closing tag. A print lands where # the filter runs. test_expect_success CGIT_LUA 'a lua filter prints where it runs' ' cat >print.lua <<-\EOF && function filter_open(...) end function filter_write(str) print("printed", 1, nil, true) html(str) end function filter_close() return 0 end EOF cgitrc_repo printrc foo "repo.commit-filter=lua:$PWD/print.lua" && cgit_rc printrc "url=foo/commit" >tmp && tab=$(printf "\t") && grep "
printed${tab}1${tab}nil${tab}true$" tmp && sed -n "/<\/html>/,\$p" tmp >tail && ! grep printed tail ' test_done