aboutsummaryrefslogtreecommitdiff
path: root/tests/test-pre-commit-hook.bats
blob: 413c71d0c94bc7d3473b6fcdc5a922fb4f511844 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env bats
# Tests for githooks/pre-commit — the secret scan and paren check.
#
# The scan reads its input through a pipeline:
#
#   added_lines="$(git diff --cached ... | grep '^+' | grep -v '^+++' || true)"
#
# `grep` exits 1 when it matches nothing, which is the ordinary case, so the
# `|| true` has to stay.  But with no `pipefail` it also swallows a failure of
# `git diff` itself, and an empty `added_lines` makes the scan search nothing,
# find nothing, and report clean.  A gate that passes without looking is the
# failure this file exists to pin: the fail-open test drives a broken `git diff`
# and asserts the hook refuses rather than exiting 0.
#
# Each test builds a throwaway git repo in BATS_TEST_TMPDIR, so nothing touches
# the real repository or its hooks.

setup() {
    HOOK="${BATS_TEST_DIRNAME}/../githooks/pre-commit"
    REPO="${BATS_TEST_TMPDIR}/repo"
    mkdir -p "$REPO"
    cd "$REPO" || return 1
    git init -q .
    git config user.email t@example.com
    git config user.name  Test
    # Split so the fixtures never appear as credential-shaped literals here.
    AWS_TAIL="IOSFODNN7EXAMPLE"
    WORD_TAIL="word"
}

# Put a stub `git` ahead of the real one that fails for the staged-diff call
# and delegates everything else, so only the pipeline under test breaks.
break_staged_diff() {
    mkdir -p "${BATS_TEST_TMPDIR}/bin"
    cat > "${BATS_TEST_TMPDIR}/bin/git" <<'STUB'
#!/usr/bin/env bash
if [ "${1:-}" = "diff" ] && [ "${2:-}" = "--cached" ] && [ "${3:-}" = "-U0" ]; then
  echo "simulated git failure" >&2
  exit 128
fi
exec /usr/bin/git "$@"
STUB
    chmod +x "${BATS_TEST_TMPDIR}/bin/git"
    PATH="${BATS_TEST_TMPDIR}/bin:$PATH"
}

# ------------------------------- Normal cases -------------------------------

@test "secret scan: blocks a staged AWS key" {
    # Assembled at runtime: a literal key-shaped string in this file would trip
    # the very hook under test on every commit that touches it, and this repo
    # mirrors to a public remote.
    printf 'aws = "%s"\n' "AKIA${AWS_TAIL}" > creds.txt
    git add creds.txt
    run "$HOOK"
    [ "$status" -eq 1 ]
    [[ "$output" == *"potential secret"* ]]
}

@test "secret scan: blocks a staged keyword=value password" {
    printf '%s = "%s"\n' "pass${WORD_TAIL}" "correcthorsebatterystaple" > conf.txt
    git add conf.txt
    run "$HOOK"
    [ "$status" -eq 1 ]
    [[ "$output" == *"potential secret"* ]]
}

@test "secret scan: allows an ordinary staged file" {
    printf 'just some prose\n' > notes.txt
    git add notes.txt
    run "$HOOK"
    [ "$status" -eq 0 ]
}

# ------------------------------ Boundary cases ------------------------------

@test "secret scan: allows a commit with nothing staged" {
    run "$HOOK"
    [ "$status" -eq 0 ]
}

@test "paren check: blocks an unbalanced staged .el file" {
    printf '(defun broken ()\n  (message "no close"\n' > bad.el
    git add bad.el
    run "$HOOK"
    [ "$status" -eq 1 ]
    [[ "$output" == *"paren check failed"* ]]
}

@test "paren check: allows a balanced staged .el file" {
    printf '(defun fine ()\n  (message "ok"))\n' > good.el
    git add good.el
    run "$HOOK"
    [ "$status" -eq 0 ]
}

# -------------------------------- Error cases -------------------------------

@test "secret scan: refuses to pass when the staged diff cannot be read" {
    # The scan must not report clean after searching nothing.  Without a
    # pipefail-aware guard the broken diff yields an empty added_lines and the
    # hook exits 0, letting a real secret through unscanned.
    printf 'aws = "%s"\n' "AKIA${AWS_TAIL}" > creds.txt
    git add creds.txt
    break_staged_diff
    run "$HOOK"
    [ "$status" -ne 0 ]
}

@test "paren check: refuses to pass when the staged file list cannot be read" {
    printf '(defun broken ()\n  (message "no close"\n' > bad.el
    git add bad.el
    mkdir -p "${BATS_TEST_TMPDIR}/bin2"
    cat > "${BATS_TEST_TMPDIR}/bin2/git" <<'STUB'
#!/usr/bin/env bash
if [ "${1:-}" = "diff" ] && [ "${2:-}" = "--cached" ] && [ "${3:-}" = "--name-only" ]; then
  echo "simulated git failure" >&2
  exit 128
fi
exec /usr/bin/git "$@"
STUB
    chmod +x "${BATS_TEST_TMPDIR}/bin2/git"
    PATH="${BATS_TEST_TMPDIR}/bin2:$PATH"
    run "$HOOK"
    [ "$status" -ne 0 ]
}