diff options
| author | Adam Soltys <[email protected]> | 2026-03-17 01:21:48 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-17 01:34:28 -0700 |
| commit | 136ba2a12e87178f6d83fba2874abf4c9ad32915 (patch) | |
| tree | 347fe88201f1d708b0464b299f1ea56f2e86800d /lib | |
| parent | c21dbc2e6fb56c634351d83ddfc6fd73ce4a3de5 (diff) | |
| download | coinos-server-136ba2a12e87178f6d83fba2874abf4c9ad32915.tar.gz coinos-server-136ba2a12e87178f6d83fba2874abf4c9ad32915.zip | |
fix: add account ownership checks and harden PIN validation
- Validate account ownership (aid) before allowing ark send/sync/vault
operations and in debit/sendOnchain as defense-in-depth
- Harden PIN check to reject missing/empty pin when user has one set
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/auth.ts | 9 | ||||
| -rw-r--r-- | lib/payments.ts | 8 |
2 files changed, 16 insertions, 1 deletions
diff --git a/lib/auth.ts b/lib/auth.ts index 30d5438b..c8d5afab 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -49,5 +49,12 @@ export const admin = async (c, next) => { }; export const requirePin = async ({ body, user }) => { - if (!user || (user.pin && user.pin !== body.pin)) fail("Invalid pin"); + if (!user) fail("Invalid pin"); + if (user.pin && (!body?.pin || user.pin !== body.pin)) fail("Invalid pin"); +}; + +export const requireAccountOwnership = async (db, uid, aid) => { + if (!aid || aid === uid) return; + const index = await db.lPos(`${uid}:accounts`, aid); + if (index === null) fail("unauthorized"); }; diff --git a/lib/payments.ts b/lib/payments.ts index 2737f886..78f20684 100644 --- a/lib/payments.ts +++ b/lib/payments.ts @@ -152,6 +152,12 @@ export const debit = async ({ }) => { amount = Number.parseInt(amount); + // Defense-in-depth: verify account ownership at the debit level + if (aid && user && aid !== user.id) { + const index = await db.lPos(`${user.id}:accounts`, aid); + if (index === null) fail("unauthorized"); + } + const whitelisted = await db.sIsMember("whitelist", user?.username?.toLowerCase().trim()); const blacklisted = await db.sIsMember("blacklist", user?.username?.toLowerCase().trim()); @@ -715,6 +721,8 @@ export const sendOnchain = async (params) => { // Non-custodial bitcoin account — use esplora if (aid !== user.id) { + const index = await db.lPos(`${user.id}:accounts`, aid); + if (index === null) fail("unauthorized"); return sendNonCustodial(params); } |
