From 5e10afebf15e6e1542dc3046ea3ff0fc675d0826 Mon Sep 17 00:00:00 2001 From: FreightDesk Date: Sun, 7 Jun 2026 19:34:22 +0000 Subject: [PATCH] [OWL] Security fixes: remove hardcoded password, add setup form, soft-delete migration - Replace hardcoded admin123 with user-defined password via /setup form - Add proper GET/POST /setup routes - Create setup.ejs view with password validation (min 6 chars) - Add migration 003: soft-delete columns (deleted_at) on loads/payments/shippers/vehicles - Add load_count column to shippers - requireRole middleware already present in auth.js --- supabase/migrations/003_soft_delete.sql | 24 +++++++++++++ webapp/src/server.js | 35 ++++++++++++++----- webapp/src/views/pages/setup.ejs | 46 +++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 supabase/migrations/003_soft_delete.sql create mode 100644 webapp/src/views/pages/setup.ejs diff --git a/supabase/migrations/003_soft_delete.sql b/supabase/migrations/003_soft_delete.sql new file mode 100644 index 0000000..80ca5ea --- /dev/null +++ b/supabase/migrations/003_soft_delete.sql @@ -0,0 +1,24 @@ +-- ============================================================ +-- FreightDesk — Migration 003: Soft Delete + Security +-- ============================================================ + +-- Add soft-delete columns +ALTER TABLE loads ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE payments ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE shippers ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE vehicles ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; + +-- Add role column to portal_users if not exists +ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS role TEXT DEFAULT 'admin'; + +-- Add index for soft-delete queries +CREATE INDEX IF NOT EXISTS idx_loads_deleted_at ON loads(deleted_at) WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS idx_payments_deleted_at ON payments(deleted_at) WHERE deleted_at IS NULL; + +-- Add load_count to shippers for quick reference +ALTER TABLE shippers ADD COLUMN IF NOT EXISTS load_count INTEGER DEFAULT 0; + +-- Update load_count for existing shippers +UPDATE shippers SET load_count = ( + SELECT COUNT(*) FROM loads WHERE loads.shipper_id = shippers.id +); diff --git a/webapp/src/server.js b/webapp/src/server.js index 557ba75..c7b6f5a 100644 --- a/webapp/src/server.js +++ b/webapp/src/server.js @@ -145,26 +145,45 @@ app.get('/logout', (req, res) => { }); app.get('/setup', asyncHandler(async (req, res) => { - // Check if admin exists + // Check if any user exists const { count } = await supabase .from('portal_users') - .select('*', { count: 'exact', head: true }) - .eq('username', 'admin'); + .select('*', { count: 'exact', head: true }); if (count > 0) { return res.redirect('/login'); } - // Create default admin - const hash = await bcrypt.hash('admin123', 10); - await supabase.from('portal_users').insert({ - username: 'admin', + res.render('pages/setup', { error: null }); +})); + +app.post('/setup', asyncHandler(async (req, res) => { + const { count } = await supabase + .from('portal_users') + .select('*', { count: 'exact', head: true }); + + if (count > 0) { + return res.redirect('/login'); + } + + const { username, password } = req.body; + if (!username || !password || password.length < 6) { + return res.render('pages/setup', { error: 'Username required and password must be at least 6 characters' }); + } + + const hash = await bcrypt.hash(password, 10); + const { error } = await supabase.from('portal_users').insert({ + username, password_hash: hash, role: 'admin', is_active: true, }); - res.send('

Admin created!

Username: admin

Password: admin123

Go to login

'); + if (error) { + return res.render('pages/setup', { error: 'Failed to create admin. ' + error.message }); + } + + res.redirect('/login'); })); // ============================================================ diff --git a/webapp/src/views/pages/setup.ejs b/webapp/src/views/pages/setup.ejs new file mode 100644 index 0000000..f72cea7 --- /dev/null +++ b/webapp/src/views/pages/setup.ejs @@ -0,0 +1,46 @@ + + + + + + Setup — <%= appName %> + + + + +
+ +
+ + +