oracledb 6.10.0 → 7.0.0

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 (84) hide show
  1. package/LICENSE.txt +1 -1
  2. package/NOTICE.txt +1 -1
  3. package/README.md +9 -9
  4. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  5. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  10. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  12. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +1 -0
  13. package/eslint.config.js +39 -0
  14. package/lib/connection.js +412 -37
  15. package/lib/endUserSecurityContext.js +176 -0
  16. package/lib/errors.js +86 -5
  17. package/lib/impl/connection.js +76 -2
  18. package/lib/impl/datahandlers/buffer.js +19 -5
  19. package/lib/impl/datahandlers/vector.js +25 -5
  20. package/lib/impl/lob.js +9 -0
  21. package/lib/impl/parserHelpers.js +443 -0
  22. package/lib/impl/resultset.js +1 -1
  23. package/lib/lob.js +27 -14
  24. package/lib/obfuscation.js +97 -0
  25. package/lib/oracledb.js +148 -43
  26. package/lib/parser.js +204 -0
  27. package/lib/pipeline.js +113 -0
  28. package/lib/pool.js +16 -1
  29. package/lib/resultset.js +42 -21
  30. package/lib/settings.js +1 -0
  31. package/lib/thin/connection.js +467 -53
  32. package/lib/thin/dbObject.js +7 -1
  33. package/lib/thin/lob.js +20 -1
  34. package/lib/thin/pool.js +42 -62
  35. package/lib/thin/protocol/capabilities.js +18 -3
  36. package/lib/thin/protocol/constants.js +82 -2
  37. package/lib/thin/protocol/encryptDecrypt.js +43 -8
  38. package/lib/thin/protocol/messages/auth.js +6 -1
  39. package/lib/thin/protocol/messages/base.js +245 -4
  40. package/lib/thin/protocol/messages/dataType.js +8 -3
  41. package/lib/thin/protocol/messages/directPathLoadStream.js +377 -0
  42. package/lib/thin/protocol/messages/directPathOp.js +65 -0
  43. package/lib/thin/protocol/messages/directPathPrepare.js +158 -0
  44. package/lib/thin/protocol/messages/endPipeline.js +56 -0
  45. package/lib/thin/protocol/messages/execute.js +9 -7
  46. package/lib/thin/protocol/messages/index.js +9 -1
  47. package/lib/thin/protocol/messages/protocol.js +2 -2
  48. package/lib/thin/protocol/messages/withData.js +8 -140
  49. package/lib/thin/protocol/packet.js +115 -24
  50. package/lib/thin/protocol/protocol.js +110 -25
  51. package/lib/thin/protocol/utils.js +1 -26
  52. package/lib/thin/resultSet.js +13 -19
  53. package/lib/thin/sqlnet/ezConnectResolver.js +2 -2
  54. package/lib/thin/sqlnet/networkSession.js +62 -40
  55. package/lib/thin/sqlnet/ntTcp.js +24 -0
  56. package/lib/thin/sqlnet/nvStrToNvPair.js +50 -25
  57. package/lib/thin/sqlnet/packet.js +25 -4
  58. package/lib/thin/sqlnet/paramParser.js +10 -7
  59. package/lib/thin/statementCache.js +2 -4
  60. package/lib/traceHandler.js +59 -2
  61. package/lib/transformer.js +8 -3
  62. package/lib/types.js +35 -21
  63. package/lib/util.js +6 -6
  64. package/lib/version.js +3 -3
  65. package/package/install.js +5 -5
  66. package/package/prunebinaries.js +1 -1
  67. package/package.json +1 -1
  68. package/plugins/configProviders/awsCommon.js +125 -0
  69. package/plugins/configProviders/awss3/index.js +138 -0
  70. package/plugins/configProviders/awssecretsmanager/index.js +147 -0
  71. package/plugins/configProviders/azure/index.js +4 -5
  72. package/plugins/token/extensionAzure/index.js +0 -1
  73. package/plugins/token/extensionOci/index.js +0 -1
  74. package/build/Release/oracledb-6.10.0-darwin-arm64.node +0 -0
  75. package/build/Release/oracledb-6.10.0-darwin-arm64.node-buildinfo.txt +0 -1
  76. package/build/Release/oracledb-6.10.0-darwin-x64.node +0 -0
  77. package/build/Release/oracledb-6.10.0-darwin-x64.node-buildinfo.txt +0 -1
  78. package/build/Release/oracledb-6.10.0-js-buildinfo.txt +0 -1
  79. package/build/Release/oracledb-6.10.0-linux-arm64.node +0 -0
  80. package/build/Release/oracledb-6.10.0-linux-arm64.node-buildinfo.txt +0 -1
  81. package/build/Release/oracledb-6.10.0-linux-x64.node +0 -0
  82. package/build/Release/oracledb-6.10.0-linux-x64.node-buildinfo.txt +0 -1
  83. package/build/Release/oracledb-6.10.0-win32-x64.node +0 -0
  84. package/build/Release/oracledb-6.10.0-win32-x64.node-buildinfo.txt +0 -1
