#!/bin/sh # The plain page hands over a repository's own bytes. A file with no better # guess is served as text unless its bytes look binary, a directory answers # with a bare listing of links, and a path that names nothing comes back as # a 404 rather than an empty document. The blob page, which does the same # for an object named by its id, is checked here too. test_description='Check content on plain page' . ./setup.sh test_expect_success 'generate foo/plain/file-1' ' cgit_ok_url "foo/plain/file-1" >tmp ' test_expect_success 'a text file is served as text/plain' ' head -n 1 tmp | grep "Status: 200 OK" && sed "/^$/q" tmp | grep "^Content-Type: text/plain; charset=UTF-8$" ' test_expect_success 'the body carries the blob byte for byte' ' strip_headers body && git -C repos/foo show master:file-1 >expect && test_cmp expect body ' # Without html serving the bytes are the repository's, so the browser is # told neither to guess a type of its own nor to load what they reference. # A repository that serves html vouches for its files and gets neither. test_expect_success 'a plain file is served under the untrusted headers' ' cgit_ok_url "foo/plain/file-1" >tmp && sed "/^$/q" tmp >headers && test_grep "^X-Content-Type-Options: nosniff$" headers && test_grep "^Content-Security-Policy: default-src .none.$" headers && cgitrc_copy servingrc enable-html-serving=1 && cgit_ok servingrc "url=foo/plain/file-1" >tmp && sed "/^$/q" tmp >headers && test_grep "^Content-Type: text/plain" headers && test_grep ! "^X-Content-Type-Options:" headers && test_grep ! "^Content-Security-Policy:" headers ' test_expect_success 'a missing path is a 404' ' cgit_url "foo/plain/no-such-file" >tmp && head -n 1 tmp | grep "Status: 404" ' test_expect_success 'a directory answers with a listing of links' ' cgit_ok_url "foo/plain" >tmp && test_grep "
  • file-1
  • " tmp && test_grep "
  • file-5
  • " tmp ' # A folder's listing leads back up a level, to the root's listing from a # folder at the top. test_expect_success 'a folder listing links its entries and its parent' ' test_create_repo repos/folders && ( cd repos/folders && mkdir -p dir/sub && echo a >dir/a.txt && echo b >dir/sub/b.txt && git add dir && git commit -m folders ) && cgitrc_repo foldersrc folders && cgit_ok foldersrc "url=folders/plain/dir" >tmp && test_grep "/dir/" tmp && test_grep "
  • \.\./
  • " tmp && test_grep "
  • a.txt
  • " tmp && test_grep "
  • sub
  • " tmp && cgit_ok foldersrc "url=folders/plain/dir/sub" >tmp && test_grep "
  • \.\./
  • " tmp && test_grep "
  • b.txt
  • " tmp ' # A symlink is answered with its target as text. A submodule's commit lives # in another repository, so its entry names that commit rather than linking # a path here. test_expect_success 'plain serves a link target and lists a submodule' ' mkrepo repos/plainmod 1 && ( cd repos/plainmod && sub=$(git rev-parse HEAD) && git update-index --add --cacheinfo 160000,$sub,sub && link=$(printf file-1 | git hash-object -w --stdin) && git update-index --add --cacheinfo 120000,$link,link && git commit -m "link and submodule" ) && cgitrc_repo plainmodrc plainmod && cgit_ok plainmodrc "url=plainmod/plain/link" >tmp && strip_headers body && printf file-1 >expect && test_cmp expect body && sub=$(git -C repos/plainmod rev-parse HEAD~1) && cgit_ok plainmodrc "url=plainmod/plain/" >tmp && test_grep "
  • sub @ " tmp && test_grep ! "href=./plainmod/plain/sub" tmp ' test_expect_success 'a blob whose bytes look binary is served as octet-stream' ' ( cd repos/foo && printf "\000\001\002" >blob.bin && git add blob.bin && git commit -m binary ) && cgit_ok_url "foo/plain/blob.bin" >tmp && sed "/^$/q" tmp | grep "^Content-Type: application/octet-stream$" ' # The blob page hands over one object's bytes by its id alone, under the # same untrusted headers as plain, with nothing in a tree to name a type by. test_expect_success 'the blob page serves an object by its id' ' blob=$(git -C repos/foo rev-parse master:file-1) && cgit_ok_url "foo/blob/&id=$blob" >tmp && sed "/^$/q" tmp >headers && test_grep "^Content-Type: text/plain; charset=UTF-8$" headers && test_grep "^X-Content-Type-Options: nosniff$" headers && test_grep "^Content-Security-Policy: default-src .none.$" headers && strip_headers body && git -C repos/foo cat-file blob $blob >expect && test_cmp expect body ' test_expect_success 'the blob page serves binary bytes as octet-stream' ' blob=$(printf "\\000\\001" | git -C repos/foo hash-object -w --stdin) && cgit_ok_url "foo/blob/&id=$blob" >tmp && sed "/^$/q" tmp >headers && test_grep "^Content-Type: application/octet-stream$" headers ' test_expect_success 'the blob page refuses an id that is no object id' ' cgit_url "foo/blob/&id=xyz" >tmp && test_grep "^Status: 404" tmp && test_grep "
    Bad object id: xyz
    " tmp ' # RFC 7303 names the XML types by an xml subtype or a +xml suffix, and those # take a charset like text does. A type that only spells xml inside a word, # as the Office formats do, is binary and gets none. test_expect_success 'only text and xml types carry a charset' ' ( cd repos/foo && printf "PK" >doc.docx && printf "" >pic.svg && git add doc.docx pic.svg && git commit -m types ) && docx=application/vnd.openxmlformats-officedocument. && docx=${docx}wordprocessingml.document && cgitrc_copy typesrc enable-html-serving=1 "mimetype.docx=$docx" \ mimetype.svg=image/svg+xml && cgit_ok typesrc "url=foo/plain/doc.docx" >tmp && sed "/^$/q" tmp | grep "^Content-Type: $docx$" && cgit_ok typesrc "url=foo/plain/pic.svg" >tmp && sed "/^$/q" tmp | grep "^Content-Type: image/svg+xml; charset=UTF-8$" ' # Without html serving a text or application type gives way to the sniffed # one, except PDF, which a browser shows without running the site's code. test_expect_success 'only PDF keeps an application type without html serving' ' test_create_repo repos/types && ( cd repos/types && printf "%%PDF-1.4" >doc.pdf && printf "alert(1)" >run.js && git add doc.pdf run.js && git commit -m types ) && cgitrc_repo pdfrc types mimetype.pdf=application/pdf mimetype.js=application/javascript && cgit_ok pdfrc "url=types/plain/doc.pdf" >tmp && sed "/^$/q" tmp | grep "^Content-Type: application/pdf$" && cgit_ok pdfrc "url=types/plain/run.js" >tmp && sed "/^$/q" tmp | grep "^Content-Type: text/plain; charset=UTF-8$" ' # A tree can carry two entries of one name, which only a repository with # fsck off accepts, and the plain view answers the first alone. test_expect_success 'plain answers once for duplicate tree entries' ' test_create_repo repos/dup && ( cd repos/dup && blob=$(printf PNGDATA | git hash-object -w --stdin) && inner=$(printf "100644 blob %s\\tinside\\n" $blob | git mktree) && top=$(printf "100644 blob %s\\tx.png\\n040000 tree %s\\tx.png\\n" $blob $inner | git mktree --missing) && c=$(echo dup | git commit-tree $top) && git update-ref refs/heads/master $c ) && cgitrc_repo duprc dup mimetype.png=image/png && cgit_ok duprc "url=dup/plain/x.png" >tmp && test $(grep -c "^Status:" tmp) = 1 && test $(grep -c "^Content-Type:" tmp) = 1 && grep "^Content-Type: image/png" tmp ' # A header line cannot carry a control byte, and a web server refuses the # whole response over one, so a file name holding one is sent with a stand-in. # The commit joins the dup repository the test above built. test_expect_success 'a control character in a file name stays out of the headers' ' ( cd repos/dup && blob=$(printf x | git hash-object -w --stdin) && top=$(printf "100644 blob %s\\tctl\\001name.txt\\n" $blob | git mktree) && c=$(echo ctl | git commit-tree $top) && git update-ref refs/heads/ctl $c ) && cgit_ok duprc "url=dup/plain/ctl%01name.txt&h=ctl" >tmp && grep "^Content-Disposition: inline; filename=.ctl?name.txt.$" tmp ' # A mime.types file gives an extension the type of the first line naming it, # so a line naming nothing, or a comment naming the extension ahead of the # real line, has to be passed over. Each test writes a file of its own. test_expect_success 'set up a file whose type only a mimetype file knows' ' test_create_repo repos/mime && test_commit -C repos/mime --no-tag note note.cgt ' test_expect_success 'a blank line in the mimetype file is skipped' ' printf "\n \nimage/x-cgit cgt\n" >blank.types && cgitrc_repo blankrc mime "mimetype-file=$PWD/blank.types" && cgit_ok blankrc "url=mime/plain/note.cgt" >tmp && grep "^Content-Type: image/x-cgit$" tmp ' test_expect_success 'an indented comment in the mimetype file is skipped' ' printf " # cgt used to be image/x-old\nimage/x-cgit cgt\n" >comment.types && cgitrc_repo commentrc mime "mimetype-file=$PWD/comment.types" && cgit_ok commentrc "url=mime/plain/note.cgt" >tmp && grep "^Content-Type: image/x-cgit$" tmp ' test_done