nebula
nebula는 AI 기반 침투 테스트 도우미로 탐색·노트 작성·취약점 분석을 자동화한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗이 저장소는 1단계에서 학생 본인이 GitHub 저장소와 Vercel 배포를 만드는 출발점입니다. 포함된 메모 네 건은 가상 자료입니다. 실제 학생 자료, 토큰, 비밀키를 넣지 마세요.
src/decider.mjs)에 규칙이 둘 있습니다. RULE_IDS는 ['device_registered', 'step_up_required']입니다. 기기 등록 규칙(6단계)이 먼저 적용되어 미등록 기기는 deny(device_not_registered)입니다.signals)는 엔진이 검증해 넘긴 가상 정보(source: judge_fixture)일 때만 믿습니다. none(현재 실제 경로)·모르는 출처·누락·잘못된 모양은 "근거 없음"이고, 비어 있는 값을 정상값으로 채우지 않으며 위험하다고 단정하지도 않습니다. 판정기는 헤더·쿠키·브라우저가 보낸 위치를 읽지 않습니다(위조해도 신뢰하는 정보로 올라가지 않음). recentEvents는 최대 12건, 모양이 틀린 항목은 버립니다.step_up_required: 평소와 다른 시각(7unusual), 다른 지역(foreign), 최근 15분의 활동 10건 이상 중 두 가지 이상이 겹치면 step_up(이유 step_up_required)입니다. 하나만으로는 허용하고, 지역 하나만으로 막지 않습니다. 이 기준(7stepUp.verified가 true이고 authAgeSeconds가 0~900)은 허용합니다. 사유 이름(odd_hour·new_network·foreign_region·heavy_activity)은 riskReasons()로 시험에서 확인하며, 응답 형식(다섯 항목)에는 실리지 않습니다.npm run decider:test(등록 기기 통과·미등록 거부·복합 이상 접속 추가 확인·다시 확인 후 허용 네 항목이 모두 통과), npm run fixture:7(정상 사건 allow, 복합 이상 사건 step_up), npm run test:decider(입력 검사·규칙 시험). 가상 요청 연습이며 실제 반 엔진이나 심판의 결과가 아닙니다.stepUp과 step_up 결정이 있지만, 이 저장소의 서버 함수(/api/notes)는 판정기를 부르지 않고 반 엔진이 새 로그인 시각을 검증하는 환경도 여기에 없습니다. 그래서 "추가 확인 전 실제 자료 차단"과 "새 로그인 뒤 요청 허용"은 측정하지 않았습니다. 제출 묶음의 직접 점검에도 미실행으로 적었습니다./aleph.json·nosniff)은 그대로입니다.src/decider.mjs)에 규칙 device_registered 하나가 있습니다. 판정 요청의 deviceRegistered가 true일 때만 allow(이유 approved)이고, false이거나 값이 없거나 true가 아닌 모든 경우는 deny와 이유 device_not_registered입니다. 요청에서는 계약(docs/DECIDER_REQUEST.md)에 있는 requestId와 deviceRegistered만 읽고, request.identity·device·role·geo는 쓰지 않습니다. RULE_IDS는 ['device_registered']입니다.schema·requestId·decision·reasonCode·ruleIds)만 담습니다. 자료실 주소와 브라우저 화면은 바꾸지 않았고, 5단계까지의 기능은 그대로입니다.npm run decider:test를 실행합니다. 정상이면 「등록 기기의 정상 요청」과 「미등록 기기의 가상 요청」이 둘 다 통과로 나옵니다. 이 명령은 가상 요청 연습이며 실제 심판 판정이나 반 엔진의 결과가 아닙니다.'true', 숫자 1, null인 요청도 거부되고 응답 형식이 맞음./api/notes)와 이 판정기는 아직 연결되어 있지 않습니다.src/attack-check.mjs)은 5단계와 같고, 6단계용 항목을 더하지 않았습니다. 기기 등록 여부는 엔진이 정하는 값이라 학생 컴퓨터에서 바깥 요청으로 흉내 낼 수 없기 때문입니다./api/notes, /api/notes/:id)로만 갑니다. 화면 코드(public/index.html)가 Supabase를 직접 부르는 곳은 로그인·로그아웃·세션 확인(auth)뿐이고, 메모 자료를 직접 읽거나 고치는 곳은 없습니다(찾아봤고 없음). 그래서 제작 1에서는 파일을 바꾸지 않았습니다.public.notes): PUBLIC·anon·authenticated의 직접 권한이 모두 없습니다. 서버 함수는 서버 전용 비밀 키(service_role)로만 접속합니다. 서버 함수의 로그인·소유자 검사는 그대로입니다. RLS 정책 네 개도 남아 있어, 나중에 권한이 다시 열려도 본인 행만 허용되는 두 번째 방어선이 됩니다. 적용한 SQL은 supabase/*.local.sql에 있고 Git에는 올리지 않습니다.aleph.config.json: step은 5, originalApiUrl은 쿼리 없는 원본 자료 HTTPS 경로(…supabase.co/rest/v1/notes)입니다. 허용 경로(allowedRoutes)와 로그인 발급자 정보는 4단계와 같습니다.anon·authenticated는 SELECT·INSERT·UPDATE·DELETE가 모두 false, service_role은 모두 true. 공개(anon) 키로 DB에 직접 요청하면 401(permission denied for table notes)(4단계에서 확인). 시험 계정 A·B로 화면에서 각자 자기 메모 읽기·추가·수정·삭제가 정상.authenticated)으로 실제 직접 요청을 보내 본 시험은 하지 않았습니다. 권한 표로만 확인했습니다. 심판의 재현도 아닙니다.sb_publishable_…)가 있습니다. 로그인(Auth)을 브라우저가 직접 부르기 때문이고, 공개용 키이며 DB 직접 접근 권한은 없습니다. 이전 공개 커밋(dd089bb)과 옛 배포에는 가상 메모가 그대로 남아 있습니다.npm run test:notes, npm run test:attack, npm run test:r5. 가상 요청 연습이며 실제 배포나 심판 판정이 아닙니다.npm run bundle은 4단계 점검에 더해 공개 화면 HTML과 /aleph.json에 서버 전용 키·토큰 모양의 문자열이 있는지 봅니다(있는지 여부만 기록하고 값은 싣지 않음). 요청을 보내지 못하면 미실행으로 남습니다.src/notes-service.mjs)는 읽기·추가·수정·삭제를 모두 본인 메모로 제한합니다. 한 건 요청은 DB에 id와 owner_id(검증된 사용자)를 함께 걸고, 돌아온 행의 주인도 다시 비교합니다. 추가할 때 주인은 검증된 사용자 ID로만 저장하며 URL·본문의 소유자 값은 믿지 않습니다.404입니다. 수정 본문에 owner_id·userId 같은 소유자 값이 있으면 403(OWNER_CHANGE_FORBIDDEN)으로 거부하고 소유자는 바뀌지 않습니다.public.notes): RLS를 켜고 PUBLIC·anon·authenticated의 권한을 모두 회수한 뒤, authenticated에 SELECT·INSERT·UPDATE·DELETE만 주었습니다. 네 정책 모두 auth.uid() = owner_id일 때만 허용합니다(UPDATE는 기존 행과 새 행 둘 다). 서버 함수는 서버 전용 비밀 키로 접속하므로 이 변경과 상관없이 동작합니다. 적용한 SQL은 supabase/*.local.sql에 있고 Git에는 올리지 않습니다.anon은 네 권한 모두 없음, authenticated는 네 권한 있음, 정책 네 개. 공개(anon) 키로 DB에 직접 요청하면 401(permission denied for table notes). 시험 계정 A·B로 화면에서 각자 자기 메모만 보이고 추가·수정·삭제가 됨.authenticated 역할의 직접 접근(심판이 재현할 수 없는 부분이라 점수에서 제외됨).npm run test:notes(서버 함수·A/B 소유자 시험, 가짜 Supabase), npm run test:attack(자기 점검 코드), npm run test:r5. 가상 요청 연습이며 실제 배포나 심판 판정이 아닙니다.npm run bundle은 3단계 점검에 더해 공개 키로 DB에 직접 묻는 요청(anon_direct_data_api)이 거부되는지 봅니다. 요청을 보내지 못하면 미실행으로 남습니다. 두 시험 계정의 토큰이 없어서 A가 B의 메모를 건드리는 요청은 이 점검에 넣지 않았고 서버 시험으로만 확인합니다.dd089bb)과 옛 배포에는 가상 메모가 그대로 남아 있습니다. 서버 함수가 비밀 키로 DB를 읽으므로 앱 쪽 검사 코드가 틀리면 RLS가 막아 주지 못합니다(RLS는 직접 접근을 막는 두 번째 방어선입니다).example.com)으로 합니다. 실제 이메일은 코드·Git·제출 묶음에 넣지 않습니다.public/index.html)에서 Supabase Auth 이메일·비밀번호로 로그인·로그아웃합니다. 로그인한 사람만 메모를 추가·수정·삭제하고 목록을 봅니다. 로그인 전에는 서버에 요청도 보내지 않습니다.api/notes.js, api/notes/[id].js(구현은 src/notes-service.mjs)는 Authorization: Bearer <토큰>을 시작 틀의 도우미 src/verify-login.mjs로 검사합니다. 도우미는 고치지 않았습니다. 토큰이 없거나 위조·만료·다른 대상용이면 401과 JSON 오류 문구(LOGIN_REQUIRED)를 돌려줍니다. 브라우저가 보내는 userId·role은 믿지 않고, 메모의 주인은 검사가 끝난 토큰의 사용자 ID로만 정합니다.aleph.config.json의 allowedRoutes): GET /api/notes, POST /api/notes, GET·PUT·DELETE /api/notes/:id. 발급자 정보(identityProvider)는 Supabase 프로젝트의 Auth 주소입니다. 화면에 있는 Project URL과 publishable key는 공개용이고, 서버 전용 비밀 키는 Vercel 환경변수에만 있습니다.aleph.config.json의 step은 3입니다. 응답에는 X-Content-Type-Options: nosniff(vercel.json)가 붙고, /aleph.json은 계속 열립니다.npm run test:notes(서버 함수, 가짜 Supabase), npm run test:attack(자기 점검 코드), npm run test:r5. 가상 요청 연습이며 실제 배포나 심판 판정이 아닙니다. 배포 확인은 curl.exe -s -i <내 배포 주소>/api/notes가 로그인 없이 401과 JSON 오류 문구를 주는지 봅니다.npm run bundle은 작업 트리가 깨끗하고 Git에 올리지 않는 bundle-notes.json이 있어야 합니다. src/attack-check.mjs가 배포 주소로 직접 보낸 요청의 결과만 적습니다. 로그인 없는 추가·조회와 일부러 만든 가짜 토큰(위조 서명·만료·다른 대상) 요청이 거부되는지 보고, 요청을 보내지 못하면 미실행으로 남깁니다. 심판의 판정이 아닙니다.public/에서 빠졌고, 학습용 Supabase 테이블 notes에 있습니다. 테이블을 만드는 SQL은 메모 본문이 들어 있어 Git에 올리지 않습니다.public/index.html)은 서버 함수 /api/notes(api/notes.js)로 메모를 읽습니다. 함수는 Vercel 환경변수 SUPABASE_URL과 서버 전용 SUPABASE_SECRET_KEY를 서버에서만 읽습니다. 값은 Vercel 설정의 비밀 입력란에 직접 넣고 코드·Git·로그·응답에 두지 않습니다.aleph.config.json의 step은 2입니다. 빌드는 public/data.json을 만들지 않고, 배포의 /aleph.json은 계속 만듭니다. /data.json을 열면 메모가 없어야 합니다./api/notes에는 로그인 확인이 없었고 3단계에서 막았습니다.dd089bb)과 옛 배포에는 가상 메모가 그대로 남아 있습니다. 확인 절차는 제작 3에서 적습니다.npm run test:notes, npm run test:attack, npm run test:r5. 가상 요청 연습이며 실제 배포나 심판 판정이 아닙니다. npm run build -- --local은 정적 결과물만 만들고 서버 함수나 배포를 증명하지 않습니다.npm run bundle은 작업 트리가 깨끗하고 Git에 올리지 않는 bundle-notes.json이 있어야 합니다. src/attack-check.mjs가 배포 주소로 로그인 없는 요청을 직접 보내 결과를 적고, 요청을 보내지 못한 점검은 미실행으로 남깁니다. 심판의 판정이 아닙니다.가상 메모 문장은 실습용 가상으로 시작해 기록으로 끝납니다. 아래 검색식은 이 README의 글자와 겹치지 않게 골랐습니다. <내 배포 주소>는 Vercel Domains의 짧은 주소(https://…vercel.app)로 바꿉니다.
git fetch origin
git grep -n -E "실습용 가상 [^ ]+ 기록" origin/main
False, 마지막 /api/notes 줄은 True여야 합니다. 마지막 줄은 검색이 제대로 되는지 보는 대조입니다. 마지막이 False이면 한글 검색이 깨진 것이라 앞의 False도 믿을 수 없습니다. 첫 줄은 한글을 읽게 하는 설정이니 같이 실행하세요.
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
foreach ($p in '/', '/data.json', '/aleph.json', '/api/notes') { "$p -> " + [bool]((curl.exe -s "<내 배포 주소>$p") -match '실습용 가상') }
data.json과 public/data.json에 각 4줄). 여기서 0건이면 검색 방법이 틀린 것이라 1·2번의 0건도 믿을 수 없습니다. 한글 입력 문제일 수 있으니 chcp 65001 뒤에 다시 실행하거나, GitHub 저장소 검색 칸에서 같은 문장을 찾아 보세요.
git grep -n -E "실습용 가상 [^ ]+ 기록" dd089bb
401과 JSON 오류 문구가 나와야 정상): 메모가 돌아오면 아직 열려 있는 것입니다.
curl.exe -s <내 배포 주소>/api/notes
이 확인의 한계
dd089bb에는 메모가 그대로 남아 있습니다. Git 이력은 지우지 않았습니다./api/notes는 2단계 당시 로그인 없이 메모를 돌려줬고, 3단계에서 401로 막았습니다(아래 기록은 2단계 시점).확인 기록 (2026-10-28)
| 확인 | 결과 |
|---|---|
GitHub 최신(37ec184) 검색 (1번) | 0건 (실행함) |
옛 커밋 dd089bb 대조 검색 (3번) | 8줄 걸림: data.json 4줄, public/data.json 4줄 (실행함) |
배포 /data.json HTTP 상태 | 404 (직접 요청해 확인함) |
배포 /·/data.json·/aleph.json 문장 검색 (2번) | /·/data.json·/aleph.json은 모두 False, 대조인 /api/notes는 True (UTF-8 설정을 넣은 명령, 학생이 직접 실행한 결과를 받아 기록). 한글 읽기 설정 없이 돌린 첫 검색은 대조가 False로 깨져서 무효 처리함 |
배포 /api/notes (4번) | HTTP 200, 로그인 없이 가상 메모 네 건(title·content만)이 돌아옴. 공개 약점이 남아 있음 (학생이 직접 실행한 결과를 받아 기록) |
https://…vercel.app 주소를 방어전 1단계 카드에 붙여넣고 제출합니다. 저장소 주소나 설정 파일은 적지 않습니다.배포가 끝나면 /에서 점령된 가상 자료실을 볼 수 있습니다. /data.json에는 같은 가상 메모가 공개됩니다. 이 공개 상태를 확인하는 것이 1단계의 출발점입니다. 1단계 접수와 심판 판정은 포털에서 확인합니다.
vercel.json은 정적 결과물 public을 배포합니다. 빌드 명령 npm run build는 Vercel이 제공하는 GitHub 저장소 소유자·이름, 커밋 SHA, 배포 URL을 검증하고 public/aleph.json을 생성합니다. 이 값이 없으면 빌드가 실패하므로, 성공한 것처럼 빈 주소를 내보내지 않습니다. aleph.json의 내용만으로 저장소 소유권이나 방어 성공을 인정하지 않습니다. 심판이 공개 저장소의 실제 커밋과 배포된 자료를 따로 대조해야 합니다.
aleph.config.json의 repoUrl과 publicAppUrl은 1단계에서는 자리표시자였고 학생이 편집하지 않습니다. 2단계 저장점에서 실제 저장소와 배포 주소로 채웠고, 3단계에서 로그인 발급자와 허용 경로를 더했고 4단계에서 단계 값을 4로 맞췄습니다. 2단계 이후 코딩 도구가 필요한 설정과 보호 기능을 단계별로 작성합니다. npm run bundle과 bundle-notes.json도 1단계의 세 걸음에는 포함되지 않습니다.
로컬에서 가상 화면만 확인할 때는 npm run build -- --local을 사용합니다. 로컬 실행은 Vercel 배포나 심판 접수를 증명하지 않습니다. 저장소의 src/attack-check.mjs는 실제 배포가 된 뒤 /data.json을 비로그인으로 요청해 공개 가상 메모의 확인 표시를 읽습니다.
AGENTS.md를 먼저 읽히고 한 번에 한 제작 단위만 요청하세요. 2단계부터는 자료 보호를 구현할 때 public/data.json을 복사하는 1단계 빌드 흐름도 함께 바꿔야 합니다. 3단계 이후의 로그인, 허용 경로, 5단계의 원본 API 주소, 6단계 이후 정책 규칙은 해당 단계 원고와 계약에 맞춰 추가합니다. 비밀번호·토큰·서버 전용 키·실제 학생 기록을 코드, Git, 제출 묶음에 넣지 않습니다.
src/decider.mjs와 src/detect.mjs의 로컬 시험은 반 엔진이나 운영 심판의 결과가 아닙니다. 1단계 이후 제출 묶음 계약 aleph.defense.submission.v2는 scripts/bundle.mjs에 남아 있으며, 코딩 도구가 해당 단계의 최신 배포 주소와 Git 원격을 맞춘 뒤 사용합니다.