@@ -28,7 +28,6 @@
28
28
 
29
29
  const { Buffer } = require('buffer');
30
30
  const constants = require('./constants');
31
- const crypto = require('crypto');
32
31
 
33
32
  class OutOfPacketsError extends Error { }
34
33
 
@@ -52,31 +51,7 @@ function encodeRowID(rowID) {
52
51
  }
53
52
  }
54
53
 
55
- // obfuscate value
56
- function setObfuscatedValue(value) {
57
- const valueBytes = Buffer.from(value);
58
- const obfuscatedBytes = crypto.randomBytes(valueBytes.length);
59
- for (let i = 0; i < valueBytes.length; i++) {
60
- valueBytes[i] = obfuscatedBytes[i] ^ valueBytes[i];
61
- }
62
- return {obfuscatedValue: obfuscatedBytes, value: valueBytes};
63
- }
64
-
65
- // returns the deobfuscated value, after removing the obfuscation
66
- // and clear memory of temporary Buffers coming from Buffer pool
67
- function getDeobfuscatedValue(valueBytes, obfuscatedBytes) {
68
- const buf = Buffer.from(valueBytes);
69
- for (let i = 0; i < valueBytes.length; i++) {
70
- buf[i] = valueBytes[i] ^ obfuscatedBytes[i];
71
- }
72
- const retVal = buf.toString();
73
- buf.fill(0);
74
- return retVal;
75
- }
76
-
77
54
  module.exports = {
78
55
  encodeRowID,
79
- getDeobfuscatedValue,
80
- OutOfPacketsError,
81
- setObfuscatedValue
56
+ OutOfPacketsError
82
57
  };
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -94,26 +94,20 @@ class ThinResultSetImpl extends ResultSetImpl {
94
94
  async getRows(numRows, options) {
95
95
  options.fetchArraySize = numRows || this.options.fetchArraySize;
96
96
  options.prefetchRows = this.options.prefetchRows;
97
- if (this.statement.bufferRowCount - this.statement.bufferRowIndex >= options.fetchArraySize) {
97
+ if (this.statement.bufferRowCount - this.statement.bufferRowIndex >= options.fetchArraySize)
98
98
  return this._processRows(options.fetchArraySize);
99
- } else {
100
- // We fetch for the required number of row
101
- options.fetchArraySize = options.fetchArraySize - (this.statement.bufferRowCount - this.statement.bufferRowIndex);
102
- const prevBufferRowCount = this.statement.bufferRowCount;
103
- if (this.statement.moreRowsToFetch && options.fetchArraySize > 0) {
104
- await this._fetchMoreRows(options);
105
- }
106
- options.fetchArraySize = numRows || this.options.fetchArraySize;
107
- if (prevBufferRowCount === this.statement.bufferRowCount) {
108
- const numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
109
- this.statement.bufferRowCount = 0;
110
- if (numRowsFetched > 0) {
111
- return this._processRows(numRowsFetched);
112
- }
113
- return [];
114
- }
115
- }
99
+
100
+ // We fetch for the required number of rows
101
+ options.fetchArraySize = options.fetchArraySize - (this.statement.bufferRowCount - this.statement.bufferRowIndex);
102
+ const prevBufferRowCount = this.statement.bufferRowCount;
103
+ if (this.statement.moreRowsToFetch && options.fetchArraySize > 0)
104
+ await this._fetchMoreRows(options);
105
+ options.fetchArraySize = numRows || this.options.fetchArraySize;
116
106
  const numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
107
+ if (prevBufferRowCount === this.statement.bufferRowCount)
108
+ this.statement.bufferRowCount = 0;
109
+
110
+ // process the rows fetched
117
111
  return this._processRows(numRowsFetched);
118
112
  }
119
113
 
@@ -294,7 +294,7 @@ class EZConnectResolver {
294
294
  if (serverDNMatch != null)
295
295
  securityInfo.push(`(SSL_SERVER_DN_MATCH=${serverDNMatch})`);
296
296
  if (serverCertDN != null)
297
- securityInfo.push(`(SSL_SERVER_CERT_DN=${serverCertDN}})`);
297
+ securityInfo.push(`(SSL_SERVER_CERT_DN=${serverCertDN})`);
298
298
  if (walletDir != null)
299
299
  securityInfo.push(`(MY_WALLET_DIRECTORY=${walletDir})`);
300
300
  }
@@ -416,7 +416,7 @@ class EZConnectResolver {
416
416
  // if it is an URL alias then add to URL props
417
417
  this.urlProps.set(propertyName, value);
418
418
  } else {
419
- this.connectionProps.set(propertyName, value);
419
+ this.connectionProps.set(key, value);
420
420
  }
421
421
  }
