From ef2f55cad81baa1083c399b90a78d6a817f02ffd Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Tue, 10 Oct 2023 23:32:10 -0700 Subject: [PATCH] Run trufflehog as a pre-commit hook Fix #199 --- README.md | 10 +++++++ shallow_backup/backup.py | 2 +- shallow_backup/git_wrapper.py | 55 +++++++++++++++++++++++++++++------ shallow_backup/utils.py | 1 + 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e4f984cc..dc3719de 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,14 @@ $ cd shallow-backup $ pip3 install . ``` +### Dependencies +--- + +- `pre-commit` +- `trufflehog` + +If you are missing the dependencies, you will be guided to install them. + ### Usage --- @@ -110,6 +118,8 @@ This backup tool is git-integrated, meaning that you can easily store your backu _If you choose to back up to a public repository, look at every file you're backing up to make sure you want it to be public._ +NOTE: As of `v6.2`, `trufflehog` is run as a required precommit hook and will detect secrets. + **How can I maintain a separate repo for my dotfiles?** `shallow-backup` makes this easy! After making your first backup, `cd` into the `dotfiles/` directory and run `$ git init`. Create a `.gitignore`, and a create / set up (link the upstream remote, etc) a new repo on your favorite version control platform. With operations involving the parent `shallow-backup` repo, `shallow-backup` will prompt you interactively to update the nested submodule. After that is taken care of, `shallow-backup` will move on to updating the parent. The `dotfiles` repo will be tracked as a submodule. diff --git a/shallow_backup/backup.py b/shallow_backup/backup.py index d7649bb1..70fe4457 100644 --- a/shallow_backup/backup.py +++ b/shallow_backup/backup.py @@ -84,7 +84,7 @@ def backup_dotfiles( # Fix https://github.com/alichtman/shallow-backup/issues/230 for dest_path in [path_pair[1] for path_pair in dotfiles_mp_in + dotfolders_mp_in]: - print(f"Creating: {os.path.split(dest_path)[0]}") + # print(f"Creating: {os.path.split(dest_path)[0]}") safe_mkdir(os.path.split(dest_path)[0]) with mp.Pool(mp.cpu_count()): diff --git a/shallow_backup/git_wrapper.py b/shallow_backup/git_wrapper.py index 1ead7ac9..90b5181b 100644 --- a/shallow_backup/git_wrapper.py +++ b/shallow_backup/git_wrapper.py @@ -1,9 +1,10 @@ import os from pathlib import Path +import subprocess import sys import git from git import GitCommandError -from shutil import move +from shutil import move, which from .printing import * from .config import get_config from .utils import safe_mkdir @@ -88,7 +89,7 @@ def handle_separate_git_dir_in_dotfiles(dotfiles_path: Path, dry_run: bool = Fal print_yellow_bold("Checking for separate git repo in dotfiles directory...") if ".git" in os.listdir(dotfiles_path): dotfiles_repo = git.Repo(dotfiles_path) - if dotfiles_repo.is_dirty(): + if dotfiles_repo.git.status("--porcelain"): print_green_bold("Detected a nested dotfiles repo that is dirty!!") print_green_bold( "Do you want to create and push a commit in this repo first, before dealing with the parent?" @@ -102,6 +103,10 @@ def handle_separate_git_dir_in_dotfiles(dotfiles_path: Path, dry_run: bool = Fal dotfiles_repo, message="dotfiles", dry_run=dry_run ) print_green_bold("Switching back to parent shallow-backup repo...") + else: + print_green_bold("Detected a nested dotfiles repo that is clean.") + else: + print_yellow_bold("No nested dotfiles repo detected.") def prompt_to_show_git_diff(repo): @@ -109,13 +114,44 @@ def prompt_to_show_git_diff(repo): print(repo.git.diff(staged=True, color="always")) -def git_add_all_and_print_status(repo): +def git_add_all_and_print_status(repo: git.Repo): print_yellow("Staging all files for commit...") repo.git.add(all=True) + print_yellow_bold(f"Git status of {repo.working_dir}") print(repo.git.status()) prompt_to_show_git_diff(repo) +def install_trufflehog_git_hook(repo: git.Repo): + """ + Make sure trufflehog and pre-commit are installed and on the PATH. Then register a pre-commit hook for the repo. + """ + if not which("trufflehog"): + print_red_bold("trufflehog (https://github.com/trufflesecurity/trufflehog) is not installed. Please install it to continue.") + sys.exit() + if not which("pre-commit"): + print_red_bold("pre-commit (https://pre-commit.com/) is not installed. Please install it to continue.") + sys.exit() + + + precommit_file = Path(repo.working_dir) / ".pre-commit-config.yaml" + if not precommit_file.exists(): + print_yellow_bold("Adding pre-commit config file...") + with open(precommit_file, "w+") as f: + f.write("""repos: +- repo: local + hooks: + - id: trufflehog + name: TruffleHog + description: Detect secrets in your data. + entry: bash -c 'trufflehog git file://.' + language: system + stages: ["commit", "push"]""") + + # Safe to run every time + subprocess.call("pre-commit install", cwd=repo.working_dir, shell=True) + + def git_add_all_commit_push(repo: git.Repo, message: str, dry_run: bool = False): """ Stages all changed files in dir_path and its children folders for commit, @@ -124,21 +160,22 @@ def git_add_all_commit_push(repo: git.Repo, message: str, dry_run: bool = False) :param git.repo repo: The repo :param str message: The commit message """ + install_trufflehog_git_hook(repo) if repo.is_dirty(): git_add_all_and_print_status(repo) - if not prompt_yes_no("Make a commit? Ctrl-C to exit", Fore.BLUE): + if not prompt_yes_no("Make a commit (with a trufflehog pre-commit hook)? Ctrl-C to exit", Fore.YELLOW): return if dry_run: print_yellow_bold("Dry run: Would have made a commit!") return print_yellow_bold("Making new commit...") - repo.git.add(A=True) try: - repo.git.commit(m=COMMIT_MSG[message]) + stdout = subprocess.run(["git", "commit", "-m", f"{COMMIT_MSG[message]}"], cwd=repo.working_dir).stdout + print(stdout) + if prompt_yes_no("Does the trufflehog output contain any secrets? If so, please exit and remove them.", Fore.YELLOW): + sys.exit() except git.exc.GitCommandError as e: - error = e.stdout.strip() - error = error[error.find("'") + 1 : -1] - print_red_bold(f"ERROR on Commit: {e.command}\n{error}\n") + print_red_bold(f"Error while making a commit: {e.command}\n{e}\n") print_red_bold( "Please open a new issue at https://github.com/alichtman/shallow-backup/issues/new" ) diff --git a/shallow_backup/utils.py b/shallow_backup/utils.py index cff3049a..717e7746 100644 --- a/shallow_backup/utils.py +++ b/shallow_backup/utils.py @@ -113,6 +113,7 @@ def mkdir_overwrite(path): or full_path.endswith(".gitignore") or full_path.endswith("README.md") or full_path.endswith("img") + or full_path.endswith(".pre-commit-config.yaml") ): continue