neon 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +70 -5
  2. package/dist/_chunks/auth_selection-DGgq6ifc.js +83 -0
  3. package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
  4. package/dist/_chunks/credentials-MYdHdKah.js +188 -0
  5. package/dist/_chunks/env-NbA61JR3.js +585 -0
  6. package/dist/_chunks/env_services-Tz9G4JeT.js +531 -0
  7. package/dist/_chunks/paths-DMq0Lt7a.js +151 -0
  8. package/dist/_chunks/profiles-Ir29rqns.js +217 -0
  9. package/dist/_chunks/psql-DWH-kc69.js +2169 -0
  10. package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
  11. package/dist/_chunks/secure_file-BucZj4yQ.js +39 -0
  12. package/dist/analytics.js +163 -207
  13. package/dist/api.js +815 -758
  14. package/dist/auth.js +121 -141
  15. package/dist/auth_context.js +39 -53
  16. package/dist/cli.js +4 -7
  17. package/dist/commands/api.js +220 -250
  18. package/dist/commands/api_keys.js +251 -314
  19. package/dist/commands/auth.js +283 -328
  20. package/dist/commands/bootstrap.js +372 -437
  21. package/dist/commands/branches.js +304 -455
  22. package/dist/commands/bucket.js +374 -514
  23. package/dist/commands/checkout.js +213 -298
  24. package/dist/commands/config.js +573 -690
  25. package/dist/commands/connection_string.js +137 -165
  26. package/dist/commands/data_api.js +238 -260
  27. package/dist/commands/databases.js +67 -76
  28. package/dist/commands/deploy.js +31 -25
  29. package/dist/commands/dev.js +639 -719
  30. package/dist/commands/diff.js +156 -200
  31. package/dist/commands/env.js +255 -305
  32. package/dist/commands/functions.js +275 -355
  33. package/dist/commands/index.js +70 -65
  34. package/dist/commands/init.js +84 -119
  35. package/dist/commands/inspect.js +55 -55
  36. package/dist/commands/ip_allow.js +88 -120
  37. package/dist/commands/link.js +874 -1019
  38. package/dist/commands/logs.js +291 -0
  39. package/dist/commands/neon_auth.js +725 -933
  40. package/dist/commands/operations.js +34 -25
  41. package/dist/commands/orgs.js +28 -18
  42. package/dist/commands/profile.js +615 -846
  43. package/dist/commands/projects.js +313 -373
  44. package/dist/commands/psql.js +60 -58
  45. package/dist/commands/roles.js +55 -58
  46. package/dist/commands/schema_diff.js +87 -131
  47. package/dist/commands/set_context.js +34 -26
  48. package/dist/commands/snapshots.js +288 -413
  49. package/dist/commands/status.js +41 -37
  50. package/dist/commands/user.js +21 -10
  51. package/dist/commands/vpc_endpoints.js +85 -113
  52. package/dist/config.js +7 -6
  53. package/dist/config_format.js +50 -66
  54. package/dist/config_template.js +128 -157
  55. package/dist/context.js +183 -235
  56. package/dist/current_branch_fast_path.js +40 -49
  57. package/dist/dev/env.js +2 -446
  58. package/dist/dev/functions.js +54 -68
  59. package/dist/dev/inputs.js +46 -58
  60. package/dist/dev/runtime.js +135 -164
  61. package/dist/dev/websocket.js +766 -959
  62. package/dist/env.js +27 -33
  63. package/dist/env_file.js +118 -132
  64. package/dist/env_services.js +2 -51
  65. package/dist/errors.js +57 -68
  66. package/dist/functions_api.js +45 -43
  67. package/dist/help.js +189 -140
  68. package/dist/index.js +182 -257
  69. package/dist/init/agents.js +137 -118
  70. package/dist/init/auth.js +58 -68
  71. package/dist/init/bootstrap.js +325 -396
  72. package/dist/init/build_config.js +4 -2
  73. package/dist/init/detect_agent.js +56 -101
  74. package/dist/init/editors.js +35 -52
  75. package/dist/init/enrich_output.js +51 -66
  76. package/dist/init/extension.js +134 -171
  77. package/dist/init/inspect.js +179 -266
  78. package/dist/init/interactive.js +510 -622
  79. package/dist/init/neonctl.js +117 -168
  80. package/dist/init/orchestrate.js +157 -173
  81. package/dist/init/phases/auth.js +188 -202
  82. package/dist/init/phases/cleanup.js +23 -23
  83. package/dist/init/phases/db.js +251 -277
  84. package/dist/init/phases/getting_started.js +213 -223
  85. package/dist/init/phases/mcp.js +174 -224
  86. package/dist/init/phases/migrations.js +247 -248
  87. package/dist/init/phases/neon_auth.js +114 -133
  88. package/dist/init/phases/setup.js +546 -703
  89. package/dist/init/phases/skills.js +75 -86
  90. package/dist/init/phases/status.js +72 -67
  91. package/dist/init/resolve_context.js +102 -99
  92. package/dist/init/route_command.js +91 -98
  93. package/dist/init/skills.js +174 -218
  94. package/dist/init/vsix.js +77 -99
  95. package/dist/log.js +17 -16
  96. package/dist/neon_services.js +104 -129
  97. package/dist/parameters.gen.js +481 -471
  98. package/dist/pkg.js +17 -19
  99. package/dist/profile_keys.js +44 -47
  100. package/dist/psql/cli.js +44 -47
  101. package/dist/psql/command/cmd_cond.js +231 -406
  102. package/dist/psql/command/cmd_connect.js +557 -764
  103. package/dist/psql/command/cmd_copy.js +728 -984
  104. package/dist/psql/command/cmd_describe.js +1499 -1688
  105. package/dist/psql/command/cmd_format.js +733 -905
  106. package/dist/psql/command/cmd_io.js +2 -2193
  107. package/dist/psql/command/cmd_lo.js +297 -359
  108. package/dist/psql/command/cmd_meta.js +727 -878
  109. package/dist/psql/command/cmd_misc.js +138 -172
  110. package/dist/psql/command/cmd_pipeline.js +2 -1148
  111. package/dist/psql/command/cmd_restrict.js +119 -155
  112. package/dist/psql/command/cmd_show.js +529 -688
  113. package/dist/psql/command/dispatch.js +260 -325
  114. package/dist/psql/command/inputQueue.js +35 -33
  115. package/dist/psql/command/shared.js +49 -63
  116. package/dist/psql/complete/filenames.js +90 -133
  117. package/dist/psql/complete/index.js +59 -97
  118. package/dist/psql/complete/matcher.js +236 -300
  119. package/dist/psql/complete/psqlVars.js +218 -223
  120. package/dist/psql/complete/queries.js +159 -177
  121. package/dist/psql/complete/rules.js +1493 -2299
  122. package/dist/psql/core/common.js +2 -1253
  123. package/dist/psql/core/help.js +456 -546
  124. package/dist/psql/core/mainloop.js +692 -1303
  125. package/dist/psql/core/prompt.js +391 -408
  126. package/dist/psql/core/settings.js +429 -644
  127. package/dist/psql/core/sqlHelp.js +480 -554
  128. package/dist/psql/core/startup.js +2 -846
  129. package/dist/psql/core/syncVars.js +67 -110
  130. package/dist/psql/core/variables.js +156 -278
  131. package/dist/psql/describe/formatters.js +884 -1285
  132. package/dist/psql/describe/processNamePattern.js +173 -260
  133. package/dist/psql/describe/queries.js +1368 -2403
  134. package/dist/psql/describe/versionGate.js +32 -41
  135. package/dist/psql/index.js +2 -2030
  136. package/dist/psql/io/history.js +232 -271
  137. package/dist/psql/io/input.js +103 -108
  138. package/dist/psql/io/lineEditor/buffer.js +238 -319
  139. package/dist/psql/io/lineEditor/complete.js +135 -213
  140. package/dist/psql/io/lineEditor/filename.js +139 -148
  141. package/dist/psql/io/lineEditor/index.js +653 -870
  142. package/dist/psql/io/lineEditor/keymap.js +544 -702
  143. package/dist/psql/io/lineEditor/vt100.js +294 -341
  144. package/dist/psql/io/pgpass.js +158 -187
  145. package/dist/psql/io/pgservice.js +146 -183
  146. package/dist/psql/io/psqlrc.js +328 -403
  147. package/dist/psql/print/aligned.js +1020 -1683
  148. package/dist/psql/print/asciidoc.js +180 -214
  149. package/dist/psql/print/crosstab.js +281 -442
  150. package/dist/psql/print/csv.js +48 -70
  151. package/dist/psql/print/html.js +195 -226
  152. package/dist/psql/print/json.js +75 -88
  153. package/dist/psql/print/latex.js +291 -364
  154. package/dist/psql/print/pager.js +171 -242
  155. package/dist/psql/print/troff.js +194 -226
  156. package/dist/psql/print/unaligned.js +69 -95
  157. package/dist/psql/print/units.js +167 -169
  158. package/dist/psql/scanner/slash.js +428 -483
  159. package/dist/psql/scanner/sql.js +445 -889
  160. package/dist/psql/scanner/stringutils.js +309 -379
  161. package/dist/psql/types/index.js +8 -7
  162. package/dist/psql/types/scanner.js +25 -22
  163. package/dist/psql/wire/connection.js +2042 -2803
  164. package/dist/psql/wire/copy.js +84 -100
  165. package/dist/psql/wire/notify.js +39 -59
  166. package/dist/psql/wire/pipeline.js +305 -518
  167. package/dist/psql/wire/protocol.js +349 -417
  168. package/dist/psql/wire/sasl.js +180 -265
  169. package/dist/psql/wire/tls.js +400 -561
  170. package/dist/storage_api.js +115 -129
  171. package/dist/test_utils/fixtures.js +94 -113
  172. package/dist/test_utils/oauth_server.js +10 -7
  173. package/dist/test_utils/project_dir.js +33 -0
  174. package/dist/utils/ai_gateway_notice.js +131 -162
  175. package/dist/utils/api_enums.js +21 -28
  176. package/dist/utils/auth.js +10 -4
  177. package/dist/utils/branch_notice.js +20 -19
  178. package/dist/utils/branch_picker.js +83 -89
  179. package/dist/utils/cli_name.js +15 -12
  180. package/dist/utils/compute_units.js +20 -27
  181. package/dist/utils/config_diff.js +127 -158
  182. package/dist/utils/enrichers.js +95 -148
  183. package/dist/utils/esbuild.js +130 -189
  184. package/dist/utils/flags.js +35 -47
  185. package/dist/utils/formats.js +8 -15
  186. package/dist/utils/git_diff.js +69 -80
  187. package/dist/utils/inspect_db.js +101 -143
  188. package/dist/utils/inspect_queries.js +179 -142
  189. package/dist/utils/middlewares.js +39 -45
  190. package/dist/utils/openapi.js +87 -99
  191. package/dist/utils/package_manager.js +312 -110
  192. package/dist/utils/point_in_time.js +49 -53
  193. package/dist/utils/psql.js +89 -106
  194. package/dist/utils/service_picker.js +55 -58
  195. package/dist/utils/string.js +5 -5
  196. package/dist/utils/ui.js +38 -55
  197. package/dist/utils/write_sync.js +26 -35
  198. package/dist/utils/zip.js +4 -3
  199. package/dist/writer.js +67 -87
  200. package/package.json +11 -6
  201. package/dist/_shared/auth_selection.js +0 -86
  202. package/dist/_shared/credentials.js +0 -209
  203. package/dist/_shared/env-core/env.js +0 -558
  204. package/dist/_shared/env-core/reuse-secrets.js +0 -223
  205. package/dist/_shared/paths.js +0 -148
  206. package/dist/_shared/profiles.js +0 -276
  207. package/dist/_shared/secure_file.js +0 -43
@@ -1,294 +1,209 @@
1
+ import { createHash, createHmac, pbkdf2Sync, randomBytes, timingSafeEqual } from "node:crypto";
2
+ //#region src/psql/wire/sasl.ts
1
3
  /**
2
- * SASL / SCRAM-SHA-256[-PLUS] client for the psql wire layer (WP-03).
3
- *
4
- * Ported from node-postgres' `packages/pg/lib/crypto/sasl.js` (MIT,
5
- * Copyright (c) 2010-2020 Brian Carlson). Adaptations from upstream:
6
- *
7
- * - Pure Node `node:crypto` (sync PBKDF2 / HMAC) instead of SubtleCrypto;
8
- * this module is sync because the wire layer drives it from a state
9
- * machine that already buffers bytes.
10
- * - The `pg` `Connection` / `Stream` plumbing is removed. Channel binding
11
- * data (the certificate hash) is passed in as `Buffer` by the caller,
12
- * not derived from a TLS socket. The caller (WP-02) is responsible for
13
- * extracting `tls-server-end-point` per RFC 5929 (i.e. SHA-256 over
14
- * the cert's `tbsCertificate`, or the cert's own signature hash when
15
- * strong enough — that policy lives upstream of this module).
16
- * - Returns Buffer-typed messages because the wire framer consumes
17
- * Buffers; upstream pg works in strings because its message reader
18
- * does the same.
19
- * - SASLprep: ported upstream's permissive minimal SASLprep (the three
20
- * transformations that actually change byte content). The full RFC
21
- * 4013 prohibition + bidi tables are *not* implemented — matches
22
- * libpq's and pg's behaviour, see comment on `saslprep` below.
23
- *
24
- * Server signature verification uses `crypto.timingSafeEqual` to avoid
25
- * leaking timing information about the comparison result.
26
- */
27
- import { createHash, createHmac, randomBytes as nodeRandomBytes, pbkdf2Sync, timingSafeEqual, } from "node:crypto";
28
- export class SaslMechanismError extends Error {
29
- constructor(message) {
30
- super(message);
31
- this.name = "SaslMechanismError";
32
- }
33
- }
34
- export class SaslVerificationError extends Error {
35
- constructor(message) {
36
- super(message);
37
- this.name = "SaslVerificationError";
38
- }
39
- }
40
- export class SaslProtocolError extends Error {
41
- constructor(message) {
42
- super(message);
43
- this.name = "SaslProtocolError";
44
- }
45
- }
46
- export function createScramClient(opts) {
47
- const { mechanism, gs2Header, cbindInput } = chooseMechanism(opts.mechanisms, opts.channelBinding);
48
- const rng = opts.randomBytes ?? nodeRandomBytes;
49
- // 18 raw bytes => 24 base64 chars; same width as upstream pg.
50
- // Commas are not part of the base64 alphabet, so the nonce is automatically
51
- // SCRAM-safe (RFC 5802 §5.1 forbids `,` in `r=`).
52
- const clientNonce = rng(18).toString("base64");
53
- const internal = {
54
- state: "init",
55
- mechanism,
56
- clientNonce,
57
- gs2Header,
58
- cbindInput,
59
- password: opts.password,
60
- serverSignature: null,
61
- };
62
- return {
63
- mechanism,
64
- start: () => start(internal),
65
- continue: (serverFirst) => continueSession(internal, serverFirst),
66
- finish: (serverFinal) => {
67
- finishSession(internal, serverFinal);
68
- },
69
- };
4
+ * SASL / SCRAM-SHA-256[-PLUS] client for the psql wire layer (WP-03).
5
+ *
6
+ * Ported from node-postgres' `packages/pg/lib/crypto/sasl.js` (MIT,
7
+ * Copyright (c) 2010-2020 Brian Carlson). Adaptations from upstream:
8
+ *
9
+ * - Pure Node `node:crypto` (sync PBKDF2 / HMAC) instead of SubtleCrypto;
10
+ * this module is sync because the wire layer drives it from a state
11
+ * machine that already buffers bytes.
12
+ * - The `pg` `Connection` / `Stream` plumbing is removed. Channel binding
13
+ * data (the certificate hash) is passed in as `Buffer` by the caller,
14
+ * not derived from a TLS socket. The caller (WP-02) is responsible for
15
+ * extracting `tls-server-end-point` per RFC 5929 (i.e. SHA-256 over
16
+ * the cert's `tbsCertificate`, or the cert's own signature hash when
17
+ * strong enough — that policy lives upstream of this module).
18
+ * - Returns Buffer-typed messages because the wire framer consumes
19
+ * Buffers; upstream pg works in strings because its message reader
20
+ * does the same.
21
+ * - SASLprep: ported upstream's permissive minimal SASLprep (the three
22
+ * transformations that actually change byte content). The full RFC
23
+ * 4013 prohibition + bidi tables are *not* implemented — matches
24
+ * libpq's and pg's behaviour, see comment on `saslprep` below.
25
+ *
26
+ * Server signature verification uses `crypto.timingSafeEqual` to avoid
27
+ * leaking timing information about the comparison result.
28
+ */
29
+ var SaslMechanismError = class extends Error {
30
+ constructor(message) {
31
+ super(message);
32
+ this.name = "SaslMechanismError";
33
+ }
34
+ };
35
+ var SaslVerificationError = class extends Error {
36
+ constructor(message) {
37
+ super(message);
38
+ this.name = "SaslVerificationError";
39
+ }
40
+ };
41
+ var SaslProtocolError = class extends Error {
42
+ constructor(message) {
43
+ super(message);
44
+ this.name = "SaslProtocolError";
45
+ }
46
+ };
47
+ function createScramClient(opts) {
48
+ const { mechanism, gs2Header, cbindInput } = chooseMechanism(opts.mechanisms, opts.channelBinding);
49
+ const internal = {
50
+ state: "init",
51
+ mechanism,
52
+ clientNonce: (opts.randomBytes ?? randomBytes)(18).toString("base64"),
53
+ gs2Header,
54
+ cbindInput,
55
+ password: opts.password,
56
+ serverSignature: null
57
+ };
58
+ return {
59
+ mechanism,
60
+ start: () => start(internal),
61
+ continue: (serverFirst) => continueSession(internal, serverFirst),
62
+ finish: (serverFinal) => {
63
+ finishSession(internal, serverFinal);
64
+ }
65
+ };
70
66
  }
71
67
  function chooseMechanism(advertised, channelBinding) {
72
- const hasPlus = advertised.includes("SCRAM-SHA-256-PLUS");
73
- const hasPlain = advertised.includes("SCRAM-SHA-256");
74
- if (channelBinding && hasPlus) {
75
- const gs2 = "p=tls-server-end-point,,";
76
- return {
77
- mechanism: "SCRAM-SHA-256-PLUS",
78
- gs2Header: gs2,
79
- cbindInput: Buffer.concat([
80
- Buffer.from(gs2, "utf8"),
81
- channelBinding.data,
82
- ]),
83
- };
84
- }
85
- if (hasPlain) {
86
- // Client has channel-binding data but server doesn't advertise PLUS: per
87
- // RFC 5802 §6 the client must signal this with `y` so a downgrade attack
88
- // is detected (the server, if PLUS-capable, would have advertised it).
89
- const gs2 = channelBinding ? "y,," : "n,,";
90
- return {
91
- mechanism: "SCRAM-SHA-256",
92
- gs2Header: gs2,
93
- cbindInput: Buffer.from(gs2, "utf8"),
94
- };
95
- }
96
- throw new SaslMechanismError(`SASL: server offered [${advertised.join(", ")}] but only SCRAM-SHA-256 and SCRAM-SHA-256-PLUS are supported`);
68
+ const hasPlus = advertised.includes("SCRAM-SHA-256-PLUS");
69
+ const hasPlain = advertised.includes("SCRAM-SHA-256");
70
+ if (channelBinding && hasPlus) {
71
+ const gs2 = "p=tls-server-end-point,,";
72
+ return {
73
+ mechanism: "SCRAM-SHA-256-PLUS",
74
+ gs2Header: gs2,
75
+ cbindInput: Buffer.concat([Buffer.from(gs2, "utf8"), channelBinding.data])
76
+ };
77
+ }
78
+ if (hasPlain) {
79
+ const gs2 = channelBinding ? "y,," : "n,,";
80
+ return {
81
+ mechanism: "SCRAM-SHA-256",
82
+ gs2Header: gs2,
83
+ cbindInput: Buffer.from(gs2, "utf8")
84
+ };
85
+ }
86
+ throw new SaslMechanismError(`SASL: server offered [${advertised.join(", ")}] but only SCRAM-SHA-256 and SCRAM-SHA-256-PLUS are supported`);
97
87
  }
98
88
  function start(s) {
99
- if (s.state !== "init") {
100
- throw new SaslProtocolError(`SASL: start() called in state ${s.state}`);
101
- }
102
- // n=,r=<nonce> — the username field is empty because PostgreSQL ignores it
103
- // (the actual user was sent in StartupMessage) and an empty `n=` matches
104
- // libpq's wire output. Note pg sends `n=*` instead; both interoperate.
105
- const clientFirstBare = `n=,r=${s.clientNonce}`;
106
- const clientFirstMessage = Buffer.from(s.gs2Header + clientFirstBare, "utf8");
107
- s.state = "sent-first";
108
- return { mechanism: s.mechanism, clientFirstMessage };
89
+ if (s.state !== "init") throw new SaslProtocolError(`SASL: start() called in state ${s.state}`);
90
+ const clientFirstBare = `n=,r=${s.clientNonce}`;
91
+ const clientFirstMessage = Buffer.from(s.gs2Header + clientFirstBare, "utf8");
92
+ s.state = "sent-first";
93
+ return {
94
+ mechanism: s.mechanism,
95
+ clientFirstMessage
96
+ };
109
97
  }
110
98
  function continueSession(s, serverFirst) {
111
- if (s.state !== "sent-first") {
112
- throw new SaslProtocolError(`SASL: continue() called in state ${s.state}`);
113
- }
114
- const serverFirstStr = serverFirst.toString("utf8");
115
- const parsed = parseServerFirst(serverFirstStr);
116
- if (!parsed.nonce.startsWith(s.clientNonce)) {
117
- throw new SaslProtocolError("SASL: server nonce does not start with client nonce");
118
- }
119
- if (parsed.nonce.length === s.clientNonce.length) {
120
- throw new SaslProtocolError("SASL: server nonce is too short");
121
- }
122
- const clientFirstBare = `n=,r=${s.clientNonce}`;
123
- const serverFirstString = `r=${parsed.nonce},s=${parsed.salt},i=${String(parsed.iteration)}`;
124
- // c=<base64 of GS2 header || optional CB data>
125
- const channelBindingB64 = s.cbindInput.toString("base64");
126
- const clientFinalWithoutProof = `c=${channelBindingB64},r=${parsed.nonce}`;
127
- const authMessage = `${clientFirstBare},${serverFirstString},${clientFinalWithoutProof}`;
128
- const saltBytes = Buffer.from(parsed.salt, "base64");
129
- // SASLprep the password before PBKDF2 (RFC 5802 §2.2). Our impl is minimal;
130
- // see saslprep() comment for the deviation from full RFC 4013.
131
- const normalizedPassword = saslprep(s.password);
132
- const saltedPassword = pbkdf2Sync(Buffer.from(normalizedPassword, "utf8"), saltBytes, parsed.iteration, 32, "sha256");
133
- const clientKey = hmac(saltedPassword, "Client Key");
134
- const storedKey = sha256(clientKey);
135
- const clientSignature = hmac(storedKey, authMessage);
136
- const clientProof = xor(clientKey, clientSignature);
137
- const serverKey = hmac(saltedPassword, "Server Key");
138
- s.serverSignature = hmac(serverKey, authMessage);
139
- s.state = "sent-final";
140
- return Buffer.from(`${clientFinalWithoutProof},p=${clientProof.toString("base64")}`, "utf8");
99
+ if (s.state !== "sent-first") throw new SaslProtocolError(`SASL: continue() called in state ${s.state}`);
100
+ const parsed = parseServerFirst(serverFirst.toString("utf8"));
101
+ if (!parsed.nonce.startsWith(s.clientNonce)) throw new SaslProtocolError("SASL: server nonce does not start with client nonce");
102
+ if (parsed.nonce.length === s.clientNonce.length) throw new SaslProtocolError("SASL: server nonce is too short");
103
+ const clientFirstBare = `n=,r=${s.clientNonce}`;
104
+ const serverFirstString = `r=${parsed.nonce},s=${parsed.salt},i=${String(parsed.iteration)}`;
105
+ const clientFinalWithoutProof = `c=${s.cbindInput.toString("base64")},r=${parsed.nonce}`;
106
+ const authMessage = `${clientFirstBare},${serverFirstString},${clientFinalWithoutProof}`;
107
+ const saltBytes = Buffer.from(parsed.salt, "base64");
108
+ const normalizedPassword = saslprep(s.password);
109
+ const saltedPassword = pbkdf2Sync(Buffer.from(normalizedPassword, "utf8"), saltBytes, parsed.iteration, 32, "sha256");
110
+ const clientKey = hmac(saltedPassword, "Client Key");
111
+ const clientProof = xor(clientKey, hmac(sha256(clientKey), authMessage));
112
+ s.serverSignature = hmac(hmac(saltedPassword, "Server Key"), authMessage);
113
+ s.state = "sent-final";
114
+ return Buffer.from(`${clientFinalWithoutProof},p=${clientProof.toString("base64")}`, "utf8");
141
115
  }
142
116
  function finishSession(s, serverFinal) {
143
- if (s.state !== "sent-final") {
144
- throw new SaslProtocolError(`SASL: finish() called in state ${s.state}`);
145
- }
146
- if (s.serverSignature === null) {
147
- // Defensive: should be impossible because state machine guards it.
148
- throw new SaslProtocolError("SASL: no server signature recorded");
149
- }
150
- const parsed = parseServerFinal(serverFinal.toString("utf8"));
151
- const received = Buffer.from(parsed.serverSignature, "base64");
152
- // Constant-time comparison: defeats timing side channels that could let an
153
- // attacker (with control over `v=`) learn the prefix of our derived signature.
154
- if (received.length !== s.serverSignature.length ||
155
- !timingSafeEqual(received, s.serverSignature)) {
156
- throw new SaslVerificationError("SASL: server signature does not match — possible MITM or wrong password");
157
- }
158
- s.state = "done";
117
+ if (s.state !== "sent-final") throw new SaslProtocolError(`SASL: finish() called in state ${s.state}`);
118
+ if (s.serverSignature === null) throw new SaslProtocolError("SASL: no server signature recorded");
119
+ const parsed = parseServerFinal(serverFinal.toString("utf8"));
120
+ const received = Buffer.from(parsed.serverSignature, "base64");
121
+ if (received.length !== s.serverSignature.length || !timingSafeEqual(received, s.serverSignature)) throw new SaslVerificationError("SASL: server signature does not match — possible MITM or wrong password");
122
+ s.state = "done";
159
123
  }
160
124
  function parseServerFirst(text) {
161
- const attrs = parseAttributePairs(text);
162
- const nonce = attrs.get("r");
163
- if (!nonce) {
164
- throw new SaslProtocolError("SASL: server-first missing `r=` (nonce)");
165
- }
166
- if (!isPrintableChars(nonce)) {
167
- throw new SaslProtocolError("SASL: server-first nonce contains non-printable characters");
168
- }
169
- const salt = attrs.get("s");
170
- if (!salt) {
171
- throw new SaslProtocolError("SASL: server-first missing `s=` (salt)");
172
- }
173
- if (!isBase64(salt)) {
174
- throw new SaslProtocolError("SASL: server-first salt is not base64");
175
- }
176
- const iterStr = attrs.get("i");
177
- if (!iterStr) {
178
- throw new SaslProtocolError("SASL: server-first missing `i=` (iterations)");
179
- }
180
- if (!/^[1-9][0-9]*$/.test(iterStr)) {
181
- throw new SaslProtocolError(`SASL: server-first iteration count is not a positive integer: ${iterStr}`);
182
- }
183
- return { nonce, salt, iteration: parseInt(iterStr, 10) };
125
+ const attrs = parseAttributePairs(text);
126
+ const nonce = attrs.get("r");
127
+ if (!nonce) throw new SaslProtocolError("SASL: server-first missing `r=` (nonce)");
128
+ if (!isPrintableChars(nonce)) throw new SaslProtocolError("SASL: server-first nonce contains non-printable characters");
129
+ const salt = attrs.get("s");
130
+ if (!salt) throw new SaslProtocolError("SASL: server-first missing `s=` (salt)");
131
+ if (!isBase64(salt)) throw new SaslProtocolError("SASL: server-first salt is not base64");
132
+ const iterStr = attrs.get("i");
133
+ if (!iterStr) throw new SaslProtocolError("SASL: server-first missing `i=` (iterations)");
134
+ if (!/^[1-9][0-9]*$/.test(iterStr)) throw new SaslProtocolError(`SASL: server-first iteration count is not a positive integer: ${iterStr}`);
135
+ return {
136
+ nonce,
137
+ salt,
138
+ iteration: parseInt(iterStr, 10)
139
+ };
184
140
  }
185
141
  function parseServerFinal(text) {
186
- const attrs = parseAttributePairs(text);
187
- const errorAttr = attrs.get("e");
188
- if (errorAttr) {
189
- throw new SaslVerificationError(`SASL: server-final returned error: ${errorAttr}`);
190
- }
191
- const v = attrs.get("v");
192
- if (!v) {
193
- throw new SaslProtocolError("SASL: server-final missing `v=` (server signature)");
194
- }
195
- if (!isBase64(v)) {
196
- throw new SaslProtocolError("SASL: server-final `v=` is not base64");
197
- }
198
- return { serverSignature: v };
142
+ const attrs = parseAttributePairs(text);
143
+ const errorAttr = attrs.get("e");
144
+ if (errorAttr) throw new SaslVerificationError(`SASL: server-final returned error: ${errorAttr}`);
145
+ const v = attrs.get("v");
146
+ if (!v) throw new SaslProtocolError("SASL: server-final missing `v=` (server signature)");
147
+ if (!isBase64(v)) throw new SaslProtocolError("SASL: server-final `v=` is not base64");
148
+ return { serverSignature: v };
199
149
  }
200
150
  function parseAttributePairs(text) {
201
- const out = new Map();
202
- for (const pair of text.split(",")) {
203
- if (pair.length < 2 || pair[1] !== "=") {
204
- throw new SaslProtocolError(`SASL: malformed attribute pair: ${JSON.stringify(pair)}`);
205
- }
206
- const key = pair[0];
207
- // RFC 5802 attribute lists never repeat an attribute; a duplicate is
208
- // malformed server input. Reject it rather than silently last-wins,
209
- // so a non-conformant/hostile server can't smuggle a shadow value
210
- // (e.g. `v=<good>,v=<evil>`) past the parse.
211
- if (out.has(key)) {
212
- throw new SaslProtocolError(`SASL: duplicate attribute ${JSON.stringify(key)} in message`);
213
- }
214
- out.set(key, pair.substring(2));
215
- }
216
- return out;
151
+ const out = /* @__PURE__ */ new Map();
152
+ for (const pair of text.split(",")) {
153
+ if (pair.length < 2 || pair[1] !== "=") throw new SaslProtocolError(`SASL: malformed attribute pair: ${JSON.stringify(pair)}`);
154
+ const key = pair[0];
155
+ if (out.has(key)) throw new SaslProtocolError(`SASL: duplicate attribute ${JSON.stringify(key)} in message`);
156
+ out.set(key, pair.substring(2));
157
+ }
158
+ return out;
217
159
  }
218
- // printable = %x21-2B / %x2D-7E ;; printable ASCII except ","
219
160
  function isPrintableChars(text) {
220
- for (let i = 0; i < text.length; i++) {
221
- const c = text.charCodeAt(i);
222
- if (!((c >= 0x21 && c <= 0x2b) || (c >= 0x2d && c <= 0x7e))) {
223
- return false;
224
- }
225
- }
226
- return true;
161
+ for (let i = 0; i < text.length; i++) {
162
+ const c = text.charCodeAt(i);
163
+ if (!(c >= 33 && c <= 43 || c >= 45 && c <= 126)) return false;
164
+ }
165
+ return true;
227
166
  }
228
167
  function isBase64(text) {
229
- return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(text);
168
+ return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(text);
230
169
  }
231
- // ---------------------------------------------------------------------------
232
- // Crypto helpers
233
- // ---------------------------------------------------------------------------
234
170
  function hmac(key, msg) {
235
- const h = createHmac("sha256", key);
236
- h.update(msg);
237
- return h.digest();
171
+ const h = createHmac("sha256", key);
172
+ h.update(msg);
173
+ return h.digest();
238
174
  }
239
175
  function sha256(data) {
240
- return createHash("sha256").update(data).digest();
176
+ return createHash("sha256").update(data).digest();
241
177
  }
242
178
  function xor(a, b) {
243
- if (a.length !== b.length) {
244
- throw new SaslProtocolError(`SASL: XOR length mismatch (${String(a.length)} vs ${String(b.length)})`);
245
- }
246
- const out = Buffer.alloc(a.length);
247
- for (let i = 0; i < a.length; i++) {
248
- out[i] = a[i] ^ b[i];
249
- }
250
- return out;
179
+ if (a.length !== b.length) throw new SaslProtocolError(`SASL: XOR length mismatch (${String(a.length)} vs ${String(b.length)})`);
180
+ const out = Buffer.alloc(a.length);
181
+ for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];
182
+ return out;
251
183
  }
252
- // ---------------------------------------------------------------------------
253
- // SASLprep
254
- // ---------------------------------------------------------------------------
255
184
  /**
256
- * Minimal SASLprep (RFC 4013) — exactly the three transformations that
257
- * change byte content, ported from node-postgres' implementation:
258
- *
259
- * 1. RFC 3454 Table C.1.2 (non-ASCII space) -> U+0020 SPACE.
260
- * 2. RFC 3454 Table B.1 (commonly mapped to nothing) -> empty.
261
- * 3. NFKC normalization.
262
- *
263
- * We deliberately *skip* the RFC 4013 §2.3 prohibition table and the §6
264
- * bidi checks. libpq is forgiving on those paths and PostgreSQL's own
265
- * SASLprep matches that leniency for legacy roles — implementing strict
266
- * RFC 4013 here would lock out passwords that already auth against the
267
- * server. This deviation matches upstream pg.
268
- *
269
- * The character classes below intentionally contain combining marks and
270
- * zero-width joiners (RFC 3454 Table B.1) and named spaces (Table C.1.2);
271
- * they are spelled with `\u` escapes so the source is portable across
272
- * encodings.
273
- */
274
- // Built once at module load: regex character classes for the two SASLprep
275
- // transformations. They are constructed from \uXXXX escape sequences in a
276
- // string so the source file stays ASCII-only and so we sidestep ESLint's
277
- // `no-irregular-whitespace` and `no-misleading-character-class` rules —
278
- // those only inspect raw regex literals, not dynamically-built patterns.
279
- const NON_ASCII_SPACE_RE = new RegExp("[" + "\u00A0\u1680" + "\u2000-\u200B" + "\u202F\u205F\u3000" + "]", "g");
280
- // Combining marks and ZWJ are intentionally in this class; RFC 3454 Table B.1
281
- // strips them precisely because they combine with neighbouring code points.
282
- const MAPPED_TO_NOTHING_RE = new RegExp("[" +
283
- "\u00AD\u034F\u1806" +
284
- "\u180B-\u180D" +
285
- "\u200C\u200D\u2060" +
286
- "\uFE00-\uFE0F" +
287
- "\uFEFF" +
288
- "]", "g");
185
+ * Minimal SASLprep (RFC 4013) — exactly the three transformations that
186
+ * change byte content, ported from node-postgres' implementation:
187
+ *
188
+ * 1. RFC 3454 Table C.1.2 (non-ASCII space) -> U+0020 SPACE.
189
+ * 2. RFC 3454 Table B.1 (commonly mapped to nothing) -> empty.
190
+ * 3. NFKC normalization.
191
+ *
192
+ * We deliberately *skip* the RFC 4013 §2.3 prohibition table and the §6
193
+ * bidi checks. libpq is forgiving on those paths and PostgreSQL's own
194
+ * SASLprep matches that leniency for legacy roles — implementing strict
195
+ * RFC 4013 here would lock out passwords that already auth against the
196
+ * server. This deviation matches upstream pg.
197
+ *
198
+ * The character classes below intentionally contain combining marks and
199
+ * zero-width joiners (RFC 3454 Table B.1) and named spaces (Table C.1.2);
200
+ * they are spelled with `\u` escapes so the source is portable across
201
+ * encodings.
202
+ */
203
+ const NON_ASCII_SPACE_RE = /* @__PURE__ */ new RegExp("[\xA0  -​   ]", "g");
204
+ const MAPPED_TO_NOTHING_RE = /* @__PURE__ */ new RegExp("[­͏᠆᠋-᠍‌‍⁠︀-️]", "g");
289
205
  function saslprep(password) {
290
- return password
291
- .replace(NON_ASCII_SPACE_RE, " ")
292
- .replace(MAPPED_TO_NOTHING_RE, "")
293
- .normalize("NFKC");
206
+ return password.replace(NON_ASCII_SPACE_RE, " ").replace(MAPPED_TO_NOTHING_RE, "").normalize("NFKC");
294
207
  }
208
+ //#endregion
209
+ export { SaslMechanismError, SaslProtocolError, SaslVerificationError, createScramClient };