GitHub 트렌딩 딥다이브 · 2026-06-23

maskara 딥다이브
— 코딩 에이전트 로그에 새어든 비밀키를 찾아 가려주는 오프라인 CLI

mrgoonie/maskara. Claude Code·Codex·Cursor·Gemini CLI 같은 코딩 에이전트가 디스크에 남긴 대화 로그를 훑어, 거기 섞여 들어간 OpenAI·Anthropic·GitHub·AWS 토큰, JWT, 개인키, DB 접속 주소 같은 비밀값을 찾아내는 작은 Go 명령줄 도구다. 찾은 비밀은 전체를 보여주지 않고 가린 미리보기 + SHA-256 지문만 리포트(마크다운·JSON)로 남기고, 원하면 로컬 사본을 *.maskara.bak 백업과 함께 가림 처리(redact)한다. 핵심은 철저히 오프라인 — 발견한 내용을 어디로도 보내지 않는다. 이름은 mascara(마스카라) + mask(가리다)의 합성어. (저장소: mrgoonie/maskara · 언어 Go · ★낮음(초기 v0.1단계) · 라이선스 MIT · TrendShift 라이브 멘션 등재)

목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석 (비밀키가 가려지기까지)
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

"AI한테 시킨 작업 기록에 비밀번호가 적혀 있진 않은지, 내 컴퓨터 안에서만 조용히 점검한다."

한 줄로

maskara는 코딩 에이전트가 남긴 대화 로그에서 비밀키·토큰 같은 민감 정보를 찾아 가려주는 오프라인 명령줄 도구다.

요즘 우리는 Claude Code, Codex, Cursor 같은 AI 코딩 에이전트에게 일을 시키면서 "이 API 키로 테스트해줘", ".env 파일 좀 봐줘" 같은 말을 무심코 한다. 그 대화는 전부 내 컴퓨터의 로그 파일로 차곡차곡 쌓인다. maskara는 그 로그 더미를 훑어 "여기 진짜 비밀번호가 적혀 있다"를 짚어주고, 그 자리에 안전한 표식을 덮어쓴다. 마치 마스카라가 번지면 닦아내듯, 새어 나간 비밀을 가려준다.

용어
시크릿 (secret)
프로그램이 외부 서비스에 접속할 때 쓰는 비밀 자격증명을 통칭한다 — API 키, 액세스 토큰, 비밀번호, 개인키(private key), DB 접속 주소 등. 집 열쇠와 같아서, 한 번 남의 손에 들어가면 그 키로 잠긴 모든 문이 열린다. 코드에 직접 적지 말고 환경변수·비밀 저장소에 두라고 하는 이유가 이것이다.
용어
리댁션 (redaction, 가림 처리)
문서에서 민감한 부분을 검은 띠로 덮어 가리는 행위. 정부 문서 공개 때 이름·주소를 까맣게 칠하는 그 작업이다. maskara는 로그 파일 속 비밀값을 [MASKARA_REDACTED:openai-api-key] 같은 표식으로 바꿔치기해, 원래 값을 파일에서 지워 버린다(원본은 백업).

구체적으로 maskara는 Go로 짜인 단일 바이너리 CLI다. 아직 초기 단계(v0.1 수준)의 작은 프로젝트지만 구조가 깔끔하다. 인자 없이 그냥 maskara라고 치면 "스캔 → (백업 후) 가림 → 리포트 작성"의 전체 워크플로가 한 번에 돌고, scan/report/guardrails 하위 명령으로 단계를 나눠 쓸 수도 있다. 이름 그대로 마스카라(mascara) + 마스크(mask) — "비밀을 흘려 울지 않으려면 가려라"는 농담 섞인 작명이다.

이 문서가 주목하는 건 도구의 사용법만이 아니라 그 안의 설계다. "정규식으로 비밀을 어떻게 잡아내는가", "전체 값을 안 보여주면서 어떻게 같은 비밀임을 증명하는가(지문)", "남의 파일을 고치는 위험한 작업을 어떻게 안전하게 하는가" — 보안 도구를 처음 만들어 보려는 개발자에게 좋은 교본이다.

2왜 주목받는가

"비밀은 git 저장소만 새는 게 아니다 — AI와의 대화 기록이 새로운 구멍이다."

먼저, 왜 에이전트 로그에 비밀이 새는가

전통적으로 "비밀 유출"은 git 커밋에 .env를 실수로 넣는 것을 뜻했다. 그래서 git-secrets, ggshield 같은 도구가 커밋 전에 막아준다. 그런데 AI 코딩 에이전트 시대에는 완전히 새로운 누출 경로가 생겼다. 사용자가 에이전트에게 "이 토큰으로 API 호출해줘", "왜 인증이 안 되는지 봐줘" 하며 비밀값을 채팅에 그대로 붙여넣고, 에이전트도 디버깅하다 키를 출력한다. 이 모든 대화는 ~/.claude/projects, ~/.codex/sessions 같은 곳에 JSON·JSONL 로그로 영구 저장된다. git에는 안 올라가니 기존 도구는 못 잡지만, 백업·동기화·실수 공유로 새어 나갈 수 있는 진짜 위험이다.

