尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
자료실은 5단계 서버 인증·메모 소유권 보호, 6단계 등록 기기 접근제어, 7단계 복합 이상 접속 추가 확인을 유지하고 8단계 자원 구역 판정을 더했습니다. aleph.config.json의 step은 8이며 originalApiUrl, 로그인 발급자, 실제 Production 주소는 보존했습니다. Production 동작은 별도로 검증해야 합니다. 실제 개인정보, 비밀번호, 토큰, 서버 전용 키를 저장소에 넣지 마세요.檢視完整 README ↗
재구현 시작 기준 main은 9fe5c4ada8f3adbc7deb26a735995f257d6fada3이고, 11단계 기준 커밋은 1e06d42f4abb69cad677c159af536d8aa9732553입니다. 현재 단계는 aleph.config.json의 step: 12입니다. 공개 주소는 https://choi-bujang-secret-vault-jade-alpha.vercel.app입니다. 아래의 이전 단계 기록에 적힌 수치와 단계 설정은 당시 기록입니다.
npm ci를 실행합니다. 비밀값은 배포 환경의 비밀 입력란에만 설정합니다.npm run build는 Vercel의 배포 식별 환경에서 공개 파일을 만듭니다. 로컬 확인에는 npm run build -- --local을 사용합니다. 이 저장소에는 상주 로컬 웹 서버 시작 스크립트가 없습니다.npm run test:stage10으로 가상 사본 미리보기·복원·재시험을 확인합니다. POST /api/restore는 검증된 심판 B와 가상 fixture에 한정하며 운영 데이터 복구 절차가 아닙니다.npm test, npm run detect, npm run decider, npm run fixture:7, npm run build -- --local, git diff --check를 실행합니다. 커밋 후 깨끗한 작업 트리와 무시된 bundle-notes.json을 준비해 npm run bundle을 실행합니다.학생 구현: src/decider.mjs, src/detect.mjs, api/notes.js, PWA, 작전실·검증표입니다. 제공 엔진: ALEPH SDP 엔진입니다. 엔진의 실제 탐지 사건과 회수 결과가 제공되지 않으면 NOT_RUN으로 표시합니다. 복구 카드의 버전은 학생이 만든 시험 사본 버전이며 실제 복구 실행 결과는 NOT_RUN입니다.
승인된 기기에서 GET /api/operations를 호출하면 기존 /api/device 함수를 통해 41종 표를 반환합니다. 작전실 서버는 설정된 공개 HTTPS 주소에 익명 GET /aleph.json, GET /api/notes/:id, GET /api/notes만 시도합니다. 실제 HTTP 응답을 받은 항목만 PASS 또는 FAIL로 기록하고, 요청 실패·미실행 항목은 NOT_RUN으로 둡니다. 수치는 매번 실제 응답에서 계산하므로 고정된 통과 건수가 아닙니다. 요청 ID는 작전실이 발급해 X-Request-Id로 보낸 상관 ID이며 대상 API가 발급한 ID가 아닙니다. 응답 본문·메모 본문·쿠키·토큰·비밀번호는 표에 저장하거나 표시하지 않습니다.
src/attack-check.mjs는 배포 주소 형식만 검증합니다. npm run bundle에서는 요청을 보내지 않고 모든 attackAttempts를 observed: "not_run"으로 기록합니다. 이는 공식 심판 점수가 아닙니다.
xdr/fixtures/brute-force.json의 수업용 Wazuh 경보 28건을 xdr/brute-force/read-alerts.mjs가 시각·출발 주소·계정·규칙 수준·가린 설명으로 읽습니다. patterns.json은 MITRE ATT&CK T1110의 짧은 시간 로그인 실패와 여러 계정 대입 근거를 기록합니다. 심판이 단독으로 불러오는 decide.mjs는 import·파일 접근·네트워크 요청 없이 block·alert·record와 0~1 확신도를 반환합니다.
재현: node xdr/brute-force/read-alerts.mjs | wc -l은 28줄이어야 합니다. npm run xdr:run -- brute-force는 xdr/brute-force/result.json을 갱신합니다. 시험 fixture의 결과는 block 10 · alert 9 · record 9, 정상 이벤트 차단 0건입니다. node xdr/brute-force/respond.mjs는 확실한 차단 후보의 출발 주소·만료 시각·근거 경보 ID를 가진 거부 규칙을 만들고, 차단·알림 판정 19건을 xdr/alerts.log에 한 줄씩 추가합니다. 이 명령을 다시 실행하면 알림 로그도 추가됩니다.
respond.mjs의 checkDenyRules는 ALEPH SDP 엔진이 검증해 전달한 출발 주소에만 적용하는 별도 ZTNA 거부 단계입니다. 기존 src/decider.mjs 요청에는 검증된 출발 주소 필드가 없으므로 브라우저가 주장한 주소로 차단하지 않으며, 실제 운영 엔진에 규칙을 설치했다고 기록하지 않습니다. 규칙은 15분 뒤 만료되고 같은 주소에서 정상 이벤트가 확인되면 설치를 보류합니다. 이 결과는 수업용 경보 재생이며 실제 로그인 공격 차단이나 공식 심판 판정이 아닙니다.
xdr/fixtures/web-injection.json의 수업용 Wazuh 경보 26건을 xdr/web-injection/read-alerts.mjs가 시각·출발 주소·계정·규칙 수준·가린 설명으로 읽습니다. patterns.json은 MITRE ATT&CK T1190을 근거로 SQL 주입, 스크립트 삽입, 반복 경로 이탈, 단발성 의심 입력을 구분합니다. decide.mjs는 다른 파일이나 패키지를 불러오지 않고 경보 하나만으로 판단합니다.
재현: node xdr/web-injection/read-alerts.mjs | wc -l은 26줄이어야 합니다. npm run xdr:run -- web-injection은 xdr/web-injection/result.json을 갱신합니다. 시험 경보 결과는 block 8 · alert 9 · record 9, 일반 이벤트 차단 0건입니다. node xdr/web-injection/respond.mjs는 반복된 명확한 주입 경보에 한해 15분짜리 거부 규칙 후보를 만들고 차단·알림 판정 17건을 xdr/alerts.log에 추가합니다. 재실행하면 로그도 추가됩니다.
respond.mjs의 checkDenyRules는 ALEPH SDP 엔진이 검증해 전달한 출발 주소로만 평가할 별도 거부 단계입니다. 기존 판정기 계약에 검증된 출발 주소가 없어 src/decider.mjs 규칙은 변경하지 않았습니다. 같은 주소에서 일반 이벤트가 보이면 규칙 설치를 보류합니다. 실제 운영 엔진의 규칙 설치, 실제 웹 공격 차단, 공식 심판 판정은 확인하지 않았습니다.
xdr/fixtures/known-cve.json의 수업용 Wazuh 경보 24건을 xdr/known-cve/read-alerts.mjs가 시각·출발 주소·계정·규칙 수준·가린 설명으로 읽습니다. 원격 조회 문자열은 읽기 결과와 감사 로그에 넣지 않습니다. patterns.json은 CVE-2021-44228과 MITRE ATT&CK T1190을 근거로 완전한 JNDI 원격 조회 형태와 불확실한 취약점 점검 흔적을 구분합니다. decide.mjs는 import·파일 접근·네트워크 요청 없이 경보 하나만으로 판단합니다.
재현: node xdr/known-cve/read-alerts.mjs | wc -l은 24줄이어야 합니다. npm run xdr:run -- known-cve는 xdr/known-cve/result.json을 갱신합니다. 시험 경보 결과는 block 8 · alert 8 · record 8, 일반 이벤트 차단 0건입니다. node xdr/known-cve/respond.mjs는 완전한 원격 조회 경보의 출발 주소에 한해 15분짜리 거부 규칙 후보를 만들고 차단·알림 판정 16건을 xdr/alerts.log에 추가합니다. 재실행하면 로그도 추가됩니다.
respond.mjs의 checkDenyRules는 ALEPH SDP 엔진이 검증해 전달한 출발 주소로만 평가할 별도 거부 단계입니다. 기존 src/decider.mjs 계약에는 검증된 출발 주소가 없어 판정기 규칙은 변경하지 않았습니다. 같은 주소에서 일반 이벤트가 보이면 설치를 보류합니다. 실제 운영 엔진의 규칙 설치, 취약한 라이브러리 버전 점검·패치, 공식 심판 판정은 확인하지 않았습니다.
xdr/fixtures/persistence.json의 수업용 Wazuh 경보 24건을 xdr/persistence/read-alerts.mjs가 시각·출발 주소·계정·규칙 수준·가린 설명으로 읽습니다. patterns.json은 MITRE ATT&CK T1543·T1053을 근거로 서명 없는 서비스, 낯선 예약 작업, 시작 항목 변경을 구분합니다. decide.mjs는 import·파일 접근·네트워크 요청 없이 경보 하나만으로 판단합니다.
재현: node xdr/persistence/read-alerts.mjs | wc -l은 24줄이어야 합니다. npm run xdr:run -- persistence는 xdr/persistence/result.json을 갱신합니다. 시험 경보 결과는 block 8 · alert 8 · record 8, 일반 이벤트 차단 0건입니다. node xdr/persistence/respond.mjs는 시험용 기기 매핑을 사용해 15분짜리 격리 규칙 후보 8건을 만들고 차단·알림 판정 16건을 xdr/alerts.log에 추가합니다. 재실행하면 로그도 추가됩니다.
respond.mjs는 신뢰할 수 있는 Wazuh 기기 이름과 ALEPH SDP 엔진의 검증된 기기 참조를 매핑받을 때만 규칙 후보를 만듭니다. 기존 src/decider.mjs 요청에는 그 매핑이 없어 판정기 규칙은 변경하지 않았습니다. 일반 이벤트만으로는 격리 규칙을 만들지 않고, 다른 기기는 통과하며 규칙은 만료됩니다. 실제 운영 기기 격리·심판 판정은 확인하지 않았습니다.
xdr/fixtures/privilege.json의 수업용 Wazuh 경보 26건을 xdr/privilege/read-alerts.mjs가 시각·출발 주소·계정·규칙 수준·가린 설명으로 읽습니다. patterns.json은 MITRE ATT&CK T1068·T1078을 근거로 관리자 그룹 추가와 낯선 관리자 명령을 구분합니다. decide.mjs는 import·파일 접근·네트워크 요청 없이 경보 하나만으로 판단합니다.
재현: node xdr/privilege/read-alerts.mjs | wc -l은 26줄이어야 합니다. npm run xdr:run -- privilege는 xdr/privilege/result.json을 갱신합니다. 시험 경보 결과는 block 9 · alert 8 · record 9, 일반 이벤트 차단 0건입니다. node xdr/privilege/respond.mjs는 시험용 계정 매핑으로 15분짜리 세션 회수 규칙 후보 9건을 만들고 차단·알림 판정 17건을 xdr/alerts.log에 추가합니다. 재실행하면 로그도 추가됩니다.
respond.mjs는 Wazuh 계정 이름을 ALEPH SDP 엔진의 검증된 사용자 참조로 매핑받을 때만 회수 콜백과 별도 ZTNA 거부 규칙을 호출합니다. 기존 src/decider.mjs 규칙은 변경하지 않았습니다. 일반 이벤트만으로는 회수 후보를 만들지 않고, 관련 없는 계정은 통과하며 규칙은 만료됩니다. 실제 운영 세션 회수·심판 판정은 확인하지 않았습니다.
xdr/fixtures/exfiltration.json의 수업용 Wazuh 경보 30건을 xdr/exfiltration/read-alerts.mjs가 시각·출발 주소·계정·규칙 수준·가린 설명으로 읽습니다. patterns.json은 MITRE ATT&CK T1041·T1048을 근거로 낯선 목적지로의 대량 전송, 업무 시간 밖 압축 자료 전송, 반복 대량 전송을 구분합니다. decide.mjs는 import·파일 접근·네트워크 요청 없이 경보 하나만으로 판단합니다.
재현: node xdr/exfiltration/read-alerts.mjs | wc -l은 30줄이어야 합니다. npm run xdr:run -- exfiltration은 xdr/exfiltration/result.json을 갱신합니다. 시험 경보 결과는 block 10 · alert 10 · record 10, 일반 이벤트 차단 0건입니다. node xdr/exfiltration/respond.mjs는 15분짜리 목적지 거부 규칙 후보 10건을 만들고 차단·알림 판정 20건을 xdr/alerts.log에 추가합니다. 재실행하면 로그도 추가됩니다.
respond.mjs의 별도 ZTNA 거부 단계는 ALEPH SDP 엔진이 확인해 전달한 외부 목적지 주소에만 적용합니다. 일반 이벤트에서 쓰인 목적지는 규칙 후보에서 제외하고, 기존 src/decider.mjs 규칙은 변경하지 않았습니다. 실제 운영 전송 차단·심판 판정은 확인하지 않았습니다.
기존 1~10단계의 인증·소유권·복구 기능을 유지하며, 11단계는 설치형 PWA와 서버 측 기기 승인 검사를 추가합니다. aleph.config.json의 step은 11이며 public/manifest.webmanifest와 public/sw.js를 통해 설치 안내와 공개 앱 셸의 오프라인 안내를 제공합니다. Service Worker는 인증·기기·메모·복구 API 응답을 캐시하지 않습니다.
/api/device는 검증된 HttpOnly 로그인 세션과 서버 발급 HttpOnly 브라우저 참조 쿠키를 사용합니다. 브라우저가 보낸 x-device-id, 설치 상태, 요청 본문의 권한 주장은 신뢰하지 않습니다. 이 쿠키는 브라우저 프로필 참조일 뿐 WebAuthn 또는 물리 기기 증명이 아닙니다.POST /api/device의 {"action":"request"}이며 상태는 pending으로만 바뀝니다. 일반 학생의 approve/reject 요청은 거부됩니다. 별도 GET /api/device-approval은 승인 대기 요청을 관리자에게 보여주고, POST /api/device-approval은 { "action": "approve"|"reject", "userId": "...", "deviceRef": "<SHA-256 참조>" } 요청을 처리합니다. 해당 API는 검증된 로그인 UUID와 활성 device_approval_admins 레코드를 확인하고, CSRF/Origin 보호와 상태 전이를 적용합니다. 관리자는 자기 계정의 기기를 승인할 수 없습니다. 승인 취소는 현재 사용자의 승인된 브라우저 참조에 한해 가능합니다.expired로 처리되어 메모 접근을 막으며 다시 승인 요청을 해야 합니다./api/notes 및 /api/notes/:id는 사용자 세션, Supabase의 승인 레코드 approved, 기존 owner_id 조건을 모두 확인합니다. 미승인·취소·조회 실패는 메모 DB 질의 전에 차단됩니다.device_approvals, device_approval_events, device_approval_admins를 사용합니다. Migration은 운영 DB에 자동 실행되지 않습니다. 운영 관리자를 등록하려면 Supabase Dashboard의 Authentication > Users에서 이미 존재하는 사용자의 UUID를 확인하고, supabase/bootstrap/device_approval_admin.sql의 placeholder를 그 UUID로 바꾼 뒤 SQL Editor에서 수동 실행해야 합니다. 실제 계정 UUID를 코드에 넣거나 공개 API로 관리자 등록을 하지 마세요. 관리자 조회/승인 저장소 오류 시 승인 처리는 fail-closed 됩니다.npm test, npm run detect, npm run decider, npm run fixture:7, npm run build -- --local. 통합 테스트는 실제 api/device.js와 api/notes.js 핸들러를 검증하지만 Supabase 운영 DB, Vercel 배포 또는 공식 승인자를 검증하지 않습니다.자료실은 1~10단계의 인증·소유권·기기·자원 구역·로컬 사건 탐지 기능을 유지합니다. 10단계는 운영 복원 기능이 아니라 출처가 명시된 로컬 가상 fixture를 이용하는 복구 절차 연습입니다. aleph.config.json의 step은 11이며 기존 API와 로그인 설정은 보존했습니다. Production 동작은 별도로 검증해야 합니다. 실제 개인정보, 비밀번호, 토큰, 서버 전용 키를 저장소에 넣지 마세요.
test/fixtures/의 자료는 이 단계 테스트를 위해 새로 만든 가상 fixture이며, 9단계 기준 커밋을 참고하지만 실제 과거 백업이나 운영 사용자 자료가 아닙니다.npm run test:stage10은 실제 변경 없는 미리보기와 인메모리 복구를 시험합니다. 선택 대상은 시험 notes 정책 하나와 fixture의 가상 메모 ID 하나뿐입니다. 미리보기는 변경 서비스·필드만 보여주며 메모 본문은 출력하지 않습니다. 다른 시험 서비스 정책과 기존 감사 항목은 보존되고, 이미 같은 상태인 반복 복구는 자료를 다시 쓰지 않습니다.stage10-safe-policy-fixture-v1의 notes 정책만 적용하고 나머지 서비스를 보존합니다. 메모 시뮬레이션은 ID 623e4567-e89b-42d3-a456-000000000001, 소유자 723e4567-e89b-42d3-a456-000000000001로 고정된 가상 사본 한 건만 적용하고 복구 뒤 ID·소유자·본문 해시를 재검증합니다. 이 ID들은 운영 계정·메모 ID가 아닙니다.restoreRoute는 POST /api/restore이며 Vercel의 api/restore.js 서버 함수에 연결됩니다. 복구 요청은 기존 createLoginVerifier가 검증한 심판 JWT 중 identity b만 허용합니다. 브라우저 세션 쿠키, 학생 토큰, identity a, 요청 본문의 role/owner 주장은 권한을 주지 않습니다.{"action":"preview"|"restore","noteId":"623e4567-e89b-42d3-a456-000000000001"}만 받습니다. 먼저 action: preview로 버전과 정책 변경 필드, 가상 메모 ID·소유자·내용 해시를 확인하고, 명시적 action: restore 요청에서 다시 검증한 후 처리합니다. allowedRoutes는 학습자 메모 판정 경로이므로 기존 GET /api/notes/:id를 유지합니다.src/decider.mjs의 decide(request)를 다시 호출합니다. 각 호출은 docs/DECIDER_REQUEST.md의 18개 요청 필드만 사용하며, 정상 개별 메모 GET·공지 GET·재인증 후 GET은 allow, 미등록 기기·목록·POST/PUT/DELETE·공지 쓰기·강사 자원·추가 인증으로도 금지 쓰기는 deny, 복합 이상 요청은 step_up이어야 합니다. 별도로 같은 후보 메모 상태에서 ID·소유자·본문 해시와 비대상 정책을 확인합니다.api/notes.js 핸들러를 호출하고, 그 핸들러에 주입한 인증 mock 및 메모리 클라이언트가 복구 후보의 동일 메모 배열을 읽어 시험합니다. A 자기 메모와 B 자기 메모는 HTTP 200, 서로의 메모는 HTTP 404가 기대값입니다. 이는 server-mock 테스트이지 실계정 JWT 검증이나 Production API 호출은 아닙니다. JWT 검증은 복구 POST 자체에서 기존 검증기를 별도로 적용합니다.DETECT_EVENTS 계약은 at, method, identity.sub만 제공합니다. 현재 연습기는 같은 가명 주체의 GET 사건이 어느 10분 구간에서든 120회를 초과하면 propose_revocation / burst_read와 해당 주체를 반환하고, 그 외에는 observe합니다. 정확히 120회는 관찰 대상입니다.npm run detect / npm run detect:test 및 npm run decider, npm run fixture:7은 가상 연습입니다. 실제 엔진 사건 전달, 회수 집행, 소요 시간은 연결하거나 측정하지 않았습니다.npm run detect, npm run decider, npm run fixture:7, 지정 회귀 테스트 30개, npm run build -- --local, git diff --check, 별도 복제본 npm install이 통과했습니다.reasonCode 등록, Production 이벤트 전달·복합 신호·회수 집행 및 시간 측정은 미검증입니다.service, method, path, route, queryLength만 사용합니다. 등록 기기에서 service: notes, GET, 정확한 GET /notes/:id route, 쿼리 없음, 단일 비어 있지 않은 ID 경로만 허용합니다. 메모 ID는 UUID 형식을 강제하지 않습니다.deny / resource_access_denied (resource_boundary)입니다. 유효한 추가 인증으로도 이 거부를 바꿀 수 없습니다.announcements / GET / /announcements 조합만 허용하며, 쓰기·쿼리·추가 경로·유사 경로는 거부합니다. 이는 예시 요청 판정일 뿐 공식 엔진의 실제 공지 경로나 운영 API 지원을 뜻하지 않습니다. 강사 전용 자료와 기타 미등록 구역은 거부합니다.allowedRoutes에는 실제 메모 API에 대응하는 판정 정책의 GET /api/notes/:id만 둡니다. 공지 API 경로는 운영 계약이 없어 추가하지 않았으며, 이 목록은 Vercel 라우팅 설정이 아닙니다. 기존 실제 메모 CRUD 경로는 그대로 유지됩니다.owner_id 무시, 목록의 서버 검증 사용자 필터와 단건 GET/PUT/DELETE의 검증 사용자 owner_id 조건을 재사용했습니다. A/B 사용자 교차 접근은 기존 API 테스트의 404 기대를 유지합니다. 메모 API 파일은 변경하지 않았습니다.npm run decider의 가상 판정 34건, npm run fixture:7, 지정 회귀 테스트 30개, npm run build -- --local, git diff --check, 별도 복제본 npm install이 통과했습니다. 추적 파일의 비밀 패턴 검사는 기존 test/r5.test.mjs의 가상 Bearer fixture 한 건만 탐지했으며 실제 비밀값이 아닙니다. 공개 산출물에서는 패턴이 탐지되지 않았습니다.npm run decider와 npm run fixture:7은 가상 요청 연습이지 심판 판정이 아닙니다.resource_access_denied가 운영 등록부의 허용 reasonCode인지도 확인하지 않았습니다./는 Supabase Auth 이메일·비밀번호 로그인/로그아웃과 저장 세션 복원을 표시합니다. 유효한 Access Token이 있는 로그인 상태에서만 메모를 요청·표시하며 로그아웃 또는 401/403 응답이면 메모를 지우고 세션을 종료합니다.@supabase/supabase-js 2.117.2의 로컬 UMD 파일을 빌드에서 복사합니다. 메모 요청은 실제 SDK 세션 Access Token을 Bearer Authorization으로 전달하며 토큰은 화면이나 로그에 출력하지 않습니다.GET/POST /api/notes와 GET/PUT/DELETE /api/notes/:id는 src/verify-login.mjs의 createLoginVerifier 결과를 확인한 뒤에만 DB에 접근합니다. 인증만으로 타인의 메모를 읽거나 바꿀 수 있었던 IDOR 문제를 해결하기 위해 서버가 검증한 identity.userId를 목록·단건 조회·수정·삭제의 owner_id 조건에 적용합니다. 생성 시에도 owner_id는 이 검증된 사용자 ID로 지정하며 클라이언트의 같은 이름 필드는 권한 결정에 사용하지 않습니다. 다른 사용자 소유 또는 없는 ID는 같은 404 NOTE_NOT_FOUND 응답을 반환합니다. 목록은 { notes: [{ id, title, body }] }, 개별 GET/PUT은 { id, title, body }, POST/DELETE는 { id } 형식을 유지합니다. API의 body는 DB content에 매핑됩니다. 토큰이 없거나 검증에 실패하면 401 JSON을 반환하고 DB에 접근하지 않습니다.public/data.json을 만들지 않으며, 이전 로컬 빌드에서 남은 파일도 제거합니다. Vercel의 정적 산출물 경로는 기존처럼 public입니다.public/aleph.json에 기록합니다. 실제 Vercel 배포에서만 이 파일의 제공 여부를 확인할 수 있습니다.X-Content-Type-Options: nosniff 헤더를 설정합니다.SUPABASE_SECRET_KEY를 사용하고, Supabase SDK의 표준 서버용 createClient 옵션(autoRefreshToken: false, persistSession: false)을 사용합니다. 토큰 검증 전에는 메모 테이블을 조회하지 않으며 API 키와 인증 헤더를 응답·로그에 포함하지 않습니다.로컬 빌드는 npm run build -- --local로 확인할 수 있습니다. 로그인 연결을 켜려면 빌드 환경변수 SUPABASE_URL과 SUPABASE_PUBLISHABLE_KEY를 설정해야 합니다. Publishable Key는 브라우저 설정 파일에 포함되는 공개 값이며 Secret Key가 아닙니다. 값이 없으면 빌드는 성공하고 로그인 UI는 설정 미완료 상태로 표시됩니다.
public.notes에는 A 소유 가상 메모 4건과 B 소유 가상 메모 1건이 있다고 전달받았습니다. 이 세션에서는 원격 DB 상태를 직접 확인하거나 SQL을 실행하지 않았습니다.public.notes에 한정해 RLS를 활성화하고 PUBLIC, anon, authenticated의 직접 테이블 권한을 회수하며 service_role에 CRUD 권한을 부여합니다. authenticated에게는 테이블 CRUD를 부여하지 않습니다. 기존 8개 소유자 정책은 삭제·수정하지 않고, 새 환경에서만 없는 정책을 생성합니다. 다른 테이블, auth.users, 메모 행·내용·소유자 정보는 변경하지 않습니다. SQL은 아직 운영 DB에 실행하지 않았습니다. 적용 전에 supabase/verify.sql로 권한과 정책 수를 확인하고, 기존 메모와 소유자 정보가 보존되는지 별도로 확인하세요.SUPABASE_URL과 SUPABASE_SECRET_KEY를 서버 런타임 환경변수로 설정한 뒤 재배포합니다. 키 값은 브라우저나 Git에 넣지 마세요.SUPABASE_URL과 SUPABASE_PUBLISHABLE_KEY를 Vercel Production 빌드 환경에도 설정합니다. 실제 Publishable Key는 Supabase Dashboard의 Project Settings → API Keys에서 직접 확인하세요. 이 값은 공개되도록 설계된 키입니다.aleph.config.json의 issuer와 JWKS URL은 Supabase 프로젝트의 공개 OIDC discovery 문서와 JWKS endpoint에서 확인한 Auth 주소입니다. audience는 표준 Supabase Auth Access Token 값인 authenticated입니다. 검증 도우미는 비대칭 서명 검증과 Supabase Auth의 getClaims 경로를 사용합니다. 현재 확인된 프로젝트 JWKS는 ES256 공개 키를 제공하며, 실제 계정 토큰의 서명·claims 검증은 로그인 테스트 시 확인해야 합니다.SUPABASE_SECRET_KEY로 수행합니다. service_role은 RLS를 우회하므로 각 API 쿼리의 검증된 identity.userId 기반 owner_id 필터가 접근 경계이며 반드시 유지해야 합니다. authenticated의 DB 직접 권한을 제거해도 API의 JWT 검증과 서버 CRUD는 이 키·소유자 필터로 동작합니다.Cache-Control: no-store입니다.owner_id는 UUID 컬럼이며 auth.users 외래키가 없습니다. RLS는 authenticated 요청에서 auth.uid()와 owner_id를 비교합니다. 서버 API의 SUPABASE_SECRET_KEY/service_role은 RLS를 우회할 수 있으므로 각 API 쿼리의 소유자 필터도 반드시 유지해야 합니다. SQL Editor를 관리자 권한으로 사용한 결과만으로는 일반 authenticated 사용자의 RLS 제한을 입증할 수 없습니다.
npm run test:r5 && npm run test:package로 실행합니다. 테스트는 두 사용자 데이터 분리, 교차 사용자 GET/PUT/DELETE의 404 및 변경 없음, 위조 owner_id 무시를 mock DB에서 확인합니다. 실제 Supabase 계정 CRUD와 Production 동작은 별도 확인 전까지 미검증입니다.npm run build -- --local로 확인합니다. public/data.json을 만들지 않으며, 이전 파일이 있으면 제거합니다. /aleph.json은 실제 Vercel 빌드에서 실제 커밋 정보와 step 7을 기록합니다.supabase/verify.sql의 has_table_privilege 결과에서 anon/authenticated는 CRUD 모두 false, service_role은 모두 true여야 합니다. information_schema.role_table_grants 결과도 함께 확인하세요. 이 프로젝트에서는 아직 실행하지 않았습니다./data.json은 404, /aleph.json의 step은 5, 첫 화면은 nosniff, 무인증 API는 JSON 401, 각 로그인 계정은 자기 메모 CRUD 성공 및 타인 메모 접근 404여야 합니다.APP_URL='https://YOUR_APP.vercel.app'
curl -sS -o /dev/null -w '%{http_code}\n' "$APP_URL/data.json"
curl -fsS "$APP_URL/aleph.json"
curl -fsSI "$APP_URL/" | grep -i '^x-content-type-options:'
curl -i "$APP_URL/api/notes"
SUPABASE_SECRET_KEY가 RLS를 우회하더라도 각 쿼리에 소유자 조건을 명시해 적용합니다. 기존 8개 RLS 정책은 보존되며, 권한 회수 후에도 서버 측 소유자 조건을 대체하지 않습니다.npm run bundle은 커밋되지 않은 변경과 자리표시자 배포 설정이 있으면 통과하지 않습니다. 유효한 설정을 갖춘 뒤 깨끗한 작업 트리에서 실행하며, 외부 공격 점검은 not_run으로 남깁니다.src/decider.mjs의 device_registered 규칙은 요청의 deviceRegistered가 정확히 true일 때만 기기 등록 조건을 통과시킵니다. false, 누락, null 또는 다른 타입은 deny와 device_not_registered로 거부합니다.allow됩니다. 계약상 의심 신호가 있고 유효한 추가 인증이 없으면 step_up_required를 반환합니다. stepUp.verified === true는 authAgeSeconds가 숫자 정수 0~900일 때만 추가 인증 완료로 인정됩니다. 이 판정기는 계약에 정의되지 않은 사용자·기기·역할 필드를 읽지 않습니다.npm run decider는 가상 요청으로 등록 기기 허용, 미등록/잘못된 등록 정보 거부, 의심 신호와 추가 인증 조건을 연습합니다. npm run decider:test도 같은 검사를 실행합니다. 이는 로컬 가상 연습이며 실제 심판 판정이 아닙니다.aleph.config.json의 step은 6입니다. 별도 프로그램, 인증서 또는 개인키 설치가 필요하지 않습니다. 기존 프로젝트 의존성으로 실행합니다.npm run decider. 등록 기기의 정상 요청은 allow, 미등록 기기와 등록 정보 누락은 deny/device_not_registered여야 합니다. 의심 신호의 추가 인증 전 요청은 step_up, 계약 범위 안에서 추가 인증이 확인된 요청은 allow여야 합니다.deny/device_not_registered입니다.judge_fixture이고 비정상 시간(0~5시 또는 23시), 새로운 네트워크(unusual), 최근 5분 내 대량 조회 사건(risk_signal, count 10 이상)이 모두 함께 있으면 step_up을 요구합니다. 지역 신호만 다른 요청은 거부하지 않으며, 지역 값을 판정 근거로 쓰지 않습니다.none이고 지역·네트워크는 unknown입니다. judge_fixture 값은 로컬 가상 연습 전용이며, 브라우저가 보낸 IP·위치·헤더를 신뢰 신호로 승격하지 않습니다.stepUp.verified와 authAgeSeconds가 있지만, 실제 재로그인 화면·재인증 처리·자료 접근 차단 연결은 정의되어 있지 않습니다. 로컬 판정 결과만으로 운영 재로그인이나 자료 보호가 작동한다고 주장하지 않습니다.npm run decider의 20개 가상 판정, npm run fixture:7의 정상 allow·복합 이상 step_up, 지정된 6개 회귀 테스트 30개, npm run build -- --local, git diff --check가 통과했습니다. 별도 임시 복제본에서 npm install도 성공했습니다. 추적 파일 검사에서 실제 비밀값은 확인되지 않았으며 기존 테스트의 가상 인증 문자열만 패턴에 걸렸습니다.이전 data.json에서 다섯 메모의 제목·본문 각각을 가져와 아래 검색 명령의 자리표시자에 넣습니다. 명령에 메모 본문을 저장소에 적어 두지 마세요.
git grep -I -l -- '<기존 메모 제목 또는 본문>'
git grep --cached -I -l -- '<기존 메모 제목 또는 본문>'
git grep -I -l -- '<기존 메모 제목 또는 본문>' HEAD
grep -R -I -l -- '<기존 메모 제목 또는 본문>' public/
grep -R -I -l -E 'sb_secret_[A-Za-z0-9_-]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|Bearer[[:space:]]+[A-Za-z0-9._~+/-]{16,}' api/ src/ scripts/ test/ package/ public/
git check-ignore -v supabase/private-migration.sql
이번 검사에서 현재 작업 트리의 추적 소스와 로컬 공개 산출물에서는 네 메모의 제목·본문이 검색되지 않았고, public/data.json도 없습니다. 저장점 커밋 전 origin/main은 1단계 커밋이었고, git grep origin/main에서 기존 data.json, public/data.json에 본문이 남아 있는 것이 확인됐습니다. 이 저장점 커밋은 두 파일 삭제를 기록하지만 GitHub 최신 공개 브랜치에는 Push 전까지 반영되지 않습니다. 이전 공개 커밋과 기존 Vercel 배포 이력도 남아 있으며, 이번 검사로 그 이력이 삭제됐다고 주장하지 않습니다.
5단계 변경은 로컬 파일과 SQL 초안에만 반영했습니다. 운영 DB에는 SQL을 실행하지 않았고, Git 커밋·Push·Vercel 배포 및 실제 계정/Production 검증도 하지 않았습니다. 이전 공개 커밋과 배포 이력은 별도로 남아 있습니다.
키 검사는 sb_secret_ 실키 패턴, 개인키·토큰 패턴을 코드와 공개 빌드 산출물에서 검색합니다. 현재 브라우저 HTML과 공개 빌드에서 키 값은 발견되지 않았습니다. 서버 환경변수 이름만 코드에 있으며 키 값은 저장하지 않습니다. src/decider.mjs와 src/detect.mjs의 로컬 시험은 운영 심판의 판정이 아닙니다.
status: not_run입니다. 사례 예외는 fail, 재시험 실행기 예외는 not_run 및 상위 partial로 기록되며 실패 시 restored: false와 HTTP 409를 반환하고 복구 후보를 저장하지 않습니다. 누락·변조 백업은 재시험 전에 중단됩니다.npm run test:stage10 15개 통과. 복원 mock POST 한 건에서 복원 뒤 18건(가상 판정기 12, 동일 후보 상태를 읽는 메모 API server-mock 4, 메모/비대상 정책 무결성 2)이 실행되어 pass 18 / fail 0 / not_run 0이 응답과 감사에 기록됐습니다. 변조된 금지 요청 응답 주입은 복원을 막았고, 재시험 예외는 pass가 아닌 fail/not_run으로 처리됐습니다. npm test와 npm run test 각각 48개, 지정 1~9단계 보안 회귀 30개, npm run detect, npm run decider, npm run fixture:7, npm run build -- --local, git diff --check도 통과했습니다. 탐지·판정 및 재시험은 로컬 가상/mock 검증이지 공식 심판 판정이나 Production 복원이 아닙니다. 의존성 변경이 없어 npm install은 다시 실행하지 않았습니다.