
Engagement Managerは、オフェンシブセキュリティエンゲージメントを追跡するためのWebアプリケーションです。Next.js、Prisma、PostgreSQLで構築されたモダンなUIを備えています。
Engagement Managerは、オフェンシブセキュリティエンゲージメントを追跡するためのWebアプリケーションです。Next.js、Prisma、PostgreSQLで構築されたモダンなUIを備えています。このアプリには、カレンダー、エンゲージメント、クライアント、連絡先、所見、オペレーターが含まれます。

エクスポートは2 MB、1回のインポートあたり500所見に制限され、ユーザーごとのプレビューと確認のレート制限があります。アプリケーションは、手動作成、テンプレート、スキャナーインポートを通じて、合計最大10,000所見、1つのエンゲージメントにつき500所見を受け入れます。グローバルのFindingsリストは1ページあたり100行を読み込み、エンゲージメント/レポートの所見クエリは同じエンゲージメントごとの制限によって上限が設定されます。不明なレイアウトは、サイレントに成功したインポートとして扱われるのではなく、目に見える形で失敗します。スキャナーの重大度は提案です。承認前にそのコンテキストを確認してください。参照されたURL、HTML、埋め込みリモート画像は取得または実行されません。
レポートは1〜100所見、最大100の証拠画像 (各5 MB、合計入力20 MB)、500ページ、25 MBの出力を許可します。発行はエンゲージメントあたり50バージョン、アプリケーション全体で発行されたPDFは1 GBに制限されます。プレビューと発行にはユーザーごとのレート制限があり、アプリケーションプロセスごとに一度に1つの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、保護されたデータベース/バックアップストレージ、監査出力の運用監視が必要です。
このアップグレードをデプロイする前に、通常のアプリケーションバックアップを取得し、追加的な20260904221808_reporting_workflowおよび20260906194500_add_revocable_sessionsマイグレーションをnpm run db:migrateで適用してから、Prisma Clientを再生成してリビルドします。既存の所見はバージョン1のDraftとして開始され、既存のブラウザCookieはサーバーに裏付けられたセッションIDを受け取るために再度サインインする必要があります。既存のデータベースをリセットしないでください。バックアップには、既存のフルデータベースエクスポートを通じて新しいテーブルと発行されたPDFが含まれます。
npm test npm run lint npx tsc --noEmit --noUnusedLocals --noUnusedParameters npm run build npm audit
`npm test` は Node の非分離テストモードを `tsx` と共に使用するため、個々の TypeScript テストケースが実行され、ファイルサブプロセスの成功を報告するだけにはなりません。明示的なアサーションの合計を 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` を使用します。これには、使用時にデフォルト以外のポートを含む、1つのパブリックホストが含まれている必要があります。それ以外の場合、プロキシはパブリックな `Host` ヘッダーを保持する必要があります。これは、ソースごとの正確なログイン制限のために必要な本番トポロジーです。未設定の場合、スプーフィングを防ぐためにヘッダーは無視され、ログインはより高い1分間の共有フォールバックバジェットを使用するため、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
DATABASE_URL を em_app(または任意のユーザー名)を使用するように設定します。マイグレーションは 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 として再エクスポートしてください。プロジェクトディレクトリから、1つのコマンドでパッケージの更新をインストールし、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
本番環境の値(DATABASE_URL、JWT_SECRET は32文字以上)を設定した .env を作成します。
データベースマイグレーションを適用します: ```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 には管理者パスワードが必要で、em-backup-YYYY-MM-DD-HHMM.zip という名前の .zip をアプリケーションディレクトリ内の backups/(engagement-mgr/backups/)に保存します。エクスポートが成功したら、Admin ページの Download を使用します。短命な署名付きグラントが HttpOnly Cookie に保持され、バックアップを作成した管理者に対してのみ機能します。
em-backup-2026-06-02-1430.zip。| パス | 内容 |
|---|---|
engagement-manager-backup/database.dump | pg_dump による完全な PostgreSQL カスタム形式ダンプ(スキーマ、テーブル、データ、enum、リレーション) |
engagement-manager-backup/uploads/ | データベースで参照されている Finding のスクリーンショットファイル |
.zip のみを受け付け、現在のデータベースと uploads/ フォルダを置き換えます。ブラウザからの復元は 8 MB に制限されているため、展開が Web プロセスを独占することはありません。より大きなアーカイブの場合は、アプリケーションを停止し、アプリケーションユーザーとして npm run db:restore -- /absolute/path/to/em-backup.zip を実行してください。オフラインコマンドは作業ディレクトリから .env を読み込み、.env または環境に空でない DATABASE_URL が必要です。最大 500 MB の通常ファイルを受け付け、各アーカイブエントリを展開後サイズの制限までストリーミングします。データベースの復元は 1 つのトランザクションで実行されます。アーカイブエントリの数、パス、圧縮率、展開後サイズは、ファイルがインストールされる前に検証されます。バックアップ、復元、リセット、スクリーンショットファイルの変更は、排他的なメンテナンスロックを共有するため、データベースのコミットとファイルシステムのスワップが重複することはありません。確認には管理者パスワードが必要です。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 ダイジェストを保存します。ユーザーの author/reviewer リレーションシップは 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 です。ブラウザのダウンロードは GET /api/db/backup?file=… を使用し、Admin セッションと HttpOnly Cookie 内の 5 分間の署名付きグラントを伴います。HttpOnly、SameSite=Lax Cookie に保存された jose JWT と、ログアウトで失効する対応するサーバー側の Session 行を使用します。Cookie の有効期限は意図的に省略され、ブラウザセッションの動作を維持します。署名付きトークンとデータベースレコードの両方が 1 日後に期限切れになります。トランザクション的なアドミッションは、アカウントごとに最大 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 レスポンスを返します。