Files
2nd/10_Wiki/Topics/Coding/DevSec_Pre_Commit_Security.md
T
2026-05-09 21:08:02 +09:00

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
devsecops
pre-commit
hook
vibe-coding
language applicable_to
Various
DevOps
pre-commit
husky
lefthook
lint-staged
gitleaks
secret detection

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 가 검사.

🔗 관련 문서