First commit
This commit is contained in:
372
AGENTS.md
Normal file
372
AGENTS.md
Normal file
@@ -0,0 +1,372 @@
|
||||
# CertRemind 開発ガイド
|
||||
|
||||
## 概要
|
||||
|
||||
- アプリケーション名: CertRemind
|
||||
- 目的: ユーザーが登録した Web サイトの TLS/SSL 証明書の有効期限を監視し、期限切れ前にアプリ内アラート、Webhook、Push 通知で知らせる。
|
||||
- 現在の実装状況: `development_plan.md` のフェーズ 8 までの機能を実装済み。MVP の主要機能に加え、基本的な品質向上、テスト、運用準備も整備済み。
|
||||
|
||||
## 技術スタック
|
||||
|
||||
- Node.js v22
|
||||
- pnpm
|
||||
- Hono.js
|
||||
- React.js / Vite
|
||||
- Radix UI
|
||||
- lucide-react
|
||||
- PostgreSQL
|
||||
- Docker Compose
|
||||
- OpenSSL
|
||||
- Argon2id
|
||||
- TOTP: `otplib`
|
||||
- Push 通知: `web-push`
|
||||
- QR コード: `qrcode.react`
|
||||
- テスト: Vitest
|
||||
- Lint / Format: ESLint / Prettier
|
||||
|
||||
## 主要コマンド
|
||||
|
||||
```text
|
||||
pnpm install
|
||||
docker compose up -d postgres
|
||||
pnpm dev
|
||||
pnpm monitor:once
|
||||
pnpm monitor:worker
|
||||
pnpm lint
|
||||
pnpm test
|
||||
pnpm exec vite build
|
||||
pnpm format
|
||||
```
|
||||
|
||||
開発サーバー:
|
||||
|
||||
```text
|
||||
Frontend: http://127.0.0.1:5173/
|
||||
API: http://127.0.0.1:3000
|
||||
```
|
||||
|
||||
## ディレクトリ構成
|
||||
|
||||
```text
|
||||
db/schema.sql
|
||||
README.md
|
||||
public/push-sw.js
|
||||
src/client
|
||||
src/client/api/client.js
|
||||
src/client/components
|
||||
src/client/components/Toast.jsx
|
||||
src/client/routes
|
||||
src/client/styles/app.css
|
||||
src/server
|
||||
src/server/app.js
|
||||
src/server/index.js
|
||||
src/server/config/env.js
|
||||
src/server/db/pool.js
|
||||
src/server/middleware
|
||||
src/server/modules
|
||||
src/server/jobs/monitorCertificates.js
|
||||
src/server/jobs/monitorWorker.js
|
||||
src/server/utils/logger.js
|
||||
tests/apiSecurity.test.js
|
||||
tests/monitoring.test.js
|
||||
tests/urlPolicy.test.js
|
||||
```
|
||||
|
||||
## 環境変数
|
||||
|
||||
`.env.example` を参照すること。
|
||||
|
||||
主な項目:
|
||||
|
||||
- `NODE_ENV`
|
||||
- `PORT`
|
||||
- `DATABASE_URL`
|
||||
- `COOKIE_SECRET`
|
||||
- `VAPID_PUBLIC_KEY`
|
||||
- `VAPID_PRIVATE_KEY`
|
||||
- `VAPID_SUBJECT`
|
||||
- `OPENSSL_PATH`
|
||||
|
||||
補足:
|
||||
|
||||
- `OPENSSL_PATH` 未設定時は `openssl` を使う。
|
||||
- Windows では Git 付属の `openssl.exe` を自動検出する実装がある。
|
||||
- VAPID private key が未設定の場合、Push 実送信は失敗として `delivery_result` に記録される。
|
||||
|
||||
## データベース
|
||||
|
||||
DDL は `db/schema.sql` に保管している。
|
||||
|
||||
実装済みテーブル:
|
||||
|
||||
- `users`
|
||||
- `user_totp`
|
||||
- `sessions`
|
||||
- `sites`
|
||||
- `notification_methods`
|
||||
- `site_alert_conditions`
|
||||
- `alert_history`
|
||||
|
||||
設計メモ:
|
||||
|
||||
- 主キーは UUID。
|
||||
- 全テーブルに `created_at` / `updated_at` を持つ。
|
||||
- `updated_at` はトリガーで更新する。
|
||||
- `sites` は最新の証明書期限、確認日時、取得失敗内容を保持する。
|
||||
- ユーザー関連データは `users` 削除を起点に CASCADE で削除する。
|
||||
- サイト削除時、通知条件は CASCADE で削除する。
|
||||
- アラート履歴の `site_id` はサイト削除時に `NULL` へ変更する。
|
||||
- アラート重複抑制は `alert_history.dedupe_key` を使う。
|
||||
|
||||
注意:
|
||||
|
||||
- `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。
|
||||
|
||||
## 実装済み API
|
||||
|
||||
```text
|
||||
GET /api/health
|
||||
|
||||
GET /api/auth/csrf
|
||||
POST /api/auth/register
|
||||
POST /api/auth/login
|
||||
POST /api/auth/logout
|
||||
GET /api/auth/me
|
||||
|
||||
GET /api/sites
|
||||
POST /api/sites
|
||||
GET /api/sites/:siteId
|
||||
PATCH /api/sites/:siteId
|
||||
DELETE /api/sites/:siteId
|
||||
|
||||
GET /api/sites/:siteId/settings
|
||||
PUT /api/sites/:siteId/settings
|
||||
DELETE /api/sites/:siteId/settings
|
||||
|
||||
GET /api/alerts
|
||||
PATCH /api/alerts/:alertId/read
|
||||
DELETE /api/alerts/:alertId
|
||||
|
||||
GET /api/notification-methods
|
||||
POST /api/notification-methods/webhooks
|
||||
PATCH /api/notification-methods/webhooks/:methodId
|
||||
DELETE /api/notification-methods/webhooks/:methodId
|
||||
POST /api/notification-methods/push-subscriptions
|
||||
|
||||
GET /api/account
|
||||
PATCH /api/account/profile
|
||||
PATCH /api/account/password
|
||||
POST /api/account/totp/setup
|
||||
POST /api/account/totp/verify
|
||||
DELETE /api/account/totp
|
||||
DELETE /api/account
|
||||
```
|
||||
|
||||
## 実装済み画面
|
||||
|
||||
認証前:
|
||||
|
||||
- 登録画面
|
||||
- ログイン画面
|
||||
- 2 段階認証コード入力
|
||||
|
||||
認証後:
|
||||
|
||||
- URL ルーティング
|
||||
- `/sites`
|
||||
- `/sites/:siteId/settings`
|
||||
- `/alerts`
|
||||
- `/notifications`
|
||||
- `/account`
|
||||
- `/login`
|
||||
- `/register`
|
||||
- 直リンク、初期読込み、ブラウザバック / フォワードに対応
|
||||
- 未認証で保護画面 URL を開いた場合はログイン画面を表示し、ログイン後に元の画面へ復帰する
|
||||
- 左サイドメニュー
|
||||
- PC 幅では展開表示
|
||||
- 720px 以下ではアイコンのみの畳み表示
|
||||
- サイト一覧、アラート履歴、通知方法、アカウント、ログアウトに対応
|
||||
- 共通トースト通知
|
||||
- 認証後画面の正常メッセージ、エラーメッセージを右上に表示
|
||||
- 5 秒後の自動非表示と手動クローズに対応
|
||||
- ログイン / 登録画面のエラーはインライン表示を維持
|
||||
- サイト一覧
|
||||
- サイト追加
|
||||
- 最新監視結果に基づく証明書期限表示
|
||||
- 確認ダイアログ付きサイト削除
|
||||
- サイト設定
|
||||
- エイリアス編集
|
||||
- 通知タイミング設定
|
||||
- 時間 / 日 / 週間の単位指定
|
||||
- 複数タイミングの追加
|
||||
- 確認ダイアログ付き通知タイミング削除
|
||||
- アプリ内アラート必須表示
|
||||
- Webhook 選択
|
||||
- Push 通知フラグ設定
|
||||
- 確認ダイアログ付き設定削除
|
||||
- アラート一覧
|
||||
- サイト絞り込み
|
||||
- アラート種類絞り込み
|
||||
- 開始日時 / 終了日時絞り込み
|
||||
- 既読更新
|
||||
- 確認ダイアログ付き履歴削除
|
||||
- 通知方法管理
|
||||
- Webhook 登録
|
||||
- Webhook 編集
|
||||
- 確認ダイアログ付き Webhook 削除
|
||||
- ブラウザ Push 通知の許可状態表示
|
||||
- VAPID public key がある場合の Push 購読登録
|
||||
- アカウント設定
|
||||
- 表示名更新
|
||||
- ダイアログでのパスワード更新
|
||||
- パスワード更新後の全セッション無効化
|
||||
- ステップ式ポップアップでの 2 段階認証セットアップ
|
||||
- 2 段階認証 QR コード表示
|
||||
- シークレット文字列表示
|
||||
- 確認ダイアログ付き 2 段階認証解除
|
||||
- 確認ダイアログ付きアカウント削除
|
||||
|
||||
## 入力検証
|
||||
|
||||
サーバー側:
|
||||
|
||||
- Zod でリクエストを検証する。
|
||||
- クライアントから送られる `user_id` は信用しない。
|
||||
- 認証済み API はセッションのユーザー ID を使う。
|
||||
- SQL はプレースホルダを使う。
|
||||
|
||||
クライアント側:
|
||||
|
||||
- 登録 / ログイン、サイト、Webhook、アラート絞り込み、アカウント設定で入力必須チェックと形式チェックを実装済み。
|
||||
- クライアント側検証は UX 用であり、サーバー側検証を省略してはいけない。
|
||||
|
||||
## 認証・セッション
|
||||
|
||||
- パスワードは Argon2id でハッシュ化する。
|
||||
- セッションは `sessions` テーブルで管理する。
|
||||
- セッション Cookie は HttpOnly / SameSite=Lax。
|
||||
- 本番環境では Secure Cookie を有効化する。
|
||||
- CSRF トークンを状態変更 API で必須にする。
|
||||
- パスワード更新時は対象ユーザーの全セッションを削除し、現在の Cookie も削除する。
|
||||
- TOTP が有効なユーザーはログイン時に OTP 検証が必須。
|
||||
|
||||
## サイト管理
|
||||
|
||||
- サイト URL は HTTPS のみ許可する。
|
||||
- URL は `normalizeHttpsUrl` で正規化する。
|
||||
- `localhost`、private IPv4、loopback IPv4 は拒否する。
|
||||
- サイト API は常にセッションユーザーの所有データのみ扱う。
|
||||
- サイト設定の通知タイミングは内部的に時間単位で保存する。
|
||||
- 通知タイミングは 1 時間以上、17520 時間以内。
|
||||
- 同じサイトに同じ通知タイミングを重複登録できない。
|
||||
|
||||
## アラート履歴
|
||||
|
||||
- アラート履歴は `alert_history` に保存する。
|
||||
- アラート一覧はログインユーザーの履歴のみ返す。
|
||||
- 既読更新、削除もログインユーザーの履歴のみ対象にする。
|
||||
- 絞り込み条件:
|
||||
- サイト
|
||||
- アラート種類
|
||||
- 開始日時
|
||||
- 終了日時
|
||||
- アラート重複抑制には `dedupe_key` を使う。
|
||||
|
||||
## 通知方法
|
||||
|
||||
Webhook:
|
||||
|
||||
- HTTPS のみ許可する。
|
||||
- `normalizeHttpsUrl` を通し、localhost / private IPv4 / loopback IPv4 を拒否する。
|
||||
- Slack 互換 Webhook として送信する。
|
||||
- 更新・削除はログインユーザーの通知方法のみ対象。
|
||||
|
||||
Push:
|
||||
|
||||
- ブラウザ Push 購読情報を `notification_methods` に保存する。
|
||||
- endpoint は HTTPS のみ許可する。
|
||||
- Service Worker は `public/push-sw.js`。
|
||||
- VAPID key は環境変数で管理する。
|
||||
|
||||
## 証明書監視ジョブ
|
||||
|
||||
一回実行:
|
||||
|
||||
```text
|
||||
pnpm monitor:once
|
||||
```
|
||||
|
||||
定期実行 worker:
|
||||
|
||||
```text
|
||||
pnpm monitor:worker
|
||||
```
|
||||
|
||||
処理内容:
|
||||
|
||||
- 登録サイトを取得する。
|
||||
- OpenSSL で証明書期限を取得する。
|
||||
- 成功時は `sites.certificate_expires_at` / `certificate_checked_at` を更新し、取得失敗内容をクリアする。
|
||||
- 失敗時は `sites.certificate_checked_at` / `certificate_check_error` を更新し、既存の期限値は残す。
|
||||
- サイトごとの通知条件を評価する。
|
||||
- 条件一致時にアラート履歴を作成する。
|
||||
- Webhook / Push 通知を送信する。
|
||||
- 証明書取得失敗も `certificate_check_failed` としてアラート化する。
|
||||
- サイト単位の失敗で全体を止めない。
|
||||
- 外部通信を伴う監視処理は並列数を制限する。
|
||||
- Webhook / Push の送信失敗は `delivery_result` に記録する。
|
||||
- 監視ジョブの開始、終了、失敗は構造化ログで出力する。
|
||||
- `pnpm monitor:worker` は 1 時間ごとに監視ジョブを実行する長時間起動プロセス。
|
||||
|
||||
注意:
|
||||
|
||||
- サンドボックス内では外部 TLS 接続が `Permission denied` になる場合がある。その場合は権限付き実行が必要。
|
||||
- 定期実行は `pnpm monitor:worker` で提供済み。運用要件に応じて cron やタスクスケジューラから `pnpm monitor:once` を実行する構成も選択できる。
|
||||
|
||||
## セキュリティ方針
|
||||
|
||||
- リクエスト改ざんに注意する。
|
||||
- クライアントから送信されてきたデータを信頼しない。
|
||||
- CSRF トークンを使用する。
|
||||
- パスワードを平文保存しない。
|
||||
- SQL インジェクションを避けるためプレースホルダを使う。
|
||||
- 認可はセッションユーザーを基準に判定する。
|
||||
- URL ルーティングは表示状態の復元用であり、認可は必ず API 側のセッションユーザー基準で判定する。
|
||||
- 未認証時の復帰先 URL はアプリ内の許可済みパスのみ扱い、外部 URL へのリダイレクトに使わない。
|
||||
- 1 リクエストがサーバー全体の処理をブロックしないようにする。
|
||||
- 外部通信はタイムアウトや並列数制限を設定する。
|
||||
- 削除操作には確認ダイアログを置く。
|
||||
- API の未捕捉エラーは構造化ログで記録する。
|
||||
|
||||
## テスト・運用準備
|
||||
|
||||
- `tests/urlPolicy.test.js` で URL 正規化と基本的な SSRF 対策を検証する。
|
||||
- `tests/apiSecurity.test.js` で CSRF、認証必須、セッションユーザー基準の認可、Webhook URL 検証、通知条件の所有者確認を検証する。
|
||||
- `tests/monitoring.test.js` で証明書監視成功 / 失敗時の DB 更新、アラート作成、通知呼び出しを検証する。
|
||||
- `README.md` に開発環境起動手順、検証コマンド、DB 注意点、環境変数一覧を記載済み。
|
||||
- サンドボックス環境では Vite / Vitest の設定解決で権限付き実行が必要になる場合がある。
|
||||
|
||||
## 既知の強化候補
|
||||
|
||||
- API 単体テスト / 統合テストをさらに増やす。
|
||||
- ログイン試行回数制限を追加する。
|
||||
- SSRF 対策として DNS 解決後の IP チェックを追加する。
|
||||
- IPv6 private / link-local / unique local address の検証を追加する。
|
||||
- セッションローテーションを追加する。
|
||||
- E2E テストを追加する。
|
||||
|
||||
## 開発時の注意
|
||||
|
||||
- `development_status.md` は作業後に更新する。
|
||||
- DB 変更を行う場合は `db/schema.sql` も更新する。
|
||||
- API を追加したら、認証、CSRF、認可、入力検証を確認する。
|
||||
- UI の削除操作には確認ダイアログを追加する。
|
||||
- 既存の左サイドメニューとレスポンシブ挙動を崩さない。
|
||||
- フロントエンドの入力チェックを追加しても、サーバー側検証を必ず維持する。
|
||||
- 変更後は少なくとも以下を実行する。
|
||||
|
||||
```text
|
||||
pnpm lint
|
||||
pnpm test
|
||||
pnpm exec vite build
|
||||
```
|
||||
Reference in New Issue
Block a user