Script monkeys ahoy!
Verifying the integrity of the Gradle Wrapper JAR, for version control repositories that allow users to include a binary payload: gradle/wrapper/gradle-wrapper.jar
It does other stuff too.
In the browser viewing: auditgradle.sh or
git clone https://bitbucket.org/snippets/KJ_Duncan/LpLedA/portable-shell-script-to-audit-the-gradle.git
For more Gradle adventures drop in and see @jdugan6240 efforts locally at discuss kakoune or knock on the door at his repository on github.
For the click fearing here’s the copy and paste job:
auditgradle.sh
#! /usr/bin/env sh
# ------------------------------------------------------------------------------------------------------------ #
# Table of Contents
# 1) Introduction
# 2) Debug
# 3) Security
# 4) Standard out
# 5) Short circuit
# 6) Domain
# 7) Call stack
# 8) Helper functions
# 9) Entry point
# 10) Appendices
# ------------------------------------------------------------------------------------------------------------ #
# INTRODUCTION ---------------------------------------------------------------------------------------------- #
# ------------------------------------------------------------------------------------------------------------ #
#
# Version control repositories that allow users to include a binary payload:
# gradle/wrapper/gradle-wrapper.jar
#
# Gradle 2021, Verifying the integrity of the Gradle Wrapper JAR, The Gradle Wrapper, viewed 04 February 2021,
# <https://docs.gradle.org/current/userguide/gradle_wrapper.html#wrapper_checksum_verification>
#
# Enjoy Gradle? Meet Hugh, he programs the sht out of it! Thanks Hugh.
#
# Greene, H 2021, The Holy Gradle, holygradle-plugins, viewed 25 March 2021,
# https://bitbucket.org/HughG/holygradle-plugins/src/master/
#
# Life's better with music.
#
# Jackson, E 2009, Bulletproof, La Roux, Spotify, Youtube,
# https://open.spotify.com/track/3kMrazSvILsgcwtidZd1Qd?si=afef67527d5f4e4f
# https://youtu.be/Kk8eJh4i8Lo
#
# ------------------------------------------------------------------------------------------------------------ #
# DEBUG ------------------------------------------------------------------------------------------------------ #
# ------------------------------------------------------------------------------------------------------------ #
# Prints trace report to file and the current options available for this shell to the terminal,
# invoke via; -debug <command>, --debug <command>. Refer to Appendix 1
_print_set_debug() { exec 2>auditgradle.debug; set -xo; }
# ------------------------------------------------------------------------------------------------------------ #
# SECURITY --------------------------------------------------------------------------------------------------- #
# ------------------------------------------------------------------------------------------------------------ #
# TEST: as a parent function 'cd() { command cd "$@" >/dev/null; pwd; }' is not invoked.
# IFS is set to null, field splitting is set to the default value. Refer to Appendix 2.
IFS=
# ------------------------------------------------------------------------------------------------------------ #
# Unset all possible aliases.
\unalias -a
# ------------------------------------------------------------------------------------------------------------ #
# Ensure env, getconf, and command are not a user function. Refer to Appendix 2.
unset -f env getconf command
# ------------------------------------------------------------------------------------------------------------ #
# Put on a reliable PATH prefix, as order matters.
# before: /usr/bin/*
# after: /bin/*
PATH="$(command -p getconf PATH):$PATH"
# ------------------------------------------------------------------------------------------------------------ #
# STANDARD OUT ----------------------------------------------------------------------------------------------- #
# ------------------------------------------------------------------------------------------------------------ #
# Prints information to the users terminal. Refer to Appendix 3.
_print_info() { env printf "%s \n" "$*" 1>&2; }
# ------------------------------------------------------------------------------------------------------------ #
# Invoke via; -h, -help, --help, -\?.
_print_help() {
cat 0<<_EOF_
Usage: ${0##*/} [-h|-help|--help] |
[[-g|-get|--get] <version> <checksum>] |
[[-c|-check|--check] <version>] |
[[-u|-upgrade|--upgrade] <version> <checksum>]
-g, -get, --get
VERSION="\$2" CHECKSUM="\$3"
$ gradle wrapper \\
--gradle-version "\$VERSION" \\
--distribution-type all \\
--gradle-distribution-sha256-sum "\$CHECKSUM"
# -------------------------------------------------------------------- #
Gradle 2021, Gradle distribution and wrapper JAR checksum reference,
viewed 04 February 2021, <https://gradle.org/release-checksums/>
# -------------------------------------------------------------------- #
Integrated Development Environment (IDE) pulls:
Complete (-all|-bin) ZIP Checksum
gradle/wrapper/gradle.wrapper.properties
# -------------------------------------------------------------------- #
Command Line Interface (CLI) pulls:
Wrapper JAR Checksum
$ ./gradlew tasks
# -------------------------------------------------------------------- #
-c, -check, --check
VERSION="\$2"
$ cd \$PWD/gradle/wrapper
$ wget --tries=1 \\
--no-cookies \\
--max-redirect=0 \\
--secure-protocol=TLSv1_2 \\
--append-output=auditgradle.log \\
--output-document=gradle-wrapper.jar.sha256 \\
--header="Accept: application/gzip, application/octet-stream" \\
https://downloads.gradle-dn.com/distributions/gradle-"\$VERSION"-wrapper.jar.sha256
$ echo " gradle-wrapper.jar" >> gradle-wrapper.jar.sha256
$ sha256sum --check gradle-wrapper.jar.sha256
# -------------------------------------------------------------------- #
-u, -upgrade, --upgrade
VERSION="\$2" CHECKSUM="\$3"
$ ./gradlew wrapper \\
--gradle-version "\$VERSION" \\
--distribution-type all \\
--gradle-distribution-sha256-sum "\$CHECKSUM"
Run the following commands:
$ jcmd -l
> org.gradle.launcher.daemon.bootstrap.GradleDaemon \$VERSION
$ ./gradlew --stop
> Stopping Daemon(s)
> 1 Daemon stopped
_EOF_
}
# -------------------------------------------------------------------------------------------------- #
# SHORT CIRCUIT ------------------------------------------------------------------------------------ #
# -------------------------------------------------------------------------------------------------- #
# Zero positional arguments upon initialisation prints the help message to the users terminal.
if [ $# -eq 0 ]; then _print_help 2>/dev/null; exit 1; fi
# -------------------------------------------------------------------------------------------------- #
# An adjunct check for system utilities to process the calls made by this script.
# Refer to Appendix 4.
_check_dependency() {
if ! (type "$1" >/dev/null 2>&1); then
_print_info "ERROR: missing dependency can't find $1"
exit 1
fi
}
# -------------------------------------------------------------------------------------------------- #
# DOMAIN ------------------------------------------------------------------------------------------- #
# -------------------------------------------------------------------------------------------------- #
# From the present working directory check the user's path includes gradle/wrapper/ and neither
# directory is a symbolic representation. Then call the Gradle host with wget and output a https
# auditgradle.log and the gradle-wrapper.jar.sha256 file. Refer to Appendix 5.
_check_gradle_wrapper() {
version="$1"
root=$(env pwd -P)
location="$root/gradle/wrapper"
# ------------------------------------------------------------------------------------------------ #
if [ -d "$location" ];
then if [ -h "$root"/gradle ] || [ -h "$root"/gradle/wrapper ];
then _print_info "Either directory is a symbolic link: \$PWD/gradle/wrapper"; exit 1;
else cd -P "$location" 2>/dev/null || { _print_info "cd action failed"; exit 1; }
fi
else _print_info "No such directory: \$PWD/gradle/wrapper"; exit 1;
fi
# ------------------------------------------------------------------------------------------------ #
_print_info "downloading $version of gradle-wrapper.jar.sha256..."
# ------------------------------------------------------------------------------------------------ #
wget --tries=1 \
--no-cookies \
--max-redirect=0 \
--secure-protocol=TLSv1_2 \
--output-document=gradle-wrapper.jar.sha256 \
--append-output="$root/auditgradle.log" \
--header="Accept: application/gzip, application/octet-stream" \
https://downloads.gradle-dn.com/distributions/gradle-"$version"-wrapper.jar.sha256
# ------------------------------------------------------------------------------------------------ #
_print_info "wget's auditgradle.log in $root..."
}
# -------------------------------------------------------------------------------------------------- #
# From the present working directory use the system's Gradle distribution to pull a gradle
# wrapper version with the associated checksum. Distribution type all enables both IDE
# code-completion and Gradle documentation.
_get_gradle_wrapper() {
version="$1"
checksum="$2"
# ------------------------------------------------------------------------------------------------ #
_print_info "Wrapper JAR Checksum: $checksum"
_print_info "Downloading $version of gradle-wrapper.jar..."
# ------------------------------------------------------------------------------------------------ #
gradle wrapper \
--gradle-version "$version" \
--distribution-type all \
--gradle-distribution-sha256-sum "$checksum"
}
# -------------------------------------------------------------------------------------------------- #
# In the present working directory find the projects gradlew executable. Then update to the
# gradle wrapper version with the associated checksum. Distribution type all enables both IDE
# code-completion and Gradle documentation. Refer to Appendix 6.
_upgrade_gradle_wrapper() {
version="$1"
checksum="$2"
# ------------------------------------------------------------------------------------------------ #
gw=$(find -P . -maxdepth 1 -mindepth 1 -mount -executable -name 'gradlew' -readable -type f)
if [ -z "$gw" ]; then _print_info "ERROR: gradlew not found in current working directory"; exit 1; fi
# ------------------------------------------------------------------------------------------------ #
_print_info "Wrapper JAR Checksum: $checksum"
_print_info "Downloading $version of gradle-wrapper.jar..."
# ------------------------------------------------------------------------------------------------ #
$gw wrapper \
--gradle-version "$version" \
--distribution-type all \
--gradle-distribution-sha256-sum "$checksum"
}
# -------------------------------------------------------------------------------------------------- #
# From the projects gradle/wrapper directory check the local gradle-wrapper.jar checksum.
# Refer to Appendix 7.
_shasum_gradle_wrapper() {
_print_info "checking sha256 of gradle-wrapper.jar..."
# ------------------------------------------------------------------------------------------------ #
echo " gradle-wrapper.jar" 1>> gradle-wrapper.jar.sha256
# ------------------------------------------------------------------------------------------------ #
sha256sum --check gradle-wrapper.jar.sha256
# ------------------------------------------------------------------------------------------------ #
SHA256=$(cat gradle-wrapper.jar.sha256)
SHA256SUM=$(sha256sum gradle-wrapper.jar)
# ------------------------------------------------------------------------------------------------ #
_print_info "1) sha256: $SHA256"
_print_info "2) sha256sum: $SHA256SUM"
# ------------------------------------------------------------------------------------------------ #
rm --interactive=once --verbose gradle-wrapper.jar.sha256
}
# -------------------------------------------------------------------------------------------------- #
# CALL STACK --------------------------------------------------------------------------------------- #
# -------------------------------------------------------------------------------------------------- #
# Check the parent system has the required utilities installed; wget, sha256sum,
# and make a call to two script functions.
_run_check() {
_print_info "Running: check gradle wrapper"
# ------------------------------------------------------------------------------------------------ #
_check_dependency wget
_check_dependency sha256sum
# ------------------------------------------------------------------------------------------------ #
_check_gradle_wrapper "$1"
_shasum_gradle_wrapper
}
# -------------------------------------------------------------------------------------------------- #
# Check the parent system has the required utility installed; gradle, and call a script function.
_run_get() {
_print_info "Running: get gradle wrapper"
# ------------------------------------------------------------------------------------------------ #
_check_dependency gradle
# ------------------------------------------------------------------------------------------------ #
_get_gradle_wrapper "$1" "$2"
}
# -------------------------------------------------------------------------------------------------- #
# Run the scripts function to upgrade the gradle-wrapper.jar.
_run_upgrade() {
_print_info "Running: upgrade gradle wrapper"
# ------------------------------------------------------------------------------------------------ #
_upgrade_gradle_wrapper "$1" "$2"
}
# -------------------------------------------------------------------------------------------------- #
# HELPER FUNCTIONS --------------------------------------------------------------------------------- #
# -------------------------------------------------------------------------------------------------- #
# Checks regular expression for natural numbers:
# <digit>.<digit> or <digit>.<digit>.<digit>
# <digit><digit>.<digit><digit> or <digit><digit>.<digit><digit>.<digit><digit>
#
# Pattern conforms to the Gradle continuum policy. Refer to Appendix 8.
_confirm_version() {
VERSION=$( expr "$1" : '\(^\([0-9]\{1,2\}\.[0-9]\{1,2\}\.[0-9]\{1,2\}\)$\|^\([0-9]\{1,2\}\.[0-9]\{1,2\}\)$\)' 2>/dev/null )
}
# -------------------------------------------------------------------------------------------------- #
# Checksum must conform to a length of 64 containing only Hindu-Arabic Numerals and
# the English language lowercase letters. Refer to Appendix 8.
_confirm_checksum() {
CHECKSUM=$( expr "$1" : '^\([0-9a-z]\{64\}\)$' 2>/dev/null )
}
# -------------------------------------------------------------------------------------------------- #
# The user's input checksum conforms to a length of 64. Refer to Appendix 8.
_confirm_length() {
return $( expr length "$1" = 64 >/dev/null 2>&1 );
}
# -------------------------------------------------------------------------------------------------- #
VERSION_MSG="Version format is numerical (i.e., 5.2 also 6.8.3): <digit>.<digit>.?<digit>?"
# These are global variables, duplication at the function level is intentional for flexibility.
VERSION=
CHECKSUM=
# -------------------------------------------------------------------------------------------------- #
# ENTRY POINT -------------------------------------------------------------------------------------- #
# -------------------------------------------------------------------------------------------------- #
if [ "$1" = "-debug" ] || [ "$1" = "--debug" ]; then shift; _print_set_debug; fi
# -------------------------------------------------------------------------------------------------- #
case "$1" in
( -c | -check | --check )
if [ "$2" ];
then maybe="$2"
_confirm_version "$maybe"
if [ "$VERSION" = "$maybe" ];
then _run_check "$VERSION"
else _print_info "$VERSION_MSG"
fi
else _print_info "Option requires an argument: $1 <version>"
fi
;;
( -g | -get | --get )
if [ "$2" ] && [ "$3" ];
then maybe="$2"
_confirm_version "$maybe"
if [ "$VERSION" = "$maybe" ];
then kinda="$3"
if ( _confirm_length "$kinda" );
then _confirm_checksum "$kinda"
if [ "$CHECKSUM" = "$kinda" ];
then _run_get "$VERSION" "$CHECKSUM"
else _print_info "Invalid gradle checksum: $3"
fi
else _print_info "Invalid checksum length: $3"
fi
else _print_info "$VERSION_MSG"
fi
else _print_info "Option requires two arguments: $1 <version> <checksum>"
fi
;;
( -h | -help | --help | -\? )
_print_help 1>&2
;;
( -u | -upgrade | --upgrade )
if [ "$2" ] && [ "$3" ];
then maybe="$2"
_confirm_version "$maybe"
if [ "$VERSION" = "$maybe" ];
then kinda="$3"
if ( _confirm_length "$kinda" );
then _confirm_checksum "$kinda"
if [ "$CHECKSUM" = "$kinda" ];
then _run_upgrade "$VERSION" "$CHECKSUM"
else _print_info "Invalid gradle checksum: $3"
fi
else _print_info "Invalid checksum length: $3"
fi
else _print_info "$VERSION_MSG"
fi
else _print_info "Option requires two arguments: $1 <version> <checksum>"
fi
;;
( * )
_print_info "Option $1 invalid argument."
;;
esac
exit $?
# -------------------------------------------------------------------------------------------------- #
# APPENDICES --------------------------------------------------------------------------------------- #
# -------------------------------------------------------------------------------------------------- #
# Appendix 1 - _print_set_debug
#
# set -o
# Write the current settings of the options to standard output in an unspecified format.
#
# set -x
# The shell shall write to standard error a trace for each command after it expands the command and before it executes it.
#
# example:
# $ ./auditgradle.sh -debug -check 6.8.3
# $ less auditgradle.debug
#
# The Open Group 2018, set - set or unset options and positional parameters, Base Specifications Issue 7, 2018 edt,
# viewed 24 March 2021, https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_25
#
# -------------------------------------------------------------------------------------------------- #
# Appendix 2 - IFS='<space><tab><newline>'
#
# If IFS is not set, it shall behave as normal for an unset variable, except that
# field splitting by the shell and line splitting by the read utility shall be
# performed as if the value of IFS is <space> <tab> <newline> (2.5.3 Shell Variables).
#
# NOTE: Text editors are subject to user configurations (kakoune/editorconfig), also
# repositories or transit may introduce anomalies. Double check IFS environment
# variable prior to running scripts.
#
# IEEE and The Open Group 2018, 8. Environment Variables, viewed 12 April 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
#
# IEEE and The Open Group 2018, IFS, 2.5.3 Shell Variables, viewed 12 April 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
#
# IEEE and The Open Group 2018, env - set the environment for command invocation, viewed 12 April 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html
#
# IEEE and The Open Group 2018, getconf - get configuration values, viewed 12 April 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html
#
# -------------------------------------------------------------------------------------------------- #
# Appendix 3 - _print_info
#
# printf: Due to shell aliases and built-in printf functions, using an unadorned printf
# interactively or in a script may get you different functionality [...]. Invoke
# it via env (i.e., env printf …) to avoid interference from the shell.
#
# GNU 2020, 15.2 printf: Format and print data, GNU Coreutils, viewed 24 March 2021,
# https://www.gnu.org/software/coreutils/manual/html_node/printf-invocation.html
#
# -------------------------------------------------------------------------------------------------- #
# Appendix 4 - _check_dependency
#
# The conditional statement: if ! $(builtin command -V "$1" >/dev/null 2>&1) ; then
#
# Path traversal provides an inconsequential location for the
# utility. Therefore, command substitution jargon and disposable.
# In addition to, the -V flag is dispensable as the output is not
# read by the user.
#
#
# Command Search and Execution
# --------------------------------------------------------------
# A) 2.14. Special Built-In Utilities
# break exec set
# colon exit shift
# continue export times
# dot readonly trap
# eval return unset
# --------------------------------------------------------------
# B)
# alloc comparguments comptry history pushd
# autoload compcall compvalues hist readarray
# bind compctl declare let repeat
# bindkey compdescribe dirs local savehistory
# builtin compfiles disable login source
# bye compgen disown logout shopt
# caller compgroups dosh map stop
# cap complete echotc mapfile suspend
# chdir compquote echoti popd typeset
# clone comptags help print whence
# ---------------------------------------------------------------
# C) 2.9.5 Function Definition Command
# ---------------------------------------------------------------
# D)
# alias false hash pwd unalias
# bg fc jobs read wait
# cd fg kill true
# command getopts newgrp umask
# ---------------------------------------------------------------
# E)
# path
# ---------------------------------------------------------------
#
# IEEE and The Open Group 2018, Command Search and Execution, viewed 28 March 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01
#
# IEEE and The Open Group 2018, command - execute a simple command, viewed 28 March 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
#
# IEEE and The Open Group 2018, sh - shell, the standard command language interpreter, viewed 12 April 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html
#
# IEEE and The Open Group 2018, type - write a description of command type, viewed 12 April 2021,
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/type.html
#
# -------------------------------------------------------------------------------------------------- #
# Appendix 5 - _check_gradle_wrapper
#
# env --chdir=GUARANTEED_PATH, path is unsubstantiated: `pwd`/gradle/wrapper
#
# The default behaviour is --physical; pwd, realpath, cd.
# realpath --no-symlinks does not --canonicalize-existing path for symlinks.
# cd -P into a physical directory not it's symbolic representation.
#
# If either directories; gradle, gradle/wrapper is a symbolic then a base directory
# is not honoured given the below utility function:
# realpath --canonicalize-existing --physical --relative-base="$root" gradle/wrapper
#
# Redirects +1
# Referer: https://services.gradle.org/distributions/
# Host: downloads.gradle-dn.com
# As at 30 March 2021, wget --secure-protocol=TLSv1_3 is unsupported at host
#
# wget --input-file=If there are URLs both on the command line and in an input file, those on the
# command lines will be the first ones to be retrieved.
#
# pwd: Due to shell aliases and built-in pwd functions, using an unadorned pwd
# interactively or in a script may get you different functionality [...].
# Invoke it via env (i.e., env pwd …) to avoid interference from the shell.
#
# $ env --ignore-environment --debug pwd -P
# > cleaning environ
# > executing: pwd
# > arg[0]= ‘pwd’
# > arg[1]= ‘-P’
#
# GNU 2021, 2.4 Logging and Input File Options, GNU Wget 1.21.1-dirty Manual, viewed 23 March 2021,
# https://www.gnu.org/software/wget/manual/html_node/Logging-and-Input-File-Options.html
#
# GNU 2020, 19.1 pwd: Print working directory, Coreutils - GNU core utilities, viewed 23 March 2021,
# https://www.gnu.org/software/coreutils/manual/html_node/pwd-invocation.html
#
# GNU 2020, 23.2 env: Run a command in a modified environment, Coreutils - GNU core utilities, viewed 23 March 2021,
# https://www.gnu.org/software/coreutils/manual/html_node/env-invocation.html
#
# -------------------------------------------------------------------------------------------------- #
# Appendix 6 - _upgrade_gradle_wrapper
#
# find (Options Globals Tests)
# where
# Options = -P
# Globals = -maxdepth 1 -mindepth 1 -mount
# Tests = -executable -name 'gradlew' -readable -type f
#
# GNU 2021, 8.1 Invoking find, GNU Findutils 4.8.0, viewed 29 March 2021,
# https://www.gnu.org/software/findutils/manual/html_node/find_html/Invoking-find.html
#
# -------------------------------------------------------------------------------------------------- #
# Appendix 7 - _shasum_gradle_wrapper
#
# rm --interactive=once prompts the user for confirmation if three or more files require deletion
#
# GNU 2020, 11.5 rm: Remove files or directories, GNU Coreutils Manual, viewed 29 March 2021,
# https://www.gnu.org/software/coreutils/manual/html_node/rm-invocation.html
#
# -------------------------------------------------------------------------------------------------- #
# Appendix 8 - _confirm_version, _confirm_checksum, _confirm_length
#
# version has; three groups, separated by '.' , start [0-9], middle [0-9], end [0-9]
# version regex: (expr '6.8.3' : '^\([0-9]\{1,2\}\.[0-9]\{1,2\}\.[0-9]\{1,2\}\)$') returns match or null
#
# checksum has; a fixed length, no separation characters, contains [0-9a-z]
# checksum regex: (expr '7faa7198769f872826c8ef4f1450f839ec27f0b4d5d1e51bade63667cbccd205' : '^\([0-9a-z]\{64\}\)$') return match or null
# checksum length: (expr length '7faa7198769f872826c8ef4f1450f839ec27f0b4d5d1e51bade63667cbccd205' = 64) returns 1 or 0
#
# length redirection: 2>/dev/null; returns 0 or 1 or null
# length redirection: >/dev/null 2>&1; true or returns 1 or 2
#
# GNU 2020, 16.4 expr: Evaluate expressions, GNU Coreutils Manual, viewed 31 March 2021,
# https://www.gnu.org/software/coreutils/manual/html_node/expr-invocation.html
#
# gradle-completion 2017, Security risk (?): Backticks in descriptions are attempted to eval!, Issue #91, gradle/gradle-completion,
# viewed 10 March 2021, https://github.com/gradle/gradle-completion/issues/91
#
# -------------------------------------------------------------------------------------------------- #
Whats Gradle?