diff --git a/README.md b/README.md index 7d5fb77b..c04af4f9 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,12 @@ ## Table of Contents - [What is Talisman?](#what-is-talisman) - [Installation](#installation) - - [[Recommended approach]](#recommended-approach) + - [Install onto path (recommended approach)](#install-onto-path-recommended-approach) - [Installation as a global hook template](#installation-as-a-global-hook-template) - - [Handling existing hooks](#handling-existing-hooks) - - [1. Pre-commit (Linux/Unix)](#1-pre-commit-linuxunix) - - [2. Husky (Linux/Unix/Windows)](#2-husky-linuxunixwindows) - - [Windows](#windows) - - [Linux/Unix](#linuxunix) - - [Windows](#windows-1) - - [Linux/Unix](#linuxunix-1) - [Installation to a single project](#installation-to-a-single-project) - - [Handling existing hooks](#handling-existing-hooks-1) +- [Using with hook frameworks](#using-with-hook-frameworks) + - [Pre-commit](#pre-commit) + - [Husky](#husky) - [Upgrading](#upgrading) - [Talisman in action](#talisman-in-action) - [Validations](#validations) @@ -41,13 +36,15 @@ - [Uninstallation](#uninstallation) - [Uninstallation from a global hook template](#uninstallation-from-a-global-hook-template) - [Uninstallation from a single repository](#uninstallation-from-a-single-repository) - - [Contributing to Talisman](#contributing-to-talisman) +- [Contributing to Talisman](#contributing-to-talisman) # What is Talisman? -Talisman is a tool that installs a hook to your repository to ensure that potential secrets or sensitive information do not leave the developer's workstation. -It validates the outgoing changeset for things that look suspicious - such as potential SSH -keys, authorization tokens, private keys etc. +Talisman is a tool that scans git changesets to ensure that potential secrets +or sensitive information do not leave the developer's workstation. + +It validates the outgoing changeset for things that look suspicious - such as +potential SSH keys, authorization tokens, private keys etc. # Installation @@ -55,8 +52,9 @@ Talisman supports MAC OSX, Linux and Windows. Talisman can be installed and used in one of the following ways: -1. As a git hook as a global [git hook template](https://git-scm.com/docs/git-init#_template_directory) and a CLI utility (for git repo scanning) -2. As a git hook into a single git repository +1. As a standalone executable +2. As a git hook as a global [git hook template](https://git-scm.com/docs/git-init#_template_directory) and a CLI utility (for git repo scanning) +3. As a git hook into a single git repository Talisman can be set up as either a pre-commit or pre-push hook on the git repositories. @@ -64,11 +62,34 @@ Find the instructions below. *Disclaimer: Secrets creeping in via a forced push in a git repository cannot be detected by Talisman. A forced push is believed to be notorious in its own ways, and we suggest git repository admins to apply appropriate measures to authorize such activities.* +## Install onto path (recommended approach) + +We recommend installing `talisman` onto your path so that it is available for +git hook frameworks and scripts. Pick the correct binary for your system from +our [Releases Page](https://github.com/thoughtworks/talisman/releases), or run +our [install script](https://github.com/thoughtworks/talisman/blob/main/install.sh): + +``` +bash -c "$(curl --silent https://raw.githubusercontent.com/thoughtworks/talisman/main/install.sh)" +``` + +Or set environment variable `INSTALL_LOCATION` to specify a custom location for +the binary: + +``` +INSTALL_LOCATION=/usr/local/bin bash -c "$(curl --silent https://raw.githubusercontent.com/thoughtworks/talisman/main/install.sh)" +``` + +Or using linuxbrew in Linux and homebrew in macOS by running the following +command in terminal: + +``` +brew install talisman +``` -## [Recommended approach] ## Installation as a global hook template -We recommend installing Talisman as a **pre-commit git hook template**, as that will cause +We offer scripts that will install Talisman as a **pre-commit git hook template**, as that will cause Talisman to be present, not only in your existing git repositories, but also in any new repository that you 'init' or 'clone'. @@ -96,90 +117,11 @@ bash -c "$(curl --silent https://thoughtworks.github.io/talisman/scripts/install If you choose to set the `$PATH` later, please export TALISMAN\_HOME=$HOME/.talisman/bin to the path. -3. Choose a base directory where Talisman should scan for all git repositories, and setup a git hook (pre-commit or pre-push, as chosen in step 1) as a symlink. - This script will not clobber pre-existing hooks. If you have existing hooks, [look for ways to chain Talisman into them.](#handling-existing-hooks) +3. Choose a base directory where Talisman should scan for all git repositories, and set up a git hook (pre-commit or pre-push, as chosen in step 1) as a symlink. + This script will not clobber pre-existing hooks. If you have existing hooks you can add talisman through a [hook framework](#using-with-hook-frameworks) - you can set SEARCH_ROOT environment variable with the path of the base directory before executing the installation so you don't need to input it manually during the installation - -#### Alternative installation using brew - -Talisman can also be installed using linuxbrew in Linux and homebrew in macOS by running the following command in terminal - - ``` -brew install talisman -``` - - -### Handling existing hooks -Installation of Talisman globally does not clobber pre-existing hooks on repositories.
-If the installation script finds any existing hooks, it will only indicate so on the console.
-To achieve running multiple hooks we suggest (but not limited to) the following two tools - -#### 1. Pre-commit (Linux/Unix) -Use [pre-commit](https://pre-commit.com) tool to manage all the existing hooks along with Talisman. -In the suggestion, it will prompt the following code to be included in .pre-commit-config.yaml - -``` - - repo: local - hooks: - - id: talisman-precommit - name: talisman - entry: bash -c 'if [ -n "${TALISMAN_HOME:-}" ]; then ${TALISMAN_HOME}/talisman_hook_script pre-commit; else echo "TALISMAN does not exist. Consider installing from https://github.com/thoughtworks/talisman . If you already have talisman installed, please ensure TALISMAN_HOME variable is set to where talisman_hook_script resides, for example, TALISMAN_HOME=${HOME}/.talisman/bin"; fi' - language: system - pass_filenames: false - types: [text] - verbose: true -``` - -#### 2. Husky (Linux/Unix/Windows) -[husky](https://typicode.github.io/husky) is an npm module for managing git hooks. -In order to use husky, make sure you have already set TALISMAN_HOME to `$PATH`. - -+ **Existing Users** - - If you already are using husky, add the following lines to husky pre-commit in package.json - - ###### Windows - - ``` - "bash -c '\"%TALISMAN_HOME%\\${TALISMAN_BINARY_NAME}\" --githook pre-commit'" -``` - - ###### Linux/Unix - - ``` - $TALISMAN_HOME/talisman_hook_script pre-commit -``` -+ **New Users** - - If you want to use husky with multiple hooks along with talisman, add the following snippet to you package json. -###### Windows - - ``` - { - "husky": { - "hooks": { - "pre-commit": "bash -c '\"%TALISMAN_HOME%\\${TALISMAN_BINARY_NAME}\" --githook pre-commit'" && "other-scripts" - } - } - } -``` - - ###### Linux/Unix - - ``` - { - "husky": { - "hooks": { - "pre-commit": "$TALISMAN_HOME/talisman_hook_script pre-commit" && "other-scripts" - } - } - } -``` - - - ## Installation to a single project ```bash @@ -197,24 +139,41 @@ cd my-git-project ~/install-talisman.sh pre-commit ``` -### Handling existing hooks -Talisman will need to be chained with any existing git hooks.You can use [pre-commit](https://pre-commit.com) git hooks framework to handle this. +*Disclaimer: Talisman cannot guarantee its functionality in Microsoft's unsupported versions of Windows. Anyway Talisman is successfully tested on Windows 7 and server 2008 R2, which might not work in future releases.* + +# Using with hook frameworks + +Globally installing talisman as a hook will not clobber any existing hooks. If +the installation script finds any existing hooks, it will only indicate so on +the console. To run multiple hooks we suggest using a hook framework, such as +pre-commit or husky. These instructions assume that the talisman executable is +installed somewhere on your system's path. + +## Pre-commit -Add this to your `.pre-commit-config.yaml` (be sure to update `rev` to point to -a real git revision!) +Use [pre-commit](https://pre-commit.com) tool to manage all the existing hooks +along with Talisman. In the suggestion, it will prompt the following code to be +included in .pre-commit-config.yaml: ```yaml - repo: https://github.com/thoughtworks/talisman rev: 'v1.28.0' # Update me! hooks: - # either `commit` or `push` support - # - id: talisman-push - - id: talisman-commit - entry: cmd --githook pre-commit - + # both pre-commit and pre-push supported + # - id: talisman-push + - id: talisman-commit + entry: cmd --githook pre-commit ``` -*Disclaimer: Talisman cannot guarantee its functionality in Microsoft's unsupported versions of Windows. Anyway Talisman is successfully tested on Windows 7 and server 2008 R2, which might not work in future releases.* +## Husky + +[husky](https://typicode.github.io/husky) is an npm module for managing hooks. +Add the following line to the husky pre-commit configuration in your +`package.json`: + +``` +talisman --githook pre-commit +``` # Upgrading Since release v0.4.4, Talisman automatically updates the binary to the latest release, when the hook is invoked (at pre-commit/pre-push, as set up). So, just sit back, relax, and keep using the latest Talisman without any extra efforts. @@ -504,8 +463,8 @@ To run the checksum please "cd" into the root of your repository and run the fol For Example: `talisman --checksum="*.pem *.txt"` -1. This command finds all the .pem files in the respository and calculates collective checksum of all those files and outputs a yaml format for .talismanrc. In the same way it deals with the .txt files. -2. Multiple file names / patterns can be given with space seperation. +1. This command finds all the .pem files in the repository and calculates collective checksum of all those files and outputs a yaml format for .talismanrc. In the same way it deals with the .txt files. +2. Multiple file names / patterns can be given with space separation. Example output: @@ -591,6 +550,6 @@ When you installed Talisman, it must have created a pre-commit or pre-push hook You can remove the hook manually by deleting the Talisman pre-commit or pre-push hook from .git/hooks folder in repository. -## Contributing to Talisman +# Contributing to Talisman To contribute to Talisman, have a look at our [contributing guide](contributing.md). diff --git a/install.sh b/install.sh index 89b30327..dc1fb7b4 100755 --- a/install.sh +++ b/install.sh @@ -1,256 +1,138 @@ #!/bin/bash -# Hello there! If you update the talisman version, please remember to: -# - Test that this script works with no args, and the `pre-push` / `pre-commit` arg. -# - also update `install.sh` in the gh_pages branch of this repo, so that -# gets updated too. -# Thanks! - set -euo pipefail -DEBUG=${DEBUG:-''} -HOOK_NAME="${1:-pre-push}" -case "$HOOK_NAME" in -pre-commit | pre-push) REPO_HOOK_TARGET=".git/hooks/${HOOK_NAME}" ;; -*) - echo "Unknown Hook name '${HOOK_NAME}'. Please check parameters" - exit 1 - ;; -esac - -# we call run() at the end of the script to prevent inconsistent state in case -# user runs with curl|bash and curl fails in the middle of the download -# (https://www.seancassidy.me/dont-pipe-to-your-shell.html) -run() { - declare TALISMAN_BINARY_NAME - - IFS=$'\n' - - GITHUB_URL="https://github.com/thoughtworks/talisman" - VERSION=$(curl --silent "https://api.github.com/repos/thoughtworks/talisman/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - BINARY_BASE_URL="$GITHUB_URL/releases/download/$VERSION" - REPO_HOOK_BIN_DIR=".git/hooks/bin" - - DEFAULT_GLOBAL_TEMPLATE_DIR="$HOME/.git-templates" - - declare DOWNLOADED_BINARY - TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'talisman_setup') - trap "rm -r ${TEMP_DIR}" EXIT - chmod 0700 ${TEMP_DIR} - - E_HOOK_ALREADY_PRESENT=1 - E_CHECKSUM_MISMATCH=2 - E_USER_CANCEL=3 - E_HEADLESS=4 - E_UNSUPPORTED_ARCH=5 - E_DEPENDENCY_NOT_FOUND=6 - - echo_error() { - echo -ne $(tput setaf 1) >&2 - echo "$1" >&2 - echo -ne $(tput sgr0) >&2 - } - export -f echo_error +declare BINARY_NAME - function echo_debug() { - [[ -z "${DEBUG}" ]] && return - echo -ne $(tput setaf 3) >&2 - echo "$1" >&2 - echo -ne $(tput sgr0) >&2 - } - export -f echo_debug +E_UNSUPPORTED_ARCH=5 +CHECKSUM_FILE_NAME='checksums' - echo_success() { - echo -ne $(tput setaf 2) - echo "$1" >&2 - echo -ne $(tput sgr0) - } - export -f echo_success - - operating_system() { - OS=$(uname -s) - case $OS in - "Linux") - echo "linux" - ;; - "Darwin") - echo "darwin" - ;; - MINGW32_NT-*) - echo "windows" - ;; - MINGW64_NT-*) - echo "windows" - ;; - MINGW64_NT-6.3*) - echo "windows" - ;; - *) - echo_error "Talisman currently only supports Windows, Linux and MacOS(darwin) systems." - echo_error "If this is a problem for you, please open an issue: https://github.com/${INSTALL_ORG_REPO}/issues/new" - exit $E_UNSUPPORTED_ARCH - ;; - esac +DEBUG=${DEBUG:-''} +VERSION=${VERSION:-'latest'} +INSTALL_ORG_REPO=${INSTALL_ORG_REPO:-'thoughtworks/talisman'} +INSTALL_LOCATION=${INSTALL_LOCATION:-'/usr/local/bin'} + +function echo_error() { + echo -ne "$(tput setaf 1)" >&2 + echo "$1" >&2 + echo -ne "$(tput sgr0)" >&2 } - binary_arch_suffix() { - declare OS - OS=$(operating_system) - ARCH=$(uname -m) - case $ARCH in - "x86_64") - OS="${OS}_amd64" - ;; - "i686" | "i386") - OS="${OS}_386" - ;; - "arm64") - OS="${OS}_arm64" - ;; - *) - echo_error "Talisman currently only supports x86 and x86_64 architectures." - echo_error "If this is a problem for you, please open an issue: https://github.com/thoughtworks/talisman/issues/new" - exit $E_UNSUPPORTED_ARCH - ;; - esac - - TALISMAN_BINARY_NAME="talisman_${OS}" - if [[ $OS == *"windows"* ]]; then - TALISMAN_BINARY_NAME="${TALISMAN_BINARY_NAME}.exe" - fi - } - - function download() { - OBJECT=$1 - DOWNLOAD_URL=${BINARY_BASE_URL}/${OBJECT} - echo "Downloading ${OBJECT} from ${DOWNLOAD_URL}" - curl --location --silent ${DOWNLOAD_URL} >"$TEMP_DIR/${OBJECT}" - } - - function verify_checksum() { - FILE_NAME=$1 - CHECKSUM_FILE_NAME='checksums' - echo_debug "Verifying checksum for ${FILE_NAME}" - download ${CHECKSUM_FILE_NAME} - - pushd ${TEMP_DIR} >/dev/null 2>&1 - grep ${TALISMAN_BINARY_NAME} ${CHECKSUM_FILE_NAME} >${CHECKSUM_FILE_NAME}.single - - if ! command -v shasum &> /dev/null; then - sha256sum -c ${CHECKSUM_FILE_NAME}.single - else - shasum -a 256 -c ${CHECKSUM_FILE_NAME}.single - fi - popd >/dev/null 2>&1 - echo_debug "Checksum verification successful!" - echo - } - - function download_and_verify() { - binary_arch_suffix - download "${TALISMAN_BINARY_NAME}" - DOWNLOADED_BINARY="${TEMP_DIR}/${TALISMAN_BINARY_NAME}" - verify_checksum "${TALISMAN_BINARY_NAME}" - } - - install_to_repo() { - if [[ -x "$REPO_HOOK_TARGET" ]]; then - echo_error "Oops, it looks like you already have a ${HOOK_NAME} hook installed at '${REPO_HOOK_TARGET}'." - echo_error "If this is expected, you should consider setting-up a tool to allow git hook chaining," - echo_error "like pre-commit (brew install pre-commit) or Husky or any other tool of your choice." - echo_error "WARNING! Talisman hook not installed." - exit $E_HOOK_ALREADY_PRESENT - fi - - download_and_verify - - mkdir -p "$REPO_HOOK_BIN_DIR" - TALISMAN_BIN_TARGET="${REPO_HOOK_BIN_DIR}/talisman" - cp "$DOWNLOADED_BINARY" "$TALISMAN_BIN_TARGET" - chmod +x "$TALISMAN_BIN_TARGET" - - cat >"$REPO_HOOK_TARGET" <&2 + echo "$1" >&2 + echo -ne "$(tput sgr0)" >&2 +} - TEMPLATE_DIR=$(git config --global init.templatedir) || true +function echo_success() { + echo -ne "$(tput setaf 2)" + echo "$1" >&2 + echo -ne "$(tput sgr0)" +} - echo "Not running from inside a git repository... installing as a" - echo "git template." - echo - echo "If you meant to install to a specific repo, 'cd' into that" - echo "repo and run this script again." - echo - echo "Installing as a template will automatically add Talisman to" - echo "any new repo that you 'init' or 'clone'." - echo +function operating_system() { + OS=$(uname -s) + case $OS in + "Linux") + echo "linux" + ;; + "Darwin") + echo "darwin" + ;; + MINGW32_NT-* | MINGW64_NT-* | MSYS_NT-*) + echo "windows" + ;; + *) + echo_error "Talisman currently only supports Windows, Linux, and MacOS (darwin) systems." + echo_error "If this is a problem for you, please open an issue: https://github.com/$INSTALL_ORG_REPO/issues/new" + exit $E_UNSUPPORTED_ARCH + ;; + esac +} - if [[ "$TEMPLATE_DIR" == "" ]]; then - echo "No git template directory is configured. Let's add one." - echo "(this will override any system git templates and modify your git config file)" - echo - read -u1 -p "Git template directory: ($DEFAULT_GLOBAL_TEMPLATE_DIR) " TEMPLATE_DIR - echo - TEMPLATE_DIR=${TEMPLATE_DIR:-$DEFAULT_GLOBAL_TEMPLATE_DIR} - git config --global init.templatedir $TEMPLATE_DIR - else - echo "You already have a git template directory configured." - echo - read -u1 -p "Add Talisman to '$TEMPLATE_DIR/hooks?' (Y/n) " USE_EXISTING - echo +function architecture() { + ARCH=$(uname -m) + case $ARCH in + "x86_64") + echo "amd64" + ;; + "i686" | "i386") + echo "386" + ;; + "arm64" | "aarch64") + echo "arm64" + ;; + *) + echo_error "Talisman currently only supports x86 and x86_64 and arm64 architectures." + echo_error "If this is a problem for you, please open an issue: https://github.com/$INSTALL_ORG_REPO/issues/new" + exit $E_UNSUPPORTED_ARCH + ;; + esac +} - case "$USE_EXISTING" in - Y | y | "") ;; # okay, continue - *) - echo_error "Not installing Talisman." - echo_error "If you were trying to install into a single git repo, re-run this command from that repo." - echo_error "You can always download/compile manually from our Github page: $GITHUB_URL" - exit $E_USER_CANCEL - ;; - esac - fi +function set_binary_name() { + BINARY_NAME="talisman_$(operating_system)_$(architecture)" + if [ "$(operating_system)" = "windows" ]; then + BINARY_NAME="$BINARY_NAME.exe" + fi + echo_success "Selected $BINARY_NAME" +} - # Support '~' in path - TEMPLATE_DIR=${TEMPLATE_DIR/#\~/$HOME} +function download() { + ASSETS=$(curl -Ls https://api.github.com/repos/"$INSTALL_ORG_REPO"/releases/latest | + grep download_url | awk '{print $2}' | tr -d '"') + BINARY_URL=$(echo "$ASSETS" | grep "$BINARY_NAME") + CHECKSUM_URL=$(echo "$ASSETS" | grep $CHECKSUM_FILE_NAME) + echo_debug "Downloading $BINARY_NAME and from $BINARY_URL" + curl --location --silent "$BINARY_URL" >"$TEMP_DIR/$BINARY_NAME" + echo_debug "Downloading $CHECKSUM_FILE_NAME and from $CHECKSUM_URL" + curl --location --silent "$CHECKSUM_URL" >"$TEMP_DIR/$CHECKSUM_FILE_NAME" + echo_success "Downloaded talisman binary and checksums" +} - if [ -f "$TEMPLATE_DIR/hooks/${HOOK_NAME}" ]; then - echo_error "Oops, it looks like you already have a ${HOOK_NAME} hook installed at '$TEMPLATE_DIR/hooks/${HOOK_NAME}'." - echo_error "If this is expected, you should consider setting-up a tool to allow git hook chaining," - echo_error "like pre-commit (brew install pre-commit) or Husky or any other tool of your choice." - echo_error "WARNING! Talisman hook not installed." - exit $E_HOOK_ALREADY_PRESENT - fi +function verify_checksum() { + pushd "$TEMP_DIR" >/dev/null 2>&1 - mkdir -p "$TEMPLATE_DIR/hooks" + if ! command -v shasum &> /dev/null; then + sha256sum --ignore-missing -c $CHECKSUM_FILE_NAME + else + shasum -a 256 --ignore-missing -c $CHECKSUM_FILE_NAME + fi - download_and_verify + popd >/dev/null 2>&1 + echo_success "Checksum OK" +} - cp "$DOWNLOADED_BINARY" "$TEMPLATE_DIR/hooks/${HOOK_NAME}" - chmod +x "$TEMPLATE_DIR/hooks/${HOOK_NAME}" +function install() { + if (touch "$INSTALL_LOCATION/$BINARY_NAME" &>/dev/null); then + cp "$TEMP_DIR/$BINARY_NAME" "$INSTALL_LOCATION/$BINARY_NAME" + chmod +x "$INSTALL_LOCATION/$BINARY_NAME" + ln -s "$INSTALL_LOCATION/$BINARY_NAME" "$INSTALL_LOCATION/talisman" + elif (which sudo &>/dev/null); then + sudo cp "$TEMP_DIR/$BINARY_NAME" "$INSTALL_LOCATION/$BINARY_NAME" + sudo chmod +x "$INSTALL_LOCATION/$BINARY_NAME" + sudo ln -s "$INSTALL_LOCATION/$BINARY_NAME" "$INSTALL_LOCATION/talisman" + else + echo_error "Insufficient permission to install to $INSTALL_LOCATION" + exit 126 + fi +} - echo_success "Talisman successfully installed." - } +function run() { + TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'talisman_setup') + # shellcheck disable=SC2064 + trap "rm -r $TEMP_DIR" EXIT + chmod 0700 "$TEMP_DIR" - if [ ! -d "./.git" ]; then - install_to_git_templates - else - install_to_repo + if [ ! -d "$INSTALL_LOCATION" ]; then + echo_error "$INSTALL_LOCATION is not a directory!" + exit 1 fi + + set_binary_name + download + verify_checksum + install } -run $0 $@ +run