스킬
c1c8a8c 기준입니다.
security-audit-skill은 코딩 에이전트가 소스를 조사하고, 취약점 후보를 찾고, 다른 에이전트에게 반박을 맡긴 뒤, 결과를 JSON과 보고서로 남기도록 안내하는 저장소입니다. 여기서 스킬은 에이전트가 읽는 작업 지침 묶음이며, 실제 모델 호출과 도구 실행은 사용 중인 에이전트 플랫폼이 담당합니다.
이 프로젝트의 핵심은 취약점을 많이 주장하는 것이 아니라, 어떤 증거로 무엇을 확정했는지 추적하게 만드는 것입니다. 이를 위해 절차를 Markdown에 적고, 기록이 그 절차의 데이터 규칙을 지켰는지는 JavaScript로 검사합니다.
발견자는 단서를 찾고, 검증자는 그 설명이 틀릴 가능성을 조사합니다. 조정자는 누가 어느 구역을 확인했는지 장부에 적고, 최종 기록의 형식을 검사합니다.
장부에 도장이 찍혔다고 사건이 자동으로 입증되지는 않습니다. 이 저장소에서도 JSON 검증기의 통과와 취약점의 실제 입증은 별개의 일입니다.
| 판정값 | 쉬운 뜻 | 기록의 핵심 |
|---|---|---|
confirmed | 증거로 확정한 취약점 | 소스 경로, 제한된 재현 결과, 영향, 수정 전략, 심각도 |
needs_validation | 근거는 있으나 결정적 사실이 빠진 후보 | 정확한 미확인 사실과 이를 해소할 검증 계획. 심각도 없음 |
rejected | 소스나 검증으로 반박된 후보 | 주장했던 원인과 반박 이유. 같은 오해의 반복 방지 |
예를 들어 소스에 소유권 검사가 보이지 않아도, 앞단 공통 함수가 이미 검사할 수 있습니다.
그 경로를 확인하면 rejected가 될 수 있습니다.
반면 생성된 코드가 없어 호출 경로를 끝까지 볼 수 없다면, 구체적인 차단 요인을 적은 needs_validation이 적절합니다.
skills/security-audit/SKILL.md는 기본 동작을 guidance로 정합니다.
보안 질문이나 특정 코드 검토에는 필요한 지침만 사용합니다.
코드베이스 감사·침투 테스트·전체 검토 또는 보고서 산출물을 명시적으로 요청할 때 full audit의 여섯 단계를 적용합니다.
이 글에서는 실제 대상을 공격하지 않습니다. 문서와 검증기 소스를 읽으며, “검사 함수가 있다”와 “그 함수가 올바른 사용자·자원·작업을 검사한다”의 차이를 배웁니다.
Cloudflare는 2026-06-18 공식 글에서 단일 저장소용 보안 스킬을 발전시켜 여러 저장소를 다루는 감사 시스템을 만들었다고 설명합니다. 이 저장소의 README도 자신을 그 출발점으로 소개합니다. 공개된 것은 전체 사내 시스템이 아니라 한 저장소를 검토하는 데 쓰는 지침과 결과 검증기입니다. Cloudflare 공식 배경 글
TrendShift Daily #9 선정과 GitHub 스타 40,122개는 2026-10-23 기준입니다. 일일 순위와 누적 스타 수만으로 관심이 늘어난 직접 원인을 판단할 수는 없습니다. 다만 공개된 방법론, 독립 검증, 기계가 읽을 수 있는 산출물은 관심을 끌 만한 설계상 이유로 해석할 수 있습니다.
| 접근 | 중심 기능 | 이 저장소와 비교해 배울 점 |
|---|---|---|
| 일반 대화형 검토 | 모델에게 코드와 질문 전달 | 지침을 별도로 만들지 않으면 역할 분리·누락 장부·판정 계약이 자동으로 생기지 않음 |
| CodeQL | 코드를 데이터베이스로 추출하고 쿼리로 분석 | 분석 엔진의 결과를 증거 검토 절차에 연결하는 관점 |
| Semgrep | 정적 분석·의존성·비밀정보 검사와 규칙 적용 | 정해진 검사와 에이전트의 맥락 검토를 함께 설계하는 관점 |
| 이 저장소 | 역할별 조사·반박·범위 추적·결과 형식 검사 | 후보의 확정 근거와 미확인 사실을 명시적으로 관리 |
CodeQL의 처리 흐름은 공식 개요, Semgrep의 제품 범위는 공식 문서를 기준으로 비교했습니다. 이 표는 기능의 중심을 비교한 것이며, 탐지율·속도·비용의 우열을 측정한 결과가 아닙니다. 이 저장소에는 두 제품을 자동 호출하는 통합 코드도 없습니다.
이 저장소의 장점은 판단을 여러 번 검토하고 미완료 범위를 기록하도록 요구하는 데 있습니다. 그 요구를 실제로 수행하는 능력은 모델과 호스트 플랫폼에 달려 있습니다.
README 기준, 자체 시험에서 한 번의 실행이 반복 실행 전체에서 나온 취약점의 대략 절반을 찾았다고 합니다. 저장소에는 그 수치를 재계산할 데이터셋이나 벤치마크 실행기가 없습니다. 따라서 이를 일반적인 탐지율 50%로 받아들이면 안 됩니다. 공부할 부분은 수치 자체보다, 이전 기록을 다음 실행의 입력으로 삼아 빈틈을 다시 조사하는 설계입니다.
배경 글은 초기 스킬의 7단계와 결과 수집 API 제출을 설명합니다.
현재 커밋의 SKILL.md는 6단계이며 마지막은 보고서 작성입니다.
이 저장소에는 수집 API 서버나 제출 클라이언트가 없습니다. 이 글은 커밋 c1c8a8c 기준으로 구조를 설명합니다.
커밋 c1c8a8c의 추적 파일은 22개입니다.
package.json, 잠금 파일, Cargo.toml, pyproject.toml, go.mod,
Dockerfile, .github/workflows/는 없습니다.
따라서 프레임워크 버전·Node.js 최소 버전·CI 이미지 버전을 의존성 파일에서 인용할 수 없습니다.
없는 버전을 추정해서 채우지 않는 것도 소스 분석의 기본입니다.
| 계층 | 실제 구성 | 버전·설정의 근거 |
|---|---|---|
| 백엔드에 해당하는 처리 | CommonJS 검증기 2개, Node.js 표준 모듈 | require("fs"), require("path"), TextDecoder. 런타임 버전 지정 없음 |
| 프론트엔드 | 전용 웹 화면·라우터·번들 없음 | 감사 결과는 JSON과 Markdown. 브라우저 보안 문서는 감사 지침 |
| 에이전트 제어 | Markdown 지침과 YAML 형식의 선두 메타데이터 | name: security-audit. 모델·SDK 버전 고정 없음 |
| 데이터 계약 | report-schema.json과 장부 검증 코드 | 스키마 초안 버전 선언 없음. 자체 지원 키워드 목록 사용 |
| 테스트 | node:test, node:assert/strict, 자식 프로세스 검사 | 외부 테스트 라이브러리 없음. CLI 검사 기본 제한 5,000ms, 일부 15,000ms |
| 인프라·빌드 | 컴파일·번들·배포 정의 없음 | OS 샌드박스와 하위 에이전트 기능은 호스트가 제공해야 함 |
validate-findings.cjs는 fs·path·util을,
validate-coverage-ledger.cjs는 같은 Node.js 내장 모듈을 node: 접두사로 가져옵니다.
결과 검증에 필요한 외부 npm 패키지가 없다는 의미입니다.
모델 사용료, 호스트 에이전트, 대상 프로젝트의 빌드 도구까지 필요 없다는 뜻은 아닙니다.
// validate-findings.cjs: 실제 진입점과 내보내기 방식의 발췌
module.exports = {
LIMITS,
collect,
collectFindingSemanticErrors,
collectSchemaErrors,
validateDocument,
// 그 밖의 공개 보조 함수·상수는 원본 참조
};
if (require.main === module) process.exit(run(process.argv[2]));
마지막 조건 때문에 이 파일은 두 역할을 가집니다. 직접 실행하면 파일 경로를 받는 명령행 도구가 되고,
테스트에서 require()하면 함수 묶음으로 사용할 수 있습니다.
위 코드는 구조를 설명하기 위해 일부 내보내기를 생략한 발췌입니다.
require()로 다른 파일의 기능을 가져오고 module.exports로 공개할 기능을 정하는 Node.js의 모듈 방식입니다.
파일 읽기와 출력은 명령행 진입점에 두고, 데이터 검사 함수는 따로 공개하면 테스트할 범위를 좁힐 수 있습니다.| 분류 | 파일 수 | 물리적 줄 수 |
|---|---|---|
| JavaScript 구현 | 2 | 1,645줄 |
| JavaScript 테스트 | 2 | 1,392줄 |
| Markdown | 16 | 1,884줄 |
| JSON | 1 | 461줄 |
| 라이선스 텍스트 | 1 | 21줄 |
| 합계 | 22 | 5,403줄 |
집계는 .git/을 제외하고 UTF-8 파일의 줄바꿈 수를 세었습니다.
빈 줄·주석·문서 속 코드 예시를 포함하며, 순수 실행문 수가 아닙니다.
JavaScript는 합계 3,037줄이고, 테스트 파일에는 최상위 test( 선언이 각각 34개와 31개 있습니다.
선언 수를 실제 통과한 테스트 수로 해석하면 안 됩니다.
이 레포에서는 문서가 작업 방식을 정하고, 작은 프로그램이 결과 기록의 계약을 검사합니다. JavaScript만 읽으면 감사 방법을 놓치고, Markdown만 읽으면 기계적으로 보장되는 범위를 과대평가하기 쉽습니다.
아래 그림은 SKILL.md의 전체 감사 절차를 재구성한 것입니다.
저장소에 이 흐름을 자동으로 실행하는 스케줄러 함수가 있다는 뜻은 아닙니다.
Parent와 하위 에이전트는 사용 중인 플랫폼의 역할이며, 이 레포는 그 역할 사이의 계약을 제공합니다.
전체 감사의 공유 파일은 Parent만 씁니다.
하위 에이전트는 자신에게 할당된 scratch/에서만 작업하고 구조화된 결과를 반환합니다.
이 규칙은 동시 작업 중 장부가 서로 덮어써지는 문제와, 조사 대상이 증거 파일을 조작하는 문제를 함께 다룹니다.
HUNTING.md의 발견자는 units, candidates, hardening,
uncovered를 가진 JSON 객체를 돌려줍니다.
조정자는 배정한 범위 ID와 응답을 대조하고 공유 장부에 반영합니다.
발견자에게 전체 보고서를 직접 편집하게 하지 않는 구조입니다.
VALIDATION-AND-REPORTING.md의 Phase 3 검증자는 후보를 발견하지 않은 새 에이전트입니다.
Phase 5에서는 최종 confirmed·needs_validation 기록을 다시 확인합니다.
quick만 두 검토를 한 새 검증자가 맡고, 나머지 프로필은 두 검토를 분리합니다.
글을 쓴 사람이 자기 오탈자를 놓치기 쉬운 것처럼, 버그를 찾은 모델도 자기 가설을 옹호하기 쉽습니다. 새 검증자에게 “이 주장이 틀린 이유를 찾아라”는 역할을 주고, 최종 기록 편집권은 조정자에게 모읍니다.
같은 모델을 새로 호출해도 오류가 통계적으로 독립적이라는 보장은 없습니다. 여기서 독립성은 발견자와 검증자의 역할·문맥·작성 권한을 분리한다는 절차적 의미입니다. 이를 실행하는 에이전트 관리 코드는 이 저장소에 포함되어 있지 않습니다.
소스 연결: SKILL.md의 Write isolation,
HUNTING.md의 Parent consolidation and ledger update,
VALIDATION-AND-REPORTING.md의 Phase 3·5.
검증 지침 원문
report-schema.json의 최상위는 배열이고 각 원소는 oneOf 세 분기 중 정확히 하나에 맞아야 합니다.
verdict의 const가 분기를 구별합니다.
각 판정 객체의 additionalProperties: false는 발견자 응답의 임시 필드가 최종 기록으로 섞여 들어오는 것을 막습니다.
// report-schema.json의 구조를 요약한 설명용 조각
{
"type": "array",
"items": {
"oneOf": [
{ "properties": { "verdict": { "const": "confirmed" } } },
{ "properties": { "verdict": { "const": "needs_validation" } } },
{ "properties": { "verdict": { "const": "rejected" } } }
]
}
}
위 조각은 필수 필드들을 생략한 구조 설명입니다.
실제 confirmed는 execution.observed_result, remediation.strategy,
severity, confidence 등까지 요구합니다.
needs_validation에는 확정 원인인 root_cause 대신 가설임을 드러내는 claimed_root_cause가 들어갑니다.
validate-findings.cjs의 validateDocument()는 스키마 자체 검사,
데이터 한도 검사, 구조 검사, collectFindingSemanticErrors() 순으로 진행합니다.
마지막 함수는 스키마만으로 표현하기 번거로운 규칙을 확인합니다.
| 구조 검사 | 의미 검사 |
|---|---|
line은 1 이상 정수 | 여러 단계 추적은 entrypoint로 시작해 sink로 끝나야 함 |
| 판정에 맞는 필수 필드 존재 | 검증 대기 후보에 심각도 금지, 검증 계획은 적어도 한 종류 필요 |
| 배열·문자열·객체 자료형 일치 | 중복 없는 fingerprint, 사전순 정렬 |
| 허용된 심각도 문자열 사용 | 전체 심각도가 입증된 영향 등급을 넘으면 거부 |
심각도 검사는 SEVERITY_RANK로 등급을 비교합니다.
가능성과 영향을 수치로 곱해 자동 점수를 계산하는 구현은 없습니다.
문서의 “가능성 × 영향” 원칙과, 코드가 실제로 강제하는 overall_severity 상한을 구분해야 합니다.
소스 연결: collectFindingSemanticErrors()와
validateDocument().
장부의 한 단위는 입력 표면 × 신뢰 경계 × 하위 시스템 × 공격 유형의 조합입니다.
deep에서는 필요한 생명주기 구분도 더합니다.
사람에게 보여줄 이름과 canonical_refs의 안정적인 원본 참조를 별도로 둡니다.
// validate-coverage-ledger.cjs: 실제 함수
function canonicalCoverageId(refs) {
if (!isObject(refs)) throw new TypeError("canonical_refs must be an object");
const fields = hasOwn(refs, "lifecycle") ? [...REF_FIELDS, "lifecycle"] : REF_FIELDS;
if (Object.keys(refs).length !== fields.length || fields.some((field) => !hasOwn(refs, field))) {
throw new TypeError("canonical_refs has missing or unexpected fields");
}
return fields.map((field) => encodeCanonicalRef(refs[field])).join("::");
}
encodeCanonicalRef()는 UTF-8 바이트를 퍼센트 인코딩합니다.
route name은 route%20name, route-name은 그대로 남아 서로 구별됩니다.
공백을 하이픈으로 바꾸는 단순한 슬러그보다 정보 손실을 줄입니다.
입력은 NFC 정규형이어야 하며, 함수가 다른 정규형을 조용히 변환해 받아들이지는 않습니다.
validateStateInvariants()는 상태마다 허용되는 증거 모양을 검사합니다.
예를 들어 planned에는 담당자와 완료 증거가 없어야 하고,
covered에는 담당자·검토 경로·검사 기록이 있어야 합니다.
| 상태 | 담당자·증거 규칙 | 의미 |
|---|---|---|
planned | 담당자 null, 증거·미해결 목록 비움 | 아직 배정하지 않음 |
in_progress | 담당자 있음, 완료 증거는 비움 | 조사 중 |
covered | 담당자·경로·검사 있음, 미해결 없음 | 해당 단위의 검토 기록 확보 |
candidate | 담당자·경로·검사와 후보 지문 필요 | 후보 지문과 연결됨. 최종 검증 전일 수 있음 |
blocked | 담당자·부분 증거·미해결 사유 필요 | 검토했으나 막힌 지점 존재 |
deferred | 담당자 null, 현재 증거 비움, 사유 필요 | 이번 실행에서 미룸 |
not_applicable과 out_of_scope도 미배정 상태이며 제외 이유가 필요합니다.
증거가 있는 단위를 재배정할 때는 이전 증거를 attempts에 보존합니다.
validateAttempts()는 이전보다 증가한 차수, 새 담당자, 이전 산출물의 재사용 금지 등을 확인합니다.
단, 검증기는 현재 장부와 그 안의 이력만 읽습니다. 실제 실행 중 모든 전이가 적법했는지, 이전 파일이 몰래 바뀌지 않았는지까지 자동으로 증명하는 이벤트 저장소는 아닙니다.
소스 연결: canonicalCoverageId(),
validateStateInvariants(),
validateReviewedPathOwnership(), validateAttempts().
두 CLI의 readFileWithinLimit()는 파일을 열 때 O_NOFOLLOW와 O_NONBLOCK을 사용합니다.
열린 파일 기술자를 fstatSync()로 검사하고 일반 파일만 읽습니다.
읽기 전 크기와 누적 읽기 크기를 모두 제한하며, 잘못된 UTF-8은 엄격한 TextDecoder로 거부합니다.
| 한도 | 발견 기록 검증기 | 범위 장부 검증기 |
|---|---|---|
| 입력 바이트 | 5 MiB | 5 MiB |
| 최상위 배열 | 발견 기록 1,000개 | 범위 단위 10,000개 |
| 중첩 깊이 | 64단계 | 64단계 |
| 기타 한도 | 배열당 1,000개, 중복 비교 키 1 MiB | 하위 목록·객체 필드 1,000개, 사전 검사 값 500,000개 |
| 오류 출력 | 최대 100개 | 최대 100개 |
enforceJsonTextLimits()와 preflightJsonText()는 JSON.parse() 전에 구조 한도를 검사합니다.
파싱 이후에만 검사하면 거대한 입력을 이미 메모리에 펼친 뒤일 수 있기 때문입니다.
진단 메시지도 safeQuote()와 escapeUnsafeDiagnosticCharacters()를 거쳐 제어문자의 영향을 줄입니다.
검증기를 통과했다는 사실은 기록의 형식과 일관성을 뜻하며, 기록 속 주장이 사실임을 뜻하지 않습니다. 예를 들어 양의 줄 번호를 요구하지만 그 줄에 정말 해당 함수가 있는지 소스를 열어 확인하는 코드는 없습니다.
isOwnedArtifactPath()는 산출물 경로의 문자열과 소유자 접두사를 확인합니다.
실제 파일의 존재·내용·안전한 보존 여부까지 검사하지 않습니다.
CLI의 입력 열기 보호도 문서에 요구된 모든 상위 경로의 기술자 기반 순회와 동일하지 않습니다.
샌드박스 생성과 scratch/에서 artifacts/로 증거를 안전하게 복사하는 구현은 호스트의 책임입니다.
문서의 강한 요구사항을 저장소가 이미 구현해 둔 기능으로 착각하면 안 됩니다.
| 파일 | 맡은 역할 | 읽으며 던질 질문 |
|---|---|---|
SKILL.md | 모드·권한·프로필·예산·전체 절차 | 어떤 요청이 전체 감사로 이어지는가? |
RECONNAISSANCE.md | 조사 질문과 장부의 식별자 설계 | 대상의 어느 경계를 조사할 것인가? |
HUNTING.md | 배정·발견자 결과·누락 검토 반복 | 조사하지 않은 범위를 어떻게 드러내는가? |
ATTACK-CLASSES.md | 입력 주입·접근 통제·업무 논리 등 기본 관점 | 같은 기능을 다른 경로로 호출해도 통제가 같은가? |
VALIDATION-AND-REPORTING.md | 후보 반박·최종 기록·보고서 작성 | 최종 문장이 증거보다 강해지지 않았는가? |
그다음 report-schema.json의 판정 한 분기와 대응하는 테스트 데이터를 읽습니다.
773줄 검증기를 처음부터 전부 이해하려 하기보다, 특정 필드가 어디서 검사되는지 찾아가는 편이 효율적입니다.
아래 문서는 해당 기술을 구현한 모듈이 아니라 그 기술을 사용하는 감사 대상을 읽는 방법입니다.
RECONNAISSANCE.md는 의존성 이름이 보인다는 이유만으로 선택하지 말고,
실제 신뢰 경계가 드러났을 때 관련 문서를 선택하라고 요구합니다.
| 파일 | 주로 따라가는 경계 |
|---|---|
AI-AND-LLM.md | 검색된 내용·기억·모델 출력이 도구 권한과 행동으로 이어지는 경로 |
CLIENT-SIDE.md | DOM 출력, 창 사이 메시지, 브라우저 저장소와 출처 |
WEB-PROTOCOL-AND-AUTH.md | HTTP 해석·캐시·세션·토큰·계정 전환 |
MEMORY-SAFETY-AND-BINARY.md | 메모리 범위·수명·동시성·FFI·바이너리 로딩 |
SUPPLY-CHAIN-AND-RELEASE.md | 의존성 출처·빌드 입력·CI 권한·서명·업데이트 |
CLOUD-AND-DEPLOYMENT.md | IAM·워크로드·배포 설정·이벤트 발신자·비밀정보 |
PROTOCOLS-RPC-AND-MESSAGING.md | 메시지 해석·요청 식별·큐·재전송·프로토콜 권한 |
RESOURCE-EXHAUSTION-AND-AVAILABILITY.md | 입력에서 공유 CPU·메모리·큐·비용으로 이어지는 경로 |
DATA-ISOLATION-AND-LIFECYCLE.md | 원본·캐시·검색·백업·삭제·복원 사이의 권한 유지 |
DESKTOP-MOBILE-AND-LOCAL-IPC.md | 딥링크·웹뷰·내보낸 구성요소·로컬 프로세스 간 권한 |
분야별 문서를 통째로 무작정 주입하기보다, 조사할 경계에 필요한 블록을 선택합니다. 선택한 블록과 제외한 이유를 장부에 남겨 나중에 프롬프트의 누락까지 검토할 수 있게 합니다.
validate()는 배열·객체·문자열을 재귀적으로 검사합니다.
hasOwn()은 상속된 속성을 필수 필드로 인정하지 않게 합니다.
테스트에는 Object.create()로 상속한 verdict를 거부하는 사례도 있습니다.
canonicalKey()는 객체 키를 정렬해 구조화된 값을 안정적으로 비교합니다.
키 순서가 다른 {"id": 0, "label": "0"}과 {"label": "0", "id": 0}은 같은 항목으로 취급됩니다.
중복 비교를 위해 만든 문자열과 집합 자체에도 메모리 한도가 있습니다.
실습 아이디어: 두 객체의 키 순서만 바꿔 종이에 정규 비교 키를 써 보세요.
문자열 "1"과 숫자 1이 왜 다른 키를 가져야 하는지도 설명해 봅니다.
oneOf는 대충 하나 이상 맞으면 되는 조건이 아닙니다. 정확히 한 분기만 맞아야 합니다.
이 저장소의 findDiscriminator()는 실패 시 verdict와 맞는 분기를 찾아 유용한 오류를 덧붙입니다.
오류 메시지가 명확해야 에이전트와 사람 모두 기록을 바로잡기 쉽습니다.
또한 visibleContent는 이 검증기가 추가로 해석하는 사용자 정의 키워드입니다.
일반 스키마 도구로 파일을 열었다고 같은 규칙이 모두 적용되는 것은 아닙니다.
collectSchemaErrors()는 지원하지 않는 키워드를 조용히 무시하지 않고 오류로 처리합니다.
실습 아이디어: 세 판정에서 공통 필드와 전용 필드를 색으로 나눠 보세요.
needs_validation에 severity를 넣으면 왜 의미까지 달라지는지 짧은 예로 설명합니다.
codePointLength()는 JavaScript 문자열의 UTF-16 코드 단위 수와 구별해 코드 포인트 수를 셉니다.
hasValidUnicodeScalarValues()는 짝이 맞지 않는 서로게이트를 거부합니다.
hasVisibleProse()는 공백·제어·표시되지 않는 문자만 있는 설명을 걸러냅니다.
반면 시험 입력을 보관하는 execution.payloads와 교체 코드의 fixed_code는 원문 그대로 남겨야 합니다.
producerShapedFindings() 테스트 데이터는 빈 문자열과 특수 문자를 포함해 이 차이를 점검합니다.
설명문을 정리하는 규칙과 증거 원문을 보존하는 규칙을 분리한 사례입니다.
실습 아이디어: "", 공백만 있는 문자열, 한글 설명, 이모지를 표로 나누고,
각각 “보고서 설명문”과 “재현 입력”으로 허용할지 근거를 적습니다.
isSafeRelativeSourcePath()와 isSafeRelativePath()는 절대 경로,
.., 역슬래시, 콜론, 일부 Windows 예약 이름 등을 거부합니다.
그러나 문자열이 안전해 보여도 실제 경로의 구성요소가 심볼릭 링크일 수 있습니다.
이 때문에 문서는 파일 기술자에 기반한 증거 보존 절차를 별도로 요구합니다.
실습 아이디어: 가상 경로 toy/auth.js, ../auth.js, C:/auth.js,
toy/con.txt를 검사 함수의 조건과 한 줄씩 대조합니다.
파일을 만들 필요 없이 문자열과 조건만으로 연습할 수 있습니다.
장부는 모델의 대화 기억 밖에 조사 상태를 둡니다.
검증자에게는 후보와 관련 경계·증거만 전달하며, 다른 검증자의 결론은 전달하지 않도록 지침이 정합니다.
attempts는 재조사의 과거 소유자와 증거를 현재 결과에 섞지 않고 보존합니다.
에이전트의 품질은 모델의 답뿐 아니라, 어떤 상태와 증거를 다음 단계에 넘기는지에도 달려 있습니다. 이 관점은 보안 감사 외에도 코드 리뷰, 데이터 정제, 문서 검토에 적용할 수 있습니다.
실습 아이디어: 조정자·발견자·검증자 세 역할의 입력과 출력만 표로 설계해 봅니다. 어떤 파일을 누가 쓸 수 있는지까지 적으면 프롬프트가 작업 프로토콜로 바뀌기 시작합니다.
AI-AND-LLM.md는 모델이 도구를 호출할 권한이 있는지와,
사용자가 그 정확한 작업을 의도했는지를 별개의 통제로 다룹니다.
사용자가 파일 정리를 요청했는데 검색된 문서가 추가 작업을 유도했다면,
일반적인 도구 권한만으로 그 추가 작업의 의도를 입증할 수 없습니다.
같은 문서는 “프롬프트 주입 문장이 보인다”만으로 취약점을 확정하지 말라고 요구합니다. 낮은 신뢰의 입력이 실제 권한 사용·데이터 노출·원치 않은 상태 변경으로 이어지는 코드 경로가 필요합니다.
실습 아이디어: 장난감 문서 검색 기능을 가정하고, 검색 문서 작성자·질문한 사용자·도구 실행 계정·변경될 자원을 네 칸에 나눠 적어 보세요.
테스트는 형식 오류뿐 아니라 FIFO, 심볼릭 링크, 잘못된 UTF-8, 깊은 중첩,
오류 메시지의 제어문자, 재배정 시 증거 혼합을 다룹니다.
장부 테스트의 archives prior evidence when a critic assigns a fresh owner 사례는
상태 저장 설계를 테스트로 설명하는 예입니다.
실습 아이디어: 테스트 이름 하나를 골라 “어떤 입력을 넣고, 무엇이 나오면 실패인가?”를 한국어로 다시 써 보세요. FIFO·심볼릭 링크 관련 테스트에는 OS와 안전한 파일 열기 플래그의 지원 여부에 따른 건너뛰기 조건이 있습니다.
| 목적 | 필요한 것 | 공식적으로 확인되지 않은 것 |
|---|---|---|
| 이 글의 읽기 실습 | 텍스트 편집기, 파일 검색 도구, 저장소 소스 | 전용 CPU·RAM·GPU 최소 사양 없음 |
| 검증기 사용 | 호환되는 Node.js, 안전한 파일 열기 플래그를 제공하는 OS | 최소 Node.js 버전과 OS 지원표 없음 |
| 전체 감사 | 도구 사용과 병렬 하위 에이전트를 지원하는 호스트, 모델 접근 | 특정 모델·공급자·GPU 메모리 요구 없음 |
| 대상 코드의 동작 검증 | 대상별 기존 도구와 모든 통제를 갖춘 OS 샌드박스 | 보편적인 빌드 시간·메모리·디스크 상한 없음 |
README의 요구사항은 기능 중심입니다. “최소 16GB RAM” 같은 수치는 없습니다. 모델을 원격으로 쓰는지 로컬에서 추론하는지에 따라 하드웨어가 달라지며, 이 레포는 추론 서버나 모델 가중치를 제공하지 않습니다. 현재 학습 과제는 소스 읽기이므로 별도 GPU가 필요한 작업을 포함하지 않습니다.
두 검증기의 readFileWithinLimit()는 O_NOFOLLOW와 O_NONBLOCK이
정수이면서 0이 아닌지 먼저 확인합니다. 이 보호 기능이 없으면 안전하지 않은 대체 경로를 사용하지 않고 입력을 거부합니다.
따라서 “Node.js가 설치된다”와 “이 CLI가 안전하게 파일을 읽는다”는 같은 조건이 아닙니다.
테스트에는 플랫폼에 따라 일부 사례를 건너뛰는 조건도 있습니다. Windows 경로를 거부하는 문자열 규칙이 있다고 해서 Windows에서 전체 감사가 검증됐다고 결론 내릴 수는 없습니다. 실제 지원 범위는 OS의 파일 API와 개별 테스트 조건을 함께 살펴야 합니다.
scratch/만 쓰게 합니다.이는 SKILL.md가 요구하는 조건입니다.
단순히 폴더 이름을 sandbox로 짓거나 프롬프트에 “안전하게 실행”이라고 적는 것으로 충족되지 않습니다.
필수 통제를 제공하지 못하면 대상 코드를 실행하지 않고, 구체적인 차단 요인을 기록하도록 지침이 정합니다.
최초 조사에는 기본 4개 역할이 있습니다.
한 번의 조사 묶음에서 발견자 수를 H, Phase 3에 넘길 후보 수를 C로 놓고,
모든 후보가 confirmed 또는 needs_validation으로 남는다고 가정하면,
quick은 대략 4 + H + 1 + C,
standard는 대략 4 + H + 2 + 2C번의 호출을 계획할 수 있습니다.
이 식은 최초 조사 4개, 조사 후 비평 1개, 표준 모드의 별도 최종 비평 1개, 후보별 검증 횟수를 더한 한 차례 실행의 학습용 계산입니다. 추가 조사·재배정·기록 교체 검증이 발생하면 늘어나며, 토큰 수나 금액을 예측한 공식은 아닙니다.
감사 예산을 줄일 때는 검증 기준을 낮추기보다, 조사 범위와 남은 공백을 명시해야 합니다. 소스의 예산 규칙도 비평·검증 호출을 먼저 예약하고, 그 안에서 발견자 작업을 배정하도록 요구합니다.
목표: README의 제품 설명을 실제 파일 구성과 대조합니다.
완료 기준: 구현 1,645줄, 테스트 1,392줄을 재확인하고 버전 미지정 항목을 최소 세 개 설명합니다.
# 저장소 루트에서 파일 읽기·집계만 하는 명령
rg --files --hidden -g '!**/.git/**'
wc -l skills/security-audit/validate-findings.cjs \
skills/security-audit/validate-coverage-ledger.cjs \
skills/security-audit/validate-findings.test.cjs \
skills/security-audit/validate-coverage-ledger.test.cjs
rg -n '^function ' skills/security-audit/validate-findings.cjs
목표: 아래 가상 예제를 report-schema.json의 두 번째 분기와 대조합니다.
예제의 toy/ 파일은 이 저장소에 존재하는 파일이 아닙니다.
severity를 덧붙였다고 가정하고 어떤 규칙을 위반하는지 찾습니다.완료 기준: 형식이 갖춰져도 실제 파일과 증거를 따로 확인해야 하는 이유를 설명합니다.
[
{
"verdict": "needs_validation",
"fingerprint": "toy-export-owner-scope",
"title": "가상 내보내기 경로의 소유권 범위 확인",
"description": "가상 함수의 호출자 권한 전달을 끝까지 확인하지 못했다.",
"claimed_root_cause": "중간 호출에서 소유자 범위가 빠질 가능성이 있다.",
"trace": [
{
"kind": "entrypoint",
"file": "toy/export.js",
"line": 12,
"scope": "exportDocument",
"description": "문서 식별자를 받는다고 가정한 예제 진입점이다."
}
],
"evidence": [
{
"file": "toy/export.js",
"line": 12,
"description": "학습용 가정이며 실제 취약점의 증거가 아니다."
}
],
"blockers": ["예제에서 후속 권한 검사 함수의 구현을 제공하지 않았다."],
"validation_plan": {
"local": "후속 함수 소스를 확보해 두 가상 소유자의 접근 조건을 대조한다."
}
}
]
목표: canonicalCoverageId()와 validateStateInvariants()를 읽습니다.
a, b, c, d로 정해 ID를 만듭니다.route name과 route-name으로 바꿔 결과를 비교합니다.covered인데 local_checks가 빈 경우, 어떤 조건에서 거부되는지 찾습니다.attempts에 보존하고 현재 증거를 비우는 이유를 적습니다.완료 기준: 기본 ID a::b::c::d를 도출하고, 서로 다른 상태의 금지 조합 두 개를 설명합니다.
목표: validate-findings.test.cjs의 rejects severity above demonstrated impact를 읽습니다.
confirmed()가 만드는 기본 데이터를 확인합니다.medium, 전체 심각도를 high로 바꾸는 부분을 찾습니다.SEVERITY_RANK와 의미 검사 분기까지 연결해 실패 이유를 설명합니다.완료 기준: 테스트를 실행하지 않고도 어떤 불변식을 검증하는지 세 문장으로 정리합니다.
목표: “대상 테스트를 호스트 환경 변수 그대로 실행하고 결과 폴더를 통째로 복사한다”는 가상 계획을 검토합니다.
SKILL.md에서 환경 허용 목록·쓰기 격리·자원 제한 요구를 찾습니다.scratch/의 파일이 실행 종료 후에도 왜 신뢰되지 않는지 설명합니다.완료 기준: 대상 코드를 실행하지 않고, 누락된 통제와 각 통제의 책임 주체를 표로 작성합니다.
좋은 실습의 완료 기준은 명령 실행 여부보다, 어떤 규칙이 어떤 실패를 막는지 설명할 수 있는가입니다. 위 다섯 과제는 저장소 파일을 수정하지 않고 진행할 수 있습니다.
주차는 진도 구분을 위한 제안입니다. 기간보다 각 주의 산출물을 설명할 수 있는지에 초점을 맞추세요. 1~4주는 JavaScript와 데이터 모델링, 5~6주는 보안 경계와 에이전트 절차를 연결합니다.
| 주차 | 배울 것과 읽을 파일 | 완료 산출물 |
|---|---|---|
| 1주차 | 객체·배열·함수·CommonJS. 검증기의 가져오기와 진입점, 두 테스트의 기본 데이터 생성 함수 | 입력→함수→오류 목록→종료 코드 흐름도 |
| 2주차 | JSON 계약, required·oneOf·추가 필드 금지. 스키마와 validate() | 세 판정의 필수·금지 필드 비교표 |
| 3주차 | 상태 기계·불변식·안정적인 ID. 장부 검증기의 정규화·정렬·중복 검사 | 장부 상태별 허용 데이터 표와 ID 계산 예제 |
| 4주차 | 파일 기술자·심볼릭 링크·자원 한도·Unicode. 파일 읽기와 사전 구조 검사 함수 | 입력 위협→통제→검사 함수 대응표 |
| 5주차 | 인증·인가·신뢰 경계·가설 반박. ATTACK-CLASSES.md와 관심 분야 문서 한 개 | 가상 기능의 입력→권한 검사→자원 접근 추적표 |
| 6주차 | 역할 분리·예산 예약·이전 증거 재확인. 핵심 지침 다섯 개 연결 | 읽기 전용 모의 감사 계획과 완료·미완료 기준 |
WEB-PROTOCOL-AND-AUTH.md에서 세션·계정 복구를 읽고,
CLIENT-SIDE.md로 화면 출력과 메시지 출처 검사를 연결합니다.AI-AND-LLM.md에서 도구 인자·권한·행동 의도의 연결을 읽고,
DATA-ISOLATION-AND-LIFECYCLE.md로 검색과 기억의 소유권을 확장합니다.PROTOCOLS-RPC-AND-MESSAGING.md의 재전송·요청 경계와
CLOUD-AND-DEPLOYMENT.md의 실행 신원을 함께 읽습니다.MEMORY-SAFETY-AND-BINARY.md의 수명·단위·FFI 계약을 읽고,
DESKTOP-MOBILE-AND-LOCAL-IPC.md에서 프로세스 간 권한을 따라갑니다.기술 이름을 많이 외우기보다, 한 입력이 어느 권한으로 어느 자원에 도달하는지 끝까지 따라가 보세요. 이 질문을 유지하면 새로운 프레임워크나 프로토콜을 만나도 같은 학습 틀을 사용할 수 있습니다.
“이 검사는 무엇을 보장한다.” “여기까지는 소스에서 확인했다.” “이후의 사실은 아직 없으므로 어떤 증거가 더 필요하다.” 세 문장을 구분하는 습관이 코드 리뷰와 기술 문서의 정확도를 함께 높입니다.
| 용어 | 쉬운 설명 |
|---|---|
| 스킬 | 에이전트가 작업할 때 읽는 지침 묶음. 이 저장소의 실행 엔진은 호스트입니다. |
| 하네스 | 모델·도구·반복·상태·평가를 둘러싸 작업을 수행시키는 기반 장치. 공개 스킬과 전체 사내 하네스를 구별해야 합니다. |
| Parent | 역할을 배정하고 공유 장부와 최종 기록을 쓰는 조정자입니다. |
| 발견자 | 배정된 경계에서 취약점 후보를 찾는 역할입니다. 최종 확정 권한과 분리됩니다. |
| 검증자 | 발견자의 가설을 반박하려고 소스와 증거를 새로 확인하는 역할입니다. |
| 범위 비평가 | 후보의 진위보다 조사에서 빠진 표면·경계·경로를 찾는 역할입니다. |
| 주체 (principal) | 권한을 가지고 행동하는 사용자·서비스 계정·프로세스 등입니다. |
| 인증 / 인가 | 누구인지 확인하는 일과, 그 사람이 이 자원에 이 작업을 해도 되는지 확인하는 일입니다. |
| 신뢰 경계 | 서로 다른 권한·신뢰 수준 사이에서 입력을 검사하고 권한을 제한해야 하는 지점입니다. |
| 불변식 | 상태가 바뀌어도 지켜야 하는 규칙. 예: 사용자 A는 B의 비공개 문서를 읽을 수 없습니다. |
| 입력 표면 | HTTP 요청·파일·메시지·도구 인자처럼 외부나 낮은 신뢰의 값이 들어오는 곳입니다. |
| entrypoint / sink | 추적의 시작점과 보안상 결과가 발생하는 끝점. 중간 단계는 propagation입니다. |
| coverage-ledger | 어느 경계를 누가 어떤 증거로 검토했는지 적는 범위 장부입니다. |
| canonical_refs | 표시용 이름과 별개로 같은 소스 대상을 일관되게 가리키는 정규 참조입니다. |
| coverage_id | 정규 참조들을 인코딩해 만든 조사 단위의 ID입니다. 판정이 바뀌어도 조사 대상의 정체성을 유지합니다. |
| fingerprint | 같은 근본 원인의 후보를 연결하는 식별자입니다. 범위 ID와 목적이 다릅니다. |
| oneOf | 여러 계약 가운데 정확히 하나에 맞아야 한다는 조건입니다. |
| 의미 검증 | 자료형뿐 아니라 필드 사이의 관계를 검사하는 일. 전체 심각도가 영향보다 높지 않아야 하는 규칙이 예입니다. |
| NFC / 퍼센트 인코딩 | Unicode 표현의 정규형과, UTF-8 바이트를 %HH로 표현하는 방식입니다. ID의 일관성을 위해 사용합니다. |
| 파일 기술자 | 운영체제가 열린 파일을 가리키는 번호입니다. 경로를 다시 여는 대신 이미 연 대상을 검사하고 읽을 때 사용합니다. |
| 심볼릭 링크 | 다른 경로를 가리키는 파일입니다. 보이는 경로와 실제 접근 대상을 다르게 만들 수 있습니다. |
| TOCTOU | 검사한 시점과 실제 사용하는 시점 사이에 대상이 바뀌는 문제입니다. |
| 샌드박스 | 코드가 접근할 네트워크·파일·자원을 OS 수준에서 제한한 실행 환경입니다. |
| scratch / artifacts | 신뢰되지 않는 임시 작업 공간과, 조정자가 검사를 거쳐 보존하는 증거 공간입니다. |
| 심각도 / 확신도 | 문제의 피해·우선순위와 증거를 얼마나 신뢰하는지를 나타내는 서로 다른 평가입니다. |
| 행동 의도 연결 | 사용자가 의도하거나 승인한 정확한 주체·작업·대상·인자와 실행을 묶는 통제입니다. |
| hardening | 입증된 경계 침해는 없어도 방어를 강화할 수 있는 개선입니다. 확정 취약점과 나누어 기록합니다. |
| needs_validation | 구체적인 소스 근거가 있으나 결정적 사실이 빠진 상태입니다. 근거 없는 추측을 쌓는 칸이 아닙니다. |
장부의 candidate와 발견 기록의 confirmed는 서로 다른 축입니다.
전자는 조사 단위가 후보 지문과 연결되어 있음을, 후자는 후보의 최종 판정을 나타냅니다.
두 값을 하나의 상태 목록으로 섞지 않는 것이 데이터 모델을 이해하는 첫걸음입니다.
node:test의 테스트·건너뛰기·하위 테스트 개념을 확인합니다.oneOf·anyOf·allOf의 차이를 학습합니다.| 항목 | 확인 값 | 근거와 범위 |
|---|---|---|
| 선정 근거 | TrendShift Daily #9 | 2026-10-23 TrendShift 기준 |
| 스타 / 포크 | 40,122 / 2,502 | 2026-10-23 GitHub 기준 |
| 주 언어 | JavaScript | 2026-10-23 GitHub 기준 |
| 생성일 | 2026-06-18 | 2026-10-23 GitHub 기준 |
| 최신 릴리스 | 없음 | 2026-10-23 GitHub 기준 |
| 최근 push | 2026-09-14 | 2026-10-23 GitHub 기준 |
| 최근 커밋 | c1c8a8c · 2026-09-14Clarify guidance and full audit modes | 커밋 c1c8a8c 기준 |
| 코드 규모 | 22파일 · 총 5,403줄 JavaScript 3,037줄, Markdown 1,884줄, JSON 461줄, LICENSE 21줄 | .git/ 제외, 빈 줄·주석 포함 직접 집계 |
| 검증기 테스트 | 최상위 선언 65개 | 발견 기록 34개 + 장부 31개. 정적 선언 수 기준 |
| 라이선스 | MIT | 2026-10-23 GitHub 및 LICENSE 기준. 저작권 표기 2025–2026 Cloudflare, Inc. |
| 버전·배포 정의 | Node.js 최소 버전 미지정 의존성 명세·Dockerfile·CI 정의 없음 | 해당 HEAD의 추적 파일 기준 |
소스 설명은 커밋 c1c8a8c1471069fb0e188eeaff69b8e8db6564a8 기준입니다.
이 시점의 파일 구성만으로 전체 커밋 이력·기여자 수·개발 속도를 판단할 수는 없습니다.
스타·포크·생성일·push·릴리스는 2026-10-23 GitHub 기준입니다.
검증기의 형식 검사, 호스트의 감사 실행, 실제 탐지 성능은 구분해야 합니다. 성능·비용은 대상과 실행 환경에 따라 달라집니다. README의 반복 실행 성과는 저자의 관찰이며, 이를 재현할 데이터는 저장소에 없습니다.
지침이 요구하는 독립 검토·쓰기 격리·OS 샌드박스가 실제로 작동하는지는 호스트 구현에 달려 있습니다. 이 저장소에는 그 실행 기반이나 Cloudflare 사내 전체 시스템이 포함되어 있지 않습니다. 확정 기록이 0개이거나 JSON 검사가 통과했다는 사실만으로 대상 전체가 안전하다고 결론 내릴 수 없습니다.