# Redaction 게이트

> `redaction-scan.sh`, `redaction-inventory`, 조직 규칙 파일, commit message scan, pre-push hook, release gate의 범위와 exit code를 정리합니다.

- Repository: local/master-ops-with-local-mogui-ADE-orchestrator

- Human docs: https://grok-wiki.com/public/docs/local-master-ops-with-local-mogui-ade-orches-0ac7093355f3
- Complete Markdown: https://grok-wiki.com/public/docs/local-master-ops-with-local-mogui-ade-orches-0ac7093355f3/llms-full.txt

## Source Files

- `local-mogui-ade-orchestrator:scripts/redaction-scan.sh`
- `local-mogui-ade-orchestrator:scripts/redaction-inventory`
- `local-mogui-ade-orchestrator:hooks/pre-push`
- `local-mogui-ade-orchestrator:config/gitleaks.toml`
- `local-mogui-ade-orchestrator:tests/test_redaction_scan_commit_messages.py`
- `local-mogui-ade-orchestrator:tests/test_redaction_inventory.py`

---

---
title: "Redaction 게이트"
description: "`redaction-scan.sh`, `redaction-inventory`, 조직 규칙 파일, commit message scan, pre-push hook, release gate의 범위와 exit code를 정리합니다."
---

`redaction-scan.sh`는 `gitleaks`를 매칭 엔진으로 사용하되, 저장소가 실제로 내보내는 tracked content, staged index, git range, commit message 범위를 명시적으로 고정하고 결과 줄에 범위를 출력하는 fail-closed 래퍼다. 조직 고유 식별자 규칙은 저장소에 커밋하지 않고 `REDACTION_EXTRA_PATTERNS`가 가리키는 로컬 파일에서 읽으며, release gate는 `redaction-scan.sh`와 `redaction-inventory`를 함께 실행해 “탐지된 비밀”과 “규칙이 덮지 못하는 후보”를 분리한다.

## 실행 표면

| 명령 | 스캔 범위 | commit message | 기본 결과 의미 |
| --- | --- | --- | --- |
| `scripts/redaction-scan.sh` | `git ls-files`의 tracked 파일 | `not-scanned` | tracked 파일에서 finding 없음 |
| `scripts/redaction-scan.sh --staged` | index에 staged 된 `ACMR` 파일 | `not-scanned` | staged 파일에서 finding 없음 |
| `scripts/redaction-scan.sh --range A..B` | `git diff --name-only --diff-filter=ACMR A..B` | 자동 스캔 | range의 파일 변경과 해당 commit message를 함께 검사 |
| `scripts/redaction-scan.sh --commit-messages A..B` | 현재 파일 모드와 별개 | 명시 스캔 | 지정 range의 commit message를 추가 검사 |
| `scripts/redaction-inventory` | 현재 commit의 tracked 텍스트 파일과 파일명 | 해당 없음 | 규칙이 덮지 못하는 후보 토큰을 보고 |

<Note>
`redaction-scan.sh`의 clean 출력은 항상 `mode`, `files`, `commit-messages`, `org-rules`를 포함한다. `commit-messages=not-scanned` 또는 `org-rules=0`은 실패가 아닐 수 있지만, 전체 감사가 아니라는 사실을 그대로 드러내는 신호다.
</Note>

## `redaction-scan.sh`

`redaction-scan.sh`는 다음 조건에서 실행을 시작하지 않고 exit `2`를 반환한다.

| 조건 | exit code | 의미 |
| --- | ---: | --- |
| `gitleaks`가 `PATH`에 없음 | `2` | 매칭 엔진 부재로 판단 불가 |
| `config/gitleaks.toml` 없음 | `2` | 기본 규칙 부재 |
| 알 수 없는 인자 또는 잘못된 range 인자 | `2` | 사용법 오류 |
| `--range` 또는 `--commit-messages` range가 현재 저장소에서 resolve 불가 | `2` | 빈 스캔으로 오인하지 않도록 차단 |
| retired `scripts/redaction-allowlist.txt`에 실질 entry 존재 | `2` | 이전 allowlist 형식을 조용히 무시하지 않음 |
| `--require-extra` 또는 `REDACTION_REQUIRE_EXTRA=1`인데 조직 규칙이 없음 | `2` | generic rule만으로 publish 판단 금지 |
| finding 존재 | `1` | 수정 또는 gitleaks exemption 필요 |
| finding 없음 | `0` | 실행된 범위 안에서 finding 없음 |

