Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install just the talisman binary #445

Merged
merged 8 commits into from
Jan 23, 2024
181 changes: 70 additions & 111 deletions README.md
Original file line number Diff line number Diff line change
@@ -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,34 +36,60 @@
- [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

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.

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. <br>
If the installation script finds any existing hooks, it will only indicate so on the console. <br>
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 <b>automatically updates</b> 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).
352 changes: 117 additions & 235 deletions install.sh
Original file line number Diff line number Diff line change
@@ -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
# <https://thoughtworks.github.io/talisman/install.sh> 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" <<EOF
#!/bin/bash
[[ -n "\${TALISMAN_DEBUG}" ]] && DEBUG_OPTS="-d"
CMD="${PWD}/${TALISMAN_BIN_TARGET} \${DEBUG_OPTS} --githook ${HOOK_NAME}"
[[ -n "\${TALISMAN_DEBUG}" ]] && echo "ARGS are \$@"
[[ -n "\${TALISMAN_DEBUG}" ]] && echo "Executing: \${CMD}"
\${CMD}
EOF
chmod +x "$REPO_HOOK_TARGET"

echo_success "Talisman successfully installed to '$REPO_HOOK_TARGET'."
}

install_to_git_templates() {
if [[ ! -t 1 ]]; then
echo_error "Headless install to system templates is not supported."
echo_error "If you would like this feature, please open an issue: https://github.com/thoughtworks/talisman/issues/new"
exit $E_HEADLESS
fi
function echo_debug() {
[[ -z "$DEBUG" ]] && return
echo -ne "$(tput setaf 3)" >&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