From 4c26b5f65ac9b302adf3f88f377544b24980d22f Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Sat, 25 Jul 2026 09:11:47 -1000 Subject: Cap diff blobs by `max-blob-size` The diff family inflated a blob of any size just to render it. A file over the limit is reported as binary instead. --- source/shared.c | 25 ++++++++++++++++++++++--- tests/t0201-limits.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/source/shared.c b/source/shared.c index 24f9c3c..59f1c8e 100644 --- a/source/shared.c +++ b/source/shared.c @@ -311,13 +311,32 @@ int cgit_diff_files(const struct object_id *old_oid, xpparam_t diff_params; xdemitconf_t emit_params; xdemitcb_t emit_cb; + unsigned long size1 = 0, size2 = 0; + + // Read the object headers first so an oversized blob is never + // inflated into memory just to be diffed. Report it as binary, + // which suppresses inlining the same way max-blob-size does for + // the blob, plain and tree views. + if (!is_null_oid(old_oid) && + odb_read_object_info(the_repository->objects, old_oid, &size1) < 0) + return 1; + if (!is_null_oid(new_oid) && + odb_read_object_info(the_repository->objects, new_oid, &size2) < 0) + return 1; + + *old_size = size1; + *new_size = size2; + + if (ctx.cfg.max_blob_size && + (size1 / 1024 > (unsigned long)ctx.cfg.max_blob_size || + size2 / 1024 > (unsigned long)ctx.cfg.max_blob_size)) { + *binary = 1; + return 0; + } if (!load_mmfile(&file1, old_oid) || !load_mmfile(&file2, new_oid)) return 1; - *old_size = file1.size; - *new_size = file2.size; - if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { *binary = 1; diff --git a/tests/t0201-limits.sh b/tests/t0201-limits.sh index 1ab3ce6..5f04952 100755 --- a/tests/t0201-limits.sh +++ b/tests/t0201-limits.sh @@ -100,6 +100,32 @@ test_expect_success 'the single-file page is not limited by max-diff-files' ' grep "class=.hunk." tmp ' +# --- max-blob-size also bounds the diff path -------------------------------- +# The diff family must not inflate a blob larger than max-blob-size just to +# render it. Such a file is reported as binary instead of inlined. +test_expect_success 'a diff of an oversized blob is not inlined' ' + mkrepo repos/blobdiff 1 && + ( + cd repos/blobdiff && + awk "BEGIN{for(i=0;i<400;i++)print \"aaaaaaaa\"}" >big.txt && + git add -A && + git commit -m add && + awk "BEGIN{for(i=0;i<401;i++)print \"aaaaaaaa\"}" >big.txt && + git commit -am change + ) && + { + echo "virtual-root=/" && + echo "cache-size=0" && + echo "max-blob-size=1" && + echo "repo.url=blobdiff" && + echo "repo.path=$PWD/repos/blobdiff/.git" + } >blobdiffrc && + sha=$(git -C repos/blobdiff rev-parse HEAD) && + CGIT_CONFIG="$PWD/blobdiffrc" QUERY_STRING="url=blobdiff/commit/&id=$sha" cgit >tmp && + grep "Binary files differ" tmp && + ! grep "aaaaaaaa" tmp +' + # --- The shipped highlight filter degrades to escaped passthrough ----------- test_expect_success LUA 'highlight filter passes text through without scintillua' ' { -- cgit v2.8.0