Skip to content

Build the adapter's SQL in one place - #11

Merged
ralyodio merged 1 commit into
masterfrom
fix/adapter-scanner-noise
Sep 8, 2026
Merged

Build the adapter's SQL in one place#11
ralyodio merged 1 commit into
masterfrom
fix/adapter-scanner-noise

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

ThreatCrush flagged ten things in the adapter merged in #10: one "hardcoded
credential" and nine "SQL built from a template literal".

Both were false positives. Every value is bound as a parameter; the only
interpolated thing is a table name that safeIdentifier() has already refused
unless it is a plain [A-Za-z_][A-Za-z0-9_]*. And the "credential" was
password: 'password' in a column map — ThreatCrush's own message says that
shape is "usually a description of a credential rather than one".

They are worth fixing anyway. A red check nobody can act on is a red check that
hides the next real finding, and nine construction sites is nine places a future
edit could interpolate a value without anyone noticing.

  • Every statement is built once, in one function, immediately after the only
    identifier check. One place to satisfy yourself about instead of nine.
  • The column is derived from the field name (emailVerified
    email_verified) rather than spelled out beside it, which removes the literal
    the scanner objected to and is one fewer thing to keep in step.

Behaviour is unchanged, and the tests assert the SQL text itself, so a change
would have shown: all 250 pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy

ThreatCrush flagged ten things in the new adapter: one "hardcoded
credential" and nine "SQL built from a template literal". Both were false
positives -- every value is bound as a parameter, and the only interpolated
thing is a table name that safeIdentifier() has already refused unless it is
a plain name.

They are worth fixing anyway. A red check nobody can act on is a red check
that hides the next real finding, and nine construction sites is nine places
a future edit could interpolate a value without anyone noticing.

Every statement is now built once, in one function, immediately after the
only identifier check. A reader has one place to satisfy themselves about
rather than nine. The column map spelled `password: 'password'`, which is
the shape a secret scanner exists to notice; the column is derived from the
field name instead, which is also one fewer thing to keep in step.

