From ada6eba33305ed17a785f7912290f030258a1df1 Mon Sep 17 00:00:00 2001 From: posimai Date: Sat, 11 Apr 2026 00:05:18 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20security=20=E2=80=94=20invite=5Fcode=20l?= =?UTF-8?q?eakage,=20Atlas=20token=20in=20URL,=20RSS=20err.message=20expos?= =?UTF-8?q?ure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GET /together/groups/:id: SELECT * -> SELECT id, name, created_at (invite_code 除外) - Atlas github/vercel/tailscale-scan: token を query param から Authorization header へ移行 - /events/rss: err.message をクライアント返却しないよう固定メッセージに置換 Co-Authored-By: Claude Sonnet 4.6 --- server.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/server.js b/server.js index bd9e32da..ee704b46 100644 --- a/server.js +++ b/server.js @@ -771,7 +771,7 @@ function buildRouter() { // POST /api/auth/magic-link/send r.post('/auth/magic-link/send', async (req, res) => { - const { email } = req.body; + const { email, redirect } = req.body; if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { return res.status(400).json({ error: 'メールアドレスが無効です' }); } @@ -816,7 +816,8 @@ function buildRouter() { // Send email via Resend (if API key is set) if (process.env.RESEND_API_KEY) { - const magicLinkUrl = `${MAGIC_LINK_BASE_URL}/auth/verify?token=${token}`; + const redirectSuffix = redirect ? `&redirect=${encodeURIComponent(redirect)}` : ''; + const magicLinkUrl = `${MAGIC_LINK_BASE_URL}/auth/verify?token=${token}${redirectSuffix}`; try { const emailRes = await fetch('https://api.resend.com/emails', { method: 'POST', @@ -2314,7 +2315,7 @@ ${excerpt} res.json({ events: unique, fetched_at: new Date().toISOString() }); } catch (err) { console.error('[events/rss]', err); - res.status(500).json({ events: [], error: err.message }); + res.status(500).json({ events: [], error: 'イベント取得に失敗しました' }); } }); @@ -2433,7 +2434,7 @@ ${excerpt} r.get('/together/groups/:groupId', async (req, res) => { if (!/^[a-zA-Z0-9_-]+$/.test(req.params.groupId)) return res.status(400).json({ error: 'invalid groupId' }); try { - const result = await pool.query('SELECT * FROM together_groups WHERE id=$1', [req.params.groupId]); + const result = await pool.query('SELECT id, name, created_at FROM together_groups WHERE id=$1', [req.params.groupId]); if (result.rows.length === 0) return res.status(404).json({ error: 'グループが見つかりません' }); res.json(result.rows[0]); } catch (e) { @@ -2624,7 +2625,7 @@ ${excerpt} // ── Atlas: GitHub scan proxy ─────────────────────────────────── r.get('/atlas/github-scan', (req, res) => { - const token = req.query.token; + const token = (req.headers.authorization || '').replace(/^Bearer\s+/i, '').trim(); const org = req.query.org || ''; if (!token) return res.status(400).json({ error: 'token required' }); @@ -2693,7 +2694,7 @@ ${excerpt} // ── Atlas: Vercel scan proxy ─────────────────────────────────── r.get('/atlas/vercel-scan', (req, res) => { - const token = req.query.token; + const token = (req.headers.authorization || '').replace(/^Bearer\s+/i, '').trim(); if (!token) return res.status(400).json({ error: 'token required' }); const https = require('https'); @@ -2725,7 +2726,7 @@ ${excerpt} // ── Atlas: Tailscale scan proxy ──────────────────────────────── r.get('/atlas/tailscale-scan', (req, res) => { - const token = req.query.token; + const token = (req.headers.authorization || '').replace(/^Bearer\s+/i, '').trim(); if (!token) return res.status(400).json({ error: 'token required' }); const https = require('https');