From 945585c4a801cf55381e2cbe8d0a15a79ea2ea71 Mon Sep 17 00:00:00 2001 From: Rob Browning Date: Mon, 4 Oct 2021 20:54:50 -0500 Subject: [PATCH] Support signed commits (i.e. the gpgsig header) Signed-off-by: Rob Browning Tested-by: Rob Browning --- lib/bup/git.py | 21 ++++++++++++++++++++- test/int/test_git.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/bup/git.py b/lib/bup/git.py index dbfe78f..fe297cd 100644 --- a/lib/bup/git.py +++ b/lib/bup/git.py @@ -102,9 +102,26 @@ def parse_tz_offset(s): return - tz_off return tz_off +def parse_commit_gpgsig(sig): + """Return the original signature bytes. + + i.e. with the "gpgsig " header and the leading space character on + each continuation line removed. + + """ + if not sig: + return None + assert sig.startswith(b'gpgsig ') + sig = sig[7:] + return sig.replace(b'\n ', b'\n') # FIXME: derived from http://git.rsbx.net/Documents/Git_Data_Formats.txt # Make sure that's authoritative. + +# See also +# https://github.com/git/git/blob/master/Documentation/technical/signature-format.txt +# The continuation lines have only one leading space. + _start_end_char = br'[^ .,:;<>"\'\0\n]' _content_char = br'[^\0\n<>]' _safe_str_rx = br'(?:%s{1,2}|(?:%s%s*%s))' \ @@ -118,7 +135,7 @@ _mergetag_rx = br'(?:\nmergetag object [abcdefABCDEF0123456789]{40}(?:\n [^\0\n] _commit_rx = re.compile(br'''tree (?P[abcdefABCDEF0123456789]{40}) (?P%s*)author (?P%s) <(?P%s)> (?P\d+) (?P%s) committer (?P%s) <(?P%s)> (?P\d+) (?P%s)(?P%s?) - +(?Pgpgsig .*\n(?: .*\n)*)? (?P(?:.|\n)*)''' % (_parent_rx, _safe_str_rx, _safe_str_rx, _tz_rx, _safe_str_rx, _safe_str_rx, _tz_rx, @@ -132,6 +149,7 @@ CommitInfo = namedtuple('CommitInfo', ['tree', 'parents', 'author_sec', 'author_offset', 'committer_name', 'committer_mail', 'committer_sec', 'committer_offset', + 'gpgsig', 'message']) def parse_commit(content): @@ -149,6 +167,7 @@ def parse_commit(content): committer_mail=matches['committer_mail'], committer_sec=int(matches['csec']), committer_offset=parse_tz_offset(matches['ctz']), + gpgsig=parse_commit_gpgsig(matches['gpgsig']), message=matches['message']) diff --git a/test/int/test_git.py b/test/int/test_git.py index 7965c60..339f858 100644 --- a/test/int/test_git.py +++ b/test/int/test_git.py @@ -305,6 +305,42 @@ def test_commit_parsing(tmpdir): restore_env_var(b'GIT_COMMITTER_EMAIL', orig_committer_email) +gpgsig_example_1 = b'''tree 3fab08ade2fbbda60bef180bb8e0cc5724d6bd4d +parent 36db87b46a95ca5079f43dfe9b72220acab7c731 +author Rob Browning 1633397238 -0500 +committer Rob Browning 1633397238 -0500 +gpgsig -----BEGIN PGP SIGNATURE----- + + ... + -----END PGP SIGNATURE----- + +Sample signed commit. +''' + +gpgsig_example_2 = b'''tree 3fab08ade2fbbda60bef180bb8e0cc5724d6bd4d +parent 36db87b46a95ca5079f43dfe9b72220acab7c731 +author Rob Browning 1633397238 -0500 +committer Rob Browning 1633397238 -0500 +gpgsig -----BEGIN PGP SIGNATURE----- + + ... + -----END PGP SIGNATURE----- + + +Sample signed commit. +''' + +def test_commit_gpgsig_parsing(): + c = git.parse_commit(gpgsig_example_1) + assert c.gpgsig + assert c.gpgsig.startswith(b'-----BEGIN PGP SIGNATURE-----\n') + assert c.gpgsig.endswith(b'\n-----END PGP SIGNATURE-----\n') + c = git.parse_commit(gpgsig_example_2) + assert c.gpgsig + assert c.gpgsig.startswith(b'-----BEGIN PGP SIGNATURE-----') + assert c.gpgsig.endswith(b'\n-----END PGP SIGNATURE-----\n\n') + + def test_new_commit(tmpdir): environ[b'BUP_DIR'] = bupdir = tmpdir + b'/bup' git.init_repo(bupdir) -- 2.39.2