Skip to content

Instantly share code, notes, and snippets.

@nauar

nauar/pre-receive

Last active Sep 4, 2020
Embed
What would you like to do?
GIT pre-receive hook for checking file size and filename extensions
#!/bin/bash
# This script is a pre-receive hook allowing pushes whose every file:
# - is smaller than 20 M
# - and its extension is not one of the following:
# - dll
# - exe
# - war
# - ear
# - jar
# and whose directories are not named 'node_modules'.
GITCMD="git"
NULLSHA="0000000000000000000000000000000000000000"
EMPTYTREESHA=$($GITCMD hash-object -t tree /dev/null) # SHA1: "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
MAXSIZE="20"
MAXBYTES=$(( $MAXSIZE * 1048576 ))
EXIT=0
PRIVATELOGFILE="/tmp/git_private.log"
function private_log() {
moment=`date '+%d/%m/%Y %H:%M:%S'`
echo "[ $moment ] [ POLICY CHECK ] $1" >> $PRIVATELOGFILE
}
function log() {
moment=`date '+%d/%m/%Y %H:%M:%S'`
echo "[ $moment ] [ POLICY CHECK ] $1"
}
log "Starting validation..."
while read oldref newref refname; do
private_log "OLDREF: $oldref NEWREF: $newref REFNAME: $refname"
# Avoid removed branches
if [ "${newref}" = "${NULLSHA}" ]; then
continue
fi
# Set oldref properly if this is branch creation.
if [ "${oldref}" = "${NULLSHA}" ]; then
oldref=$EMPTYTREESHA
fi
# Ignore case
shopt -s nocaseglob
newFiles=$($GITCMD diff --stat --name-only --diff-filter=ACMRT ${oldref}..${newref})
if [[ $? -ne 0 ]]; then
log "Error 101: Repository incosistency. Cancelling push..."
exit 1;
fi
old_IFS=$IFS
IFS='
'
for filename in $newFiles; do
private_log "Filename: $filename"
filesize=$($GITCMD cat-file -s "${newref}:${filename}") 2> $PRIVATELOGFILE
if [[ -z $filesize ]]; then filesize=0; fi
filesize_mb=$(($filesize / 1048576))
if [ "${filesize}" -gt "${MAXBYTES}" ]; then
log "File $filename is greater than $MAXSIZE MB. Its size is $filesize_mb MB."
exit 1
fi
if [[ "$filename" =~ "node_modules" ]]; then
log "Folder 'node_modules' not allowed: $filename."
exit 1
else
if [[ "$filename" =~ \.dll$ ]] || [[ "$filename" =~ \.exe$ ]] || [[ "$filename" =~ \.war$ ]] || [[ "$filename" =~ \.ear$ ]] || [[ "$filename" =~ \.jar$ ]]; then
extension="${filename##*.}"
log "Files with extension $extension not allowed. Please remove file $filename"
exit 1
fi
fi
done
IFS=$old_IFS
done
@donganzh

This comment has been minimized.

Copy link

@donganzh donganzh commented Oct 25, 2019

Hi, I am wondering how to install a pre-receive hook similar to this in my own repo. I tried to rename pre-receive.sample to pre-receive the .git/hooks and pushed it to remote, but doesn't seem working. Thanks.

@nauar

This comment has been minimized.

Copy link
Owner Author

@nauar nauar commented Oct 25, 2019

Hi, I am wondering how to install a pre-receive hook similar to this in my own repo. I tried to rename pre-receive.sample to pre-receive the .git/hooks and pushed it to remote, but doesn't seem working. Thanks.

Hello,

Normally, the git hooks are never pushed as they are not part of the repository code (see this stack overflow question). You have to do it in the server itself and it depends on the type of server (click the links in order to see how to do it in each type):

You should do it in the server in the appropiate way depending on the server type and if it doesn't work properly yet, I'd check the following points:

  • Check that you have the interpreter installed in the remote server. Check the path of the shebang (ie: #!/bin/bash) points to an existing interpreter in your remote server.
  • Check that your hook has execution permissions. Do chmod u+x in your terminal.

I hope you find this helpful.

Kind regards,

Arnau Oncins

@donganzh

This comment has been minimized.

Copy link

@donganzh donganzh commented Oct 30, 2019

@nauar Thank you for answering my question. It helped me a lot. May I ask another question? How do you come up with EMPTYTREESHA="4b825dc642cb6eb9a060e54bf8d69288fbee4904"? Is this just a specific commit message from your repo?

Best!

Barton

@nauar

This comment has been minimized.

Copy link
Owner Author

@nauar nauar commented Oct 31, 2019

@donganzh You're welcome! :)

Regarding the empty tree sha, it's the sha that git returns for an empty tree: git hash-object -t tree /dev/null (see this stack overflow discussion about it). Note: I updated the gist, including this command instead of the constant. With it, the script will be compatible in case GIT developers want to modify the hashing algorithm from SHA1 to another one.

@Seifenhenne

This comment has been minimized.

Copy link

@Seifenhenne Seifenhenne commented Nov 29, 2019

@nauar Nice script! Just one hint: you introduced the variable filesize_mb but did not use it in the size error message, which leads to outputs that state the size in bytes followed by "MB".

Great work, thanks for sharing!

@nauar

This comment has been minimized.

Copy link
Owner Author

@nauar nauar commented Nov 30, 2019

@nauar Nice script! Just one hint: you introduced the variable filesize_mb but did not use it in the size error message, which leads to outputs that state the size in bytes followed by "MB".

Great work, thanks for sharing!

Hello @Seifenhenne.

Thanks for detecting this mistake. :) Fixed! ;)

Cheers!

Arnau Oncins

@zmes50416

This comment has been minimized.

Copy link

@zmes50416 zmes50416 commented Jan 17, 2020

Thank you for sharing!

But line 56 "$newFiles" will somehow cause all strings joined as one variable.
It seems to be the case of SC2066.
I fix this issue By removing quote: for filename in $newFiles

The tested environment is on OSX Catalina, version 3.2.57(1)-release (x86_64-apple-darwin19).

@nauar

This comment has been minimized.

Copy link
Owner Author

@nauar nauar commented Jan 19, 2020

Hello @zmes50416,

I tried without quotes and I had problems with files with spaces. Finally, I used one of the suggested solutions in SC2066 and now it works in all my tests.

Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.