aboutsummaryrefslogtreecommitdiff
path: root/languages/typescript/tests/pre-commit.bats
blob: 5519baaf50cd24b52211df41757e05da1fed9e5c (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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/usr/bin/env bats
#
# Tests for languages/typescript/githooks/pre-commit — the secret scan plus
# syntax/lint gate that runs on staged TS/JS files.
#
# The secret scan is the security-critical half and is language-independent, so
# it gets the same coverage here as in the bash bundle: a real key blocks, a
# clean diff passes, and the case-sensitivity split that keeps base64 blobs from
# false-positiving is exercised directly.
#
# Each test builds a throwaway git repo, stages content, and runs the hook from
# inside it — the hook reads `git diff --cached`, so a real index is required.

HOOK="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/githooks/pre-commit"

setup() {
  TEST_DIR="$(mktemp -d -t pre-commit-ts-bats.XXXXXX)"
  cd "$TEST_DIR" || exit 1
  git init -q .
  git config user.email t@example.com
  git config user.name Test
  # A base commit so `git diff --cached` has a parent to diff against.
  echo "seed" > seed.txt
  git add seed.txt
  git commit -qm seed
}

teardown() {
  cd / || true
  rm -rf "$TEST_DIR"
}

# ---- Normal ----------------------------------------------------------

@test "pre-commit(ts): a clean staged JS file passes (exit 0)" {
  printf 'export const f = (x) => x + 1;\n' > ok.js
  git add ok.js
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}

@test "pre-commit(ts): an empty staging area passes (exit 0)" {
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}

# ---- Error: the secret scan ------------------------------------------

@test "pre-commit(ts): an AWS key in a staged file blocks (exit 1)" {
  printf 'const KEY = "AKIAIOSFODNN7EXAMPLE";\n' > conf.ts
  git add conf.ts
  run bash "$HOOK"
  [ "$status" -eq 1 ]
  [[ "$output" == *"potential secret"* ]]
}

@test "pre-commit(ts): an sk- style token blocks (exit 1)" {
  printf 'const TOKEN = "sk-abcdefghijklmnopqrstuvwxyz0123";\n' > conf.ts
  git add conf.ts
  run bash "$HOOK"
  [ "$status" -eq 1 ]
}

@test "pre-commit(ts): a quoted api_key assignment blocks (exit 1)" {
  printf 'const api_key = "abcdefghijklmnopqrstuvwxyz";\n' > conf.ts
  git add conf.ts
  run bash "$HOOK"
  [ "$status" -eq 1 ]
}

@test "pre-commit(ts): a private-key header blocks (exit 1)" {
  printf 'const PEM = "-----BEGIN RSA PRIVATE KEY-----";\n' > conf.ts
  git add conf.ts
  run bash "$HOOK"
  [ "$status" -eq 1 ]
}

# ---- Boundary: the case-sensitivity split ----------------------------

@test "pre-commit(ts): a mixed-case base64 blob does NOT false-positive" {
  # The AWS pattern is uppercase-only by design. Under -i it would match any
  # 20-char mixed-case run, which random base64 hits often enough to block
  # real commits. This is the regression test for that split.
  printf 'const BLOB = "AKIAbcdefGHIJklmnOPqr0123456789abcdefGHIJ";\n' > data.ts
  git add data.ts
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}

@test "pre-commit(ts): a short quoted password value does NOT block" {
  # The keyword patterns require 16+ chars, so a placeholder stays quiet.
  printf 'const password = "short";\n' > conf.ts
  git add conf.ts
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}

@test "pre-commit(ts): a secret only in a REMOVED line does not block" {
  printf 'const KEY = "AKIAIOSFODNN7EXAMPLE";\n' > conf.ts
  git add conf.ts
  git commit -qm "add key"
  rm conf.ts
  git add -A
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}

# ---- Error: the syntax gate ------------------------------------------

@test "pre-commit(ts): a staged JS syntax error blocks (exit 1)" {
  command -v node >/dev/null 2>&1 || skip "node not installed"
  printf 'const x = ;\n' > bad.js
  git add bad.js
  run bash "$HOOK"
  [ "$status" -eq 1 ]
  [[ "$output" == *"syntax"* ]]
}

@test "pre-commit(ts): a staged TS syntax error blocks (exit 1)" {
  command -v tsc >/dev/null 2>&1 || skip "tsc not installed"
  printf 'export function f( {\n  return 1;\n}\n' > bad.ts
  git add bad.ts
  run bash "$HOOK"
  [ "$status" -eq 1 ]
}

@test "pre-commit(ts): valid TS-only syntax is NOT read as broken JS" {
  command -v tsc >/dev/null 2>&1 || skip "tsc not installed"
  # The regression guard for the node --check trap: `node --check` rejects
  # valid TypeScript, so using it on .ts would block every real commit.
  printf 'interface P { a: string }\nexport const p: P = { a: "x" };\n' > types.ts
  git add types.ts
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}

@test "pre-commit(ts): a TYPE error that parses does not block (out of scope)" {
  command -v tsc >/dev/null 2>&1 || skip "tsc not installed"
  printf 'const n: number = "nope";\nexport { n };\n' > typeerr.ts
  git add typeerr.ts
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}

@test "pre-commit(ts): a broken NON-TS/JS file does not trip the syntax gate" {
  printf 'this is (((not javascript\n' > notes.txt
  git add notes.txt
  run bash "$HOOK"
  [ "$status" -eq 0 ]
}