grep이란

파일 안에서 패턴에 맞는 줄을 찾아 출력하는 도구. 코드 감사의 주력 무기 — 소스에서 싱크(위험 함수)·소스(입력 진입점)·시크릿을 위치시키는 1차 도구

골격

1
grep [옵션] "패턴" [검색할 경로]

경로 안 주면 stdin 대기하며 멈춤 → 경로 필수

자주 쓰는 플래그

이 8개로 대부분 커버

1
2
3
4
5
6
7
8
-r      하위 폴더까지 재귀
-n      매칭 줄번호 표시 (파일:줄번호 로 점프)
-i      대소문자 무시
-E      확장 정규식 ( | ( ) { } + 를 특수문자로 인식)
-w      단어 단위 매칭 (exec 검색 시 execute 제외)
-l      매칭된 파일 이름만
-c      파일별 매칭 개수만
-o      매칭된 부분만 출력 (줄 전체 말고)

플래그 묶어쓰기 가능 → -r -n -E = -rnE

  • 값 받는 옵션(-A 3 등)은 묶음 맨 뒤에
  • 긴 옵션(--include)은 못 묶음

범위 좁히기

노이즈(node_modules·소스맵 등) 제거가 핵심

1
2
3
4
5
--include="*.ts"              특정 확장자만
--exclude-dir=node_modules    특정 폴더 제외
-A 3    매칭 줄 + 아래 3줄 (after)
-B 3    매칭 줄 + 위 3줄 (before)
-C 3    매칭 줄 + 위아래 3줄 (context)

정규식 최소 지식

-E와 함께

1
2
3
4
5
6
|      또는          exec|system
()     그룹          (get|post)Handler
.      임의 문자 1개
.*     임의 문자 0개+
\(     괄호 문자 자체 (특수의미 끄기 = 이스케이프)
^ $    줄 시작 / 끝

exec 대신 exec\( 로 검색하는 이유 = execute execution 같은 무관 단어 제거, 함수 호출부만 정밀 타격

실전 패턴 (코드 감사)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 싱크 사냥 (RCE 계열 위험 함수)
grep -rnE "exec\(|system|eval|spawn|child_process" src --include="*.js"

# 소스 사냥 (사용자 입력 진입점)
grep -rnE "req\.(query|body|params)|request\.args" .

# 서버 발신 요청 (SSRF 발판)
grep -rnE "fetch\(|axios|http\.request|curl|wget" . --include="*.ts"

# 하드코딩 시크릿
grep -rniE "password|secret|api_key|token" . --include="*.env*"

# 함수 호출부 추적 (reachability)
grep -rn "doRequest" .

# 매칭 줄 맥락까지 (앞뒤 검증 로직 같이 보기)
grep -rn -C 5 "exec\(" app

원칙 = grep 결과는 후보 목록일 뿐. 뜬 줄마다 “여기 들어가는 값 중 내가 조종하는 게 있나(taint)” 확인해야 진짜 싱크

낯선 코드일수록 좁게 찍지 말고, 이미 관찰한 조각(예: prisma.api)을 앵커로 넓게 긁은 뒤 결과를 눈으로 필터

zsh 따옴표 함정

zsh는 * 낀 인자를 grep에 넘기기 전에 자기가 먼저 확장 시도 → 매칭 없으면 no matches found 에러

1
2
--include=*.ts     → zsh가 * 확장 → 에러
--include="*.ts"   → 따옴표로 감싸 grep에 그대로 전달 (해결)

* 낀 옵션은 항상 따옴표

ripgrep (rg) 대안

현업 로컬 감사는 rg가 표준 — 훨씬 빠르고 .gitignore·node_modules 자동 무시, 기본이 재귀

1
2
rg "exec\(" -t ts        # -t ts = 타입스크립트만, --include 불필요
rg -C 5 "exec\("         # 맥락 포함

설치 = brew install ripgrep

grep을 버리지 않는 이유 = 침투한 원격 서버·제한 셸엔 rg 없음. grep·awk·sed·find는 어느 리눅스에나 있는 기본 생존 장비. 로컬은 rg, 원격은 grep