finding 출력은 matched value를 그대로 노출하지 않도록 AWS key, GitHub token, Slack token, `sk-`, `sk-ant-`, `Bearer`, assignment secret 형태를 masking한다. exemption은 retired allowlist가 아니라 `.gitleaksignore` fingerprint 또는 `config/gitleaks.toml`의 allowlist로 처리한다.

### 기본 gitleaks config

`config/gitleaks.toml`은 gitleaks default rule set을 확장하고, 저장소가 추가로 막아야 하는 generic class를 담는다.

| rule id | 탐지 class |
| --- | --- |
| `private_key` | PEM 또는 OpenSSH private key header |
| `aws_access_key` | AWS access key id |
| `github_token` | GitHub token prefix |
| `slack_token` | Slack API token |
| `openai_sk`, `anthropic_key` | OpenAI/Anthropic 형태 secret key |
| `bearer_token` | 긴 bearer credential literal |
| `assignment_secret`, `dotenv_export` | 코드 또는 shell export의 secret assignment |
| `home_path` | `/Users/<name>` 형태의 로컬 사용자 경로 |
| `internal_ip` | RFC1918 private IP |
| `jira_hf` | `HF-` 내부 ticket identifier |
| `slack_url` | Slack workspace/archive URL |
| `internal_host` | `.internal`, `.corp`, `.local` hostname |

placeholder, test secret, synthetic home path, scanner/config 파일 자체, bytecode, `.orca/` 같은 build/runtime 산출물은 config allowlist로 제외된다.

## 조직 규칙 파일

조직 고유 회사명, 제품명, 개인명, handle은 public repository에 커밋하지 않는다. 로컬 파일을 `REDACTION_EXTRA_PATTERNS`로 지정한다.

```bash
export REDACTION_EXTRA_PATTERNS="$HOME/.config/redaction-extra.txt"
export REDACTION_REQUIRE_EXTRA=1
scripts/redaction-scan.sh
```

규칙 파일 형식은 한 줄에 하나의 rule이다.

```text
id|description|regex
```

처리 규칙은 다음과 같다.

| 항목 | 동작 |
| --- | --- |
| 빈 줄, `#` comment | 무시 |
| separator | 처음 두 개의 `|`만 field separator로 사용하므로 regex 안의 `|`는 허용 |
| Python `re` compile 실패 | unusable line으로 count하고 skip |
| gitleaks RE2 compile 실패 | merged config canary 실패로 exit `2` |
| rule id 출력 | engine 실패 시 id만 출력하고 regex 본문은 출력하지 않음 |
| native script 부재 | 조직 규칙이 로드됐지만 비 ASCII literal이 없으면 native spelling 누락 가능성을 warning |

<Warning>
`org-rules=0`인 clean scan은 generic pattern만 통과했다는 뜻이다. 공개 push, release, owner review 직전에는 `REDACTION_REQUIRE_EXTRA=1` 또는 `--require-extra`를 사용해 조직 규칙 부재를 exit `2`로 승격한다.
</Warning>

## commit message scan

`gitleaks dir`는 commit message를 읽지 않는다. `redaction-scan.sh`는 range 모드에서 `git log --format=%H A..B`로 commit을 순회하고, 각 commit message를 `gitleaks stdin`으로 별도 검사한다. message finding은 파일명 대신 `commit:<short_sha>` label로 보고된다.

