尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
Supabase Auth 이메일·비밀번호 로그인과 로그아웃을 연결하고, 자료 API가 기존 src/verify-login.mjs로 인증 토큰을 확인합니다. 비로그인 또는 잘못된 토큰 요청은 메모 없이 JSON 오류로 거부합니다. 비밀번호와 JWT는 공식 SDK에서 처리하며 직접 만들지 않습니다. 2단계 공개 정적 파일의 메모 제거, Supabase 자료 저장, RLS 및 브라우저 직접 접근 제한을 유지합니다. 기존 숫자 ID와 메모 본문을 보존하고 api_id UUID를 추가하여 API의 id로 사용합니다. DB content는 API body로 변환합니다. 기존 메모의 owner_id를 자동으로 변경하지 않으므로 소유자가 없는 기존 자료는 내 메모 목록에 나오지 않습니다.查看完整 README ↗
Supabase Auth 이메일·비밀번호 로그인과 로그아웃을 연결하고, 자료 API가 기존 src/verify-login.mjs로 인증 토큰을 확인합니다. 비로그인 또는 잘못된 토큰 요청은 메모 없이 JSON 오류로 거부합니다. 비밀번호와 JWT는 공식 SDK에서 처리하며 직접 만들지 않습니다.
2단계 공개 정적 파일의 메모 제거, Supabase 자료 저장, RLS 및 브라우저 직접 접근 제한을 유지합니다. 기존 숫자 ID와 메모 본문을 보존하고 api_id UUID를 추가하여 API의 id로 사용합니다. DB content는 API body로 변환합니다. 기존 메모의 owner_id를 자동으로 변경하지 않으므로 소유자가 없는 기존 자료는 내 메모 목록에 나오지 않습니다.
sql/stage3-notes.sql을 실행합니다. 기존 메모는 삭제하지 않습니다.SUPABASE_SECRET_KEY를 유지합니다. 서버 전용 키를 브라우저 코드에 넣지 않습니다. 공개 프로젝트 URL과 publishable key만 public/app.js에 있습니다./api/notes는 HTTP 401 JSON 오류./aleph.json은 빌드에서 자동 생성합니다. 첫 화면에 X-Content-Type-Options: nosniff 헤더를 설정합니다.| 메서드 | 경로 | 응답 |
|---|---|---|
| GET | /api/notes | 로그인한 사용자 소유 메모 배열 |
| POST | /api/notes | {id}; 요청 {id?,title,body} |
| GET | /api/notes/:id | {id,title,body} 또는 404 |
| PUT | /api/notes/:id | {id,title,body}; 요청 {title,body} |
| DELETE | /api/notes/:id | {id} 또는 404 |
추가 시 owner_id는 서버가 검증한 사용자 ID로 저장합니다. 브라우저의 userId·role·owner_id는 신원으로 사용하지 않습니다. 단건 GET·PUT·DELETE는 검증된 사용자 ID와 owner_id가 일치하는 행만 허용합니다. 상대 메모는 404 JSON으로 거부하며, 추가·수정 본문의 owner_id 지정은 400으로 거부합니다. 현재 메모는 가상 자료만 사용합니다.
npm cinode --test test/stage3.test.mjsnpm run build (Vercel 배포 식별 환경변수 필요)npm run bundlebundle-notes.json과 artifacts/submission.json은 Git에서 제외합니다. 실제 배포 익명 요청·배포 식별·헤더 결과는 자기 점검이며 심판 판정이 아닙니다. A 로그인 및 실제 DB CRUD는 계정과 DB 설정 후 직접 확인해야 하며, 로컬 모의 DB 검사로 실제 로그인 성공을 주장하지 않습니다.
기존 가상 메모 4건을 A 소유로 연결하고 B 시험 메모 1건을 추가했습니다. 메모 테이블의 PUBLIC·anon·authenticated 권한을 회수한 뒤 authenticated에 SELECT·INSERT·UPDATE·DELETE만 허용했습니다. RLS는 SELECT·DELETE의 USING, INSERT의 WITH CHECK, UPDATE의 USING과 WITH CHECK에서 auth.uid()와 owner_id를 비교합니다. 서버 API는 서버 전용 키를 사용하므로 API에서도 소유자를 별도로 검사합니다. 사용자가 SQL 적용 후 권한 결과를 확인했습니다. 실제 배포의 A/B CRUD와 상대 메모 거부는 배포 후 확인해야 합니다.
브라우저의 메모 CRUD는 이미 /api/notes와 /api/notes/:id만 호출합니다. 직접 자료 호출이 없어 화면과 Auth 코드는 변경하지 않았습니다. 서버 전용 키와 로그인·소유자 검사를 유지합니다.
sql/stage5-notes.sql은 public.notes만 대상으로 PUBLIC·anon·authenticated의 테이블 권한과 별도 컬럼 권한을 회수합니다. 기존 RLS·소유자·자료와 service_role 권한은 보존합니다. SQL은 준비만 했으며 학습 DB 적용은 사용자가 검토 후 실행해야 합니다.
originalApiUrl은 쿼리 없는 HTTPS 원본 메모 경로입니다. allowedRoutes 다섯 개와 nosniff 헤더 설정을 유지했습니다. 공개 키는 Auth용으로 화면 코드에 남아 있으므로 공개 키 제거 가점은 구현하지 않았습니다.
검사: npm ci → node --test test/stage3.test.mjs → 저장점 커밋 후 npm run bundle. 로컬 모의 DB CRUD 통과는 실제 A/B 로그인 검사나 SQL 적용 완료를 뜻하지 않습니다. 제출 JSON과 설명 파일은 Git에서 제외합니다.
판정기는 운영 엔진이 확인한 request.deviceRegistered가 true일 때만 allow·approved를 반환합니다. false이거나 값이 없으면 deny·device_not_registered로 거부합니다. 구현한 규칙 ID는 device_registered입니다. 응답의 다섯 항목과 기존 로그인·메모 API·화면을 유지합니다. 인증서, CA, 서명 키, 기기 개인키는 생성하지 않았으며 별도 접속 프로그램 설치는 없습니다.
확인 순서:
이번 실행에서 등록·미등록 두 가상 요청 검사가 통과했습니다. 이는 로컬 연습 결과이며 실제 심판 판정이나 실제 기기 접속 확인이 아닙니다. 기존 배포 화면은 직접 열어 로그인과 본인 메모 조회를 확인합니다. 저장점 커밋 후 npm run bundle로 제출 묶음을 생성합니다.
기존 등록 기기 규칙을 유지하고 판정기 입력의 계약 항목과 신호 출처를 검사합니다. 누락되거나 출처가 불명확한 신호를 정상값으로 채우지 않습니다. 브라우저 헤더 등 계약 밖 항목은 정상 입력으로 인정하지 않습니다. 실제 접속의 source=none, region/network=unknown은 증거 없음으로 유지합니다.
judge_fixture 가상 사건에서 새벽 0~5시, unusual 네트워크, 최근 120초 이내 risk_signal 20건 이상이 겹치면 step_up·step_up_required와 step_up_required 사유를 반환합니다. 지역 하나만으로 차단하지 않습니다.
사용자 로컬 실행에서 정상 allow, 복합 이상 step_up, 미등록 기기 deny, 누락·출처 불명·위조 헤더 추가 확인을 확인했습니다. 가상 복합 이상 사건은 추가 확인 전 step_up이며, 계약 형식의 verified=true와 0~900초 authAgeSeconds가 확인되면 allow입니다.
현재 제공된 계약에는 같은 로그인 주체의 새 로그인 시각을 검증하는 절차가 명시되어 있지 않습니다. 실제 재로그인 전 자료 차단과 새 로그인 후 허용은 미실행입니다. 기존 자료 API에 가상 신호를 연결하거나 토큰 원문을 기록하지 않습니다.
재실행: npm run decider:test && npm run fixture:7 정상 사건은 allow, 복합 이상 사건은 step_up이어야 합니다. 가상 연습 결과는 실제 심판 판정이나 실제 자료 차단 증거가 아닙니다.
추가 확인 규칙 이름을 제공 연습 스크립트의 step_up_required에 맞췄습니다. 가상 재확인 뒤 허용 분기를 추가했습니다. 사용자 실행에서 정상·미등록·복합 이상·재확인 뒤 허용 4건이 통과했습니다. 실제 재로그인 전후 자료 차단 시험은 계속 미실행입니다.
메모와 공지를 notes/UUID, announcements/UUID 자원 ID로 구분합니다. 기존 메모 ID·주소·본문·화면 메뉴와 6·7단계 규칙을 유지합니다.
판정기는 정확한 서비스·메서드·경로와 검증된 주체·기기 입력을 확인합니다. 알 수 없는 경로, 전체 메모 목록 /notes, 기능 경로와 쿼리 기능 선택을 거부합니다. 계약에 강사 역할이 없으므로 판정기의 공지 쓰기는 모두 거부합니다. 기존 /api/notes 목록은 서버의 본인 소유자 필터를 유지합니다.
최종 메모 API는 검증된 사용자와 저장된 owner_id를 대조합니다. 공지 수정 API는 보호된 vault_roles의 instructor 권한을 확인합니다. 요청 본문의 role·userId·owner_id로 권한을 부여하지 않습니다. 거부 로그에는 짧은 이유 코드만 남깁니다.
사용자가 공지·권한 테이블 SQL을 적용했습니다. 두 테이블의 RLS 활성화, anon·authenticated 읽기·수정 금지, service_role 읽기·수정 허용을 사용자 실행 결과로 확인했습니다.
재실행: npm run decider:test npm run fixture:7 node --test test/stage3.test.mjs test/stage8-api.test.mjs test/stage8-decider.test.mjs
사용자 실행에서 로컬 검사 16개와 기존 판정기 연습이 통과했습니다. 실제 배포의 정상 CRUD·상대 메모 거부·학생 공지 수정 거부는 아직 미실행입니다. 로컬 검사 결과를 실제 심판 판정으로 보고하지 않습니다.
메모 경로와 동일한 id 쿼리만 허용합니다. 다른 ID, 중복 ID, 기능 선택 쿼리는 거부하며 기존 소유권 검사는 유지합니다. 사용자 실행에서 로컬 검사 17개가 통과했습니다. 실제 배포 수정 동작은 재확인 전입니다.
사용자가 로그인·메모 저장·수정·삭제 정상 동작을 확인했습니다. B의 A 메모 GET·PUT은 404, B 목록은 200이며 A 메모는 제외됐습니다. 학생의 role 위조 공지 수정은 403 instructor_required로 거부됐습니다. 로컬 검사 17개 통과와 실제 API 확인을 구분하며, 심판 판정은 제출 후 확인합니다.
운영 엔진이 검증한 가상 사건에서 시간, 요청 메서드, 가명 사용자만 추려 읽습니다. 메모 본문, 토큰, 실명은 탐지 기록이나 출력에 포함하지 않습니다. 같은 가명 사용자의 GET 요청이 어느 10분 구간에서든 120건을 초과하면 별도 탐지 결과로 propose_revocation, burst_read, 해당 subject를 반환합니다. 정확히 120건, 다른 사용자별 정상 조회, 시간 범위 밖으로 흩어진 조회는 observe입니다.
기존 접속 허용·거부 응답과 로그인·자료 API는 유지합니다. 회수 API를 만들거나 권한을 직접 변경하지 않습니다. 제공된 연습 계약에는 네트워크와 기기 식별자가 없어 네트워크 복합 탐지와 특정 기기 하나 지정은 구현하지 않았습니다. 실제 회수, 정상 기기 유지, 경과 시간은 심판 확인이 필요합니다.
재실행: npm run detect:test && npm run decider:test 사용자 실행 결과 탐지 연습 6건과 기존 판정기 연습 4건이 통과했습니다. 이는 로컬 가상 검사 결과이며 실제 심판 통과 증거가 아닙니다.
9단계 저장점 ZIP의 인증·접근 정책 파일 7개를 새 사본으로 보관했습니다. 이는 현재 시점에 만든 코드 정책 사본이며, 과거 침해 이전 DB 백업이 있다는 뜻은 아닙니다. 명세의 고정 SHA-256과 각 파일 SHA-256을 다시 읽어 대조하며, 누락·변조 시 중단합니다. 사본에는 메모 본문과 서버 비밀값이 없습니다.
시험 폴더 준비(Git Bash): mkdir -p .trial-policy && cp -R backups/policy-stage9/src .trial-policy/
비교: node scripts/policy-diff.mjs .trial-policy
정상 결과는 backupVerified=true, 복원 버전, 변경 목록, applied=false입니다.
시험 폴더의 src/resource-policy.mjs만 편집하고 다시 실행하면 해당 파일의 전후 해시를 표시합니다.
사본 파일이 없거나 변조되면 종료 코드 1로 중단하며 비교 결과도 출력하지 않습니다.
이 명령은 파일·DB·운영 정책·다른 서비스 규칙·감사 이력을 쓰거나 복원하지 않습니다.
실제 정책 복원, B 메모 백업·복원, 배포 후 재시험은 아직 미실행입니다.
node scripts/policy-restore.mjs .trial-policy로 버전·변경 파일·전후 해시·확인값을 먼저 확인합니다.
같은 명령 뒤에 --apply 표시된확인값을 붙이면 해당 차이만 복원합니다.
변경이 달라져 확인값이 맞지 않거나 사본 검증에 실패하면 중단합니다.
범위는 프로젝트 안 .trial-policy의 지정 정책 파일만이며 운영 src, DB 정책, 다른 서비스 파일과 감사 기록은 변경하지 않습니다.
사본은 모든 파일을 다시 검증하고 적용 후 해시 일치를 확인합니다.
이는 코드 정책의 시험 복원입니다. DB의 소유권 정책 복구나 실제 배포 복구를 뜻하지 않습니다.
복구 후 가상 정상/금지 요청은 아래 명령으로 확인합니다.
node --test .trial-policy/test/stage8-api.test.mjs 같은 명령을 임의로 사용하지 않습니다. 시험 폴더에는 테스트와 의존성 파일이 없습니다.
실제 A 정상 접근/B의 A 메모 접근 거부 확인은 배포·자료 복원 후 별도 실행하며 현재 미실행입니다.
서비스: 기존 Vercel 자료실/Supabase. POST /api/restore, 요청은 {"id":"복원 대상 UUID"}입니다. 기존 로그인 검증을 사용하고 검증된 사용자 ID로 본인 사본만 선택합니다. 심판 토큰은 시험 B만 허용합니다. 일반 사용자도 본인 자료만 복구할 수 있으며 확인 시험은 B의 가상 메모로만 합니다. 복구 결과에는 ID·소유자·내용 SHA-256·verified만 포함하고 메모 본문과 토큰은 출력하지 않습니다.
Supabase SQL Editor에서 sql/stage10-recovery.sql을 먼저 적용해야 합니다(아직 미적용). SQL 적용 이후 새로 만든 가상 메모의 최초 INSERT 사본을 DB 전용 테이블에 보관합니다. UPDATE는 사본을 덮어쓰지 않습니다. 기존 메모에는 사본을 소급 생성하지 않으며 사본이 없으면 복원을 거부합니다. 새 메모 전체의 최초 사본을 비공개로 저장하지만 한 번의 복구는 요청한 한 건만 처리합니다. 백업·감사 테이블은 클라이언트와 service_role의 직접 접근도 차단하며 보호된 함수만 사용합니다. 함수는 사본 ID·소유자·내용 해시를 다시 확인하고 한 트랜잭션에서 복원·재검증·감사 추가를 수행합니다. 감사 이력과 다른 메모는 삭제하지 않습니다. 운영 접근 정책과 앞 단계 판정기는 변경하지 않습니다.
실제 DB SQL 적용, 새 B 메모의 사전 백업, 변조/삭제 후 복원과 A/B 재시험은 미실행입니다. 기존 B 메모는 사본이 없으므로 SQL 적용과 배포 후 B로 새 가상 시험 메모를 만들어 확인합니다.
로컬 재실행: node --test test/stage10-restore.test.mjs test/stage3.test.mjs test/stage8-api.test.mjs test/stage8-decider.test.mjs → npm run decider:test → npm run fixture:7 → npm run detect:test.
사용자 실행에서 로컬 19개 통과. 사용자가 DB SQL 성공을 확인했습니다. 실제 배포 복구와 복구 이후 1~9단계 재시험은 아직 미실행입니다.
복구 시험 보조: B로 로그인한 자료실에서 개발자 도구 Console에 import('/recovery-check.js').then(m=>m.runRecoveryCheck()) 실행. 새로 만든 시험 한 건만 변조/삭제/복구하며 다른 메모는 변경하지 않습니다.
보조 검사는 자료 복원 검사이며 정책 복원이나 심판 판정이 아닙니다.
B 새 가상 메모의 사전 사본 검증, 변조 복구, 삭제 복구, 기존 B 메모 보존을 사용자 실행으로 확인했습니다. 복구 후 B의 A 메모 조회와 복구 요청은 각각 404였습니다. 이후 판정기 4건, 7단계 연습, 탐지 6건이 통과했습니다. 이 결과는 실제 운영 정책 복원이나 1~9단계 전체 배포 재시험 완료를 뜻하지 않습니다.
기존 public 화면에 manifest, PNG 아이콘(192/512), 설치 안내와 오프라인 안내를 붙였습니다. 설치 여부를 기기 승인으로 사용하지 않습니다. 서비스 워커에는 공개 오프라인 안내만 보관하고 로그인 토큰, 메모, API 응답은 캐시하지 않습니다.
Supabase Auth의 persistSession=false와 detectSessionInUrl=false를 적용했습니다. 이 자료실이 이전에 저장한 SDK 로그인 세션 항목만 제거하고, 다른 사이트/작업의 저장소는 지우지 않습니다. 기기 개인키는 WebCrypto의 비추출 CryptoKey로 현재 화면 메모리에만 둡니다. 페이지를 다시 열면 재로그인과 재승인이 필요합니다. 영구적인 관리 기기 증명을 구현한 것은 아닙니다.
POST /api/device는 로그인 검증 후 공개키를 pending 상태로 등록합니다. 브라우저 버튼은 승인하지 않으며, 관리자가 Supabase SQL Editor에서 화면의 기기 지문을 대조하고 승인합니다. 서비스용 DB 역할은 승인 행을 UPDATE할 권한이 없고, anon/authenticated는 기기·nonce 테이블과 함수에 접근할 수 없습니다. GET /api/device?id=기기지문은 본인 승인 상태만 반환하며 보호 메모를 반환하지 않습니다.
메모·공지·복원 API는 기존 인증 후 기기 승인과 ECDSA P-256 서명을 검증하고 기존 자료 처리로 진행합니다. 서명은 배포 origin, 로그인 토큰의 SHA-256, 기기 지문, 메서드, 실제 요청 경로와 쿼리, 정렬한 JSON 본문 SHA-256, 시각, 일회용 UUID를 묶습니다. 서명 없음, 서명 뒤 경로/본문 변경, 계정 변경, 만료/회수 기기, 재사용 요청은 거부합니다. nonce 기록은 승인 상태를 다시 검사한 DB 함수에서 원자적으로 수행하며 장애 시 허용하지 않습니다.
UPDATE public.vault_devices
SET status='approved', approved_at=now(), expires_at=now()+interval '8 hours'
WHERE id='화면에서 확인한 64자리 기기 지문' AND status='pending'
RETURNING id,status,expires_at;
회수는 관리자가 같은 지문에 status='revoked'를 설정합니다. 다음 보호 요청에서 거부됩니다. 새 탭/새로고침 후 새로운 지문을 승인할 때 이전 지문의 권한을 회수할 수 있습니다. 다른 기기/사용자를 일괄 승인하거나 회수하지 않습니다.
로컬 실행: npm ci 후 아래 API/서명 검사 29건 통과, 기존 판정기 4건·7단계 연습·탐지 6건 통과. 브라우저 화면 검사는 별도 기록하며, DB SQL 적용과 실제 배포의 승인 전후 확인은 아직 미실행입니다. 로컬 결과를 운영 심판 통과로 주장하지 않습니다. 운영 엔진의 별도 서명/승인 계약과 호환됨을 확인하지 않았습니다. 현재 자료실의 자체 기기 승인 및 서명 계약은 docs/STAGE11_DEVICE.md에 기록합니다. 판정기 요청 18개 이름과 응답 5개 이름, judgeIssuer, 기존 RULE_IDS는 변경하지 않습니다.
재실행:
node --test test/stage11-device.test.mjs test/stage3.test.mjs test/stage8-api.test.mjs test/stage8-decider.test.mjs test/stage10-restore.test.mjs
npm run decider:test && npm run fixture:7 && npm run detect:test
저장점은 DB/배포 확인 후 README 상태를 갱신하고 「11단계 저장점」으로 커밋한 뒤 npm run bundle로 생성합니다.
사용자 PC에서 로컬 검사 29건이 모두 통과했습니다. 사용자가 Supabase 기기 승인 테이블·검증 함수 SQL 적용 성공을 확인했습니다. 실제 배포의 승인 전후 화면과 심판 판정은 아직 확인 전입니다.
사용자 실제 배포에서 승인 전 메모 차단, 승인 후 기존 B 메모 표시, B 시험 메모 추가를 확인했습니다. 단건 조회/수정에서 invalid_device_signature가 발생했습니다. Vercel의 동일 id 라우팅 쿼리 추가가 원인 후보입니다. 같은 UUID 경로에 동일 id 쿼리가 하나 있고 req.query.id도 일치하는 경우에만 원래 경로 서명을 검증합니다. 원문 경로의 서명도 계속 확인하며 다른 id, 중복/기능 쿼리, 다른 자원 경로, 수정 본문 변조는 거부합니다. 추가 회귀 검사 3건을 포함한 로컬 검사 32건 통과. 사용자 배포 수정/삭제 재검사는 아직 미실행입니다.
사용자가 B 기기 승인 후 기존 메모 조회·추가·수정·삭제 정상 동작을 확인했습니다. 단건 경로 보완 후 로컬 검사 32건이 통과했습니다. 서명 없음·경로 변경·본문 변경 거부는 로컬 검사 결과이며, 실제 심판 판정은 제출 후 확인합니다.
운영 측 제공 엔진은 ALEPH SDP 엔진입니다. 운영 엔진의 신원·기기·경로 검증과 심판 집행은 제공 기능이며 학생이 개발했다고 주장하지 않습니다. 학생 구현은 src/decider.mjs의 판정 규칙, src/detect.mjs의 탐지 규칙, 자료실 API와 화면, 자체 기기 승인·서명 접속 앱, 복구 API, 작전실 요약 API입니다. 학생 판정기는 docs/DECIDER_REQUEST.md의 18개 요청 항목과 5개 응답 항목을 따릅니다. 자체 접속 앱의 기기 계약과 운영 엔진 계약은 별개이며 호환 완료를 주장하지 않습니다.
판정기·탐지 연습은 명령 실행 후 종료됩니다. 진행 중인 로컬 명령은 Ctrl+C로 중지합니다. 접속 앱은 로그아웃 후 창을 닫습니다. 이것은 서버 전체 중지나 기기 회수를 뜻하지 않습니다. 특정 시험 기기를 중지하려면 관리자가 해당 지문 한 건만 revoked로 바꾸고 다음 보호 요청의 거부를 확인합니다. 배포 전체 중지가 필요하면 Vercel 프로젝트 관리 화면에서 중지·접근 제한 기능과 적용 범위를 확인합니다. 프로젝트나 DB를 삭제하지 않습니다. 운영 엔진 중지는 수업 운영자의 관리 절차를 따릅니다.
정책 사본은 backups/policy-stage9이며 보관 버전은 stage9-2af611cfa0c2입니다. policy-diff와 policy-restore는 앞 단계 안내대로 .trial-policy의 지정 파일만 비교·복원합니다. 이 절차는 운영 DB 정책 복구가 아닙니다. 자료 복구는 승인된 접속에서 POST /api/restore로 본인 시험 메모 한 건만 요청하며, 복구 결과의 ID·소유자·해시 검증과 다른 자료 보존을 확인합니다.
12단계 코드 장애 시 git status와 git diff로 다른 변경을 먼저 구분하고, 복구 대상 파일과 사라질 내용을 확인받은 뒤 기준 커밋의 해당 파일만 복원합니다. 새로 추가한 파일은 별도 목록으로 확인하며 일괄 삭제하지 않습니다. 배포 복구는 확인된 이전 Vercel 배포를 선택하고 /aleph.json과 정상·금지 요청을 재확인합니다. 코드 복구는 DB 자료·감사 이력·관리자 기기 승인 상태를 자동으로 되돌리지 않습니다. reset --hard는 사용하지 않습니다.
GET /api/operations는 기존 로그인·기기 서명 검증을 거친 읽기 전용 API입니다. 판정기 규칙 6개는 설정 목록이며 실제 판정 성공 횟수가 아닙니다. 현재 요청 기기 상태만 DB에서 다시 확인하고 전체 회수 건수는 제공하지 않습니다. 탐지 사건과 복구 감사의 조회 연결은 미구현이므로 수치를 null과 미연결로 구분합니다. 정책 사본 버전은 manifest에서 읽은 보관 정보이며 사본 무결성 검증이나 복구 완료 증거가 아닙니다. 41종 표시는 이 화면에서 시험을 실행하지 않았다는 뜻입니다. 심판의 누적 결과를 조회하지 않습니다. 서버 로그에는 동일 요청 ID·확인 시각·요약만 남기며 신원·메모 본문·토큰·기기 지문을 남기지 않습니다. 작전실 화면은 로그아웃·오프라인·페이지 이탈 시 요약을 비웁니다.
사용자 PC에서 작전실 모의 API 검사 4건과 기존 회귀 검사 32건이 통과했습니다. 이 결과는 실제 배포, 운영 엔진 연동, 공식 41종 통과 증거가 아닙니다. 12단계 실제 배포 확인은 아직 미실행입니다. 정상·금지 요청의 기대값·실측·요청 ID·확인 시각 기록 절차는 실제 요청 확인표를 따릅니다.
검사한 배포 커밋: 911097a4006f68d6629f61b1faf12f55e05875e7. 승인 기기의 작전실 정상 조회 HTTP 200을 확인했습니다. 화면과 Vercel operations_summary 로그의 요청 ID, 서버 시각, 규칙 6개, 현재 기기 approved, 정책 사본 보관 버전이 일치했습니다. 요청 ID: 89557c02-02fa-4414-a1ec-dc9d293caa92 서버 확인 시각: 2026-10-14T03:20:57.542Z 무인증 작전실 요청은 HTTP 401이며 요약이 없었습니다. 탐지·전체 회수·실제 복구 조회는 미연결이고 공식 41종은 미실행입니다. 이 기록은 위 배포 커밋의 실측이며 이후 커밋 전체의 재시험을 뜻하지 않습니다.
수업용 Wazuh 경보 28건을 사용합니다. 원본 경보와 기존 ZTNA 규칙은 변경하지 않았습니다. read-alerts.mjs는 시각·출발 주소·가상 계정·규칙 수준·설명만 추출합니다. patterns.json은 MITRE ATT&CK T1110 및 T1110.003에 근거하며 수치 기준은 연습용 정책입니다. decide.mjs는 패턴을 내장하고 import·파일 접근·네트워크 없이 판단합니다. 확신도 0.85 이상은 block, 0.5 이상은 alert, 그 아래는 record입니다.
사용자 실행 결과: block 10 / alert 9 / record 9 / 정상 이벤트 차단 0. respond.mjs는 명확한 공격 주소 9개에 처리 시점부터 15분짜리 임시 거부 규칙을 생성합니다. 규칙에는 만료 시각과 근거 경보 번호가 있으며 알림은 xdr/alerts.log에 기록합니다. 로컬 연결 함수에서 공격 거부·애매/정상 통과·만료 해제·기존 거부 유지가 확인됐습니다.
기존 운영 판정기 계약에는 출발 IP가 없으며 실제 운영 연결은 미연결입니다. decideWithXdr는 서버/엔진이 확인한 출발 IP를 별도로 받는 연결 함수입니다. 브라우저 입력을 신뢰하거나 기존 요청 18개 항목을 변경하지 않습니다. 운영 연결 시 신뢰할 수 있는 IP 전달과 추가 규칙 ID 등록·호환 검증이 필요합니다. 로컬 함수 검사를 실제 배포 차단이나 심판 통과로 주장하지 않습니다.
재실행:
result.json은 실행기로 다시 생성할 수 있습니다. 만료되는 deny-rules.json과 alerts.log는 커밋에서 제외합니다.
수업용 Wazuh 경보 26건을 사용하며 원본과 기존 ZTNA 규칙을 보존합니다. MITRE ATT&CK T1190(https://attack.mitre.org/techniques/T1190/)에 근거한 SQL·스크립트·경로 이탈·명령 주입 신호를 검사합니다. 실제 요청 인자의 구문과 수업용 doc-* 표식을 구분해 지원하며, 단독 수업 단어는 차단 근거가 아닙니다. 반복 count 5 이상 및 수준 10 이상인 명확한 패턴은 confidence 0.95로 block입니다. 단발성 명확한 패턴은 0.7, 애매한 신호는 0.55로 alert이고 정상은 0.1로 record입니다. 수치 기준은 연습용 정책이며 MITRE가 지정한 기준이 아닙니다.
로컬 실행 결과: block 8 / alert 9 / record 9 / 정상 이벤트 차단 0. read-alerts는 경보 26건에서 26줄을 추출하고 비밀값 의심 설명을 숨깁니다. decide.mjs는 패턴을 내장하며 import·파일 접근·네트워크 없이 경보마다 답합니다. respond는 반복된 명확한 공격 주소 7개에 처리 시점부터 15분짜리 거부 규칙과 근거 경보 번호를 보관합니다. 같은 시험 묶음에서 정상 요청이 확인된 주소는 주소 단위 차단 대상에서 제외합니다. 알림은 xdr/alerts.log에 기록하며 요청 인자·본문·토큰을 기록하지 않습니다. 만료 규칙과 알림 로그는 커밋에서 제외합니다.
로컬 연결 검사에서 26건의 공격 거부/애매·정상 통과, 만료 해제, 기존 거부 유지, 실제 및 URL 인코딩 구문 탐지, 단발 알림, 비밀값 숨김이 통과했습니다. 기존 판정기 연습 4건도 통과했습니다. 이는 실제 배포 차단이나 심판 통과 증거가 아닙니다. 기존 계약에 출발 IP가 없어 실제 운영 연결은 미연결입니다. decideWithXdr는 서버/엔진이 검증한 출발 IP와 기존 판정 함수를 별도로 받는 연결 부품입니다. 기존 요청 18항목과 기존 규칙 파일은 변경하지 않았습니다. 운영 적용에는 신뢰할 수 있는 IP 전달과 추가 규칙 ID의 호환 검증이 필요합니다.
재실행: node xdr/web-injection/read-alerts.mjs → npm run xdr:run -- web-injection → node xdr/web-injection/respond.mjs.
심판용 result.json은 xdr:run으로 재생성합니다. 운영 연결 및 실제 배포 차단 확인은 별도입니다.
수업용 Wazuh 경보 24건을 사용하며 원본과 기존 ZTNA 규칙을 보존합니다. CVE-2021-44228(NVD) 및 MITRE ATT&CK T1190을 근거로 요청 머리글·인자의 완전한 JNDI 원격 조회 구문을 검사합니다. 근거: https://nvd.nist.gov/vuln/detail/CVE-2021-44228 및 https://attack.mitre.org/techniques/T1190/. 완전한 구문은 confidence 0.95로 block, 불완전 구문·점검 도구 흔적·T1190 수준 5 이상 단독 경보는 0.55로 alert, 그 외는 0.1로 record입니다. 수치는 연습용 정책이며 CVE·MITRE가 지정한 차단 기준이 아닙니다. 단순 CVE 이름·jndi 단어만으로 차단하지 않습니다. 패턴 목록을 decide.mjs 맨 위 상수에 동일하게 내장했습니다. 판단 파일은 import·파일 접근·네트워크 없이 계산합니다. read-alerts는 별도 확인용이며 시각·주소·가상 계정·수준·설명 24줄만 추출하고 비밀값 의심 설명을 숨깁니다.
로컬 실행: block 8 / alert 8 / record 8 / 정상 이벤트 차단 0. respond는 명확한 공격 주소 7개에 처리 시점부터 15분짜리 임시 거부 규칙과 근거 경보 번호를 생성합니다. 정상·애매한 요청이 함께 확인된 주소는 주소 단위 차단에서 제외합니다. 알림은 xdr/alerts.log에 경보 번호·판단·시각만 기록하며 요청 원문·토큰·메모 본문은 쓰지 않습니다. 생성되는 deny-rules.json과 alerts.log는 커밋하지 않습니다.
로컬 연결 검사: 24건의 공격 거부/애매·정상 통과, 만료 해제, 기존 거부 유지, URL 인코딩 및 머리글 탐지, 불완전 구문 알림, 단순 이름 검색 기록, 비밀값 숨김, 같은 주소의 정상 요청 보존을 확인했습니다. 기존 판정기 연습 4건과 XDR 실행기 검사 3건도 통과했습니다. 이는 실제 배포 차단이나 심판 판정이 아닙니다. 기존 요청 계약에 출발 IP가 없어 운영 연결은 미연결입니다. decideWithXdr는 서버/엔진이 검증한 출발 IP와 기존 판정 함수를 별도로 받는 연결 부품입니다. 기존 판정 함수·규칙 파일은 변경하지 않았으며 운영 적용에는 신뢰할 수 있는 IP 전달과 규칙 ID 호환 확인이 필요합니다. 취약한 라이브러리 조사·패치 작업은 이 탐지 모듈의 구현 범위에 포함하지 않습니다.
재실행: node xdr/known-cve/read-alerts.mjs → npm run xdr:run -- known-cve → node xdr/known-cve/respond.mjs.
제출 result.json은 xdr:run으로 다시 생성하며 심판은 decide.mjs 한 파일을 별도로 실행합니다.
원본 Wazuh 경보 24건과 기존 판정 규칙을 보존합니다. 서비스 생성·변경은 MITRE T1543, 예약 작업은 T1053, 시작 폴더는 정확한 분류 T1547.001에 근거합니다. patterns.json의 이름·조건·한 줄 근거를 decide.mjs 상수에 동일하게 내장했습니다. 서명 없음과 임시 실행 파일·반복 등록, 낯선 임시 파일 예약 작업, 평소 목록 밖 시작 폴더 추가를 복합 검사합니다. 높은 규칙 수준만으로 차단하지 않으며, 수치 기준은 수업용 정책입니다. 읽기 모듈은 별도로 24줄을 추출하고 비밀값 의심 설명을 숨깁니다. 판단 모듈은 import·파일 접근·네트워크 없이 계산합니다.
로컬 실행: block 8 / alert 8 / record 8 / 정상 이벤트 차단 0. 응답 모듈은 공격 주소 7개에 15분 임시 거부 규칙을 만들고 만료 시각·근거 경보 번호를 붙입니다. 정상 또는 애매한 경보도 있는 주소는 주소 단위 차단에서 제외합니다. 알림은 xdr/alerts.log에 시각·경보 번호·판단만 남기며 임시 규칙과 로그는 커밋하지 않습니다. 24건 분류와 연결 함수의 공격 거부·정상/애매 통과, 만료 해제, 기존 거부 유지, 비밀값 숨김, 같은 주소 정상 요청 보호를 검사했습니다. 기존 판정 연습 4건과 실행기 검사 3건도 통과했습니다.
기존 계약에 출발 IP가 없어 운영 연결은 미연결입니다. decideWithXdr는 서버/엔진이 확인한 IP와 기존 판정 함수를 별도로 받는 연결 부품입니다. 실제 운영 적용에는 신뢰할 수 있는 주소 전달과 규칙 ID 호환 검증이 필요합니다. 로컬 시험 결과는 실제 PC 격리·배포 차단·심판 통과 증거가 아닙니다.
재실행: node xdr/persistence/read-alerts.mjs → npm run xdr:run -- persistence → node xdr/persistence/respond.mjs.
제출 result.json은 xdr:run으로 재생성합니다.
원본 Wazuh 경보 26건과 기존 판정기 규칙을 보존합니다. T1068(취약점 악용 권한 상승), T1078(정상 계정 악용)의 공식 설명을 근거로 관리자 그룹 이상 변경과 낯선 관리자 권한 사용을 복합 검사합니다. 그룹 추가·업무 밖 시간·높은 수준·기법 태그 하나만으로 공격을 확정하지 않습니다. T1068 태그나 관리자 명령만으로 실제 취약점 악용을 입증하지 않으며 수치·한글 신호는 수업용 정책입니다. patterns.json을 decide.mjs 맨 위 상수에 동일하게 내장했습니다. decide는 import·파일 접근·네트워크 없이 계산하며 0.85 이상 block, 0.5 이상 alert, 나머지 record입니다. 별도 read-alerts는 26줄을 추출하고 비밀값 의심 설명을 숨깁니다.
로컬 실행: block 9 / alert 8 / record 9 / 정상 이벤트 차단 0. respond는 계정과 출발 주소 쌍 8개에 처리 시점부터 15분 임시 거부 규칙을 만들고 근거 경보 번호를 붙입니다. 같은 쌍에서 정상 또는 애매한 경보가 확인되면 그 쌍은 차단하지 않습니다. 다른 주소에서 같은 계정으로 들어오는 정상 요청이나 같은 주소의 다른 계정은 차단하지 않습니다. 알림은 xdr/alerts.log에 시각·경보 번호·판단만 남깁니다. 임시 규칙과 로그는 커밋하지 않습니다.
26건 분류와 연결, 만료 해제, 정상 쌍 보호, 기존 거부 보존, 승인 작업 기록, 비밀값 숨김, 패턴 상수 일치·독립 판단을 검사했습니다. 기존 판정기 연습 4건과 실행기 검사 3건도 통과했습니다. 기존 계약에는 Wazuh 계정과 주체 ID의 검증된 매핑 및 출발 주소가 없어 실제 운영 연결은 미연결입니다. decideWithXdr는 서버/엔진이 검증한 trustedAccount와 trustedSourceIp 및 기존 판정 함수를 별도로 받습니다. 브라우저 입력이나 subjectId를 Wazuh 계정으로 임의 해석하지 않습니다. 이 연결은 임시 접근 거부 부품이며 실제 토큰 폐기·운영 세션 회수·배포 차단·심판 통과 증거가 아닙니다. 운영 적용에는 신뢰할 수 있는 계정 매핑·주소 전달·추가 규칙 ID 호환 검증이 필요합니다.
재실행: node xdr/privilege/read-alerts.mjs → npm run xdr:run -- privilege → node xdr/privilege/respond.mjs.
제출 result.json은 xdr:run으로 재생성합니다.
npm run xdr:run -- exfiltration