
Engagement Manager는 공격적 보안 인게이지먼트를 추적하기 위한 웹 애플리케이션입니다. Next.js, Prisma, PostgreSQL로 구축된 현대적인 UI를 갖추고 있습니다.
Engagement Manager는 공격적 보안 인게이지먼트를 추적하기 위한 웹 애플리케이션입니다. Next.js, Prisma, PostgreSQL로 구축된 현대적인 UI를 갖추고 있습니다. 이 앱에는 캘린더, 인게이지먼트, 클라이언트, 연락처, 파인딩, 운영자가 포함됩니다.

내보내기는 가져오기당 2 MB 및 500개 파인딩으로 제한되며, 사용자별 미리보기 및 확인 속도 제한이 있습니다. 애플리케이션은 수동 생성, 템플릿 및 스캐너 가져오기를 통틀어 총 최대 10,000개의 파인딩과 단일 인게이지먼트당 500개를 허용합니다. 전역 Findings 목록은 페이지당 100개 행을 로드하며, 인게이지먼트/보고서 파인딩 쿼리는 동일한 인게이지먼트당 제한으로 제한됩니다. 알 수 없는 레이아웃은 성공적인 가져오기로 조용히 처리되지 않고 눈에 띄게 실패합니다. 스캐너 심각도는 제안 사항입니다: 승인 전에 해당 컨텍스트를 검토하십시오. 참조된 URL, HTML 및 포함된 원격 이미지는 가져오거나 실행하지 않습니다.
보고서는 1–100개의 파인딩, 최대 100개의 증거 이미지(각 5 MB, 총 입력 20 MB), 500페이지 및 25 MB 출력을 허용합니다. 발행은 인게이지먼트당 50개 버전 및 애플리케이션 전체에서 발행된 PDF 총 1 GB로 제한됩니다. 미리보기 및 발행에는 사용자별 속도 제한이 있으며, 애플리케이션 프로세스당 한 번에 하나의 PDF 렌더만 허용됩니다. 파인딩은 최대 1000개의 리비전과 500개의 댓글을 보유하며, 한도에 도달하면 기록을 덮어쓰지 않고 실패합니다. DejaVu 폰트와 해당 재배포 라이선스는 assets/fonts에 포함되어 있으며, 배포 시 이러한 자산을 유지해야 합니다(Next 출력 추적에 포함됨).
Next.js Server Actions는 증거 업로드를 위해 단일 25mb 본문 크기 제한(next.config.ts에서 설정)을 공유합니다. 로그인은 인증 또는 데이터베이스 작업 전에 4 KB 스트리밍 제한이 있는 전용 동일 출처 URL 인코딩 경로를 사용합니다.
이는 새로운 클라이언트별 테넌시 모델이 아닌 기존의 공유 인증 워크스페이스를 유지합니다. 모든 새 페이지, 액션 및 PDF 다운로드는 현재 데이터베이스 기반 세션을 확인합니다. 초안은 소유자로 범위가 지정되며, 검토, 템플릿 승인 및 발행 권한은 서버 측에서 강제됩니다. 기밀 PDF 응답은 private/no-store입니다. 최종 PDF는 명시적 보고서 필드 허용 목록만 포함하며, 비공개 초안, 검토 댓글 또는 관련 없는 인게이지먼트는 절대 포함하지 않습니다.
구현은 OWASP Top 10:2025 체크리스트를 사용합니다: 접근 검사(A01), 비공개 응답 및 기존 CSP/CSRF 제어(A02), 고정된 종속성 및 CI(A03), 기존 세션/시크릿 보호와 보고서 무결성 검사(A04/A08), 비활성 Markdown/XML 및 매개변수화된 데이터베이스 접근(A05), 제한된 처리 및 독립적 검토(A06), 라이브 세션 검사(A07), 콘텐츠 없는 감사 이벤트(A09), 실패 시 정리를 포함한 트랜잭션 변경(A10). 다이제스트는 우발적 손상을 감지하며, 디지털 서명이나 데이터베이스 관리자로부터의 보호가 아닙니다. 이는 규정 준수 인증이 아닙니다. 프로덕션에는 여전히 HTTPS, 보호된 데이터베이스/백업 스토리지 및 감사 출력의 운영 모니터링이 필요합니다.
이 업그레이드를 배포하기 전에 일반 애플리케이션 백업을 수행하고 npm run db:migrate로 추가적인 20260904221808_reporting_workflow 및 20260906194500_add_revocable_sessions 마이그레이션을 적용한 다음, Prisma Client를 재생성하고 다시 빌드하십시오. 기존 파인딩은 버전 1의 Draft로 시작하며, 기존 브라우저 쿠키는 서버 기반 세션 ID를 받기 위해 다시 로그인해야 합니다. 기존 데이터베이스를 초기화하지 마십시오. 백업에는 기존 전체 데이터베이스 내보내기를 통해 새 테이블과 발행된 PDF가 포함됩니다.
npm test npm run lint npx tsc --noEmit --noUnusedLocals --noUnusedParameters npm run build npm audit
`npm test`는 개별 TypeScript 테스트 케이스가 실제로 실행되도록 `tsx`와 함께 Node의 비격리 테스트 모드를 사용하며, 단순히 파일 하위 프로세스 성공만 보고하지 않습니다. CI에서 명시적인 어서션 총계를 계속 확인할 수 있도록 유지하세요.
데이터베이스 및 브라우저 회귀 테스트에는 마이그레이션이 적용된 **`reporting_tests`라는 전용 로컬 데이터베이스**가 필요합니다. 이 테스트들은 자체 픽스처 행을 생성하고 삭제하므로, 절대 애플리케이션 데이터베이스를 가리키지 마세요. `REPORTING_TEST_DATABASE_URL`을 해당 테스트 데이터베이스로 설정한 후 다음을 실행하세요:```bash
DATABASE_URL="$REPORTING_TEST_DATABASE_URL" npx prisma migrate deploy
npm run test:reporting
npx playwright install chromium
npm run test:browser
브라우저 스위트는 테스트 전용 세션 시크릿을 사용하여 포트 3317에서 자체 루프백 개발 서버를 시작하며, 기존 서버를 재사용하는 것을 거부합니다. 원한다면 설치된 Chromium 실행 파일로 REPORTING_TEST_BROWSER를 설정하십시오. 이 스위트는 초안 프라이버시, 충돌하는 편집, 증거 업로드, 독립적 검토, PDF 권한/불변성, JavaScript 없이 템플릿 생성, 선택적 중복 제거 가져오기를 테스트합니다. 통합 테스트는 실제 트랜잭션 충돌과 롤백을 실행합니다. 이 스위트는 원격 LAN, Safari 또는 프로덕션 배포 검증을 대체하지 않습니다.
이 애플리케이션은 Ubuntu에서 실행되도록 설계되었으며, 다음을 필요로 합니다:```bash sudo apt update && sudo apt install -y nodejs npm postgresql postgresql-client postgresql-contrib zip
`postgresql-client`는 `pg_dump`, `pg_restore`, `psql`을 제공하며, `zip`은 백업 아카이브를 생성합니다. 복원 추출은 애플리케이션에서 엄격한 항목 및 크기 검증을 통해 처리됩니다.
패키지를 설치한다고 해서 항상 PostgreSQL이 실행 중인 상태로 남는 것은 아닙니다. 역할을 생성하거나 앱을 시작하기 전에 서비스를 시작하고 활성화하십시오:```bash
sudo systemctl enable --now postgresql
sudo systemctl status postgresql --no-pager
앱이 나중에 Can't reach database server at 127.0.0.1:5432 오류로 실패하면 sudo systemctl start postgresql을 실행하고 pg_isready -h 127.0.0.1 -p 5432로 확인하세요.
앱은 Node.js ^22.12.0 또는 >=24.0.0이 필요합니다(package.json의 engines 참조). OS 패키지가 더 오래된 경우, setup.sh를 실행하기 전에 서명을 검증한 신뢰할 수 있는 패키지 소스에서 지원되는 릴리스를 설치하세요.
Prisma나 앱을 실행하기 전에 프로젝트 루트에 .env 파일을 생성하세요:```bash
cat > .env << 'EOF'
DATABASE_URL="postgresql://em_admin:em_pass@localhost:5432/engagement_manager?schema=public"
JWT_SECRET="replace-with-a-long-random-secret-at-least-32-characters"
EOF
chmod 600 .env
| 변수 | 필수 | 비고 |
|----------|----------|-------|
| `DATABASE_URL` | 예 | PostgreSQL 연결 문자열입니다. Prisma는 `schema=public` 쿼리 매개변수를 사용합니다. 백업 및 복원은 소유자 전용 임시 pgpass 파일을 사용하므로 비밀번호가 하위 프로세스 인수에 포함되지 않습니다. |
| `JWT_SECRET` | 프로덕션에서 예 | 최소 **32자**여야 합니다. 프로덕션에서는 이것이 없으면 앱이 시작을 거부합니다. 이를 교체하면 기존의 모든 세션이 무효화됩니다. |
| `TRUST_PROXY` | 아니요 | `X-Forwarded-For` / `X-Real-IP` 및 `X-Forwarded-Host`를 **덮어쓰는** 리버스 프록시 뒤에 앱이 있을 때만 `1`(또는 `true`)로 설정하십시오. 이 모드에서 존재하는 경우 로그인 출처 검사는 `X-Forwarded-Host`를 사용하며, 이는 사용 시 기본이 아닌 포트를 포함한 하나의 공용 호스트를 포함해야 합니다. 그렇지 않으면 프록시가 공용 `Host` 헤더를 보존해야 합니다. 이는 정확한 소스별 로그인 제한을 위한 필수 프로덕션 토폴로지입니다. 설정하지 않으면 스푸핑을 방지하기 위해 헤더가 무시되며, 로그인은 더 높은 1분 공유 대체 예산을 사용하므로 한 클라이언트가 15분 전역 잠금을 초래할 수 없습니다. |
| `ALLOWED_DEV_ORIGINS` | 아니요 | **개발 전용.** `/_next` 자산을 로드할 수 있도록 허용되는 추가 호스트 이름(쉼표로 구분)입니다. 서버의 현재 LAN IPv4 주소는 자동으로 허용됩니다. 안정적인 DNS 이름을 사용하려면 이것을 사용하십시오. 프로덕션 빌드는 이를 무시합니다. |
강력한 시크릿을 생성하십시오:```bash
openssl rand -base64 32
먼저 PostgreSQL이 실행 중인지 확인하세요(사전 요구 사항 참조). 자동화된 ./setup.sh가 서비스를 대신 시작해 주며, 아래 수동 단계는 서비스가 이미 실행 중이라고 가정합니다.
다음 명령을 실행하여 PostgreSQL 데이터베이스와 사용자를 생성하세요:```bash sudo -u postgres createuser --pwprompt em_admin sudo -u postgres psql -c "ALTER USER em_admin CREATEDB;" sudo -u postgres createdb --owner=em_admin engagement_manager sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE engagement_manager TO em_admin;"
### 프로덕션
**최소 권한**을 가진 전용 데이터베이스 사용자를 사용하세요 — `CREATEDB` 또는 슈퍼유저 권한을 부여하지 마세요:```bash
sudo -u postgres createuser --pwprompt em_app
sudo -u postgres createdb --owner=em_app engagement_manager
em_app(또는 선택한 사용자 이름)를 사용하도록 DATABASE_URL을 설정하세요. 마이그레이션은 npm run db:migrate를 통해 이 사용자로 실행됩니다.
참고: 데이터베이스 파일은 PostgreSQL 데이터 디렉터리(일반적으로
/var/lib/postgresql/<version>/main/)에 저장됩니다.
저장소 루트에서 다음을 실행하세요:```bash chmod +x setup.sh ./setup.sh
스크립트는 필수 구성 요소를 설치하고, PostgreSQL 서비스를 시작 및 활성화하며, 데이터베이스 사용자 이름과 비밀번호를 묻고, `chmod 600` `.env`를 작성하고, PostgreSQL 역할과 데이터베이스를 생성하고, 마이그레이션을 적용하고, 기본 관리자 계정을 시드합니다. 프로덕션 모드에서는 `npm run build`도 완료하고 프로덕션 시작 명령만 출력합니다. 원격 셸 스크립트에서 Node.js를 설치하지 않으므로, 지원되는 Node.js 릴리스를 먼저 설치하십시오.
헤드리스 또는 CI 사용 시:```bash
sudo install -d -m 700 -o "$USER" /secure
openssl rand -base64 24 > /secure/db-password
chmod 600 /secure/db-password
./setup.sh -y --db-user=em_admin --db-pass-file=/secure/db-password
모든 옵션을 보려면 ./setup.sh --help를 실행하세요.
--db-pass=...는 명령줄 비밀 정보가 다른 프로세스에 노출되므로 제거되었습니다. 비밀번호를 소유자 전용 파일에 넣고 이전 인수를 --db-pass-file=/secure/db-password로 교체하세요. 위의 자동화된 설정 예시는 복사해서 바로 사용할 수 있습니다.setup.sh는 더 이상 Node.js를 설치하지 않습니다. 실행하기 전에 신뢰할 수 있는 패키지 소스에서 지원되는 Node.js 릴리스(^22.12.0 또는 >=24.0.0)를 설치하세요.npm ci를 사용하므로 package-lock.json이 존재하고 package.json과 동기화되어 있어야 합니다..sql 백업은 복원할 수 없습니다. 이전 서버를 폐기하기 전에 구조화된 애플리케이션 백업을 생성할 수 있는 버전으로 업그레이드하고 데이터를 .zip으로 다시 내보내세요.프로젝트 디렉터리에서 하나의 명령으로 패키지 업데이트를 설치하고, PostgreSQL이 중지되어 있으면 시작하며, 앱을 시작합니다:```bash ./run.sh
해당 창을 열어 두세요. 출력된 Local 또는 Network 주소를 사용하세요.
직접 시작하려면: PostgreSQL이 실행 중이어야 합니다(필요한 경우 `sudo systemctl start postgresql`). 그런 다음 개발 서버를 시작하세요:```bash
npm run dev
시작 시 루프백 URL과 이 머신의 LAN 주소를 모두 출력합니다:```
`npm run dev`와 `npm start`는 `0.0.0.0`에 바인딩하므로 Network URL이 LAN에서 작동합니다. LAN 접근은 신뢰할 수 있는 네트워크에서의 실험실 전용으로 취급하십시오. 개발 모드는 공용 인터넷에 대해 강화되지 않았습니다.
**호스트 이름**(IP가 아님)으로 앱을 열었는데 원격 브라우저가 빈 흰색 페이지로 표시되면, 해당 이름을 `.env`에 추가하고 재시작하십시오:```bash
ALLOWED_DEV_ORIGINS=dev.office.example
^22.12.0 또는 >=24.0.0 (package.json의 engines 참조)Secure로 표시됩니다.uploads/ 디렉터리(취약점 스크린샷)를 위한 영구 스토리지저장소를 클론하고 의존성을 설치합니다: ```bash npm ci
프로덕션 값을 사용하여 .env를 생성합니다 (DATABASE_URL, JWT_SECRET ≥ 32자).
데이터베이스 마이그레이션을 적용합니다: ```bash npm run db:migrate
배포 전 검사를 실행합니다: ```bash npm run audit npm run typecheck npm run build
NODE_ENV=production으로 애플리케이션을 시작합니다: ```bash
NODE_ENV=production npm run start
실제 서버의 경우, 프로세스 관리자(systemd, PM2 등) 아래에서 이를 실행하고 TLS 종료를 위해 앞에 리버스 프록시를 배치하십시오.
JWT_SECRET이 최소 32자이며 git에 커밋되지 않음NODE_ENV=production이 설정됨CREATEDB 또는 슈퍼유저 권한이 없음uploads/가 영구 디스크에 있으며 백업에 포함됨backups/가 영구 디스크에 있음pg_dump, pg_restore, zip을 사용할 수 있음데이터베이스를 시드한 후, 생성된 임시 관리자 계정을 사용하여 로그인할 수 있습니다:
adminnpx prisma db seed / npm run db:seed에 의해 소유자 전용 initial-admin-credentials.txt에 한 번 기록됨참고: 첫 로그인 시 이 임시 비밀번호를 변경해야 합니다. 그 직후
initial-admin-credentials.txt를 삭제하십시오. 모든 비밀번호는 최소 16자여야 하며 대문자, 소문자, 숫자, 기호를 포함해야 합니다.
/dashboard/users)에서 전체 애플리케이션 데이터를 백업하고 복원할 수 있습니다.Admin에서 Database 패널은 Backup, Restore, Reset 버튼을 표시합니다. Users 패널은 계정을 나열하고 사용자를 추가하기 위한 New User 버튼을 제공합니다. Appearance 패널을 통해 관리자는 애플리케이션 전체의 강조 색상을 선택할 수 있습니다.
Backup은 관리자 비밀번호를 요구한 다음, 애플리케이션 디렉터리(engagement-mgr/backups/)의 backups/에 em-backup-YYYY-MM-DD-HHMM.zip이라는 이름의 .zip을 저장합니다. 성공적인 내보내기 후 Admin 페이지에서 Download를 사용하십시오. 단기 서명된 권한 부여가 HttpOnly 쿠키에 보관되며 백업을 생성한 관리자에게만 작동합니다.
em-backup-2026-06-02-1430.zip.| 경로 | 내용 |
|---|---|
engagement-manager-backup/database.dump | pg_dump의 전체 PostgreSQL 사용자 정의 형식 덤프 (스키마, 테이블, 데이터, 열거형, 관계) |
engagement-manager-backup/uploads/ | 데이터베이스에서 참조되는 발견 스크린샷 파일 |
.zip만 허용하며 현재 데이터베이스와 uploads/ 폴더를 대체합니다. 브라우저 복원은 8 MB로 제한되므로 압축 해제가 웹 프로세스를 독점할 수 없습니다. 더 큰 아카이브의 경우 애플리케이션을 중지하고 애플리케이션 사용자로 npm run db:restore -- /absolute/path/to/em-backup.zip을 실행하십시오. 오프라인 명령은 작업 디렉터리에서 .env를 로드하며 .env 또는 환경에 비어 있지 않은 DATABASE_URL이 필요합니다. 최대 500 MB의 일반 파일을 허용하며 각 아카이브 항목을 확장 크기 제한을 통해 스트리밍합니다. 데이터베이스 복원은 하나의 트랜잭션에서 실행됩니다; 아카이브 항목 수, 경로, 압축 비율, 확장 크기는 파일이 설치되기 전에 검증됩니다. 백업, 복원, 재설정, 스크린샷 파일 변경은 배타적 유지 관리 잠금을 공유하므로 데이터베이스 커밋과 파일 시스템 교체가 겹칠 수 없습니다. 확인을 위해 관리자 비밀번호가 필요합니다.admin을 다시 생성합니다. RESET을 입력하고 확인하는 관리자의 현재 비밀번호를 다시 입력해야 합니다. 해당 비밀번호는 다시 생성된 계정의 임시 비밀번호가 되며 첫 로그인 시 변경해야 합니다.이전 서버
.zip을 저장하고 새 서버로 복사합니다 (예: scp 또는 rsync 사용): ```bash
scp em-backup-2026-06-02-1430.zip user@new-server:/path/to/
새 서버
DATABASE_URL과 JWT_SECRET이 포함된 .env를 생성합니다(환경 구성 참조).npm ci.initial-admin-credentials.txt를 사용하여 admin으로 로그인하고, 임시 비밀번호를 변경한 뒤 자격 증명 파일을 삭제합니다./dashboard/users)을 열고 Database 아래의 Restore를 클릭한 다음, 이전 서버의 .zip을 선택하고 관리자 비밀번호를 입력한 뒤 확인합니다.참고 사항
uploads/ 디렉터리도 덮어씁니다.git clone(또는 동일 리비전 배포)을 사용하여 앱이 백업에서 예상하는 스키마와 일치하도록 합니다. 이전 서버가 복제한 코드보다 최신 스키마를 실행했다면 가져오기 전에 버전을 맞춥니다.이 섹션은 Engagement Manager 애플리케이션의 아키텍처, 데이터베이스 스키마, 보안 조치, 완료된 개발 단계를 문서화합니다.
Modal.tsx와 globals.css의 .modal-panel을 통해 완전히 불투명합니다.리포팅 추가 사항: Finding은 version, reviewStatus, authorId, reviewerId, templateId, importFingerprint도 저장합니다. Screenshot은 sortOrder를 저장합니다. FindingTemplate은 검토된 재사용 가능 문구를 보관하고, FindingRevision은 변경 불가능한 텍스트 리비전을 보관하며, FindingDraft는 충돌 버전이 있는 사용자별 비공개 초안을 보관하고, FindingComment는 검토 논의를 기록하며, EngagementReport는 보고서 제목, 경영진 요약, 정렬된 finding ID를 보관하고, IssuedReport는 발행된 각 버전에 대해 변경 불가능한 PDF, 콘텐츠 스냅샷, SHA-256 다이제스트를 저장합니다. 사용자 작성자/검토자 관계는 SetNull을 사용하며, 비공개 초안은 해당 사용자가 제거되면 삭제됩니다. 리포팅 레코드는 상위 engagement/finding 수명 주기를 따릅니다.
id, username, passwordHash, role (Admin, User), lastPasswordChange, lastLogin, sessions, createdAt, updatedAt.id, userId, expiresAt, createdAt — 서버 측 레코드를 통해 각 서명된 로그인 세션을 로그아웃 시 개별적으로 취소할 수 있습니다.key, count, — 원자적 소스 및 비밀번호 확인 시도 예약. 비밀번호 검증에는 제한된 동시성 한도도 있습니다.기존 모델에 새 필드를 추가하려면(예: Engagement의 focus):
prisma/schema.prisma를 열고 원하는 모델에 필드를 추가합니다: ```prisma
model Engagement {
id String @id @default(uuid())
codeName String
focus String? // new field
...
}
prisma/schema.prisma에 대한 모든 변경 사항은 다음을 반드시 따라야 합니다: ```bash
npx prisma migrate dev --name describe_your_change
이것은 마이그레이션을 생성하고, 데이터베이스를 업데이트하며, Prisma Client 타입을 재생성합니다.
admin 계정은 Prisma seed를 통해 생성됩니다. Admin 역할은 모든 레코드에 대한 전체 생성/편집/삭제 권한을 가집니다. User 역할은 findings와 screenshots를 생성, 편집, 삭제할 수 있으며, 그 외 모든 엔티티(engagements, clients, contacts, operators)는 사용자에게 읽기 전용입니다. 모든 대시보드 페이지는 기밀 데이터를 읽기 전에 데이터베이스에 대해 세션을 갱신합니다. 관리자만 Admin 페이지(/dashboard/users)에 접근하고, 계정을 관리하고, 애플리케이션 전체의 하이라이트 색상을 변경하고, 데이터베이스를 백업, 복원 또는 초기화할 수 있습니다. 백업, 복원, 초기화는 비밀번호 재확인이 필요합니다. 백업 생성은 Server Action이며, 브라우저 다운로드는 Admin 세션과 HttpOnly 쿠키에 담긴 5분짜리 서명된 grant를 사용하여 GET /api/db/backup?file=…을 통해 이루어집니다.HttpOnly, SameSite=Lax 쿠키에 저장된 jose JWT와 로그아웃 시 폐기되는 일치하는 서버 측 Session 행을 사용합니다. 브라우저 세션 동작을 유지하기 위해 쿠키 만료는 의도적으로 생략되었으며, 서명된 토큰과 데이터베이스 레코드 모두 하루 후에 만료됩니다. 트랜잭션 기반 허용은 계정당 최대 10개의 활성 세션을 유지합니다.src/proxy.ts)는 모든 보호된 라우트에 대해 세션 검사와 90일 비밀번호 교체를 강제합니다.| 스캐너/내보내기 제품군 | 허용되는 내보내기 |
|---|
| Burp Suite | Issues XML, 비활성 내부 스키마 DTD 포함 |
| Nessus / Tenable | Nessus v2 XML (.nessus) |
| Nmap | XML; 열린 포트와 해당 스크립트 출력은 추론된 취약점이 아닌 정보성 관찰 내용이 됨 |
| OpenVAS / Greenbone | 네이티브 XML 보고서 또는 GMP get_reports_response |
| OWASP ZAP | 사이트 및 경고가 포함된 전통적인 JSON 보고서 |
| Nuclei | JSON Lines (-jsonl) |
| Qualys | 스캔 결과 XML (SCAN/IP 구조), 별도의 호스트 탐지 API 형식이 아님 |
| Semgrep / CodeQL 및 기타 SARIF 생성기 | SARIF JSON runs, rules 및 results |
resetAthighlightColor (Red, Blue, Teal, Green, Purple, Amber 중 하나)와 updatedAt을 가진 애플리케이션 전역 싱글턴 설정 레코드.id, codeName, clientId, chargeCode, status (Prep, Recon, Testing, Reporting, Complete), focus, type (AI, Code_Review, Firewall, Multi, Pentest, Phishing, Physical, Purple_Team, Red_Team, USB_Drop, Vishing, Web_App, Wireless), location (Internal, External), startPrep, endPrep, startRecon, endRecon, startTesting, endTesting, startReporting, endReporting, outbrief, objectives, targets, exclusions, notes, operators (M:N), contacts/trustedAgents (Contact와의 M:N), findings, findingContexts, createdAt, updatedAt.id, company (DB 컬럼: companyName), address, city, state, zip, phone (DB 컬럼: phoneNumber), website, notes, contacts, engagements, createdAt, updatedAt.id, clientId, name, title, email, phone (DB 컬럼: phoneNumber), notes, assignedEngagements, trustedEngagements, createdAt, updatedAt.id, engagementId (선택 사항), title, category, severity, background, remediation, supportingData (DB 컬럼: supportingLinks), screenshots, engagementContext, createdAt, updatedAt.id, engagementId, findingId, observation, affectedHosts, createdAt, updatedAt.id, findingId, filePath, description, createdAt.id, name, title, email, phoneNumber, discord, github, notes, engagements (M:N), createdAt, updatedAt./api/uploads 라우트는 IDOR을 방지하고 no-store 응답을 반환합니다.