```bash
# 파일 변경과 commit message를 함께 검사
scripts/redaction-scan.sh --range origin/main..HEAD

# 파일 모드는 유지하고 message만 추가 검사
scripts/redaction-scan.sh --commit-messages origin/main..HEAD
```

`--commit-messages`는 range 인자가 없으면 exit `2`다. clean range scan은 `commit-messages=<count>`를 출력하므로 “message를 검사하지 않은 clean”과 “message까지 검사한 clean”을 구분할 수 있다.

## pre-push hook

저장소에는 opt-in `hooks/pre-push`가 있다. clone마다 다음 설정으로 활성화한다.

```bash
git config core.hooksPath hooks
```

hook은 git이 stdin으로 전달하는 `<local ref> <local sha> <remote ref> <remote sha>` 행을 읽고, push되는 ref마다 스캔 범위를 만든다.

| push 형태 | hook 동작 |
| --- | --- |
| remote ref 삭제 | 스캔할 내용이 없으므로 skip |
| 기존 remote ref 업데이트 | `remote_sha..local_sha` range scan |
| 새 ref | `origin/main`과 merge-base를 찾아 `base..local_sha` range scan |
| remote tip object가 로컬에 없음 | `origin/main` merge-base로 fallback |
| usable range가 하나도 없음 | tracked tree scan으로 fallback |

hook은 caller environment를 그대로 사용한다. 즉 `REDACTION_EXTRA_PATTERNS`와 `REDACTION_REQUIRE_EXTRA`를 설정한 checkout에서는 조직 규칙까지 강제하고, 설정하지 않은 public contributor checkout에서는 generic rule scan으로 동작한다.

## `redaction-inventory`

`redaction-inventory`는 scan의 반대 질문을 다룬다. “규칙이 이름 붙인 패턴”을 찾는 대신, 현재 tracked tree에 있는 후보 token 중 어떤 것이 로컬 조직 규칙으로 덮이지 않는지 보고한다.

```bash
REDACTION_EXTRA_PATTERNS="$HOME/.config/redaction-extra.txt" scripts/redaction-inventory
scripts/redaction-inventory --baseline .redaction-inventory-baseline
scripts/redaction-inventory --min-count 2
scripts/redaction-inventory --json
```

| 후보 bucket | 수집 기준 |
| --- | --- |
| `kebab` | kebab-case repository/product/organization 형태 token |
| `word_in_korean` | 한글 주변 40자 안의 Latin word |
| `home_user` | `/Users/<name>`에서 추출한 user name |
| `email_domain` | email address의 domain |

binary 파일은 첫 8KB 안의 NUL byte로 제외한다. tracked 파일 본문뿐 아니라 tracked 파일명도 후보 harvest에 포함한다. baseline 파일에 들어 있는 token은 검토된 후보로 간주해 출력하지 않는다.

### inventory exit code

| exit code | 의미 | release gate 처리 |
| ---: | --- | --- |
| `0` | uncovered candidate 없음 | 통과 |
| `1` | uncovered candidate 있음 | 정상 triage 상태. candidate는 secret 판정이 아니며 검토 대상 |
| `2` | 판단 불가 | release 차단 |

exit `2` 사례는 `REDACTION_EXTRA_PATTERNS` 미설정, usable rule 없음, git repository 아님, tracked file 없음이다. JSON 출력은 `rules`, `rules_unusable`, `rules_considered`, `tracked_files`, `baseline`, `uncovered_total`, `uncovered`를 포함한다.

## release gate

release runbook은 새 파일이 scanner에 보이도록 먼저 `git add -A`를 요구한다. 그 뒤 테스트, tool naming gate, redaction scan, inventory를 순서대로 실행한다.

```bash
set -e
PYTHONPATH=src uv run pytest tests -q
bash master-ops/scripts/test-tool-naming.sh
./scripts/redaction-scan.sh
rc=0
./scripts/redaction-inventory || rc=$?
if [ "$rc" -ne 0 ]; then [ "$rc" -eq 1 ] || exit "$rc"; fi
```

이 release gate에서 `redaction-scan.sh`의 exit `1`은 `set -e` 때문에 즉시 차단된다. `redaction-inventory`의 exit `1`은 “후보가 있다”는 triage 상태로 허용되지만, exit `2`는 판단 불가이므로 release cut을 막는다.

`master-ops/scripts/pr-steward-status`가 제시하는 PR 검증 명령은 더 엄격하게 다음 두 gate를 조직 규칙 필수 모드로 실행한다.

```bash
REDACTION_REQUIRE_EXTRA=1 REDACTION_EXTRA_PATTERNS=~/.config/redaction-extra.txt ./scripts/redaction-scan.sh
REDACTION_REQUIRE_EXTRA=1 REDACTION_EXTRA_PATTERNS=~/.config/redaction-extra.txt ./scripts/redaction-inventory
```

## 범위 밖 표면

repository gate는 repository content와 commit message를 읽는다. 다음 표면은 파일 tree에 없으므로 `redaction-scan.sh`가 보지 않는다.

| 표면 | 담당 guard |
| --- | --- |
| PR body | `master-ops/scripts/pr-body-check`의 body redaction scan |
| PR comment, review body, issue body | `master-ops/scripts/conversation-redaction-scan` |
| release note 초안, forge web UI 입력 | 게시 전 별도 grep 또는 conversation-surface scan |
| 이미 push된 과거 history 전체 | 선택한 range 밖은 보지 않음. history rewrite 검증은 별도 절차 |

`conversation-redaction-scan`은 GitHub `gh` CLI를 사용해 PR body, PR comment, review body, issue body를 가져오고 absolute home path class를 matched value 없이 `surface|number|author|locator|pattern_class`로 출력한다. 이 guard는 repository redaction gate를 대체하지 않고, repository gate가 구조적으로 읽을 수 없는 forge 대화 표면을 보완한다.

## 운영 체크

<Steps>
<Step title="로컬 규칙을 준비한다">
`~/.config/redaction-extra.txt` 같은 version control 밖 경로에 `id|description|regex` 형식의 조직 규칙을 둔다. rule 본문은 scanner 출력이나 문서에 붙이지 않는다.
</Step>

<Step title="push 전 hook을 켠다">
clone마다 `git config core.hooksPath hooks`를 설정한다. 조직 규칙이 필요한 host는 shell profile 또는 작업 세션에서 `REDACTION_EXTRA_PATTERNS`와 `REDACTION_REQUIRE_EXTRA=1`을 함께 설정한다.
</Step>

<Step title="release 전 gate를 실행한다">
새 파일을 `git add -A`로 tracked/staged 상태에 올린 뒤 release runbook의 gate 명령을 실행한다. `redaction-scan.sh` exit `1` 또는 `2`, `redaction-inventory` exit `2`는 release 차단으로 처리한다.
</Step>

<Step title="candidate를 triage한다">
`redaction-inventory` exit `1`은 즉시 secret 판정이 아니다. 실제 조직 식별자는 규칙으로 추가하고, 의도적으로 허용된 token은 baseline에 기록한다.
</Step>
</Steps>

## Related pages

<CardGroup>
<Card title="방어 인벤토리" href="/defense-inventory">
Redaction scan과 inventory가 어떤 failure mode를 막는지 다른 runtime guard와 함께 비교한다.
</Card>
<Card title="CLI 참조" href="/cli-reference">
`redaction-scan.sh`, `redaction-inventory`, preflight, release 관련 command surface와 exit code를 빠르게 확인한다.
</Card>
<Card title="기여와 릴리스" href="/contributing-release">
릴리스 전 테스트, redaction gate, changelog, tag owner approval 흐름을 함께 실행한다.
</Card>
<Card title="문제 해결" href="/troubleshooting">
`redaction cannot decide`, missing `gitleaks`, 조직 규칙 부재, range resolve 실패를 증상별로 처리한다.
</Card>
</CardGroup>
