aboutsummaryrefslogtreecommitdiff
path: root/languages/typescript/tests/pre-commit.bats
diff options
context:
space:
mode:
Diffstat (limited to 'languages/typescript/tests/pre-commit.bats')
-rw-r--r--languages/typescript/tests/pre-commit.bats150
1 files changed, 150 insertions, 0 deletions
diff --git a/languages/typescript/tests/pre-commit.bats b/languages/typescript/tests/pre-commit.bats
new file mode 100644
index 0000000..5519baa
--- /dev/null
+++ b/languages/typescript/tests/pre-commit.bats
@@ -0,0 +1,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 ]
+}