diff options
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Fold a tag's signature away under its message
The armour ran on for more lines than most tag messages, as part of the message itself. git finds the signature for every kind it can sign with, so PGP, SSH and X.509 all fold the same way.
-rw-r--r--README.txt2
-rw-r--r--assets/cgit.css15
-rw-r--r--source/ui-tag.c29
-rwxr-xr-xtests/t0005-markup.sh21
-rwxr-xr-xtests/t0113-tag.sh31
5 files changed, 94 insertions, 4 deletions
diff --git a/README.txt b/README.txt
index db55ec9..e160af5 100644
--- a/README.txt
+++ b/README.txt
@@ -101,6 +101,8 @@ script added through the css and js options has stable hooks to work with.
A character in a file that a browser would start a new line at, though git
does not, such as a carriage return with no line feed after it, is shown as
an escape sequence in span.escape.
+* A signed tag's signature, of whatever kind git reads, is folded away in
+ details.signature after the tag's message.
* The bundled auth filters write their login form as form.login, with
p.login-error above it after a failed login.
* Colours, fonts and metrics are custom properties on div#cgit, so a theme can
diff --git a/assets/cgit.css b/assets/cgit.css
index 7a4472a..49b00ab 100644
--- a/assets/cgit.css
+++ b/assets/cgit.css
@@ -875,6 +875,21 @@ div#cgit table.commit-trailers {
margin-top: 1em;
}
+div#cgit details.signature {
+ margin-top: 1em;
+}
+
+div#cgit details.signature summary {
+ color: var(--muted);
+ cursor: pointer;
+}
+
+div#cgit details.signature pre {
+ margin: 0.5em 0 0;
+ font-family: var(--font-mono);
+ overflow-x: auto;
+}
+
div#cgit table.commit-trailers th {
text-align: left;
font-weight: normal;
diff --git a/source/ui-tag.c b/source/ui-tag.c
index 5fa8a17..246ff12 100644
--- a/source/ui-tag.c
+++ b/source/ui-tag.c
@@ -11,6 +11,7 @@
#include "cgit.h"
#include "filter.h"
+#include "gpg-interface.h"
#include "html.h"
#include "parsing.h"
#include "shared.h"
@@ -71,17 +72,31 @@ static void print_download_row(const char *revname, const struct object *obj)
}
/*
- * Cutting the subject out terminates the message in place, which is safe only
- * because the caller frees it straight after. The blank lines between the
- * subject and the body are skipped, as the commit page skips them.
+ * Cutting the subject and the signature out terminates the message in place,
+ * which is safe only because the caller frees it straight after. The blank
+ * lines between the subject and the body are skipped, as the commit page skips
+ * them.
*/
static void print_message(char *msg)
{
- char *newline, *body;
+ char *newline, *body, *signature = NULL;
+ size_t len, start;
if (!msg)
return;
+ // git finds the signature of any kind it can sign with, PGP, SSH or
+ // X.509, and it goes in a fold after the message, since its armour
+ // would otherwise run on for more lines than the message itself.
+ len = strlen(msg);
+ start = parse_signed_buffer(msg, len);
+ if (start < len) {
+ signature = xstrdup(msg + start);
+ while (start > 0 && msg[start - 1] == '\n')
+ start--;
+ msg[start] = '\0';
+ }
+
html("<div class='commit-subject'>");
newline = strchr(msg, '\n');
if (newline)
@@ -96,6 +111,12 @@ static void print_message(char *msg)
html_txt(body);
html("</div>");
}
+ if (signature) {
+ html("<details class='signature'><summary>Signature</summary><pre>");
+ html_txt(signature);
+ html("</pre></details>");
+ free(signature);
+ }
}
static void print_annotated_tag(const char *revname, const struct object_id *oid)
diff --git a/tests/t0005-markup.sh b/tests/t0005-markup.sh
index 5d802a7..0ba8ded 100755
--- a/tests/t0005-markup.sh
+++ b/tests/t0005-markup.sh
@@ -194,4 +194,25 @@ test_expect_success 'embedded mode marks the root' '
grep "<div id=.cgit. class=.cgit-embedded. data-page=.summary." tmp
'
+test_expect_success 'a signed tag folds its signature away' '
+ (
+ cd repos/hooks &&
+ {
+ echo "object $(git rev-parse HEAD)" &&
+ echo "type commit" &&
+ echo "tag signed" &&
+ echo "tagger C O Mitter <committer@example.com> 1700000000 +0000" &&
+ echo &&
+ echo "Signed" &&
+ echo &&
+ echo "-----BEGIN SSH SIGNATURE-----" &&
+ echo "U1NIU0lH" &&
+ echo "-----END SSH SIGNATURE-----"
+ } >signed-tag &&
+ git update-ref refs/tags/signed $(git mktag <signed-tag)
+ ) &&
+ hooks "hooks/tag/&h=signed" >tmp &&
+ grep "<details class=.signature.><summary>Signature</summary>" tmp
+'
+
test_done
diff --git a/tests/t0113-tag.sh b/tests/t0113-tag.sh
index ff50f90..ec6f3c3 100755
--- a/tests/t0113-tag.sh
+++ b/tests/t0113-tag.sh
@@ -59,4 +59,35 @@ test_expect_success 'a tag of anything but a commit offers no download' '
grep ">download<" tmp
'
+# git reads the signature off the end of a tag's message, so a block shaped
+# like one is enough to show it is folded away, with nothing to verify.
+test_expect_success 'a signed tag folds its signature after the message' '
+ (
+ cd repos/foo &&
+ {
+ echo "object $(git rev-parse HEAD)" &&
+ echo "type commit" &&
+ echo "tag signed" &&
+ echo "tagger C O Mitter <committer@example.com> 1700000000 +0000" &&
+ echo &&
+ echo "Signed release" &&
+ echo &&
+ echo "Notes." &&
+ echo &&
+ echo "-----BEGIN PGP SIGNATURE-----" &&
+ echo "iQEzBAABCAAdFiEE" &&
+ echo "-----END PGP SIGNATURE-----"
+ } >signed-tag &&
+ git update-ref refs/tags/signed $(git mktag <signed-tag)
+ ) &&
+ cgit_url "foo/tag/&h=signed" >tmp &&
+ tr -d "\n" <tmp >flat &&
+ grep "<div class=.commit-msg.>Notes.</div><details class=.signature.><summary>Signature</summary><pre>-----BEGIN PGP SIGNATURE-----iQEzBAABCAAdFiEE-----END PGP SIGNATURE-----</pre></details>" flat
+'
+
+test_expect_success 'an unsigned tag has no fold' '
+ cgit_url "foo/tag/&h=v1.0" >tmp &&
+ ! grep "details class=.signature." tmp
+'
+
test_done