diff options
| author | Bryce Kwon <bryce@brycekwon.com> | |
|---|---|---|
| committer | Bryce Kwon <bryce@brycekwon.com> | |
| commit | ||
| parent | ||
| tree | ||
| download | ||
Harden the page renderers
Diffstat (limited to '')
| -rw-r--r-- | source/ui-snapshot.c | 71 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 file changed, 60 insertions, 11 deletions
diff --git a/source/ui-snapshot.c b/source/ui-snapshot.c index a9bf858..5f2d69d 100644 --- a/source/ui-snapshot.c +++ b/source/ui-snapshot.c @@ -15,6 +15,7 @@ #include "cgit.h" #include "filter.h" #include "html.h" +#include "shared.h" #include "ui-shared.h" #include "ui-snapshot.h" @@ -61,6 +62,31 @@ static int write_zip_archive(const char *hex, const char *prefix) return write_archive_format("--format=zip", hex, prefix); } +static int program_on_path(const char *program) +{ + const char *path = getenv("PATH"); + struct strbuf full = STRBUF_INIT; + int found = 0; + + if (!path) + return 0; + while (!found) { + const char *end = strchrnul(path, ':'); + + strbuf_reset(&full); + strbuf_add(&full, path, end - path); + if (!full.len) + strbuf_addch(&full, '.'); + strbuf_addf(&full, "/%s", program); + found = !access(full.buf, X_OK); + if (!*end) + break; + path = end + 1; + } + strbuf_release(&full); + return found; +} + static int write_compressed_tar_archive(const char *hex, const char *prefix, char *argv[]) { struct cgit_exec_filter filter; @@ -76,24 +102,28 @@ static int write_compressed_tar_archive(const char *hex, const char *prefix, cha static int write_tar_gzip_archive(const char *hex, const char *prefix) { char *argv[] = { "gzip", "-n", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); } static int write_tar_bzip2_archive(const char *hex, const char *prefix) { char *argv[] = { "bzip2", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); } static int write_tar_lzip_archive(const char *hex, const char *prefix) { char *argv[] = { "lzip", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); } static int write_tar_xz_archive(const char *hex, const char *prefix) { char *argv[] = { "xz", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); } @@ -102,20 +132,21 @@ static int write_tar_zstd_archive(const char *hex, const char *prefix) // Single-threaded like the other compressors, since -T0 would let one // request pin every core. char *argv[] = { "zstd", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); } // ui-shared.c reads entry zero as the tar whose signature stands in for every // tar variant, so this order cannot change. const struct cgit_snapshot_format cgit_snapshot_formats[] = { - { ".tar", "application/x-tar", write_tar_archive }, - { ".tar.gz", "application/gzip", write_tar_gzip_archive }, - { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive }, - { ".tar.lz", "application/x-lzip", write_tar_lzip_archive }, - { ".tar.xz", "application/x-xz", write_tar_xz_archive }, - { ".tar.zst", "application/zstd", write_tar_zstd_archive }, - { ".zip", "application/zip", write_zip_archive }, - { NULL } + { ".tar", "application/x-tar", write_tar_archive, NULL }, + { ".tar.gz", "application/gzip", write_tar_gzip_archive, "gzip" }, + { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, "bzip2" }, + { ".tar.lz", "application/x-lzip", write_tar_lzip_archive, "lzip" }, + { ".tar.xz", "application/x-xz", write_tar_xz_archive, "xz" }, + { ".tar.zst", "application/zstd", write_tar_zstd_archive, "zstd" }, + { ".zip", "application/zip", write_zip_archive, NULL }, + { 0 } }; // Each tree is read the first time a signature for that format is asked for, @@ -142,7 +173,10 @@ static int resolves(const char *rev) { struct object_id oid; - return repo_get_oid(the_repository, rev, &oid) == 0; + // The name goes no further than this lookup, so a leading dash is + // fine here, while the wider syntax is refused as in cgit_valid_rev. + return !check_refname_format(rev, REFNAME_ALLOW_ONELEVEL) && + !repo_get_oid(the_repository, rev, &oid); } static const char *ref_from_filename(const struct cgit_repo *repo, const char *filename, @@ -197,14 +231,23 @@ static int send_snapshot(const struct cgit_snapshot_format *format, const char * return 1; } if (!lookup_commit_reference(the_repository, &oid)) { - cgit_print_error_page(400, "Bad Request", "Not a commit reference: %s", hex); + cgit_print_error_page(404, "Not Found", "Not a commit: %s", hex); + return 1; + } + // A compressor that cannot run only shows up as a broken pipe once + // the tar is under way, so it is looked for while a status can still + // say so. + if (format->program && !program_on_path(format->program)) { + cgit_print_error_page(500, "Internal Server Error", "Unable to run %s", format->program); return 1; } ctx.page.mimetype = xstrdup(format->mimetype); ctx.page.filename = xstrdup(filename); cgit_print_http_headers(); init_archivers(); - format->write_func(hex, prefix); + // The archiver reads its arguments as a command line, so it gets the + // resolved id and never the name. + format->write_func(oid_to_hex(&oid), prefix); return 0; } @@ -321,6 +364,12 @@ void cgit_print_snapshot(const char *head, const char *hex, const char *filename return; } + // The name becomes the prefix of every member of the archive, and a + // revision expression such as :/pattern could carry a path in it. + if (!hex && dwim && strchr(filename, '/')) { + cgit_print_error_page(404, "Not Found", "Not found"); + return; + } if (!hex && dwim) { hex = ref_from_filename(ctx.repo, filename, f); if (!hex) { |
