Tuesday, March 7, 2017

[How To]: Not commit when a string is present in git

The Use Case
Howdy! So, I ran into an interesting problem this week. In order to test something, I had to make a change to code that should NEVER be commit... then, I commit it. D'oh!

If you want to protect yourself from something similar, or prevent data leakage, read on.

The Setup
When you clone or initialize a git project, git creates a "./.git" folder for you. This is where it tracks information about the repository, and stores some git metadata and logs.

Another thing this folder contains are "hooks", which are run at some point during the git workflow. For instance, if we had a "pre-push" hook, it would run before the push goes up to the remote.

So, what can we do with these hooks? Anything that BASH lets us. The most common use case for hooks is stopping a commit if something isn't right with the project, which is what we'll be doing in our case. However, you could do things like:

  • Print out the total number of lines changed
  • Check for a string and just give warnings (i,e mispellings, swear words)
  • Queue a local or remote backup
  • Package the project
Really, anything.

Protecting Ourselves
We would like to not commit if a string is present in the file. Imagine if we wanted to search for //[DANGER]. The presence of [DANGER] in any tracked file would stop the commit, so you could remember to go back and change the reason for it being there.

To do this, open the pre-commit.sample file and save it as pre-commit (with no extension). If there isn't a sample file, simply make the pre-commit file.

Before we write any code, let's look at what we want:

  • If a string is present ANYWHERE in the directory, fail
Alternatively, in large code bases, we want:
  • Search the diff for newly added instances of the string
Since searching through every file is going to take too long to do on every commit.

The Steps

Make the File
You should now have a file called ./.git/hooks/pre-commit. You make need to mark the pre-commit file as executable with chmod + x pre-commit.

Add this code to the file

printf "[Check]: Searching for commit mollyguard: '%s'..." "$breakingString"
sleep 1
#                                                                       VVVVVVV YOUR REGEX RIGHT THERE
if [ $(git diff --word-diff --cached --diff-filter=ACUM -U0 | grep -E "+*\[DANGER\]" | wc -l) -gt 0 ]
printf "\n"
cat <<\EOF
Error: Trying to commit a tree that contains uncommitable changes 

Search the projects for $breakingString and figure out why the comment is there
exit 1;

printf "Good!\n"

What this code is doing: 
  • Look at the last diff
  • Search the diff for "+{string}" (because + means it was ADDED, not removed in the last commit)
  • Count the lines
If the lines are greater than 0, and instance was found, so we shouldn't commit this branch.

Of course, this is searching just the latest commit, because searching a large software project is going to take a while. If you're working on a smaller project (or on a very fast machine), you can replace the if statement with simply

if [ $(grep -R "\[DANGER\]"../../  | wc- l) > 0 ]

Sunday, March 5, 2017

.NET - "%5C" Showing Up on URLs

Are you a developer, who is accepting a file or some string of text and putting it into a cloud? Are you, also, per chance, retrieving its URL, and notcing this pesky "%5C popping up in our URLs? Well, I know recently I was. It was a pretty strange, out of the blue error with an interesting cause. Here are some details for you:

What Causes This?
The actual causes of this problem are numerous. The bottom line is, '%5C' in a URL means that the backslash '\' got encoded in the URL. Somewhere along the way, either unintentionally or on purpose, a string containing one wound up in the encoding step.

The culprit for many users is using .NET's Path utility for trying to parse URLs. Here's an example:

Let's look at expectation vs Reality

Code: Path.GetDirectoryName("I/want/to/upload/to/this/place")
Expected: "I/want/to/upload/to/this/"
Actual: "I\want\to\upload\to\this"

This is because Path uses a field called Path.DirectorySeperatorChar, which on Windows is '\'... A bit backwards that Path can READ forward slashes as directory names, yet only output backslashes in the return value.

How Do I Fix This?
There are plenty of good solutions to fix this. However, with so many different causes to the problem, it's hard to say exactly.

To Fix it in a simple string
It's possible that all you need to do is Path.GetDirectoryName(your_string).Replace(Path.DirectorySeperatorChar, Path.AltDirectorySeperatorChar), and you're good to go.

Hopefully this shed some light on what the problem may be in your case. Hopefully, moving forward, Microsoft is not afraid to add a .NET feature like PathFlags or something so that this doesn't crop up as much as it currently does.