From 34c99245b33a3fe23a132ff0df18d64d251a848c Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Fri, 14 Aug 2026 10:58:51 -1000 Subject: Treat text blobs with stray nuls as binary --- source/ui-blame.c | 14 +++++++++++++- source/ui-blob.c | 19 ++++++++++++++++--- source/ui-tree.c | 5 ++++- 3 files changed, 33 insertions(+), 5 deletions(-) (limited to 'source') diff --git a/source/ui-blame.c b/source/ui-blame.c index 80512a0..a7010d0 100644 --- a/source/ui-blame.c +++ b/source/ui-blame.c @@ -307,7 +307,19 @@ static void print_blame_page(const struct object_id *oid, const char *path, cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path); html(")\n"); - if (buffer_is_binary(buf, size)) { + // A NUL past the window buffer_is_binary sniffs would end html_txt + // early while the hash and line number columns still cover the whole + // file, so a blob holding one anywhere is treated as binary too. + if (buffer_is_binary(buf, size) || memchr(buf, 0, size)) { + struct blame_entry *ent = sb.ent, *next; + + // The scoreboard is normally consumed by the column passes, + // so this early exit has to release it itself. + for (; ent; ent = next) { + next = ent->next; + free(ent); + } + free((void *)sb.final_buf); html("
blob is binary.
"); goto cleanup; } diff --git a/source/ui-blob.c b/source/ui-blob.c index beb968b..db1d0c1 100644 --- a/source/ui-blob.c +++ b/source/ui-blob.c @@ -126,10 +126,23 @@ int cgit_print_file(char *path, const char *head, int file_only, int html_escape // html_txt wants a terminated string, and git leaves a spare byte // past every object it reads, so this write stays in the allocation. buf[size] = '\0'; - if (html_escape) - html_txt(buf); - else + if (html_escape) { + // html_txt stops at a NUL, so a blob holding one is written + // segment by segment with a replacement character standing in + // for each NUL byte, rather than silently cut short. + const char *p = buf, *end = buf + size; + + while (p < end) { + html_txt(p); + p += strlen(p); + while (p < end && !*p) { + html("\xef\xbf\xbd"); + p++; + } + } + } else { html_raw(buf, size); + } free(buf); return 0; } diff --git a/source/ui-tree.c b/source/ui-tree.c index 7d24d53..7df7375 100644 --- a/source/ui-tree.c +++ b/source/ui-tree.c @@ -191,7 +191,10 @@ static bool print_object(const struct object_id *oid, const char *path, "Error reading object %s", oid_to_hex(oid)); return false; } - is_binary = buffer_is_binary(buf, size); + // buffer_is_binary only sniffs the front of the blob, and a NUL past + // that window would end the escaped text render early while the line + // number column still counts the whole file, so check the rest too. + is_binary = buffer_is_binary(buf, size) || memchr(buf, 0, size); cgit_set_title_from_path(path); -- cgit v2.8.0