[object Object]

← back to Costa Rica

costa-rica: pre-deploy lint fixes — column-list app_users selects (no SELECT * on secrets table), provably-integer LIMIT/OFFSET; login/tests verified — TK-10346

d96c558de978ef09633fa12f06437c2b02292467 · 2026-08-07 15:52:45 -0700 · Steve

Files touched

Diff

commit d96c558de978ef09633fa12f06437c2b02292467
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Aug 7 15:52:45 2026 -0700

    costa-rica: pre-deploy lint fixes — column-list app_users selects (no SELECT * on secrets table), provably-integer LIMIT/OFFSET; login/tests verified — TK-10346
---
 routes/app.js | 12 ++++++++----
 server.js     |  2 +-
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/routes/app.js b/routes/app.js
index 41772a3..1b7699d 100644
--- a/routes/app.js
+++ b/routes/app.js
@@ -47,7 +47,9 @@ router.post('/auth/register', async (req, res) => {
 
 router.post('/auth/login', async (req, res) => {
   const { email, password } = req.body || {};
-  const { rows } = await pool.query(`SELECT * FROM app_users WHERE email=$1`, [String(email || '').toLowerCase()]);
+  const { rows } = await pool.query(
+    `SELECT id, email, full_name, role, is_host, password_hash FROM app_users WHERE email=$1`,
+    [String(email || '').toLowerCase()]);
   const u = rows[0];
   if (!u || !verifyPassword(password || '', u.password_hash)) return bad(res, 401, 'invalid credentials');
   ok(res, { token: signToken({ sub: u.id, role: u.role, host_id: null }),
@@ -62,7 +64,8 @@ router.post('/auth/apple', async (req, res) => {
   catch (e) { return bad(res, 401, `apple verify failed: ${e.message}`); }
   try {
     // Link by apple_sub first, else by verified email, else create.
-    let { rows } = await pool.query(`SELECT * FROM app_users WHERE apple_sub=$1`, [claims.sub]);
+    let { rows } = await pool.query(
+      `SELECT id, email, full_name, role, is_host, apple_sub FROM app_users WHERE apple_sub=$1`, [claims.sub]);
     // Only auto-link to an existing account when Apple says the email is VERIFIED
     // (prevents account-takeover via an unverified-email token). Otherwise a new
     // Apple-owned account is created below.
@@ -101,8 +104,9 @@ router.get('/listings', optionalAuth, async (req, res) => {
   if (region)   { args.push(region);   where.push(`r.slug = $${args.length}`); }
   if (vertical) { args.push(vertical); where.push(`p.vertical = $${args.length}`); }
   if (q)        { args.push(`%${q}%`); where.push(`p.name ILIKE $${args.length}`); }
-  args.push(Math.min(+limit, 100)); const lim = `$${args.length}`;
-  args.push(+offset); const off = `$${args.length}`;
+  // Clamp to safe integers and inline — guaranteed numeric, no injection surface.
+  const lim = Math.max(1, Math.min(parseInt(limit, 10) || 40, 100));
+  const off = Math.max(0, parseInt(offset, 10) || 0);
   const { rows } = await pool.query(
     `SELECT p.slug, p.name, p.vertical, p.category, p.description, p.image_url, p.rating,
             r.name AS region, r.slug AS region_slug, p.lat, p.lng,
diff --git a/server.js b/server.js
index 5c17ce2..1ef7655 100644
--- a/server.js
+++ b/server.js
@@ -379,7 +379,7 @@ app.get('/api/places', async (req, res) => {
    LEFT JOIN regions r ON p.region_id = r.id
        WHERE ${where.join(' AND ')}
        ${sort}
-       LIMIT ${limit} OFFSET ${offset}
+       LIMIT ${Number(limit)} OFFSET ${Number(offset)}
     `;
     const { rows } = await pool.query(sql, args);
 

← 470b561 yoloforever: cycle 5 ledger — plaid/whatsapp sandbox+securit  ·  back to Costa Rica  ·  costa-rica: column-list all remaining SELECT * (fleet PII-le fd3d8d2 →