Security update: Patch React and Next.js CVE vulnerabilities

Updated React to 19.2.1 and Next.js to 16.0.7 to address critical security vulnerabilities:
- CVE-2025-55182: React Server Components deserialization flaw
- CVE-2025-66478: Next.js RSC implementation vulnerability

Also includes:
- Add PATCH endpoint for geofence updates
- Reorder admin navigation items
- Add geofence update functionality in database layer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-04 09:30:47 +00:00
parent 96d7314317
commit 3ac82621c8
5 changed files with 191 additions and 54 deletions

View File

@@ -19,10 +19,10 @@ export default function AdminLayout({
const allNavigation = [
{ name: "Dashboard", href: "/admin", roles: ['ADMIN', 'VIEWER'], superAdminOnly: false },
{ name: "Devices", href: "/admin/devices", roles: ['ADMIN', 'VIEWER'], superAdminOnly: false },
{ name: "Geofences", href: "/admin/geofences", roles: ['ADMIN'], superAdminOnly: false },
{ name: "MQTT Provisioning", href: "/admin/mqtt", roles: ['ADMIN'], superAdminOnly: false },
{ name: "Setup Guide", href: "/admin/setup", roles: ['ADMIN', 'VIEWER'], superAdminOnly: false },
{ name: "Geofences", href: "/admin/geofences", roles: ['ADMIN'], superAdminOnly: false },
{ name: "Users", href: "/admin/users", roles: ['ADMIN'], superAdminOnly: false },
{ name: "Setup Guide", href: "/admin/setup", roles: ['ADMIN', 'VIEWER'], superAdminOnly: false },
{ name: "Settings", href: "/admin/settings", roles: ['ADMIN'], superAdminOnly: true },
{ name: "Emails", href: "/admin/emails", roles: ['ADMIN'], superAdminOnly: true },
];

View File

@@ -2,6 +2,89 @@ import { NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { geofenceDb } from "@/lib/geofence-db";
// PATCH /api/geofences/[id] - Update a geofence
export async function PATCH(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth();
if (!session?.user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const userId = (session.user as any).id;
const { id: geofenceId } = await params;
// Check if geofence exists
const geofence = geofenceDb.findById(geofenceId);
if (!geofence) {
return NextResponse.json(
{ error: "Geofence not found" },
{ status: 404 }
);
}
// Check ownership
if (geofence.owner_id !== userId) {
return NextResponse.json(
{ error: "Forbidden: You can only update your own geofences" },
{ status: 403 }
);
}
// Parse request body
const body = await request.json();
const { name, description, radius_meters, color, is_active } = body;
// Build updates object with only provided fields
const updates: any = {};
if (name !== undefined) updates.name = name;
if (description !== undefined) updates.description = description;
if (radius_meters !== undefined) updates.radius_meters = radius_meters;
if (color !== undefined) updates.color = color;
if (is_active !== undefined) updates.is_active = is_active;
// Validate updates if provided
if (radius_meters !== undefined) {
if (typeof radius_meters !== 'number' || radius_meters < 50 || radius_meters > 50000) {
return NextResponse.json(
{ error: "Invalid radius_meters (must be between 50 and 50000)" },
{ status: 400 }
);
}
}
// Update geofence
const updatedGeofence = geofenceDb.update(geofenceId, updates);
if (!updatedGeofence) {
return NextResponse.json(
{ error: "Failed to update geofence" },
{ status: 500 }
);
}
console.log(`[PATCH /api/geofences/${geofenceId}] Updated geofence ${updatedGeofence.name} for user ${userId}`);
return NextResponse.json({
success: true,
geofence: updatedGeofence,
});
} catch (error) {
console.error(`[PATCH /api/geofences/[id]] Error:`, error);
return NextResponse.json(
{
error: "Failed to update geofence",
details: error instanceof Error ? error.message : "Unknown error",
},
{ status: 500 }
);
}
}
// DELETE /api/geofences/[id] - Delete a geofence
export async function DELETE(
request: Request,

View File

@@ -96,6 +96,60 @@ export const geofenceDb = {
}
},
/**
* Update a geofence (partial update)
*/
update(id: string, updates: Partial<Omit<Geofence, 'id' | 'owner_id' | 'device_id' | 'created_at' | 'updated_at'>>): Geofence | null {
const db = getDb();
try {
// Build dynamic UPDATE query for only provided fields
const updateFields: string[] = [];
const values: any[] = [];
if (updates.name !== undefined) {
updateFields.push('name = ?');
values.push(updates.name);
}
if (updates.description !== undefined) {
updateFields.push('description = ?');
values.push(updates.description);
}
if (updates.radius_meters !== undefined) {
updateFields.push('radius_meters = ?');
values.push(updates.radius_meters);
}
if (updates.color !== undefined) {
updateFields.push('color = ?');
values.push(updates.color);
}
if (updates.is_active !== undefined) {
updateFields.push('is_active = ?');
values.push(updates.is_active);
}
if (updateFields.length === 0) {
return this.findById(id);
}
// Always update updated_at
updateFields.push('updated_at = datetime(\'now\')');
const query = `UPDATE Geofence SET ${updateFields.join(', ')} WHERE id = ?`;
values.push(id);
const stmt = db.prepare(query);
const result = stmt.run(...values);
if (result.changes === 0) {
return null;
}
return this.findById(id);
} finally {
db.close();
}
},
/**
* Delete a geofence
*/

98
package-lock.json generated
View File

@@ -20,11 +20,11 @@
"better-sqlite3": "^12.4.1",
"leaflet": "^1.9.4",
"mqtt": "^5.14.1",
"next": "^16.0.3",
"next": "^16.0.7",
"next-auth": "^5.0.0-beta.30",
"nodemailer": "^7.0.10",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-email": "^4.3.2",
"react-leaflet": "^5.0.0",
"typescript": "^5.9.3",
@@ -1906,15 +1906,15 @@
}
},
"node_modules/@next/env": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.3.tgz",
"integrity": "sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.7.tgz",
"integrity": "sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==",
"license": "MIT"
},
"node_modules/@next/swc-darwin-arm64": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.3.tgz",
"integrity": "sha512-MOnbd92+OByu0p6QBAzq1ahVWzF6nyfiH07dQDez4/Nku7G249NjxDVyEfVhz8WkLiOEU+KFVnqtgcsfP2nLXg==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.7.tgz",
"integrity": "sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==",
"cpu": [
"arm64"
],
@@ -1928,9 +1928,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.3.tgz",
"integrity": "sha512-i70C4O1VmbTivYdRlk+5lj9xRc2BlK3oUikt3yJeHT1unL4LsNtN7UiOhVanFdc7vDAgZn1tV/9mQwMkWOJvHg==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.7.tgz",
"integrity": "sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==",
"cpu": [
"x64"
],
@@ -1944,9 +1944,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.3.tgz",
"integrity": "sha512-O88gCZ95sScwD00mn/AtalyCoykhhlokxH/wi1huFK+rmiP5LAYVs/i2ruk7xST6SuXN4NI5y4Xf5vepb2jf6A==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.7.tgz",
"integrity": "sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==",
"cpu": [
"arm64"
],
@@ -1960,9 +1960,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.3.tgz",
"integrity": "sha512-CEErFt78S/zYXzFIiv18iQCbRbLgBluS8z1TNDQoyPi8/Jr5qhR3e8XHAIxVxPBjDbEMITprqELVc5KTfFj0gg==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.7.tgz",
"integrity": "sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==",
"cpu": [
"arm64"
],
@@ -1976,9 +1976,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.3.tgz",
"integrity": "sha512-Tc3i+nwt6mQ+Dwzcri/WNDj56iWdycGVh5YwwklleClzPzz7UpfaMw1ci7bLl6GRYMXhWDBfe707EXNjKtiswQ==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.7.tgz",
"integrity": "sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==",
"cpu": [
"x64"
],
@@ -1992,9 +1992,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.3.tgz",
"integrity": "sha512-zTh03Z/5PBBPdTurgEtr6nY0vI9KR9Ifp/jZCcHlODzwVOEKcKRBtQIGrkc7izFgOMuXDEJBmirwpGqdM/ZixA==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.7.tgz",
"integrity": "sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==",
"cpu": [
"x64"
],
@@ -2008,9 +2008,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.3.tgz",
"integrity": "sha512-Jc1EHxtZovcJcg5zU43X3tuqzl/sS+CmLgjRP28ZT4vk869Ncm2NoF8qSTaL99gh6uOzgM99Shct06pSO6kA6g==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.7.tgz",
"integrity": "sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==",
"cpu": [
"arm64"
],
@@ -2024,9 +2024,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.3.tgz",
"integrity": "sha512-N7EJ6zbxgIYpI/sWNzpVKRMbfEGgsWuOIvzkML7wxAAZhPk1Msxuo/JDu1PKjWGrAoOLaZcIX5s+/pF5LIbBBg==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.7.tgz",
"integrity": "sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==",
"cpu": [
"x64"
],
@@ -5054,12 +5054,12 @@
}
},
"node_modules/next": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/next/-/next-16.0.3.tgz",
"integrity": "sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/next/-/next-16.0.7.tgz",
"integrity": "sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==",
"license": "MIT",
"dependencies": {
"@next/env": "16.0.3",
"@next/env": "16.0.7",
"@swc/helpers": "0.5.15",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
@@ -5072,14 +5072,14 @@
"node": ">=20.9.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "16.0.3",
"@next/swc-darwin-x64": "16.0.3",
"@next/swc-linux-arm64-gnu": "16.0.3",
"@next/swc-linux-arm64-musl": "16.0.3",
"@next/swc-linux-x64-gnu": "16.0.3",
"@next/swc-linux-x64-musl": "16.0.3",
"@next/swc-win32-arm64-msvc": "16.0.3",
"@next/swc-win32-x64-msvc": "16.0.3",
"@next/swc-darwin-arm64": "16.0.7",
"@next/swc-darwin-x64": "16.0.7",
"@next/swc-linux-arm64-gnu": "16.0.7",
"@next/swc-linux-arm64-musl": "16.0.7",
"@next/swc-linux-x64-gnu": "16.0.7",
"@next/swc-linux-x64-musl": "16.0.7",
"@next/swc-win32-arm64-msvc": "16.0.7",
"@next/swc-win32-x64-msvc": "16.0.7",
"sharp": "^0.34.4"
},
"peerDependencies": {
@@ -5586,24 +5586,24 @@
}
},
"node_modules/react": {
"version": "19.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"version": "19.2.1",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
"integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "19.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"version": "19.2.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
"integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
},
"peerDependencies": {
"react": "^19.2.0"
"react": "^19.2.1"
}
},
"node_modules/react-email": {

View File

@@ -36,11 +36,11 @@
"better-sqlite3": "^12.4.1",
"leaflet": "^1.9.4",
"mqtt": "^5.14.1",
"next": "^16.0.3",
"next": "^16.0.7",
"next-auth": "^5.0.0-beta.30",
"nodemailer": "^7.0.10",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-email": "^4.3.2",
"react-leaflet": "^5.0.0",
"typescript": "^5.9.3",