코딩 에이전트에게 결정론적 취약점 오라클(Deterministic Vulnerability Oracle)을 제공하는 방법
요약
코딩 에이전트가 보안 취약점을 판단할 때 발생하는 불확실성을 해결하기 위해 결정론적 오라클인 VulnGraph를 소개합니다. VulnGraph는 검증된 스냅샷을 통해 에이전트에게 명확한 증거와 타입이 지정된 상태를 제공하여 보안 추론의 오류를 방지합니다.
핵심 포인트
- 에이전트의 보안 추론 오류를 방지하는 결정론적 로컬 프리미티브 설계
- 검증된 콘텐츠 주소 지정 스냅샷을 통한 취약점 데이터 제공
- 단순 불리언 값이 아닌 상세한 상태(not-affected, unknown 등) 전달
- 에이전트의 행동(진행, 조사, 중단)을 유도하는 명확한 결과 구조
AI 에이전트는 코드를 작성하고, 테스트를 실행하며, 의존성을 검사하고, 풀 리퀘스트(pull requests)를 생성할 수 있습니다. 하지만 에이전트가 취약한 패키지를 마주했을 때, 그들의 보안 추론은 종종 웹 검색, 불투명한 API 점수, 또는 모델이 학습 과정에서 기억하는 내용 등으로 무너져 버리곤 합니다.
문제는 단순히 취약점 데이터가 부족하다는 점이 아닙니다. 그것은 에이전트와 그 하네스(harness)—도구를 제공하고, 명령을 실행하며, 결과를 해석하는 자동화 계층—사이의 계약(contract) 문제입니다. 만약 "일치 항목 없음"이 조용히 "안전함"으로 변하거나, 오래된 정보가 최신 정보처럼 보이거나, 네트워크 오류가 빈 결과처럼 나타난다면, 에이전트는 신뢰할 수 있는 증거 없이도 확신에 찬 답변을 내놓을 수 있습니다.
저는 다른 전제를 바탕으로 VulnGraph를 설계했습니다: 지속적으로 변화하는 취약점 인텔리전스를 결정론적인 로컬 프리미티브(deterministic local primitive)로 전환하는 것입니다. vulngraph-data는 상위 보안 소스들을 검증된 콘텐츠 주소 지정 스냅샷(content-addressed snapshots)으로 컴파일합니다. vulngraph-cli는 해당 스냅샷을 설치하고 CVE, 패키지 버전 또는 전체 락파일(lockfiles)을 오프라인에서 확인합니다.
그 결과, 인간과 기계 모두를 위해 설계된 보안 도구가 탄생했습니다: 승인된 스냅샷과 대상은 동일한 판결을 생성하며, 모든 판결은 타입이 지정된 증거(typed evidence)를 수반하고, 오래된 데이터는 명시적으로 실패를 알리며, unknown은 결코 깨끗한 상태로 오해되지 않습니다.
이 글에서는 해당 계약 뒤에 숨겨진 설계 결정 사항을 설명하고, VulnGraph를 에이전트 및 하네스 워크플로우에 어떻게 내장할 수 있는지 보여줍니다.
"찾을 수 없음"과 "안전함" 사이의 위험한 간극
에이전트가 의존성 업데이트를 검토하고 있다고 가정해 봅시다. 에이전트는 다음과 같이 겉보기에 간단해 보이는 질문에 답해야 합니다: 이 버전을 배포해도 안전한가? 유용한 답변은 하네스가 지워버리기 쉬운 차이점들에 달려 있습니다.
| 발생한 일 | 부주의한 하네스의 결론 | 계약이 보존해야 하는 것 |
|---|---|---|
| 패키지가 존재하며 버전이 모든 영향 범위 밖에 있음 | 취약점 발견되지 않음 | not-affected |
| ... | ||
| 이것들은 단순한 미적 라벨이 아닙니다. 이들은 진행(proceed), 조사(investigate), 데이터 새로고침(refresh the data), 또는 워크플로 중단(stop the workflow)과 같은 서로 다른 에이전트 행동(agent actions)으로 이어집니다. 일단 하네스가 이를 불리언(Boolean) 값으로 평탄화(flatten)해 버리면, 모델은 더 나은 프롬프팅(prompting)을 통해서도 누락된 의미를 복구할 수 없습니다. |
실제 사례에서의 차이점은 다음과 같습니다. 한 명령은 취약점이 악용된 CVE, 실제 패키지 버전, 그리고 스냅샷이 관찰한 적 없는 패키지를 확인합니다:
$ vulngraph check CVE-2024-4577 npm:lodash@4.17.15 npm:no-such-package@1.0.0
CVE-2024-4577
...
출력 결과는 에이전트에게 산문(prose)으로부터 정책을 추론하도록 요구하지 않습니다. 대신 처분(disposition), 행동(action), 안정적인 이유 코드(reason codes), 신뢰도(confidence), 그리고 결과를 생성한 정확한 스냅샷(snapshot)을 제공합니다. 사람이 읽을 수 있는 렌더링은 터미널에서 유용하며, --json은 버전 관리되는 머신 엔벨로프(machine envelope)를 통해 동일한 차이점들을 노출합니다.
변화하는 인텔리전스와 결정론적 실행의 분리
취약점 인텔리전스(Vulnerability intelligence)는 매일 변합니다. 하지만 에이전트 실행은 여전히 재현 가능(reproducible)해야 합니다. 저는 모든 확인 작업이 실시간 데이터를 가져오고 조정(reconcile)하도록 허용하는 대신, 이러한 관심사들을 분리했습니다.
CVE List V5 · EPSS · CISA KEV · exploit sources · ATT&CK · OSV · …
│
▼
...
vulngraph-data 저장소는 비결정론적(nondeterministic)인 가장자리(edge)를 담당합니다: 상위 소스(upstream sources)로부터 대량의 게시물을 검색하고 이를 하나의 그래프로 조정하는 작업입니다. 이 저장소의 출력은 결정론적(deterministic)입니다. 동일한 입력은 바이트 단위로 일치하는 시맨틱 파일(semantic files)을 생성하며, 해당 파일들의 해시(hash)는 콘텐츠에 따라 그래프의 이름을 지정하는 snapshot_id를 생성합니다. 만약 일일 빌드(daily build)에 시맨틱한 변화가 없다면, 새로운 릴리스는 생성되지 않습니다.
릴리스 아티팩트(release artifact)는 배포 경계(distribution boundary)입니다. 소비자는 원본 소스 컬렉션이나 이를 빌드한 장치(machinery)를 가질 필요가 없습니다. 대신 고정된 데이터베이스 아카이브와 스냅샷 식별자(snapshot identity), 파일 해시(file hashes), 소스 최신성(source freshness), 그래프 형식(graph format), 엔진 리비전(engine revision)을 기록한 매니페스트(manifest)를 전달받습니다.
vulngraph-cli가 결정론적(deterministic) 측면을 담당합니다. vulngraph update는 릴리스를 스테이징 영역(staging area)으로 다운로드하고, 아카이브 체크섬(checksum)과 모든 파일 해시를 검증하며, 스냅샷 식별자를 재계산하고, 형식 호환성을 확인하고, 그래프를 무결성 검사(sanity-open)한 뒤에만 이를 활성화합니다. 업데이트에 실패하더라도 마지막으로 검증된 스냅샷을 교체할 수 없습니다.
활성화 이후의 검사(checks)는 네트워크를 사용하지 않습니다. 스냅샷은 다음과 같은 결정 과정의 명시적인 입력값이 됩니다:
verdict = policy(snapshot_id, target)
해당 함수 내부에는 벽시계(wall clock), 무작위 값(random value), 모델 호출(model call) 또는 원격 응답(remote response)이 존재하지 않습니다. 스냅샷이 최신성(freshness) 및 무결성(integrity) 사전 점검(preflight)을 통과하면, 동일한 스냅샷과 타겟(target)에 대해 개발자의 노트북, CI 작업, 그리고 에이전트 샌드박스(agent sandbox)는 모두 동일한 판정(verdict)을 내립니다.
추측하지 않는 하네스 워크플로(harness workflow)
신뢰할 수 있는 통합에는 세 가지 단계가 있습니다. 업데이트 단계만이 네트워크 액세스가 필요하며, 탐색(discovery)과 검사(checks)는 하네스가 검증할 수 있는 안정적인 인터페이스입니다.
1. 계약(contract) 탐색
에이전트가 도움말 텍스트(help text)를 역공학(reverse-engineer)하게 만들지 마세요. 하네스가 기계 판독이 가능한 기능 문서(capability document)와 번들로 제공된 JSON 스키마(JSON Schemas)를 검사하도록 하십시오:
vulngraph --json capabilities
vulngraph schema command
vulngraph schema observation
...
capabilities는 명령어, 출력 스키마 버전, 종료 코드(exit codes), 그리고 두 가지 동작 보장 사항을 선언합니다: 검사는 오프라인이며 결정론적(deterministic)이라는 점입니다. 하네스는 실행 중간에 호환성 문제를 발견하는 대신, 시작 단계에서 지원 여부를 검증할 수 있습니다.
2. 제한된 단계 외부에서의 새로고침 및 사전 점검
네트워크 사용이 허용된 설정 작업(setup job)에서 스냅샷을 설치하거나 새로고침합니다:
vulngraph update
vulngraph --json status
그런 다음 결과로 생성된 VulnGraph 홈 디렉터리를 에이전트 환경 내에 마운트하거나 캐싱합니다. 이를 통해 네트워크 액세스를 모든 보안 결정 과정에서 사용 가능한 주변 능력 (ambient capability)이 아닌, 제어된 데이터 제공 (data-provisioning) 문제로 전환할 수 있습니다.
status는 스냅샷이 설치되었는지 여부, 그 식별자 (identity), 무결성 (integrity), 그리고 최신성 (freshness)을 보고합니다. 14일보다 오래된 데이터셋은 단순히 경고가 동반되는 것에 그치지 않습니다. check 명령은 응답을 거부하고 종료 코드 4로 종료됩니다.
3. 에이전트가 실제로 변경하는 아티팩트(artifact) 확인
check 명령은 개별 CVE, 패키지 좌표 (package coordinates), 그리고 의존성 파일 (dependency files)을 수용합니다. 이를 락파일 (lockfile)로 지정하면 모든 의존성을 확장하고 동일한 버전 범위 정책 (version-range policy)을 적용합니다:
vulngraph --offline --json check package-lock.json > vulngraph.json
CLI는 npm, Yarn, pnpm, Cargo, Bundler, Poetry, pip, Go, Composer, Maven, Gradle, 그리고 Pipenv의 락파일을 인식합니다. 아래의 요약된 JSON 응답은 안정적인 엔벨로프 (envelope)를 보여줍니다:
{
"schema": "vulngraph.command.v1",
"command": "check",
...
중요한 분리는 VulnGraph가 관찰 (observes)할 뿐, 차단 (gate)하지 않는다는 점입니다. 성공적인 체크는 활발하게 악용되는 취약점 (actively exploited vulnerability)을 발견하더라도 0으로 종료됩니다. 종료 코드 (exit codes)는 도구가 신뢰성 있게 완료되었는지를 설명하며, 하네스 (harness)가 반환된 처분 (dispositions)에 조직의 정책을 적용합니다.
| 처분 (Disposition) | 증거 (Evidence) | 합리적인 기본 하네스 동작 |
|---|---|---|
actively-exploited | CISA KEV에 등재됨 | 차단하거나 명시적인 긴급 예외를 요구함 |
| ... |
이러한 분리는 확률적 추론 (probabilistic reasoning)을 적절한 위치에 유지합니다. 하네스는 어떤 상태가 중단, 검토, 또는 에스컬레이션 (escalation)을 필요로 하는지 결정론적으로 결정합니다. 그러면 에이전트는 해당 증거를 사용하여 발견 사항을 설명하고, 도달 가능성 (reachability)을 조사하며, 가장 작은 안전한 업그레이드를 제안하고, 인간에게 트레이드오프 (trade-off)를 전달합니다.
최소한의 Node.js 하네스
다음 래퍼(wrapper)는 버전 관리된 VulnGraph 엔벨로프(envelope)를 다섯 가지 로컬 워크플로 결정 중 하나로 변환합니다. 이는 모델에게 운영상의 실패가 허용 가능한지 묻지 않으며, 인식되지 않은 처분(disposition)을 승인으로 간주하지도 않습니다.
import { spawnSync } from "node:child_process";
const binary = process.env.VULNGRAPH_BIN ?? "vulngraph";
...
evidence 객체는 제가 에이전트의 컨텍스트(context)에 배치할 내용입니다. 프롬프트(prompt)는 좁은 범위를 유지할 수 있습니다:
이 의존성 변경에 대해 첨부된 취약점 증거(vulnerability evidence)를 검토하십시오.
- `unknown`을 안전하거나 깨끗하다고 설명하지 마십시오.
...
결정론적 계층(deterministic layer)은 무엇이 관찰되었는지와 어떤 정책 분기(policy branch)가 적용되는지를 확립합니다. 에이전트는 자신이 잘하는 작업, 즉 리포지토리별 조사, 설명, 복구 계획(remediation planning) 및 커뮤니케이션에 기여합니다.
에이전트 대상 보안 도구를 위한 설계 교훈
그래프를 구축하는 것은 문제의 절반에 불과했습니다. 자동화를 위해 이를 안전하게 만드는 과정은 인터페이스를 보안 모델의 일부로 취급할 것을 요구했습니다.
- 무지(ignorance)를 일급 객체 결과로 만드십시오.
unknown은 데이터 조회부터 터미널 출력, 에이전트 컨텍스트에 이르기까지 모든 계층에서 유지되어야 합니다. - 점수뿐만 아니라 증거를 반환하십시오. 판결(verdict)은 인간이 감사할 수 있는 안정적인 이유와 소스 관찰 내용을 포함해야 합니다.
- 업데이트 시점과 결정 시점을 분리하십시오. 네트워크 검색은 제어된 프로비저닝(provisioning) 단계에 속해야 하며, 검사는 로컬에서 재현 가능해야 합니다.
- 모든 답변 뒤에 있는 데이터를 식별하십시오. 콘텐츠 기반 스냅샷 ID는 "스캐너가 무엇을 알고 있었는가?"를 답변 가능한 질문으로 바꿉니다.
- 도구가 자신의 계약(contract)을 설명하게 하십시오. 기능(capabilities), 스키마(schemas), 최신성(freshness) 및 종료 의미론(exit semantics)은 기계가 읽을 수 있어야 합니다.
- 게이팅 정책(gating policy)을 관찰 도구 외부에 두십시오. 리포지토리마다 위험 허용 범위가 다릅니다. 하네스(harness)가 해당 정책을 명시적으로 소유해야 합니다.
에이전트는 더 많은 토큰을 받거나 더 긴 시스템 프롬프트 (system prompt)를 받는다고 해서 신뢰할 수 있게 되는 것이 아닙니다. 에이전트는 그 환경이 구분을 보존하고, 부작용 (side effects)을 제한하며, 의미가 조용히 변할 수 없는 계약 (contracts)을 통해 증거를 제공할 때 더 신뢰할 수 있게 됩니다.
그것이 바로 제가 VulnGraph가 수행하도록 설계한 역할입니다. 자율적인 보안 권위자가 아니라, 에이전트와 그 하네스 (harness)가 추론할 수 있는 확실한 근거를 제공하는 빠르고, 로컬이며, 감사 가능한 (auditable) 취약점 증거의 원천이 되는 것입니다.
- 빌드 및 릴리스 파이프라인 (Build and release pipeline):
copyleftdev/vulngraph-data - 오프라인 CLI (Offline CLI):
copyleftdev/vulngraph-cli
공개 (Disclosure): 저는 VulnGraph를 설계했습니다. 이 기사와 표지는 AI의 도움을 받아 제작되었습니다. 저는 연결된 리포지토리 (repositories) 및 라이브 CLI 출력과 대조하여 기술적 주장을 검토했습니다.
이 오픈 소스 작업이 유용하다면, 지속적인 개발을 지원할 수 있습니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기