| #!/bin/bash |
| # git-verify-to-tip |
| # ----------------- |
| # |
| # Verify PGP signatures on all commits from the last signed tag |
| # or any arbitrary object in the repository history. |
| # |
| # This script can be installed as hooks/pre-push. |
| # |
| # Configurable parameters |
| # ----------------------- |
| # We always ensure the signing key is both GOOD and VALID, which means |
| # that the keys you are checking against should be imported into your |
| # gnupghome and signed by a trusted key (e.g. your own). If you want to |
| # use a different GNUPG directory other than the one in your home, you |
| # can "export GNUPGHOME=some/path" before running this script. In addition, |
| # you may explicitly specify the keys to trust in the ONLYKEYS parameter below, |
| # e.g. if you want to make sure the signatures came from a very small subset |
| # of developers. Pipe-separate multiple keys, e.g.: |
| # ONLYKEYS="ABAF11C65A2970B130ABE3C479BE3E4300411886|647F28654894E3BD457199BE38DBBDC86092693E" |
| ONLYKEYS= |
| # |
| # By default, we check signatures on every commit, but if you set this to |
| # --merges, we will only check signatures on merges. You can also add any |
| # other flags accepted by git-rev-list. |
| REVFLAGS= |
| # |
| # When set to "" we start from the latest annotated tag we find. |
| # You can also list an arbitrary commit object here. |
| # When running as hooks/pre-push, we ignore this entirely and use the |
| # commit information provided by git on stdin. |
| STARTFROM= |
| # |
| # We can also get these parameters from the git config. E.g.: |
| # [verify-to-tip] |
| # onlykeys = ABAF11C65A2970B130ABE3C479BE3E4300411886|647F28654894E3BD457199BE38DBBDC86092693E |
| # revflags = --merges |
| # startfrom = abcdef123456 |
| # |
| if [[ -z ${ONLYKEYS} ]]; then |
| ONLYKEYS=$(git config --get verify-to-tip.onlykeys) |
| fi |
| if [[ -z ${REVFLAGS} ]]; then |
| REVFLAGS=$(git config --get verify-to-tip.revflags) |
| fi |
| if [[ -z ${STARTFROM} ]]; then |
| STARTFROM=$(git config --get verify-to-tip.startfrom) |
| fi |
| |
| # End configuration |
| |
| function verify_raw_gpg { |
| # We are looking for [GNUPG:] GOODSIG and [GNUPG:] VALIDSIG |
| # They must be both present, or this is not a valid sig |
| COUNT=$(echo "${1}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') |
| if [[ ${COUNT} -lt 2 ]]; then |
| return 1 |
| fi |
| if [[ -z ${ONLYKEYS} ]]; then |
| return 0 |
| fi |
| if $(echo "${1}" | grep -q -E "^\[GNUPG:\] VALIDSIG .* (${ONLYKEYS})\$"); then |
| return 0 |
| fi |
| return 1 |
| } |
| |
| function verify_rev_range { |
| REVRANGE=${1} |
| REVFLAGS=${2} |
| for REV in $(git rev-list ${REVRANGE} ${REVFLAGS}); do |
| echo "Verifying $REV" |
| RAWOUT=$(git verify-commit --raw ${REV} 2>&1) |
| if ! verify_raw_gpg "${RAWOUT}"; then |
| echo "CRITICAL: ${REV} signature did NOT verify:" |
| echo "${RAWOUT}" |
| return 1 |
| fi |
| done |
| return 0 |
| } |
| |
| # Are we running from hooks/pre-push? If so, $1 and $2 should be set. |
| if [[ -z "${1}${2}" ]]; then |
| # Not running as a pre-push hook. |
| if [[ -z ${STARTFROM} ]]; then |
| # verify the last annotated tag |
| STARTFROM=$(git describe --abbrev=0) |
| RAWOUT=$(git verify-tag --raw ${STARTFROM} 2>&1) |
| else |
| # verify the arbitrary commit provided |
| RAWOUT=$(git verify-commit --raw ${STARTFROM} 2>&1) |
| fi |
| |
| echo "Verifying ${STARTFROM}" |
| if ! verify_raw_gpg "${RAWOUT}"; then |
| echo "CRITICAL: ${STARTFROM} signature did NOT verify:" |
| echo "${RAWOUT}" |
| exit 1 |
| fi |
| REVRANGE="${STARTFROM}.." |
| if ! verify_rev_range "${REVRANGE}" "${REVFLAGS}"; then |
| exit 1 |
| fi |
| else |
| # We are in a pre-push hook |
| Z40="0000000000000000000000000000000000000000" |
| |
| while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do |
| if [[ ${LOCAL_SHA} == ${Z40} ]]; then |
| # Ignore delete |
| continue |
| fi |
| if [[ ${REMOTE_SHA} == ${Z40} ]]; then |
| # New branch, examine all commits |
| REVRANGE=${LOCAL_SHA} |
| else |
| # Update to existing branch, examine new commits |
| REVRANGE="${REMOTE_SHA}..${LOCAL_SHA}" |
| fi |
| |
| if ! verify_rev_range "${REVRANGE}" "${REVFLAGS}"; then |
| exit 1 |
| fi |
| |
| done |
| fi |
| |
| echo "Verified successfully." |
| exit 0 |