422
422
 
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -34,11 +34,27 @@ const { createNode } = require('./connStrategy.js');
34
34
  const errors = require("../../errors.js");
35
35
  const { findNVPairRecurse, createNVPair, findValue } = require("./nvStrToNvPair.js");
36
36
  const { Buffer } = require('buffer');
37
- const EzConnect = require("./ezConnectResolver.js");
38
- const { NLParamParser, tnsnamesFilePath } = require("./paramParser.js");
39
- const process = require('process');
40
37
  const downHostInstance = require("./connStrategy.js").SOLE_INST_DHCACHE;
41
38
  const {ANO} = require("./ANO.js");
39
+ const { resolveConnectStr } = require('../../impl/parserHelpers.js');
40
+
41
+ const MAX_REDIRECTS = 2;
42
+
43
+ const REDIRECT_SECURITY_KEYS = new Set([
44
+ 'sslServerDNMatch',
45
+ 'sslAllowWeakDNMatch',
46
+ 'sslServerCertDN',
47
+ 'walletLocation',
48
+ 'walletPassword',
49
+ 'walletContent'
50
+ ]);
51
+
52
+ function getRedirectParams(params) {
53
+ return Object.fromEntries(
54
+ Object.entries(params).filter(([key]) => !REDIRECT_SECURITY_KEYS.has(key))
55
+ );
56
+ }
57
+
42
58
  /**
43
59
  *
44
60
  * @param {string} userConfig
@@ -68,36 +84,6 @@ async function getConnectionInfo(userConfig) {
68
84
  * @param {String} connStr Connect string
69
85
  * @returns AddressNode
70
86
  */
71
- const nlParamParser = new NLParamParser;
72
- async function resolveConnectStr(connectString, configDir) {
73
- const connStr = connectString.trim();
74
- let resolvedVal = connStr;
75
- if ((connStr.indexOf(')') == -1) || (connStr.indexOf('(') != 0)) {
76
- if ((connStr.indexOf(':') != -1) || (connStr.indexOf('/') != -1)) {
77
- const ezcnObj = new EzConnect(connStr);
78
- resolvedVal = ezcnObj.getResolvedUrl();
79
- return resolvedVal;
80
- } else {
81
- //try tns alias
82
- const namesFilePath = tnsnamesFilePath(configDir);
83
- const p = await nlParamParser.initializeNlpa(namesFilePath);
84
- resolvedVal = p.get(connStr.toUpperCase());
85
- if (!resolvedVal)
86
- errors.throwErr(errors.ERR_TNS_ENTRY_NOT_FOUND, connStr, configDir ? configDir + '/tnsnames.ora' : process.env.TNS_ADMIN + '/tnsnames.ora');
87
- if (resolvedVal.rhsType == 1) {
88
- const rString = resolvedVal.atom;
89
- if ((rString.indexOf(':') != -1) || (rString.indexOf('/') != -1)) {
90
- return new EzConnect(rString).getResolvedUrl();
91
- }
92
- }
93
- resolvedVal = resolvedVal.getListElement(0);
94
- }
95
-
96
- }
97
- return resolvedVal;
98
-
99
- }
100
-
101
87
  async function resolveAddress(connStr, userConfig) {
102
88
  const connstr = await resolveConnectStr(connStr, userConfig.configDir);
103
89
  return createNode(connstr, userConfig);
@@ -228,6 +214,8 @@ class NetworkSession {
228
214
  let connectPkt = new Packet.ConnectPacket(this.cData, this.sAtts);
229
215
  this._sendConnect(connectPkt);
230
216
 
217
+ let redirectCount = 0;
218
+
231
219
  /* Read the response */
232
220
  while (true) { // eslint-disable-line
233
221
  const packet = await this._recvPacket();
@@ -257,6 +245,11 @@ class NetworkSession {
257
245
  }
258
246
  this._sendConnect(connectPkt);
259
247
  } else if (packet.type === constants.NSPTRD) { /* REDIRECT */
248
+ if (redirectCount >= MAX_REDIRECTS) {
249
+ errors.throwErr(errors.ERR_TOO_MANY_REDIRECTS, MAX_REDIRECTS);
250
+ }
251
+ redirectCount++;
252
+
260
253
  let adrLen, adrStr, redirConnData;
261
254
 
262
255
  /* Read and connect to Redirect address */
@@ -277,8 +270,10 @@ class NetworkSession {
277
270
  const redirAddressNode = await resolveAddress(adrStr, userConfig);
278
271
  const host = address.hostname;
279
272
  address = await redirAddressNode.execute();
280
- if (address.desc)
281
- this.sAtts.setFrom(address.desc.params); /* Add on attributes from redirect connect String */
273
+ /* Add on attributes from redirect connect String */
274
+ if (address.desc?.params) {
275
+ this.sAtts.setFrom(getRedirectParams(address.desc.params));
276
+ }
282
277
  address.originHost = host;
283
278
  this.redirectPkt = null;
284
279
  this.ntAdapter.disconnect(constants.NSFIMM);
@@ -349,7 +344,7 @@ class NetworkSession {
349
344
  savedErr = err;
350
345
  try {
351
346
  address = await this.getAddress(addressNode, userConfig);
352
- } catch (err) {
347
+ } catch {
353
348
  break;
354
349
  }
355
350
  }
@@ -370,20 +365,39 @@ class NetworkSession {
370
365
  const size = packet.buf.length;
371
366
  const dataFlags = packet.buf.readUInt16BE(constants.NSPDAFLG);
372
367
  if ((dataFlags & constants.NSPDAFCMP) != 0) { // compression enabled
368
+ if (!this.sAtts.networkCompressionEnabled) {
369
+ errors.throwErr(errors.ERR_INVALID_PACKET, "Compressed packet received without negotiation");
370
+ }
373
371
  const packetHeader = packet.buf.subarray(0, constants.NSPDADAT);
374
372
  const buffertoDeCompress = packet.buf.subarray(constants.NSPDADAT, size);
375
373
  let deCompressedDataBuffer;
374
+
375
+ const maxPayload = this.sAtts.largeSDU
376
+ ? Math.min(this.sAtts.sdu, constants.NSPABSSDULN)
377
+ : Math.min(this.sAtts.sdu, constants.NSPMXSDULN);
378
+
379
+ const inflateOptions = {
380
+ finishFlush: zlib.constants.Z_SYNC_FLUSH,
381
+ maxOutputLength: maxPayload
382
+ };
376
383
  try {
377
384
  if (this.sAtts.firstRecvCompressedPacket) {
378
- deCompressedDataBuffer = zlib.inflateSync(buffertoDeCompress, {finishFlush: zlib.constants.Z_SYNC_FLUSH});
385
+ deCompressedDataBuffer = zlib.inflateSync(buffertoDeCompress, inflateOptions);
379
386
  this.sAtts.firstRecvCompressedPacket = 0;
380
387
  } else {
381
- deCompressedDataBuffer = zlib.inflateRawSync(buffertoDeCompress, {finishFlush: zlib.constants.Z_SYNC_FLUSH});
388
+ deCompressedDataBuffer = zlib.inflateRawSync(buffertoDeCompress, inflateOptions);
382
389
  }
383
390
  } catch (err) {
391
+ if (err.code == 'ERR_BUFFER_TOO_LARGE') {
392
+ errors.throwErr(errors.ERR_INVALID_PACKET,
393
+ `: decompressed packet exceeds SDU ${maxPayload}`);
394
+ }
384
395
  errors.throwErr(errors.ERR_DATA_COMPRESSION, err.message);
385
396
  }
386
397
  const resultLength = deCompressedDataBuffer.length;
398
+ if (resultLength > maxPayload) {
399
+ errors.throwErr(errors.ERR_INVALID_PACKET, `Decompressed size ${resultLength} exceeds SDU ${maxPayload}`);
400
+ }
387
401
  //concatenate packet header with decompressed data
388
402
  packet.buf = Buffer.concat([packetHeader, deCompressedDataBuffer]);
389
403
  const length = resultLength + constants.NSPDADAT;
@@ -407,6 +421,7 @@ class NetworkSession {
407
421
  if (this.sAtts.version >= constants.TNS_VERSION_MIN_END_OF_RESPONSE
408
422
  && (packet.flags & constants.TNS_ACCEPT_FLAG_HAS_END_OF_REQUEST)) {
409
423
  this.endOfRequestSupport = true;
424
+ this.pipeliningSupport = true;
410
425
  }
411
426
  if (packet.flags & constants.TNS_ACCEPT_FLAG_FAST_AUTH) {
412
427
  this.supportsFastAuth = true;
@@ -430,7 +445,7 @@ class NetworkSession {
430
445
  * Control packets are consumed internally and discarded
431
446
  */
432
447
  async _recvPacket() {
433
- while (true) { // eslint-disable-line
448
+ while (true) {
434
449
  const packet = await this.ntAdapter.receive();
435
450
  if (!packet)
436
451
  break;
@@ -501,6 +516,9 @@ class NetworkSession {
501
516
  this.isBreak = true;
502
517
  this.markerPkt.prepare(constants.NSPMKTD1, constants.NIQIMARK);
503
518
  this.sendPacket(this.markerPkt.buf);
519
+ if (this.pipelineEnabled) {
520
+ this.isBreak = false;
521
+ }
504
522
  }
505
523
 
506
524
  /**
@@ -513,6 +531,10 @@ class NetworkSession {
513
531
  this.sendPacket(this.markerPkt.buf);
514
532
  this.breakPosted = false;
515
533
  }
534
+ if (this.pipelineEnabled) {
535
+ this.isBreak = this.isReset = false;
536
+ return;
537
+ }
516
538
  /* Send Reset */
517
539
  this.markerPkt.prepare(constants.NSPMKTD1, constants.NIQRMARK);
518
540
  this.sendPacket(this.markerPkt.buf);
@@ -389,6 +389,19 @@ class NTTCP {
389
389
  this.numPacketsSinceLastWait = 0;
390
390
  }
391
391
 
392
+ /**
393
+ * Reject invalid packet and clean up
394
+ * @param {string} message Error message
395
+ * @returns {null} to clear tempBuf
396
+ */
397
+ rejectInvalidPacket(message) {
398
+ this.savedErr = new Error(message);
399
+ this.err = true;
400
+ this.stream.destroy();
401
+ this._notifyWaiters();
402
+ return null;
403
+ }
404
+
392
405
  /**
393
406
  * Start Async reads
394
407
  */
@@ -414,6 +427,17 @@ class NTTCP {
414
427
  len = tempBuf.readUInt16BE();
415
428
  }
416
429
 
430
+ if (len < PACKET_HEADER_SIZE) {
431
+ tempBuf = this.rejectInvalidPacket(`Invalid packet length: ${len} (must be at least ${PACKET_HEADER_SIZE})`);
432
+ break;
433
+ }
434
+
435
+ const maxPacket = this.largeSDU ? constants.NSPABSSDULN : constants.NSPMXSDULN;
436
+ if (len > maxPacket) {
437
+ tempBuf = this.rejectInvalidPacket(`Invalid packet length: ${len} (exceeds maximum ${maxPacket})`);
438
+ break;
439
+ }
440
+
417
441
  // not enough for a full packet so wait for more data to arrive
418
442
  if (len > tempBuf.length)
419
443
  break;
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -28,6 +28,26 @@
28
28
 
29
29
  const errors = require("../../errors.js");
30
30
 
31
+ // Maximum supported descriptor byte length.
32
+ const DEFAULT_MAX_DESCRIPTOR_LENGTH = 512 * 8;
33
+
34
+ // Maximum supported descriptor nesting depth before rejecting the parse.
35
+ const DEFAULT_MAX_DESCRIPTOR_DEPTH = 64;
36
+
37
+ function checkLength(nvString, maxLength) {
38
+ if (Buffer.byteLength(nvString, 'utf8') > maxLength) {
39
+ errors.throwErr(errors.ERR_CONNECT_DESCRIPTOR_LIMIT_EXCEEDED,
40
+ 'byte length', maxLength);
41
+ }
42
+ }
43
+
44
+ function checkDepth(depth, maxDepth) {
45
+ if (depth > maxDepth) {
46
+ errors.throwErr(errors.ERR_CONNECT_DESCRIPTOR_LIMIT_EXCEEDED,
47
+ 'nesting depth', maxDepth);
48
+ }
49
+ }
50
+
31
51
 
32
52
  /**
33
53
  * Constant which implies that the VALUE has not been set to ATOM or LIST.
@@ -556,10 +576,13 @@ class NVTokens {
556
576
  * Returns an NVPair which contains the broken-down form of nvString
557
577
  * @param nvString the nvString to parse
558
578
  */
559
- function createNVPair(nvString) {
579
+ function createNVPair(nvString, options = {}) {
580
+ const maxLength = options.maxLength ?? DEFAULT_MAX_DESCRIPTOR_LENGTH;
581
+ const maxDepth = options.maxDepth ?? DEFAULT_MAX_DESCRIPTOR_DEPTH;
582
+ checkLength(nvString, maxLength);
560
583
  const nvt = new NVTokens();
561
584
  nvt.parseTokens(nvString);
562
- return readTopLevelNVPair(nvt);
585
+ return readTopLevelNVPair(nvt, 1, maxDepth);
563
586
  }
564
587
 
565
588
  /*
@@ -567,7 +590,8 @@ function createNVPair(nvString) {
567
590
  * NVPair: (name=value)
568
591
  * value: atom | NVList
569
592
  */
570
- function readTopLevelNVPair(nvt) {
593
+ function readTopLevelNVPair(nvt, depth, maxDepth) {
594
+ checkDepth(depth, maxDepth);
571
595
  //check for opening (
572
596
  let tk = nvt.getToken();
573
597
  nvt.eatToken();
@@ -586,17 +610,18 @@ function readTopLevelNVPair(nvt) {
586
610
  }
587
611
  nvp.name = name;
588
612
 
589
- return readRightHandSide(nvp, nvt);
613
+ return readRightHandSide(nvp, nvt, depth, maxDepth);
590
614
  }
591
615
 
592
- return readRightHandSide(nvp, nvt);
616
+ return readRightHandSide(nvp, nvt, depth, maxDepth);
593
617
  }
594
618
  /*
595
619
  * function which returns the next NVPair from NVTokens.
596
620
  * NVPair: (name=value) | (name, | ,name) | ,name,
597
621
  * value: atom | NVList
598
622
  */
599
- function readNVPair(nvt) {
623
+ function readNVPair(nvt, depth, maxDepth) {
624
+ checkDepth(depth, maxDepth);
600
625
  // Opening ( or , for NVPair
601
626
  const tk = nvt.getToken();
602
627
  nvt.eatToken();
@@ -607,7 +632,7 @@ function readNVPair(nvt) {
607
632
  const name = readNVLiteral(nvt);
608
633
  const nvp = new NVPair(name);
609
634
 
610
- return readRightHandSide(nvp, nvt);
635
+ return readRightHandSide(nvp, nvt, depth, maxDepth);
611
636
  }
612
637
 
613
638
  /*
@@ -615,7 +640,7 @@ function readNVPair(nvt) {
615
640
  * NVPair: (name=value)
616
641
  * value: atom | NVList
617
642
  */
618
- function readRightHandSide(nvp, nvt) {
643
+ function readRightHandSide(nvp, nvt, depth, maxDepth) {
619
644
  let tk = nvt.getToken();
620
645
  switch (tk) {
621
646
  case TKN_EQUAL:
@@ -630,7 +655,7 @@ function readRightHandSide(nvp, nvt) {
630
655
  } else {
631
656
  // NVList is responsible for adding child NVPairs to this parent
632
657
  // NVPair.
633
- readNVList(nvt, nvp);
658
+ readNVList(nvt, nvp, depth, maxDepth);
634
659
  }
635
660
  break;
636
661
 
@@ -671,24 +696,24 @@ function readNVLiteral(nvt) {
671
696
  * function which adds a list of NVPairs to a parent NVPair.
672
697
  * NVList: NVPair NVList | epsilon
673
698
  */
674
- function readNVList(nvt, parent) {
675
- // if next token is "(" or ",", then read an NVPair
676
- // otherwise, assume epsilon
677
- const tk = nvt.getToken();
678
- if (!(tk == TKN_LPAREN || tk == TKN_COMMA)) {
679
- return; // didn't read an nvpair
680
- }
699
+ function readNVList(nvt, parent, depth, maxDepth) {
700
+ while (true) {
701
+ // if next token is "(" or ",", then read an NVPair
702
+ // otherwise, assume epsilon
703
+ const tk = nvt.getToken();
704
+ if (!(tk == TKN_LPAREN || tk == TKN_COMMA)) {
705
+ return; // didn't read an nvpair
706
+ }
681
707
 
682
- const child = readNVPair(nvt);
708
+ const child = readNVPair(nvt, depth + 1, maxDepth);
683
709
 
684
- // read a good NVPair
685
- parent.addListElement(child);
686
- if ((tk == TKN_COMMA) || (child.name == child.atom)) {
687
- if (parent.getListType != LIST_COMMASEP) // if not already set
688
- parent.setListType = LIST_COMMASEP; // set it
710
+ // read a good NVPair
711
+ parent.addListElement(child);
712
+ if ((tk == TKN_COMMA) || (child.name == child.atom)) {
713
+ if (parent.getListType != LIST_COMMASEP) // if not already set
714
+ parent.setListType = LIST_COMMASEP; // set it
715
+ }
689
716
  }
690
-
691
- readNVList(nvt, parent); // next iteration of NVList()
692
717
  }
693
718
 
694
719
  /**
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -353,6 +353,7 @@ function MarkerPacket(isLargeSDU) {
353
353
  */
354
354
  this.fromPacket = function(packet, nsi) {
355
355
  this.type = packet.buf.readUInt8(constants.NSPMKTYP);
356
+ let expectedFlag = constants.NIQRMARK;
356
357
 
357
358
  switch (this.type) {
358
359
  case constants.NSPMKTD0:
@@ -361,9 +362,11 @@ function MarkerPacket(isLargeSDU) {
361
362
  case constants.NSPMKTD1:
362
363
  this.data = packet.buf.readUInt8(constants.NSPMKDAT);
363
364
  nsi.isBreak = true;
364
- if (this.data == constants.NIQRMARK) {
365
+ if (nsi.pipelineEnabled) {
366
+ expectedFlag = constants.NIQBMARK;
367
+ }
368
+ if (this.data == expectedFlag) {
365
369
  nsi.isReset = true;
366
- nsi.isBreak = true;
367
370
  }
368
371
  break;
369
372
  default:
@@ -398,10 +401,16 @@ function ControlPacket() {
398
401
  let emfi;
399
402
  let err1;
400
403
  let err2;
404
+ if (packet.buf.length < constants.NSPCTLCMD + 2) {
405
+ errors.throwErr(errors.ERR_INVALID_PACKET);
406
+ }
401
407
 
402
408
  this.cmd = packet.buf.readUInt16BE(constants.NSPCTLCMD);
403
409
  switch (this.cmd) {
404
410
  case constants.NSPCTL_SERR:
411
+ if (packet.buf.length < constants.NSPCTLDAT + 12) {
412
+ errors.throwErr(errors.ERR_INVALID_PACKET);
413
+ }
405
414
  emfi = packet.buf.readUInt32BE(constants.NSPCTLDAT);
406
415
  err1 = packet.buf.readUInt32BE(constants.NSPCTLDAT + 4);
407
416
  err2 = packet.buf.readUInt32BE(constants.NSPCTLDAT + 8);
@@ -411,8 +420,20 @@ function ControlPacket() {
411
420
  } else if (err1 == NSESENDMESG) {
412
421
  this.errno = err1;
413
422
  this.notifLen = err2;
423
+ const copyStart = constants.NSPCTLDAT + 12;
424
+ const payloadAvailable = packet.buf.length - copyStart;
425
+ const maxNotifBytes = this.sAtts && this.sAtts.largeSDU
426
+ ? constants.NSPABSSDULN
427
+ : constants.NSPMXSDULN;
428
+ // Ensure notification length fits in the packet body and negotiated SDU to avoid over-allocation.
429
+ if (err2 < 0 || err2 > payloadAvailable || err2 > maxNotifBytes) {
430
+ errors.throwErr(
431
+ errors.ERR_INVALID_PACKET,
432
+ `Invalid notification length ${err2}`
433
+ );
434
+ }
414
435
  this.notif = Buffer.allocUnsafe(err2 + 1).fill(0);
415
- this.buf.copy(this.notif, 0, constants.NSPCTLDAT + 12, constants.NSPCTLDAT + 12 + err2);
436
+ packet.buf.copy(this.notif, 0, copyStart, copyStart + err2);
416
437
  } else {
417
438
  this.errno = err1;
418
439
  if (emfi == ORA_ERROR_EMFI_NUMBER) {
@@ -99,8 +99,16 @@ class NLParamParser {
99
99
  return tnsFileStruct.aliasht;
100
100
  }
101
101
  tnsFileStruct.readInProgress = true;
102
- await this.start(file_path, 0, tnsFileStruct); //start with 0 depth (tnsnames.ora)
103
- this.tnsnamesHT.set(file_path, tnsFileStruct);
102
+ try {
103
+ await this.start(file_path, 0, tnsFileStruct); // start with 0 depth (tnsnames.ora)
104
+ this.tnsnamesHT.set(file_path, tnsFileStruct);
105
+ } finally {
106
+ tnsFileStruct.readInProgress = false;
107
+ let waiter;
108
+ while ((waiter = tnsFileStruct.waiters.pop())) {
109
+ waiter();
110
+ }
111
+ }
104
112
  return tnsFileStruct.aliasht;
105
113
  }
106
114
 
@@ -156,11 +164,6 @@ class NLParamParser {
156
164
  await this.addNLPListElement(nvElem, depth, tnsFileStruct);
157
165
  nvElem = ""; // clear nvElem buffer after added
158
166
  }
159
- tnsFileStruct.readInProgress = false;
160
- let waiter;
161
- while ((waiter = tnsFileStruct.waiters.pop())) {
162
- waiter();
163
- }
164
167
  }
165
168
  /**
166
169
  * Given a string, this method looks if the '#' character is present.
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2024, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -105,7 +105,7 @@ class StatementCache {
105
105
  }
106
106
 
107
107
  //---------------------------------------------------------------------------
108
- // get_statement()
108
+ // getStatement()
109
109
  // Get a statement from the statement cache, or prepare a new statement
110
110
  // for use. If a statement is already in use or the statement is not
111
111
  // supposed to be cached, a copy will be made (and not returned to the
@@ -126,14 +126,12 @@ class StatementCache {
126
126
  this._cachedStatements.set(sql, stmt);
127
127
  this._adjustCache();
128
128
  }
129
- this._openCursors.add(stmt);
130
129
  } else if (forceNew || stmt.inUse) {
131
130
  if (!cacheStatement) {
132
131
  this._addCursorToClose(stmt);
133
132
  this._cachedStatements.delete(sql);
134
133
  }
135
134
  stmt = stmt._copy();
136
- this._openCursors.add(stmt);
137
135
  } else if (!cacheStatement) {
138
136
  this._cachedStatements.delete(sql);
139
137
  stmt.returnToCache = false;