위험
"git엔 안 올렸는데 왜 키가 털렸지?"

로컬 에이전트 로그는 보통 평문(plain text)이다. 클라우드 백업(iCloud·OneDrive)에 딸려 올라가거나, 디버깅 로그를 통째로 이슈에 첨부하거나, 화면 공유 중 노출되는 식으로 새어 나간다. 게다가 한 번 대화에 들어간 키는 이미 모델 제공사 서버에도 전송된 상태다. 그래서 maskara의 철학은 "발견 = 이미 노출됐다고 가정하고 즉시 폐기·재발급(rotate)하라"는 것 — 로컬 가림은 추가 확산을 막을 뿐, 만능 방패가 아니다.

maskara가 사람들 눈에 띈 이유는 이 틈새를 정확히 겨냥했기 때문이다. 같은 "시크릿 스캐너"라도 대상이 다르다.

경쟁 구도 한눈에

항목maskaraggshield (GitGuardian)git-secrets
스캔 대상에이전트 세션 로그 (~/.claude 등)git 저장소·커밋·diffgit 커밋(커밋 직전 훅)
네트워크완전 오프라인클라우드 API로 검증오프라인
가림(redact)로컬 사본을 직접 가림 + 백업탐지·차단 위주커밋 차단 위주
예방 장치가드레일(에이전트 훅·지침) 설치pre-commit 훅git 훅
규모초기 v0.1 소형 Go CLI성숙한 상용/OSS성숙한 OSS
해결
"오프라인 + 에이전트 전용"이라는 빈칸을 채웠다

기존 도구가 git이라는 문을 지켰다면, maskara는 에이전트 로그라는 새 문을 지킨다. 그리고 비밀을 다루는 도구가 그 비밀을 어디로도 보내지 않는다는 점(완전 오프라인)이 핵심 신뢰 포인트다 — 시크릿 스캐너를 쓰려다 오히려 시크릿을 클라우드로 보내는 모순을 피한다.

요약하면 주목 포인트는 셋이다. ① 새로운 위협 모델(에이전트 로그 누출)을 정조준했고, ② 탐지에서 그치지 않고 가림·백업·예방(가드레일)까지 한 바퀴를 돌며, ③ Go 단일 바이너리라 설치가 가볍고 오프라인이다. 다만 아직 작고 어린 프로젝트라, 탐지 규칙 수나 성숙도는 상용 도구에 못 미친다는 점은 솔직히 감안해야 한다.

3기술 스택 전체 지도

"Go 표준 라이브러리만으로 — 외부 의존성이 거의 없는, 의도된 미니멀 설계."

maskara의 go.mod를 열면 놀랍도록 비어 있다. 모듈 선언과 Go 1.25 한 줄뿐, 외부 라이브러리 의존성이 사실상 0개다. CLI 파싱도 cobra 같은 인기 프레임워크 대신 표준 라이브러리 flag 패키지로 직접 짰다. 보안 도구일수록 의존성을 줄여 공급망 위험(남의 코드에 숨은 악성)을 없애려는 의도된 선택이다.

