fix: security — invite_code leakage, Atlas token in URL, RSS err.message exposure
- GET /together/groups/🆔 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 <noreply@anthropic.com>
This commit is contained in:
parent
dbc30494bd
commit
ada6eba333
15
server.js
15
server.js
|
|
@ -771,7 +771,7 @@ function buildRouter() {
|
||||||
|
|
||||||
// POST /api/auth/magic-link/send
|
// POST /api/auth/magic-link/send
|
||||||
r.post('/auth/magic-link/send', async (req, res) => {
|
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)) {
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||||
return res.status(400).json({ error: 'メールアドレスが無効です' });
|
return res.status(400).json({ error: 'メールアドレスが無効です' });
|
||||||
}
|
}
|
||||||
|
|
@ -816,7 +816,8 @@ function buildRouter() {
|
||||||
|
|
||||||
// Send email via Resend (if API key is set)
|
// Send email via Resend (if API key is set)
|
||||||
if (process.env.RESEND_API_KEY) {
|
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 {
|
try {
|
||||||
const emailRes = await fetch('https://api.resend.com/emails', {
|
const emailRes = await fetch('https://api.resend.com/emails', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -2314,7 +2315,7 @@ ${excerpt}
|
||||||
res.json({ events: unique, fetched_at: new Date().toISOString() });
|
res.json({ events: unique, fetched_at: new Date().toISOString() });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[events/rss]', 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) => {
|
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' });
|
if (!/^[a-zA-Z0-9_-]+$/.test(req.params.groupId)) return res.status(400).json({ error: 'invalid groupId' });
|
||||||
try {
|
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: 'グループが見つかりません' });
|
if (result.rows.length === 0) return res.status(404).json({ error: 'グループが見つかりません' });
|
||||||
res.json(result.rows[0]);
|
res.json(result.rows[0]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -2624,7 +2625,7 @@ ${excerpt}
|
||||||
|
|
||||||
// ── Atlas: GitHub scan proxy ───────────────────────────────────
|
// ── Atlas: GitHub scan proxy ───────────────────────────────────
|
||||||
r.get('/atlas/github-scan', (req, res) => {
|
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 || '';
|
const org = req.query.org || '';
|
||||||
if (!token) return res.status(400).json({ error: 'token required' });
|
if (!token) return res.status(400).json({ error: 'token required' });
|
||||||
|
|
||||||
|
|
@ -2693,7 +2694,7 @@ ${excerpt}
|
||||||
|
|
||||||
// ── Atlas: Vercel scan proxy ───────────────────────────────────
|
// ── Atlas: Vercel scan proxy ───────────────────────────────────
|
||||||
r.get('/atlas/vercel-scan', (req, res) => {
|
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' });
|
if (!token) return res.status(400).json({ error: 'token required' });
|
||||||
|
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
|
@ -2725,7 +2726,7 @@ ${excerpt}
|
||||||
|
|
||||||
// ── Atlas: Tailscale scan proxy ────────────────────────────────
|
// ── Atlas: Tailscale scan proxy ────────────────────────────────
|
||||||
r.get('/atlas/tailscale-scan', (req, res) => {
|
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' });
|
if (!token) return res.status(400).json({ error: 'token required' });
|
||||||
|
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue