Table of Contents

<--   Back

How I Create GitHub Token Manager


TL;DR

It’s just some simple CRUD using SQLite and AES-CBC 128-bit with Git integration.

GitHub token

Back in December 2020, GitHub announce that start in August 13, 2021 token is require for any Git operations 1.

Although it’s good and you can set different permission and expire date on each token, I just don’t want to copy token on text file every Git operations like my co-worker, so this is why I create simple token manager.

Initial goal

The initial goal is just simple CLI CRUD who encrypt GitHub token and decrypt the token when I need without NodeJS and Python installed, just download the binary ➜ add to PATH ➜ done, and being able to move the database file from Linux to Windows and vice versa.

I choose C++ for this project, and I think fun and interesting to learn more about C++.

Ingredients

IngredientDetail
C++ compilerGCC for Linux and MSVC for Windows
CMakehttps://cmake.org/
linuxdeployqthttps://github.com/probonopd/linuxdeployqtbb
SQLitehttps://www.sqlite.org/index.html
PicoSHA2https://github.com/okdshin/PicoSHA2
plusaeshttps://github.com/kkAyataka/plusaes
argparsehttps://github.com/p-ranav/argparse
tabulatehttps://github.com/p-ranav/tabulate
cliphttps://github.com/dacap/clip

All ingredients pack into Git submodule except compiler, CMake, and linuxdeployqt.

Encryption

Here some pseudo code in python syntax, just give you rough idea how I implement.

pseudo code
token: str = "ghp_TOKEN"
password: str = "very_secure_password"
 
final_token: str = "GAH_FLAG_" + token # add flag to token for later decrypt check
hash_password: list[int] = sha256(password) # convert password to array of number (0 - 255)
 
# ENCRYPT
 
encrypted_size: list[int] = [len(final_token)] # the aes_decrypt need original string length
encrypted_token: list[int] = aes_encrypt(final_token, hash_password) # encrypt final_token with hash_password, resulting array of number 0 - 255
 
final_encrypted_token: str = list_number_to_hex_string(encrypted_token + encrypted_size) # convert array number to hex string and store to database
 
# DECRYPT
 
list_number_token: list[int] = hex_string_to_list_number(final_encrypted_token)
 
decrypted_size: int = list_number_token.pop()
decrypted_token: str = aes_decrypt(list_number_token, hash_password, decrypted_size)
 
if string_start_with(decrypted_token, "GAH_FLAG_"):
  print(string_replace(decrypted_token, "GAH_FLAG_", ""))
else:
  print("password wrong")

You can see the C++ version gah.cpp#L223

Git integration

Now we have fully functional what the initial goal is, store the token and copy to clipboard when we need it.

terminal output encrypt and decrypt

Did you know you can set username and token in GitHub remote or GitHub clone but only support for HTTPS protocol as far as I know, something like this.

# git clone
git clone https://username:token@github.com/username/repository.git
 
# git push
git push https://username:token@github.com/username/repository.git branch
 
# git pull
git pull https://username:token@github.com/username/repository.git branch

With that you can create something like this.

command: str = "git push https://" + username + ":" + token + "@" + remove_http(remote) + " " + branch;
exec(command);

And the result when implemented.

terminal output

In Windows is pretty easy to build and distribute the binary file into single exe since all library is a static library, Linux in another hand is hard, like Linus Torvalds say in DebConf 14_ QA with Linus Torvalds talk [YouTube video].

We basically don’t make binaries for Linux. Why? Because binaries for Linux desktop applications is a major f*ing pain in the ass.

– Linus Torvalds

Binary file

Basically most Linux applications using shared library (you can call it dependency), and the library it self should be installed and compatible with the binary (the library version when the binary it’s build).

And I found that AppImage is the solution (Flatpak and Snap is also good, but I prefer using AppImage because it easy to understand), AppImage is a Linux format file allow to pack all shared library that you need into execute binary that self extract and container when it execute, more information on https://appimage.org/.

linuxdeployqt allow to automatically pack all shared library that we need on binary file into single AppImage file, although linuxdeployqt mainly to generate AppImage for Qt application but it can use for general application, more information on https://github.com/probonopd/linuxdeployqt.

At first time I use AppImage, but I realize that all library that I use is capable to static link.

GitHub Actions

Now the final thing is GitHub Actions, let the GitHub Actions compile both for Linux and Windows and automatically create release and upload the binary file, here’s how I do it -> build_release.yml, and also others automation for CI and PR test.

AND IT’S FREE for public repository as far as I know.

free real estate

Flow actions

FLow cluster_build Build cluster_test Test and share config linux Linux test_win Win linux->test_win win Win win->test_win test_linux Linux test_win->test_linux Release Release test_linux->Release

Roadmap

What I learn

Conclusion

It was fun learning C++, CMake, and AppImage in this project, the thing that bothering me is I’m too lazy to write any validation command before execute, resulting security concern when user input malicious string.

But as long the initial goal is achieve, it’s ok for me. Thank you so much for reading this post, feel free to contribute to this project.

Project repository -> https://github.com/anasrar/gah/, have a nice day.

Footnotes

  1. Token authentication requirements for Git operations


Open GitHub Discussions