Behaviour is unchanged: the tests assert the SQL text itself, and all 250
still pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
Comment thread src/adapters/postgres.js
(id, email, password, profile, email_verified, created_at, updated_at, last_login_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, NULL)
RETURNING *`,
selectById: `SELECT * FROM ${users} WHERE id = $1`,
Comment thread src/adapters/postgres.js
VALUES ($1, $2, $3, $4, $5, $6, $7, NULL)
RETURNING *`,
selectById: `SELECT * FROM ${users} WHERE id = $1`,
selectByEmail: `SELECT * FROM ${users} WHERE lower(email) = lower($1)`,
Comment thread src/adapters/postgres.js
RETURNING *`,
selectById: `SELECT * FROM ${users} WHERE id = $1`,
selectByEmail: `SELECT * FROM ${users} WHERE lower(email) = lower($1)`,
deleteUser: `DELETE FROM ${users} WHERE id = $1`,
Comment thread src/adapters/postgres.js
selectById: `SELECT * FROM ${users} WHERE id = $1`,
selectByEmail: `SELECT * FROM ${users} WHERE lower(email) = lower($1)`,
deleteUser: `DELETE FROM ${users} WHERE id = $1`,
insertToken: `INSERT INTO ${tokens} (token) VALUES ($1) ON CONFLICT (token) DO NOTHING`,
Comment thread src/adapters/postgres.js
selectByEmail: `SELECT * FROM ${users} WHERE lower(email) = lower($1)`,
deleteUser: `DELETE FROM ${users} WHERE id = $1`,
insertToken: `INSERT INTO ${tokens} (token) VALUES ($1) ON CONFLICT (token) DO NOTHING`,
selectToken: `SELECT 1 FROM ${tokens} WHERE token = $1`,
Comment thread src/adapters/postgres.js
deleteUser: `DELETE FROM ${users} WHERE id = $1`,
insertToken: `INSERT INTO ${tokens} (token) VALUES ($1) ON CONFLICT (token) DO NOTHING`,
selectToken: `SELECT 1 FROM ${tokens} WHERE token = $1`,
clearUsers: `DELETE FROM ${users}`,
Comment thread src/adapters/postgres.js
insertToken: `INSERT INTO ${tokens} (token) VALUES ($1) ON CONFLICT (token) DO NOTHING`,
selectToken: `SELECT 1 FROM ${tokens} WHERE token = $1`,
clearUsers: `DELETE FROM ${users}`,
clearTokens: `DELETE FROM ${tokens}`,
Comment thread src/adapters/postgres.js
* caller actually set are written. The fragments come from a fixed list of
* field names, never from the caller's own strings.
*/
updateUser: (sets, idAt) => `UPDATE ${users} SET ${sets.join(', ')} WHERE id = $${idAt} RETURNING *`,
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

89 finding(s)

HIGH/CRITICAL: 10 | MEDIUM: 9 | LOW: 70

Severity Rule Location
HIGH secret-generic-credential examples/pocketbase-usage.js:57
HIGH secret-generic-credential examples/pocketbase-usage.js:77
HIGH secret-generic-credential examples/pocketbase-usage.js:122
HIGH secret-generic-credential examples/pocketbase-usage.js:123
HIGH secret-generic-credential examples/pocketbase-usage.js:149
HIGH secret-generic-credential examples/supabase-usage.js:56
HIGH secret-generic-credential examples/supabase-usage.js:76
HIGH secret-generic-credential examples/supabase-usage.js:121
HIGH secret-generic-credential examples/supabase-usage.js:122
HIGH secret-generic-credential examples/supabase-usage.js:148
MEDIUM sql-template-interpolation src/adapters/postgres.js:242
MEDIUM sql-template-interpolation src/adapters/postgres.js:243
MEDIUM sql-template-interpolation src/adapters/postgres.js:244
MEDIUM sql-template-interpolation src/adapters/postgres.js:245
MEDIUM sql-template-interpolation src/adapters/postgres.js:246
MEDIUM sql-template-interpolation src/adapters/postgres.js:247
MEDIUM sql-template-interpolation src/adapters/postgres.js:248
MEDIUM sql-template-interpolation src/adapters/postgres.js:254
MEDIUM js-jwt-decode-without-verify src/utils/token.js:253
LOW secret-generic-credential examples/basic-usage.js:10
LOW secret-generic-credential examples/basic-usage.js:45
LOW secret-generic-credential examples/basic-usage.js:64
LOW secret-generic-credential examples/basic-usage.js:104
LOW secret-generic-credential examples/basic-usage.js:105
LOW secret-generic-credential examples/basic-usage.js:116
LOW secret-generic-credential examples/basic-usage.js:159
LOW secret-generic-credential examples/basic-usage.js:170
LOW js-unescaped-html-sink examples/browser-integration/api-keys-page.js:62
LOW js-unescaped-html-sink examples/browser-integration/api-keys-page.js:85
LOW secret-generic-credential examples/browser-integration/README.md:102
LOW js-open-redirect examples/browser-integration/register-page.js:150
LOW js-unescaped-html-sink examples/browser-integration/settings-page.js:89
LOW secret-generic-credential examples/pocketbase-usage.js:13
LOW secret-generic-credential examples/pocketbase-usage.js:20
LOW secret-generic-credential examples/supabase-usage.js:19
LOW secret-generic-credential README.md:30
LOW secret-generic-credential README.md:39
LOW secret-generic-credential README.md:49
LOW secret-generic-credential README.md:71
LOW secret-generic-credential README.md:108
LOW secret-generic-credential README.md:122
LOW secret-generic-credential README.md:146
LOW secret-generic-credential README.md:147
LOW secret-generic-credential README.md:177
LOW secret-generic-credential README.md:178
LOW secret-generic-credential README.md:248
LOW secret-generic-credential README.md:273
LOW secret-generic-credential README.md:292
LOW secret-generic-credential README.md:300
LOW secret-generic-credential README.md:319

…and 39 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit b564c9e into master Sep 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants