Skip to content

Commit 915f90f

Browse files
authored
Adds console disclaimer when running direct sql from the browser (#158)
Config example for websocket: ```javascript neonConfig.disableWarningBrowser = false; const pool = new Pool({ connectionString }); ``` ```javascript const sql = neon(connectionString, { disableWarningBrowser: true }); ``` <img width="1085" alt="image" src="https://github.com/user-attachments/assets/af77e1a5-340e-4e80-8530-0a33b1292f5d" /> I guess for now this should be enough, we can document it later if needed. This way is a good way to get some signal if there are a lot of people disabling it
1 parent ae82376 commit 915f90f

File tree

7 files changed

+93
-0
lines changed

7 files changed

+93
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66

77
node_modules
88
build
9+
/.idea

src/client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Client, Connection, type ClientConfig } from 'pg';
22
import { Socket } from './shims/net';
3+
import { warnIfBrowser } from './utils';
34

45
export declare interface NeonClient {
56
connection: Connection & {
@@ -95,6 +96,10 @@ export class NeonClient extends Client {
9596

9697
const connectEvent = this.ssl ? 'sslconnect' : 'connect';
9798
con.on(connectEvent, () => {
99+
if (!this.neonConfig.disableWarningInBrowsers) {
100+
warnIfBrowser();
101+
}
102+
98103
this._handleAuthCleartextPassword();
99104
this._handleReadyForQuery();
100105
});

src/httpQuery.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import type {
2727
ParameterizedQuery,
2828
} from './httpTypes';
2929
import { SqlTemplate, UnsafeRawSql } from './sqlTemplate';
30+
import { warnIfBrowser } from './utils';
31+
32+
import { Socket as neonConfig } from './shims/net';
3033

3134
// @ts-ignore -- this isn't officially exported by pg
3235
import TypeOverrides from 'pg/lib/type-overrides';
@@ -192,6 +195,7 @@ export function neon<
192195
readOnly: neonOptReadOnly,
193196
deferrable: neonOptDeferrable,
194197
authToken,
198+
disableWarningInBrowsers,
195199
}: HTTPTransactionOptions<ArrayMode, FullResults> = {},
196200
): NeonQueryFunction<ArrayMode, FullResults> {
197201
// check the connection string
@@ -365,6 +369,10 @@ export function neon<
365369
headers['Neon-Batch-Deferrable'] = String(resolvedDeferrable);
366370
}
367371

372+
if (!(disableWarningInBrowsers || neonConfig.disableWarningInBrowsers)) {
373+
warnIfBrowser();
374+
}
375+
368376
// --- run query ---
369377

370378
let response;

src/httpTypes.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ export interface HTTPQueryOptions<
6464
* Custom type parsers. See https://github.com/brianc/node-pg-types.
6565
*/
6666
types?: typeof PgTypes;
67+
68+
/**
69+
* When `disableWarningInBrowsers` is set to `true`, it disables the warning about
70+
* running this driver in the browser.
71+
*
72+
* Default: `false`
73+
*/
74+
disableWarningInBrowsers?: boolean;
6775
}
6876

6977
export interface HTTPTransactionOptions<

src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,20 @@ circumstances are right.
1919
import type { ClientBase as PgClientBase } from 'pg';
2020
import { type SocketDefaults } from './shims/net';
2121

22+
// Ensure we are very explicit while using these apis. This ensures more type safety
23+
// specially since this library is made to run both in the browser and node.js environments.
24+
declare global {
25+
const window: Window | undefined;
26+
const document: Document | undefined;
27+
28+
interface Window {}
29+
interface Document {}
30+
}
31+
2232
export * from './httpQuery';
2333
export * from './sqlTemplate';
2434
export type * from './httpTypes';
35+
export * from './utils';
2536

2637
export { NeonPool as Pool, type NeonPoolClient as PoolClient } from './pool';
2738
export { NeonClient as Client } from './client';

src/shims/net/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export interface SocketDefaults {
102102
rootCerts: string;
103103
pipelineTLS: boolean;
104104
disableSNI: boolean;
105+
disableWarningInBrowsers: boolean;
105106
}
106107
type GlobalOnlyDefaults =
107108
| 'poolQueryViaFetch'
@@ -142,6 +143,7 @@ export class Socket extends EventEmitter {
142143
rootCerts: '',
143144
pipelineTLS: false,
144145
disableSNI: false,
146+
disableWarningInBrowsers: false,
145147
};
146148

147149
static opts: Partial<SocketDefaults> = {};
@@ -345,6 +347,34 @@ export class Socket extends EventEmitter {
345347
this.opts.disableSNI = newValue;
346348
}
347349

350+
/**
351+
* When `disableWarningInBrowsers` is set to `true`, it disables the warning about
352+
* running this driver in the browser.
353+
*
354+
* Default: `false`.
355+
*/
356+
static get disableWarningInBrowsers() {
357+
return (
358+
Socket.opts.disableWarningInBrowsers ??
359+
Socket.defaults.disableWarningInBrowsers
360+
);
361+
}
362+
static set disableWarningInBrowsers(
363+
newValue: SocketDefaults['disableWarningInBrowsers'],
364+
) {
365+
Socket.opts.disableWarningInBrowsers = newValue;
366+
}
367+
get disableWarningInBrowsers() {
368+
return (
369+
this.opts.disableWarningInBrowsers ?? Socket.disableWarningInBrowsers
370+
);
371+
}
372+
set disableWarningInBrowsers(
373+
newValue: SocketDefaults['disableWarningInBrowsers'],
374+
) {
375+
this.opts.disableWarningInBrowsers = newValue;
376+
}
377+
348378
/**
349379
* Pipelines the startup message, cleartext password message and first query
350380
* when set to `"password"`. This works only for cleartext password auth.

src/utils.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Detects if the code is running in a browser environment and displays a warning
3+
* about the security implications of running SQL directly from the browser.
4+
*/
5+
export function warnIfBrowser(): void {
6+
const isBrowser =
7+
typeof window !== 'undefined' && typeof document !== 'undefined';
8+
if (
9+
isBrowser &&
10+
typeof console !== 'undefined' &&
11+
typeof console.warn === 'function'
12+
) {
13+
console.warn(`
14+
************************************************************
15+
* *
16+
* WARNING: Running SQL directly from the browser can have *
17+
* security implications. Even if your database is *
18+
* protected by Row-Level Security (RLS), use it at your *
19+
* own risk. This approach is great for fast prototyping, *
20+
* but ensure proper safeguards are in place to prevent *
21+
* misuse or execution of expensive SQL queries by your *
22+
* end users. *
23+
* *
24+
* If you've assessed the risks, suppress this message *
25+
* using the disableWarningInBrowsers configuration *
26+
* parameter. *
27+
* *
28+
************************************************************`);
29+
}
30+
}

0 commit comments

Comments
 (0)