fix(proxy): harden security and add UpSnap debug logging
- XSS: escape serviceName in waking page HTML - Session TTL: 24h expiration with periodic cleanup - Rate limit: 5 login attempts / 15 min per IP - CORS: restrict to same-origin + localhost - SSRF: block localhost/metadata in service targets - UpSnap: log response bodies on auth/wake failures Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -8,23 +8,24 @@ async function authenticate(): Promise<string> {
|
||||
const collections = ['_superusers', 'users'];
|
||||
|
||||
for (const collection of collections) {
|
||||
const res = await fetch(
|
||||
`${config.upsnap.url}/api/collections/${collection}/auth-with-password`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
identity: config.upsnap.username,
|
||||
password: config.upsnap.password,
|
||||
}),
|
||||
}
|
||||
);
|
||||
const url = `${config.upsnap.url}/api/collections/${collection}/auth-with-password`;
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
identity: config.upsnap.username,
|
||||
password: config.upsnap.password,
|
||||
}),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as UpSnapAuthResponse;
|
||||
token = data.token;
|
||||
return token;
|
||||
}
|
||||
|
||||
const errBody = await res.text().catch(() => '');
|
||||
console.error(`[UpSnap] Auth attempt ${collection} failed (${res.status}): ${errBody}`);
|
||||
}
|
||||
|
||||
throw new Error('UpSnap auth failed: could not authenticate as superuser or user');
|
||||
@@ -67,15 +68,18 @@ export async function wakeDevice(): Promise<void> {
|
||||
{ method: 'PATCH', body: JSON.stringify({ status: 'on' }) }
|
||||
);
|
||||
|
||||
// UpSnap may also have a dedicated wake endpoint — try both approaches
|
||||
if (!res.ok) {
|
||||
// Fallback: POST to wake endpoint
|
||||
const res2 = await upSnapFetch(`/api/upsnap/wake/${config.upsnap.deviceId}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
if (!res2.ok) {
|
||||
throw new Error(`UpSnap wake failed: ${res2.status}`);
|
||||
}
|
||||
if (res.ok) return;
|
||||
|
||||
const body1 = await res.text().catch(() => '');
|
||||
console.error(`[UpSnap] PATCH wake failed (${res.status}): ${body1}`);
|
||||
|
||||
// Fallback: GET wake endpoint
|
||||
const res2 = await upSnapFetch(`/api/upsnap/wake/${config.upsnap.deviceId}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
if (!res2.ok) {
|
||||
const body2 = await res2.text().catch(() => '');
|
||||
throw new Error(`UpSnap wake failed: ${res2.status} — ${body2}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user