6.5 KiB
6.5 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| devsec-pre-commit-security | Pre-commit Security — Secret / Lint / 빠른 가드 | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Pre-commit Security
사고 나기 전에 잡기. Husky / lefthook / pre-commit framework + lint-staged + gitleaks. Secret commit 차단 + lint + format. CI 보다 빠른 feedback.
📖 핵심 개념
- Pre-commit: commit 전 hook 실행.
- Pre-push: push 전 (더 무거운 검사 가능).
- Lint-staged: 변경 파일만.
- Bypass: --no-verify (긴급).
💻 코드 패턴
Husky (Node 친화)
yarn add -D husky lint-staged
yarn husky init
// package.json
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,yaml,md}": ["prettier --write"]
}
}
# .husky/pre-commit
yarn lint-staged
yarn typecheck
Lefthook (Go, 빠름, multi-language)
# lefthook.yml
pre-commit:
parallel: true
commands:
lint:
glob: '*.{ts,tsx}'
run: yarn eslint --fix {staged_files}
stage_fixed: true
secrets:
run: gitleaks protect --staged --no-banner
format:
glob: '*.{ts,tsx,json,md}'
run: yarn prettier --write {staged_files}
stage_fixed: true
pre-push:
commands:
typecheck:
run: yarn tsc --noEmit
test:
run: yarn test --run
commit-msg:
commands:
conventional:
run: npx commitlint --edit {1}
lefthook install
pre-commit framework (Python, 다양 hook)
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: [--maxkb=500]
- id: check-merge-conflict
- id: detect-private-key
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
- repo: https://github.com/returntocorp/semgrep
rev: v1.45.0
hooks:
- id: semgrep
args: [--config=p/secrets, --error]
- repo: local
hooks:
- id: typecheck
name: TypeScript
entry: yarn tsc --noEmit
language: system
pass_filenames: false
pre-commit install
pre-commit run --all-files # 한 번 모든 파일
Secret detection (gitleaks)
# .gitleaks.toml — 자체 rule
[[rules]]
id = "company-api-key"
description = "Company internal API key"
regex = '''cmp_[a-zA-Z0-9]{32}'''
[allowlist]
paths = [
'''.*\.test\.ts$''',
'''fixtures/.*''',
]
gitleaks detect --source . --verbose
gitleaks protect --staged --verbose # pre-commit
Detect-secrets (Yelp)
# Baseline 생성 (current secrets)
detect-secrets scan --baseline .secrets.baseline
# Pre-commit
detect-secrets-hook --baseline .secrets.baseline $(git diff --cached --name-only)
→ Baseline 으로 false positive 관리.
Commit message — Conventional Commits
npm install -D @commitlint/cli @commitlint/config-conventional
// commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
# .husky/commit-msg
npx --no -- commitlint --edit $1
→ feat: xxx / fix: yyy 강제. semantic-release 와 결합.
TypeScript / lint 빠르게
# 변경 파일만 lint (lint-staged)
yarn lint-staged
# tsc — incremental
tsc --noEmit --incremental --tsBuildInfoFile .tsbuildinfo
Bypass (긴급, 사용 주의)
git commit --no-verify -m "WIP"
git push --no-verify
→ CI 가 second gate.
Force re-check (skip 한 후)
# CI
- name: Pre-commit
run: pre-commit run --all-files
→ Bypass 해도 PR 에서 잡힘.
Husky vs lefthook vs pre-commit
Husky:
+ Node 친화, 친숙
- Shell script
Lefthook:
+ 매우 빠름 (Go)
+ 병렬
+ Multi-language
pre-commit (Python):
+ 큰 hook ecosystem
+ 매우 강력 framework
- Python 의존
Skip in CI (이미 fixed)
- run: SKIP=eslint pre-commit run --all-files
Secret rotate after leak
# Force push 로 history 삭제 X — secret 가 git history 에 남음.
# 1. Secret 즉시 rotate (모든 곳)
# 2. .git history 안 secret 도 cleanup
git filter-repo --invert-paths --path file-with-secret # 강력
# 또는 BFG repo-cleaner
# 3. force push (조심) + 모든 사람 re-clone
Large file check
- repo: https://github.com/pre-commit/pre-commit-hooks
hooks:
- id: check-added-large-files
args: [--maxkb=500]
→ 큰 binary git 안 — Git LFS.
Private key check
- id: detect-private-key
→ -----BEGIN RSA PRIVATE KEY----- 자동 검출.
Branch naming
# .husky/pre-commit
branch=$(git rev-parse --abbrev-ref HEAD)
if ! [[ "$branch" =~ ^(main|feat|fix|chore)/ ]]; then
echo "Branch must start with feat/, fix/, or chore/"
exit 1
fi
Performance (큰 monorepo)
# Turborepo + pre-commit
yarn lint-staged # 변경 파일만
turbo run lint --filter='[HEAD^1]' # 변경 package 만
Onboarding 자동
# postinstall script
"scripts": {
"prepare": "husky install" # auto-install hook
}
→ npm install 시 hook 자동 setup.
🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| Node only | Husky + lint-staged |
| Multi-language / monorepo | Lefthook |
| Strong ecosystem | pre-commit framework |
| Secret detection | Gitleaks (강) / Detect-secrets (baseline) |
| Conventional commits | commitlint |
| 빠른 feedback | lint-staged + 작은 hook |
❌ 안티패턴
- 모든 hook 동기 + 느림: 사용자가 bypass.
- --no-verify 자주: 의미 잃음.
- 모든 file lint 매 commit: 큰 repo 느림. lint-staged.
- Secret 발견 후 history 안 cleanup: 영원 git history 에.
- Onboarding 수동 hook install: 새 dev 가 잊음. prepare script.
- CI 가 hook 안 검사: bypass = 통과. CI 가 second gate.
- Format / lint 가 commit 변경: stage_fixed.
🤖 LLM 활용 힌트
- Husky / lefthook + lint-staged + gitleaks 가 baseline.
- Conventional commit + commitlint.
- Bypass 가능 but CI 가 검사.