Skip to content

Instantly share code, notes, and snippets.

@nauar
Last active March 13, 2024 08:45
Show Gist options
  • Save nauar/826f85d25d692d9bc009312cb71577dd to your computer and use it in GitHub Desktop.
Save nauar/826f85d25d692d9bc009312cb71577dd to your computer and use it in GitHub Desktop.
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-zz
Copy link

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
Copy link
Author

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-zz
Copy link

@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
Copy link
Author

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
Copy link

@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
Copy link
Author

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
Copy link

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
Copy link
Author

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!

@kdevineni
Copy link

kdevineni commented Feb 2, 2023

@nauar

This script is great and does the job.....Thanks for the script....

I have a problem when a new branch is pushed , its scan entire changes , I want to scan only the changes in the current(new) branch excluding the master commits from which it was branched.

Here is my case.....
If I create new local branch from master , make some changes/commits and push.
I want to scan only the the commits/changes that are made in this new branch.

Locally I can get the changes using the command
"git cherry master | head -n 2 | tail -1" >> returns the commit of the master that this branch is created.

I want to replace the variable oldref in the script with the output of the cherry command. but the command doesn't return anything when executed from pre-receive hook

Any suggestions on how I can get the oldref with cherry command output?

@srinivas974
Copy link

srinivas974 commented Feb 7, 2023

@nauar Thanks for your script.
@kdevineni Can you try this command?
git show --pretty="" --name-only <new_ref>

@kdevineni
Copy link

@nauar Thanks for your script. @kdevineni Can you try this command? git show --pretty="" --name-only <new_ref>

@srinivas974 "git show --pretty="" --name-only <new_ref>" only returns files from the commit reference specified .
Doesn't return all the changes made in the current branch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment