npx codex-security scan .)로 돌아가는 CLI이면서, 코드에서 부를 수 있는 TypeScript SDK이기도 하다.
openai/codex-security · 제작 OpenAI · 라이선스 Apache-2.0 · SDK 버전 0.1.5 / 번들 플러그인 0.1.14 · Node.js 22+ · Python 3.10+ · TypeScript SDK + Codex SDK 래핑 · Docker 이미지 제공 · TrendShift 위클리 3위, 약 8.6k stars · 2026-07-24 첫 트렌딩)
Codex Security는 터미널에서 실행하는 AI 보안 감사 도구다. npm install @openai/codex-security로 설치하고, npx codex-security login으로 로그인한 뒤 npx codex-security scan .을 치면 현재 폴더의 코드를 스캔한다. 결과는 사람이 읽는 report.md와, 기계가 읽는 표준 JSON 문서 3종(findings.json 등), 그리고 CI 도구가 먹는 SARIF·CSV로 나온다.
겉보기엔 흔한 "AI가 코드 봐주는 도구"지만, 설계 목표가 다르다. 대부분의 AI 스캐너는 "의심스러운 것"을 잔뜩 뱉어내 개발자를 지치게 한다(경보 피로, alert fatigue). Codex Security는 반대로, 발견한 것 하나하나를 "이게 정말 공격 가능한가?"라는 질문으로 되짚어 가짜를 걸러내고 진짜만 남기는 것을 최우선으로 삼는다.
보통의 자동 스캐너는 문이 열려 있으면 무조건 "침입 위험!"이라 외친다. 하지만 그 문이 사실 담장 안쪽이라 아무도 못 들어온다면? Codex Security는 바깥에서 그 문까지 실제로 걸어올 수 있는 길(attack path)이 있는지를 코드로 추적한 뒤에야 경보를 울린다. 길이 없으면 "실제 위험 아님"으로 강등하거나 조용히 넘긴다.
그래서 결과지가 짧다. 대신 남은 항목은 "왜 위험한지"의 근거가 코드 위치까지 딸려 온다.
2026년 7월, OpenAI가 Codex(코딩 에이전트) 생태계의 공식 보안 도구로 이 저장소를 오픈소스로 공개하자마자 개발자 커뮤니티가 반응했다. 이유는 세 가지다.
AI로 취약점을 찾는 시도는 많았지만, 대부분 스타트업 제품이거나 실험적 스크립트였다. Codex Security는 프론티어 모델을 만드는 회사가 직접, 자사 코딩 에이전트(Codex)와 붙여 내놓은 것이다. 게다가 Apache-2.0 라이선스로 SDK·CLI·플러그인 전부를 공개해 "블랙박스 SaaS"가 아니라 뜯어볼 수 있게 했다.
기존 정적 분석 도구(SAST)와 AI 스캐너의 최대 불만은 가짜 경보다. 100개를 띄우면 90개가 헛것이라, 개발자가 아예 안 보게 된다. Codex Security는 발견 후 validation(검증)과 attack-path-analysis(공격 경로 분석)를 별도 단계로 두고, "실제로 공격자 입력이 이 지점에 도달함을 코드로 증명"해야만 심각도를 유지한다. 이건 그냥 마케팅 문구가 아니라 severity-policy 문서에 규칙으로 못 박혀 있다(§4 참고).
연구 프로젝트로 끝나지 않고, 실무 배치를 전제로 만들어졌다. 로컬에서 scan . 한 줄, CI에선 OPENAI_API_KEY만 넣으면 되는 비대화 모드, 저장소 목록 CSV를 통째로 돌리는 bulk-scan, 그리고 이걸 위한 전용 Docker 이미지(비루트 사용자 + seccomp 샌드박스)까지 갖췄다.
Codex Security는 규칙 기반(Semgrep·CodeQL)과 다르게 LLM 추론으로 문맥을 읽고, Copilot Autofix처럼 고치는 것까지 노리되, "검증으로 거짓을 걸러내는" 단계를 워크플로우의 1급 시민으로 올렸다는 점이 차별점이다. 또한 특정 IDE·플랫폼에 묶이지 않고 CLI/SDK/플러그인으로 이식성이 높다.
이 저장소는 크게 세 개의 층으로 이해하면 쉽다. ① 사용자가 만지는 TypeScript SDK/CLI, ② 그 밑에서 스캔 절차를 실제로 굴리는 번들 플러그인(스킬 + Python 스크립트), ③ 추론을 담당하는 Codex 엔진.
| 요소 | 무엇 / 왜 |
|---|---|
| 언어·런타임 | TypeScript 5.7 + Node.js 22+ (ESM 모듈). 테스트는 bun test로 실행 |
| @openai/codex @openai/codex-sdk | 핵심 의존성. Codex 코딩 에이전트 실행파일과 SDK(v0.144.6)를 그대로 끌어와 감싼다 — 스캐너의 "두뇌"는 Codex |
| ajv 8 | JSON Schema 검증기. 결과 JSON(findings/coverage/manifest)이 규격에 맞는지 봉인 전에 강제 검사 |
| @octokit/core | GitHub API 클라이언트 — 발견 항목을 이슈/보안 권고(advisory)로 연동 |
| papaparse · pdfjs-dist | CSV 파싱(bulk-scan 저장소 목록) · PDF 텍스트 추출(문서형 정책 읽기) |
| extract-zip · fflate | 번들 플러그인 zip을 풀고 압축 해제 — 플러그인은 압축된 형태로 배포됨 |
| smol-toml · incur · fast-uri | TOML 설정 파싱 · 재시도(백오프) · URI 정규화 유틸 |
_bundled_plugin/ 안에 스캔의 "실제 절차"가 들어 있다. 사람이 읽는 마크다운 지시서(SKILL.md)로 에이전트의 행동을 규정하고, 반복적·결정론적 작업(파일 목록화, 후보 정규화, 랭킹, SQLite 기록)은 Python 스크립트 32개가 처리한다.
| 구성물 | 역할 |
|---|---|
| skills/ (13개) | security-scan, deep-security-scan, security-diff-scan, finding-discovery, validation, attack-path-analysis, threat-model, triage-finding, fix-finding, propose-security-hardening, define-security-policy, track-findings, vulnerability-writeup |
| scripts/*.py (32개) | workbench_*(SQLite 상태 DB) · config_preflight(사전점검) · normalize_candidates · rank_preview(랭킹) · finalize_scan_contract(최종 봉인) · validate_*(계약 검증) |
| schemas/*.json | findings / coverage / scan-manifest 3종 JSON 스키마 — 출력의 "계약서" |
| references/*.md | scan-contract · final-report · sarif-adapter · security-guidance 등 규격·지침 문서 |
| .mcp.json | MCP 서버(mcp/server.mjs) 정의 — Codex 데스크톱 앱과 툴로 연결 |
| .app.json | GitHub · Linear · Atlassian(Jira) 커넥터 연동 — 발견을 티켓/이슈로 |
실제 "코드를 읽고 판단"하는 일은 Codex가 한다. SDK는 Codex를 격리된 홈 디렉토리(isolated home) 안에서 실행하고, 스캔 대상 저장소를 신뢰할 수 없는 데이터로 취급하도록 환경을 세팅한다. 인증은 ChatGPT 로그인 또는 OPENAI_API_KEY 둘 중 하나.
Codex Security의 심장은 6단계 스캔 파이프라인이다. 이 순서는 SQLite 상태 DB의 phase 컬럼에 그대로 박혀 있어(preflight → threat_model → discovery → validation → attack_path → reporting) 임의로 건너뛸 수 없다.
대부분의 스캐너는 "발견 = 결과"다. Codex Security는 발견(discovery)은 그물을 넓게 던지는 단계로 두고, 그 뒤에 검증(validation)과 공격 경로 분석(attack-path)이라는 두 개의 독립 필터를 통과시킨다. discovery는 재현율(빠뜨리지 않기)을, validation·attack-path는 정밀도(거짓 걸러내기)를 담당하는 역할 분업이다.
낚시 vs. 손질. discovery는 일단 그물로 다 건져 올린다(잡어까지). validation은 "이거 먹을 수 있는 생선 맞아?"를 하나씩 확인하고, attack-path는 "이 생선이 실제로 우리 식탁까지 오는 경로가 있나"를 따진다. 마지막 접시에는 진짜만 남는다.
attack-path-analysis/references/severity-policy.md에는 심각도 인플레이션을 막는 규칙이 빼곡하다. 핵심만 옮기면:
ignore(혹은 최대 low)로 분류.ignore로 표시해 거짓 양성임을 명시.이 문서는 대놓고 "평범한 코드 버그를 버그라는 이유만으로, 또는 스캐너가 그렇게 라벨했다는 이유만으로 high/critical로 취급하지 말라"고 지시한다. 즉 이 시스템은 자기 자신의 발견을 의심하도록 설계돼 있다. AI 스캐너가 흔히 빠지는 "과장 경보"를 규칙으로 억제하는 것이다.
스캔이 끝나면 finalize_scan_contract.py가 결과를 봉인(seal)한다. 표준 문서 3종은 크기 상한(findings 128 MiB, coverage 32 MiB, manifest 16 MiB)과 스키마 검증을 통과해야 하며, 매니페스트에는 각 문서의 해시(지문)가 기록된다. report.md·SARIF는 "봉인된 원본에서 파생된 사본(projection)"일 뿐 원본을 수정하지 못한다.
발견 항목마다 안정적 식별자가 붙는 것도 핵심이다. ruleId(취약점 유형) + identity.anchor(의미적 뿌리) + identity.instance(개별 인스턴스)로 fingerprints.primary라는 지문을 만든다. 이 지문은 줄이 밀리거나 파일 이름이 바뀌어도 유지되도록 설계돼, "지난주에 본 그 취약점이 이번 주에도 있는가"를 추적(track-findings)할 수 있다.
구조가 말해주는 것: 루트는 "배포·격리 껍데기"(Docker·seccomp)이고, 실질은 sdk/typescript 안에 다 있다. 그 안에서도 다시 "실행하는 TS 코드(src/)"와 "무엇을 어떻게 스캔할지 규정하는 플러그인(_bundled_plugin/)"이 분리돼 있다. 후자는 코드보다 지시서(마크다운) + 스키마 + 파이썬 도구의 조합이라는 점이 이 프로젝트의 성격을 잘 보여준다 — "에이전트에게 시키는 절차서"가 곧 제품인 셈이다.
이 저장소의 진짜 소스코드는 어떤 의미에선 13개의 SKILL.md 마크다운이다. 각 스킬은 프론트매터(name·description)로 "언제 이 스킬을 쓰는지"를 정의하고, 본문에 단계별 지시를 자연어로 적는다. 조건 분기("started면 A, timed_out이면 B"), 페이즈 경계, 도구 호출 순서까지 마크다운으로 규정한다. LLM 에이전트를 쓰는 개발자라면 "코드가 아니라 지시서로 흐름을 제어"하는 이 방식이 실전 참고서가 된다.
발견/검증 분리, 공격 경로 추적, 심각도 정책, 봉인 계약 — 이 조합은 "AI 출력의 신뢰도를 어떻게 공학적으로 끌어올리는가"의 교과서다. 특히 severity-policy.md는 "모델이 과장하지 않도록 규칙으로 억제하는 법"의 실물 예시다.
스캔 대상 코드는 공격자가 심어놓은 것일 수 있다. 그래서 이 도구는: (a) 저장소 파일·빌드 스크립트·심지어 사용자가 준 컨텍스트(userContext)까지 "명령이 아니라 분석 데이터"로만 취급하고, (b) Codex를 격리 홈에서 돌리며, (c) 결과물을 저장소 밖에 쓰도록 강제하고, (d) Docker에서 비루트 + seccomp로 시스템콜을 화이트리스트한다. 프롬프트 인젝션과 공급망 공격을 동시에 방어하는 다층 설계를 통째로 볼 수 있다.
일부러 취약점(예: 사용자 입력을 그대로 SQL에 넣는 코드)을 심은 미니 프로젝트를 만들고 scan .을 돌려, ① 그게 잡히는지 ② 안전한 이웃 코드는 거짓 양성으로 안 뜨는지 ③ report.md가 공격 경로를 어떻게 서술하는지 관찰해보자. 정식 과제는 §8.
| 항목 | 요구사항 / 메모 |
|---|---|
| Node.js | 22 이상 (ESM). CLI·SDK 실행의 기본 런타임 |
| Python | 3.10 이상 — 번들 플러그인의 workbench·랭킹·봉인 스크립트용 |
| Codex Security 접근권 | 필수. 실제 분석은 Codex 엔진(클라우드 모델)이 수행 → ChatGPT 로그인 또는 OPENAI_API_KEY |
| CI 환경 | OPENAI_API_KEY만 세팅하면 비대화 모드로 동작. 사인인 불필요 |
| Docker (선택) | node:22-bookworm-slim 기반. 비루트 UID 10001 + seccomp(기본 차단, 14개 콜 그룹만 허용) + /input·/output·/state 분리 |
| OS 지원 | CI가 Ubuntu · macOS · Windows 3종에서 테스트됨 |
| 디스크 | 스캔 이력·아티팩트는 CODEX_SECURITY_STATE_DIR(저장소 밖 쓰기 가능 경로)에 저장 권장 |
내 컴퓨터에서 도는 건 절차를 지휘하는 부분이고, 코드를 실제로 읽고 판단하는 무거운 추론은 Codex(클라우드)가 한다. 따라서 네트워크·API 키·토큰 비용이 든다(그래서 cost.ts에 비용 추정·상한 로직이 있다). 완전 오프라인·온프레미스 전용 스캐너를 기대했다면 성격이 다르다.
Node 22 + Python 3.10을 준비하고, 일부러 취약한 코드(예: eval(userInput), 미검증 SQL 문자열 연결)를 넣은 작은 저장소를 만든다. npx codex-security scan .을 돌려 report.md가 그 취약점을 잡는지, 어떤 심각도를 매기는지 확인한다.
과제 1의 취약 코드 바로 옆에 겉보기엔 비슷하지만 실제론 안전한 코드(입력을 파라미터 바인딩으로 처리 등)를 둔다. Codex Security가 이걸 거짓 양성으로 띄우는지 아닌지 보고, coverage.json에서 "rejected/not-applicable"로 어떻게 분류됐는지 읽어본다.
codex-security diff 계열 명령으로 워킹트리·커밋·브랜치 변경분만 스캔해본다. 전체 스캔과 결과가 어떻게 다른지, 그리고 이걸 GitHub Actions에 OPENAI_API_KEY로 붙이면 어떤 워크플로우가 되는지 .github/workflows를 참고해 설계해본다.
한 번 스캔 후, 취약 코드의 줄 위치를 바꾸거나 파일명을 바꿔서 다시 스캔한다. findings.json의 fingerprints.primary가 유지되어 "같은 취약점"으로 추적되는지, scans 명령의 비교 기능이 이를 어떻게 보여주는지 관찰한다.
skills/security-scan/SKILL.md와 attack-path-analysis/references/severity-policy.md를 정독하며, "발견→검증→공격경로→보고"가 자연어 지시로 어떻게 강제되는지 흐름도를 직접 그려본다. 여기에 workbench_schema.py의 phase 상태 전이를 겹쳐보면 코드와 지시서가 어떻게 맞물리는지 보인다.
| 주차 | 주제 / 목표 | 핵심 키워드 |
|---|---|---|
| 1주차 | 웹 취약점 기초 — 무엇이 "취약점"인가. SQLi·XSS·인증우회·IDOR·경로순회를 실제 예제로 이해 | OWASP Top 10, CWE, IDOR |
| 2주차 | 정적 분석 도구 체험 — Semgrep·CodeQL로 규칙 기반 스캔을 돌려보고, 왜 거짓 양성이 나는지 체감 | SAST, Semgrep 규칙, SARIF |
| 3주차 | 위협 모델링 & 공격 경로 — "공격 표면·신뢰 경계·도달 가능성"을 그리는 법. Codex Security의 threat-model 스킬 정독 | Threat Model, Attack Surface, Reachability |
| 4주차 | 에이전트 보안 설계 — 프롬프트 인젝션 방어, 신뢰할 수 없는 입력 격리, seccomp·비루트 컨테이너, 공급망(PATH 신뢰) | Prompt Injection, Sandboxing, seccomp, Supply Chain |
1~2주차로 취약점 자체와 기존 도구의 한계를 몸으로 익힌 뒤 3~4주차에 Codex Security를 보면, "왜 검증 단계를 따로 뒀는지", "왜 심각도 정책이 그렇게 깐깐한지"가 자연스럽게 납득된다. 순서를 바꾸면 껍데기만 보게 된다.
README.md·SECURITY.md·sdk/typescript/_bundled_plugin/skills/를 볼 것skills/attack-path-analysis/references/severity-policy.md — "심각도 인플레이션 억제" 규칙의 실물