용어
표준 라이브러리 (standard library)
언어를 설치하면 기본으로 딸려 오는 검증된 코드 모음. Go는 정규식(regexp), 해시(crypto/sha256), 파일 탐색(path/filepath), CLI 인자(flag), JSON까지 표준 라이브러리만으로 풍부하다. 외부 패키지를 끌어오지 않으면 빌드가 빠르고, "남의 코드를 믿어야 하는 위험"이 줄어든다. 보안 도구가 특히 선호하는 방식.
레이어기술역할
언어Go 1.25전체 구현. CGO_ENABLED=0로 빌드해 의존성 없는 단일 정적 바이너리 생성
CLI 파싱표준 flag 패키지서브커맨드(scan·report·guardrails)와 플래그(-a·--root·-o·--json) 직접 구현. cobra 미사용
탐지 엔진표준 regexpOpenAI·Anthropic·GitHub·AWS·Slack·Stripe·JWT·개인키·DB URL·env 대입 등 11종 패턴
지문표준 crypto/sha256비밀값을 해시해 12자 지문 생성 — 전체 값 없이 동일성 비교
가드레일 자산embed.FS지침·스킬·훅 파일을 바이너리에 내장(//go:embed)해 별도 파일 없이 배포
릴리스GoReleaser v2linux·darwin·windows × amd64·arm64 = 6종 바이너리를 한 번에 빌드·패키징
CIGitHub Actionsubuntu·macos·windows 3-OS 매트릭스에서 go test ./... + 빌드. 푸시마다 semantic 릴리스
용어
GoReleaser
Go 프로그램을 여러 운영체제·CPU용 바이너리로 한 번에 빌드·압축·배포해 주는 도구. 설정 파일(.goreleaser.yaml) 하나로 리눅스/맥/윈도우 × amd64/arm64를 동시에 만들어 GitHub Releases에 올린다. "한 번 누르면 전 플랫폼 설치본이 나오는" 자판기. maskara는 conventional commits에서 버전을 자동으로 정하는 semantic-release까지 붙여 두었다.

4아키텍처 심화 분석 — 비밀키가 가려지기까지

"내가 maskara 한 줄을 치면, Claude 로그 속 sk-... 토큰 하나가 발견되어 가려지기까지 안에서 무슨 일이 벌어지나."

maskara는 작지만 내부 패키지(internal/)가 책임별로 깔끔히 갈려 있다. 각 패키지가 컨베이어 벨트의 한 공정처럼 결과를 다음으로 넘긴다. 먼저 기본 워크플로(인자 없는 maskara) 전체 그림을 보자.

┌──────────────────────────────────────────────────────────────┐ │ $ maskara (cmd/maskara/main.go → internal/cli) │ └───────────────┬──────────────────────────────────────────────┘ │ ① 명령/플래그 해석 (flag 패키지) ▼ internal/cli scan │ report │ guardrails │ (기본=전부) │ ② 에이전트 타깃 해석 ▼ internal/agents --agent / --root → [{agent, root}] 목록 │ (auto=존재하는 로그만, all=전부) ▼ internal/scanner 각 root를 walk → 바이너리·대용량·무관 확장자 skip │ ③ 텍스트 파일만 골라 읽기 ▼ internal/detect 11종 정규식 매칭 → 값 추출 │ ④ 마스킹 finding 생성 ▼ Finding{ preview: "sk-1...아bc", sha256: 지문, redaction: 표식 } │ ⑤ 리포트로 ▼ internal/report Markdown 표 또는 JSON 출력 (가린 값 + 지문만) │ │ ⑥ 기본 워크플로에서만 (scan/report는 건너뜀) ▼ internal/redact 파일을 *.maskara.bak 로 백업 → 원본을 표식으로 덮어씀 │ (별개 경로) internal/guardrails 에이전트 지침·프라이버시 스킬·훅 설치
용어
탐지 (detection) · 정규식 (regex)
탐지는 "수상한 패턴을 찾아내는 일". maskara는 정규식(regular expression)으로 한다 — 정규식은 "이런 모양의 문자열"을 기술하는 작은 패턴 언어다. 예로 OpenAI 키는 늘 sk-로 시작해 영숫자가 20자 이상 이어진다. 이 모양을 정규식 sk-...{20,}로 적어 두면, 로그를 훑으며 그 모양에 맞는 부분을 전부 집어낸다. "특정 양식의 종이만 골라내는 분류기"라고 보면 된다.

핵심 설계 1 — 탐지 규칙 카탈로그: 한 곳에 모은 시크릿의 '몽타주'

maskara의 심장은 internal/detect규칙 카탈로그다. 비밀의 종류마다 "이런 모양이다"라는 정규식 + 심각도(critical/high/medium)를 한 줄씩 적어 둔 표다. 실제 규칙 몇 개를 보자(요점만).

// internal/detect/detect.go — DefaultRules() (발췌, 단순화)
{ID: "openai-api-key",  Sev: critical, Expr: `\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b`},
{ID: "anthropic-api-key",Sev: critical, Expr: `\bsk-ant-[A-Za-z0-9_-]{20,}\b`},
{ID: "github-token",    Sev: critical, Expr: `\bgh[pousr]_[A-Za-z0-9_]{36,}\b ...`},
{ID: "aws-access-key",  Sev: high,     Expr: `\b(?:AKIA|ASIA)[0-9A-Z]{16}\b`},
{ID: "jwt",             Sev: high,     Expr: `\beyJ...\....\....\b`},
{ID: "private-key",     Sev: critical, Expr: `-----BEGIN ... PRIVATE KEY----- ...`},
{ID: "database-url",    Sev: critical, Expr: `postgres|mysql|mongodb|redis ://...`},
{ID: "env-secret", Sev: medium, Expr: `(api_key|secret|token|password)=...`, ValueGroup: 1}

읽는 법은 간단하다. 각 규칙은 고유 ID + 심각도 + 정규식 세 가지가 핵심이다. 대부분은 매칭된 전체를 비밀로 보지만, 마지막 env-secret처럼 API_KEY=실제값 형태는 등호 뒤의 값만 비밀이므로 ValueGroup으로 "정규식 안 1번 괄호 그룹만 잡으라"고 지정한다. 매칭 뒤에는 겹치는 결과를 정리하고(dedupeOverlaps), 이미 가려진 표식(MASKARA_REDACTED)은 다시 잡지 않도록 거른다.

비유

정규식 카탈로그는 지명수배 몽타주 모음집이다. "이마에 sk- 문신, 키 20자 이상" 같은 인상착의를 종류별로 적어 두면, 순찰(스캔) 중 그 인상에 맞는 인물(문자열)을 전부 검거한다. 몽타주가 11장이라도 마을 전체(로그)를 한 번에 훑을 수 있고, 새 유형이 나타나면 몽타주 한 장만 추가하면 된다 — 이게 데이터(규칙)와 실행(스캐너)을 가른 효과다.

핵심 설계 2 — 마스킹 + 지문: 비밀을 안 보여주고 증명하기

비밀을 찾았다고 리포트에 통째로 적으면, 리포트 자체가 또 하나의 유출이 된다. maskara는 이를 두 가지로 푼다.

// Mask: 앞 4 + ... + 뒤 4 만 남긴다 (8자 이하면 그냥 ***)
Mask("sk-proj-abcdEFGH1234wxyz")  →  "sk-p...wxyz"

// 지문: 전체 값을 SHA-256 해시 → 앞 12자만 표시
sha256("sk-proj-abcdEFGH1234wxyz") → "9f86d081884c"...
용어
지문 (fingerprint, SHA-256)
SHA-256은 어떤 데이터든 항상 같은 길이의 고정 코드로 바꾸는 일방향 해시 함수다. 같은 입력 → 항상 같은 출력이지만, 출력에서 입력을 되돌릴 수는 없다. 그래서 비밀 전체를 안 보여주고도 "두 로그의 키가 같은 키인가?"를 지문끼리 대조해 확인할 수 있다. 지문을 남겨 두고 대조하는 것 — 범인의 얼굴은 안 보여주되 지문은 보관하는 수사와 똑같다.

덕분에 리포트는 "어느 파일·몇 번째 줄에, 어떤 종류의 비밀이, 가려진 미리보기와 지문은 무엇"까지만 담는다. 사람은 그것만으로 "아, 그 Stripe 키구나"를 알아채 폐기할 수 있고, 리포트가 새어도 진짜 비밀은 안 새는 것이다.

핵심 설계 3 — 안전한 파일 가림(redact): 망치기 전에 백업

남의 파일을 프로그램이 직접 수정하는 건 위험하다. internal/redact는 여러 안전장치를 겹쳐 둔다. ① 스캔 대상(targets) 바깥 경로는 거부, ② 심볼릭 링크는 거부, ③ 무엇보다 수정 전에 원본을 *.maskara.bak으로 백업한 뒤에야 덮어쓴다. 비밀 위치(start~end)를 뒤에서부터 잘라내 표식으로 바꾸는데(앞에서 바꾸면 뒤 위치가 어긋나므로), JSON처럼 구조가 깨지면 안 되는 파일은 구조 보존 방식으로 다시 시도한다.

흐름 한 줄기 — sk- 토큰 하나의 일생

// "Claude 로그에 들어간 OpenAI 키 하나"가 가려지기까지 (개념 단순화)
① $ maskara            → cli가 "기본 워크플로"로 진입
② agents.Resolve("auto") → ~/.claude/projects 가 존재 → 스캔 타깃 확정
③ scanner가 walk → conversation.jsonl (텍스트·25MB 이하) 통과, .png는 skip
④ detect.Find: 줄 안에서 sk-proj-abcd... 가 openai 규칙에 매칭
⑤ Finding 생성: preview "sk-p...wxyz", sha256 "9f86...", line=42
⑥ report.WriteOutput: 마크다운 표에 가린 미리보기+지문 기록 (값 없음)
⑦ redact.Apply: conversation.jsonl 을 .maskara.bak 로 백업
⑧ 원본의 그 자리를 [MASKARA_REDACTED:openai-api-key] 로 덮어씀
⑨ 종료 코드 1 반환 (findings 있음) — CI 등에서 "샜다"를 감지 가능
용어
종료 코드 (exit code)
프로그램이 끝날 때 운영체제에 남기는 한 자리 신호. maskara는 0 = 깨끗함/성공, 1 = 비밀 발견, 2 = 실행 오류 셋을 쓴다. 이게 중요한 이유 — CI 파이프라인이나 스크립트가 이 숫자를 보고 "1이면 빌드를 멈춰라" 식으로 자동 판단할 수 있기 때문이다. 신호등처럼, 사람이 매번 읽지 않아도 기계가 보고 멈춘다.

여기서 배울 핵심은 "읽기(스캔)·보고(리포트)·고치기(가림)가 깔끔히 분리"돼 있다는 점이다. 그래서 scan은 안전하게 들여다보기만 하고, report는 기록만 남기며, 위험한 파일 수정(가림)은 전체 워크플로일 때만 일어난다. 사용자가 "얼마나 위험한 일까지 허용할지"를 명령으로 고르게 한 설계다.

5디렉토리 구조 해부

"엔트리포인트 한 줄(cmd) + 책임별 internal 패키지 한 줌 — 작아서 하루면 다 읽는다."

maskara는 Go의 관례적 구조를 따른다. 실행 진입점은 cmd/에, 실제 로직은 외부에서 import 못 하는 internal/에 두어 "공개 API가 아니라 도구 내부 부품"임을 분명히 한다.

maskara/ ├── cmd/maskara/main.go 🚪 진입점 (딱 한 줄: cli.Execute(...) 호출) ├── internal/ 🔒 외부 import 불가 — 도구 내부 부품들 │ ├── cli/ 🎛️ 명령·플래그 해석, 워크플로 조립 (cli.go) │ │ └── help.go 서브커맨드별 도움말 텍스트 │ ├── agents/ 🎯 지원 에이전트 카탈로그 (17종) │ │ ├── agents.go Resolve/Normalize: 별칭→정규명, 타깃 경로 산출 │ │ └── catalog.go 에이전트별 기본 로그 경로(OS별 분기) │ ├── scanner/ 🚶 파일 walk + skip 규칙 (바이너리·대용량·확장자) │ ├── detect/ 🔍 정규식 규칙 11종 + 마스킹 + SHA-256 지문 │ ├── redact/ ✂️ 백업 후 가림, 구조(JSON) 보존, 경로 안전검사 │ │ ├── redact.go 원시 바이트 치환 + 안전장치 │ │ ├── files.go 백업 이름·원자적 쓰기 │ │ └── structured.go JSON 등 구조 보존 가림 │ ├── guardrails/ 🛡️ 에이전트 지침·스킬·훅 설치 (embed 자산) │ │ └── assets/ 내장될 지침/스킬/훅 템플릿 │ └── version/ 🏷️ 빌드 시 주입되는 버전·커밋·날짜 ├── docs/ 📚 agent-support-reference 등 설계 문서 ├── plans/ 🗂️ 개발 계획 문서(부트스트랩·에이전트 확장) ├── .goreleaser.yaml 📦 멀티플랫폼 릴리스 설정 ├── .github/workflows/ ⚙️ ci.yml(3-OS 테스트) · release.yml(semantic) └── go.mod 📋 module + Go 1.25 (외부 의존성 거의 0)
경로역할
cmd/maskara/실행 진입점. main()os.Argscli.Execute에 넘기고 종료 코드로 끝낸다(로직 없음)
internal/cli/두뇌 격 조립부. 서브커맨드 분기, 플래그 파싱, "scan→redact→report" 순서 엮기, 종료 코드 결정
internal/agents/지원 에이전트 17종의 정규명·별칭·기본 로그 경로. auto(존재하는 것만)·all 해석
internal/scanner/디렉토리를 재귀 탐색(walk)하며 .git/node_modules 등 skip, 바이너리·25MB 초과·무관 확장자 제외
internal/detect/핵심 탐지 엔진. 정규식 카탈로그·마스킹·지문·중복 정리
internal/redact/가림 처리 전담. 경로 안전검사·심링크 거부·백업·원자적 쓰기·JSON 구조 보존
internal/guardrails/예방책 설치기. embed.FS로 내장한 지침·스킬·훅을 에이전트별 위치에 배치(기존 파일 백업)
internal/version/버전 문자열. 빌드 시 -ldflags -X로 값 주입(소스엔 "dev"만)
용어
internal/ 패키지의 의미
Go에는 특별 약속이 있다 — internal/ 폴더 안의 코드는 그 프로젝트 바깥에서 import할 수 없다. 즉 "이건 우리 내부 부품일 뿐, 라이브러리로 가져다 쓰라고 만든 게 아니다"를 컴파일러가 강제한다. 식당의 '관계자 외 출입금지' 주방 같은 것. 덕분에 maskara는 내부 구조를 자유롭게 바꿔도 외부 사용자와의 호환성을 신경 쓸 필요가 없다.

6학습 포인트 (기술별)

"작은 레포 하나에 'Go CLI · 정규식 보안 · 안전한 파일 IO · 릴리스 자동화'가 압축돼 있다."

maskara는 코드량이 적어 처음부터 끝까지 읽어볼 수 있는 드문 실전 예제다. 분야별로 무엇을 배울지 + 작게 실습할 거리를 정리했다.

기술 ①

Go CLI 설계 — 표준 flag로 서브커맨드 만들기

배울 것: cobra 같은 프레임워크 없이 flag 패키지만으로 scan/report/guardrails 서브커맨드와 단·장 플래그(-a/--agent)를 짜는 법. FlagSet을 명령마다 따로 만들고, 입력 검증 후 종료 코드로 결과를 알리는 패턴.

실습: cli.goExecuteargs[0]로 분기하는 부분을 따라 읽고, 새 서브커맨드 하나(예: maskara rules = 규칙 목록 출력)를 상상해 시그니처를 그려 보기.

기술 ②

정규식으로 시크릿 탐지 — 오탐과 정탐 사이

배울 것: 비밀의 "모양"을 정규식으로 기술하는 법(접두사·길이·문자집합), 캡처 그룹으로 값만 뽑기(ValueGroup), 그리고 겹친 결과 정리. 핵심은 오탐(false positive)과 미탐의 균형 — 너무 느슨하면 평범한 문자열을 비밀로 오인하고, 너무 빡빡하면 진짜를 놓친다.

실습: detect.go의 규칙 하나를 골라 정규식을 한 조각씩 해부(예: \bsk-...{20,}\b\b가 왜 필요한지). 그다음 새 패턴 하나(예: Telegram 봇 토큰)를 상상해 정규식을 적어 보기.

기술 ③

안전한 파일시스템 워크 — skip과 백업

배울 것: filepath.WalkDir로 디렉토리 재귀 탐색, .git·node_modules 같은 폴더 건너뛰기(SkipDir), 바이너리 판별(앞부분에 0바이트가 있나), 용량 상한. 그리고 파일을 고치기 전 반드시 백업하고, 경로가 대상 밖이면 거부하는 방어적 코딩.

실습: scanner.golooksLikeSessionText·isBinary를 읽고 "왜 .png는 스캔 안 하나"를 설명해 보기. redact.go왜 뒤에서부터 치환하는지 종이에 위치 인덱스를 그려 확인.

기술 ④

GoReleaser + 멀티플랫폼 릴리스 자동화

배울 것: 하나의 Go 코드에서 6종 바이너리를 뽑는 크로스 컴파일(goos×goarch), -ldflags -X로 빌드 시 버전 주입, 그리고 conventional commits로 버전을 자동 결정하는 semantic-release(feat=minor, breaking=major).

실습: .goreleaser.yamlgoos/goarch 목록과 ldflags를 보고, "왜 CGO_ENABLED=0인가"(정적 바이너리·크로스컴파일 쉬움)를 한 줄로 정리. release.yml의 채널(dev=beta, main=stable) 분기도 훑기.

기술 ⑤

에이전트 가드레일 — 예방을 코드로 설치

배울 것: 탐지·정리에서 끝내지 않고 재발 방지까지 가는 발상. guardrails는 에이전트별 지침 파일·작은 프라이버시 스킬·훅 스크립트(비밀을 출력할 법한 명령을 자제시키는)를 설치한다. 자산을 //go:embed로 바이너리에 내장해 별도 파일 없이 배포하는 기법도 배운다.

실습: guardrails 명령을 --dry-run으로 돌려 "어디에 무엇을 깔려는지"만 확인. resolveAgents설치된 에이전트만 자동 감지하는 로직을 따라 읽기.

7시스템 요구사항

"순수 소프트웨어 — 설치 바이너리는 의존성 0, 빌드만 Go가 필요하다."

maskara는 단일 정적 바이너리라 설치해서 쓰는 데는 아무 런타임도 필요 없다. 소스에서 빌드하거나 개발하려는 경우에만 Go가 필요하다.

구분요구사항
설치(바이너리)의존성 없음. GitHub Releases에서 OS·아키텍처에 맞는 바이너리를 받아 PATH에 두면 끝. CGO_ENABLED=0 정적 빌드라 추가 라이브러리 불필요
빌드/개발Go 1.25 이상. go install github.com/mrgoonie/maskara/cmd/maskara@latest 또는 클론 후 go build ./cmd/maskara
지원 OSLinux · macOS · Windows (각 amd64·arm64). CI가 세 OS 모두에서 테스트
스캔 대상로컬 에이전트 로그 디렉토리(아래 표). 읽기·(가림 시) 쓰기 권한 필요. 네트워크 불필요

스캔 대상 에이전트 로그 경로(일부)

maskara는 17종 에이전트의 기본 로그 위치를 카탈로그로 알고 있다. auto실제로 존재하는 디렉토리만 골라 스캔하고, --root로 임의 경로를 지정할 수도 있다.

에이전트기본 스캔 위치
Claude Code~/.claude/projects
Codex~/.codex/sessions
Cursor~/.cursor + OS별 Cursor 설정/데이터 디렉토리
Gemini CLI~/.gemini + OS별 설정/데이터 디렉토리
GitHub Copilot~/.github-copilot + OS별 설정/데이터 디렉토리
OpenCode 외 다수홈 dotdir + OS별 config/data 디렉토리 (총 17종: Antigravity·Kimi·Droid·Kilo·Kiro·Pi·Qoder·Qwen·Trae·Hermes·OpenClaw 등)
# 설치 — Go만 있으면 한 줄
go install github.com/mrgoonie/maskara/cmd/maskara@latest

# 기본 워크플로: 스캔 + (백업 후) 가림 + 리포트
maskara

# 안전하게 들여다보기만 (가림 안 함)
maskara scan                 # 전체 자동 감지
maskara scan -a codex        # Codex 로그만

# 리포트 파일로 — JSON 출력
maskara report --json -o ./maskara-report.json

# 예방책(가드레일) — 무엇을 깔지 미리보기만
maskara guardrails --dry-run

핵심은 인자 없는 maskara가 "가림까지" 하는 강한 명령이라는 점 — 처음엔 maskara scan으로 읽기만 해보고, 결과를 확인한 뒤 가림을 결정하는 것이 안전하다. 모든 동작은 네트워크 없이 로컬에서만 일어난다.

8직접 해볼 수 있는 실습 과제

"읽기만 하면 안 남는다 — 난이도별로 손을 움직여 보자. (가림은 꼭 백업/사본에서!)"

난이도 ★☆☆ — 입문

1. maskara scan 한 번 돌려 리포트 읽기

설치 후 maskara scan만 실행해(가림 없음) 어떤 파일에서 무엇이 잡히는지 본다. 진짜 키가 없더라도, 테스트용 폴더에 API_KEY=sk-test-1234...wxyz 같은 더미 한 줄을 적고 --root로 그 폴더를 지정하면 탐지·마스킹·지문이 어떻게 출력되는지 안전하게 체감한다.

난이도 ★☆☆ — 입문

2. --json / --output 으로 리포트 형식 바꿔 보기

같은 스캔을 maskara report --json -o ./out.json으로 돌려 마크다운과 JSON 리포트를 비교한다. JSON에는 줄·열·심각도·지문이 구조화돼 들어가 "다른 도구가 이 결과를 받아 처리하기 좋은" 형태임을 확인. 기계가 읽는 출력과 사람이 읽는 출력의 차이를 느낀다.

난이도 ★★☆ — 초중급

3. 탐지 정규식 1개 읽고 새 패턴 상상하기

internal/detect/detect.go의 규칙 중 하나(예: slack-token)를 골라 정규식을 한 조각씩 해석한다. 그다음 공개 토큰 양식(예: SendGrid SG. 키, Telegram 봇 토큰)을 찾아보고 Rule 한 줄을 직접 적어 본다. 심각도를 어디에 둘지도 생각해 보기.

난이도 ★★☆ — 초중급

4. guardrails --dry-run 으로 예방책 들여다보기

maskara guardrails --dry-run을 돌려 "어떤 에이전트에, 어떤 파일(지침·스킬·훅)을, 어디에 설치하려는지" 계획만 출력해 본다. 실제로 깔지 않으니 안전하다. 출력 경로를 보고 internal/guardrails/assets/의 템플릿이 무엇인지 소스와 대조하며, "재발 방지를 코드로 설치한다"는 발상을 이해한다.

난이도 ★★★ — 고급

5. 작은 Go 서브커맨드 추가 PR 구상

cli.go의 분기 구조를 따라가, 새 읽기 전용 서브커맨드(예: maskara rules = 현재 탐지 규칙 ID·심각도 목록 출력)를 설계해 본다. detect.DefaultRules()를 호출해 표로 찍는 정도면 충분하다. 테스트(*_test.go 패턴)와 CONTRIBUTING.md를 읽고 conventional commit 형식까지 맞춰 첫 PR 흐름을 경험.

9관련 기술 심화 학습 로드맵

"Go 기초에서 '나만의 시크릿 스캐너'까지 — 5주 코스 예시."

아래는 "Go를 조금 아는 사람이 maskara 코드를 읽고 비슷한 보안 CLI를 만들 수 있는 수준"까지의 한 가지 길이다. 작은 레포라 속도를 붙이기 좋다.

주차주제무엇을 / 왜 + 학습 자료
1주Go + CLI 기초flag 패키지, os.Args, 종료 코드. Tour of Go + maskara cli.go를 교본으로 서브커맨드 패턴 익히기
2주정규식 마스터캡처 그룹·경계(\b)·게으른 매칭. regex101.com에서 detect.go의 규칙들을 직접 돌려 보기
3주파일시스템 & 해시filepath.WalkDir, 바이너리 판별, crypto/sha256. "백업 후 수정" 같은 방어적 IO 패턴 연습
4주시크릿 탐지 도메인각 제공사 토큰 양식(OpenAI·GitHub·AWS 문서), 오탐/미탐 트레이드오프. gitleaks 규칙셋과 비교
5주릴리스 자동화GoReleaser로 크로스컴파일, GitHub Actions 매트릭스, conventional commits → semantic 버전. maskara CI 따라 만들기
비유

maskara 코드베이스는 잘 정리된 1인 공방 같다. 거대한 공장(상용 도구)을 통째로 이해하려다 지치는 대신, "작업대(패키지) 하나씩"—탐지대(detect) → 가림대(redact) → 포장대(goreleaser)—을 차례로 들여다보면, 보안 CLI 한 채가 어떻게 굴러가는지 전체 그림이 손에 잡힌다. 작은 레포의 가장 큰 장점이다.

10핵심 키워드 사전

"이 문서와 maskara 코드에서 거듭 나오는 용어들 — 한 곳에 모아 풀었다."

용어
시크릿 / 크리덴셜 (secret / credential)
외부 서비스 접속에 쓰는 비밀 자격증명(API 키·토큰·비밀번호·개인키 등). 집 열쇠처럼, 새면 그 키로 잠긴 모든 것이 열린다. maskara가 찾아내는 대상.
용어
리댁션 (redaction, 가림 처리)
민감한 부분을 표식으로 덮어 가리는 일(문서의 검은 띠). maskara는 로그 속 비밀값을 [MASKARA_REDACTED:규칙ID]로 바꿔 파일에서 지운다(원본은 .maskara.bak 백업).
용어
지문 (fingerprint, SHA-256)
비밀 전체를 일방향 해시로 바꾼 고정 코드. 같은 입력→같은 지문이라 "두 로그의 키가 같은지" 대조할 수 있지만, 지문에서 원래 값은 못 되돌린다. 얼굴은 가리고 지문만 보관하는 방식.
용어
오탐 (false positive)
비밀이 아닌데 비밀로 잘못 잡는 것. 정규식이 너무 느슨하면 평범한 문자열까지 걸린다. 반대로 너무 빡빡하면 진짜를 놓친다(미탐). 시크릿 탐지의 영원한 줄타기.
용어
가드레일 / 훅 (guardrail / hook)
가드레일은 사고를 막는 난간. maskara의 guardrails는 에이전트에 지침·스킬·을 설치한다. 은 "특정 시점에 자동 실행되는 작은 스크립트" — 비밀을 출력할 법한 명령을 미리 자제시켜 재발을 막는다.
용어
정규식 (regex)
"이런 모양의 문자열"을 기술하는 패턴 언어. sk-로 시작해 영숫자 20자 이상 같은 인상착의를 적어 두면, 텍스트에서 그 모양을 전부 찾아낸다. maskara 탐지의 토대.
용어
JWT (JSON Web Token)
eyJ...로 시작하고 점(.) 두 개로 셋(헤더·페이로드·서명)이 나뉘는, 널리 쓰이는 인증 토큰 형식. 이 모양 자체가 정규식으로 잘 잡혀서 maskara의 탐지 규칙 중 하나다.
용어
env 대입 (env assignment)
API_KEY=..., SECRET: ...처럼 비밀스러운 변수명 = 값 꼴. 키 양식이 제각각이어도 이 "이름의 패턴"으로 잡는다(심각도 medium). 등호 뒤 값만 비밀이라 캡처 그룹으로 값만 추출한다.
용어
종료 코드 (exit code)
프로그램 종료 시 OS에 남기는 신호 숫자. maskara는 0=깨끗/성공, 1=비밀 발견, 2=실행 오류. CI나 스크립트가 이 숫자를 보고 자동으로 멈추거나 통과시킨다.
용어
GoReleaser / conventional commits
GoReleaser는 Go 앱을 여러 OS·CPU 바이너리로 한 번에 빌드·배포하는 도구. conventional commitsfeat:·fix: 같은 접두사로 커밋을 분류하는 규약으로, maskara는 이로부터 버전(major/minor/patch)을 자동 결정한다.
용어
오프라인 / 위협 모델 (offline / threat model)
maskara는 발견 내용을 어디로도 보내지 않는다(오프라인). 위협 모델은 "무엇으로부터 누구를 지키나"의 가정 — 여기선 "에이전트 로그를 통한 로컬 비밀 누출". "발견=노출됐다 가정하고 즉시 재발급"이 그 결론이다.

11참고 링크

공식 저장소와 비슷한 결의 도구들(README References).

GitHub 저장소 · github.com/mrgoonie/maskara — 본체. internal/detect(규칙)와 internal/cli(조립부)부터 읽으면 좋다.

ggshield (GitGuardian) · github.com/GitGuardian/ggshield — 성숙한 시크릿 스캐너. git 저장소·CI 대상, 클라우드 검증 결합형. maskara와 대상·철학을 비교해 보기 좋다.

scan-for-secrets (simonw) · github.com/simonw/scan-for-secrets — 비슷한 결의 경량 스캐너. README의 References에 인용됨.

agent-sweep · github.com/Ishannaik/agent-sweep — 에이전트 로그/흔적 정리 계열 도구. README References에 함께 소개.

Agent Support Reference · 저장소 docs/agent-support-reference.md — 지원 에이전트 정규명·별칭·경로 휴리스틱·가드레일 설치 동작 정리.