Commit 6b8001d2191 for nodejs

commit 6b8001d2191cf8453949b334e00b3a7f13372a40
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Wed Sep 23 18:52:49 2026 +0200

    crypto: validate update input encodings

    Move unknown encoding validation into validateEncoding so Hash, Hmac,
    Sign, and Verify reject invalid string input encodings alongside
    Cipher and Decipher. Preserve ignored encodings for buffer inputs.

    Signed-off-by: Filip Skokan <panva.ip@gmail.com>
    Assisted-by: Codex
    PR-URL: https://github.com/nodejs/node/pull/66247
    Fixes: https://github.com/nodejs/node/issues/45189
    Refs: https://github.com/nodejs/node/pull/45990
    Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
    Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
    Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
    Reviewed-By: James M Snell <jasnell@gmail.com>

diff --git a/lib/internal/crypto/cipher.js b/lib/internal/crypto/cipher.js
index 6521b60cb04..4b4165c5508 100644
--- a/lib/internal/crypto/cipher.js
+++ b/lib/internal/crypto/cipher.js
@@ -185,10 +185,6 @@ function _flush(callback) {
 function update(data, inputEncoding, outputEncoding) {
   if (typeof data === 'string') {
     validateEncoding(data, inputEncoding);
-    if (inputEncoding != null &&
-        normalizeEncoding(inputEncoding) === undefined) {
-      throw new ERR_UNKNOWN_ENCODING(inputEncoding);
-    }
   } else if (!isArrayBufferView(data)) {
     throw new ERR_INVALID_ARG_TYPE(
       'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data);
diff --git a/lib/internal/validators.js b/lib/internal/validators.js
index f8f1b9383d1..1c4acd51613 100644
--- a/lib/internal/validators.js
+++ b/lib/internal/validators.js
@@ -28,6 +28,7 @@ const {
     ERR_INVALID_THIS: { HideStackFramesError: ERR_INVALID_THIS },
     ERR_OUT_OF_RANGE: { HideStackFramesError: ERR_OUT_OF_RANGE },
     ERR_SOCKET_BAD_PORT: { HideStackFramesError: ERR_SOCKET_BAD_PORT },
+    ERR_UNKNOWN_ENCODING,
     ERR_UNKNOWN_SIGNAL: { HideStackFramesError: ERR_UNKNOWN_SIGNAL },
   },
   hideStackFrames,
@@ -417,6 +418,9 @@ const validateBuffer = hideStackFrames((buffer, name = 'buffer') => {
  */
 const validateEncoding = hideStackFrames((data, encoding) => {
   const normalizedEncoding = normalizeEncoding(encoding);
+  if (normalizedEncoding === undefined) {
+    throw new ERR_UNKNOWN_ENCODING(encoding);
+  }
   const length = data.length;

   if (normalizedEncoding === 'hex' && length % 2 !== 0) {
diff --git a/test/parallel/test-crypto-update-input-encoding.js b/test/parallel/test-crypto-update-input-encoding.js
new file mode 100644
index 00000000000..0edb7f57a24
--- /dev/null
+++ b/test/parallel/test-crypto-update-input-encoding.js
@@ -0,0 +1,62 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+
+const assert = require('assert');
+const crypto = require('crypto');
+const fixtures = require('../common/fixtures');
+
+const privateKey = fixtures.readKey('rsa_private.pem');
+const publicKey = fixtures.readKey('rsa_public.pem');
+const data = 'caf\u00e9';
+const bytes = Buffer.from(data);
+const signature = crypto.sign('sha256', bytes, privateKey);
+
+const factories = [
+  () => crypto.createHash('sha256'),
+  () => crypto.createHmac('sha256', 'key'),
+  () => crypto.createSign('sha256'),
+  () => crypto.createVerify('sha256'),
+  () => crypto.createCipheriv('aes-128-ctr', Buffer.alloc(16), Buffer.alloc(16)),
+  () => crypto.createDecipheriv('aes-128-ctr', Buffer.alloc(16), Buffer.alloc(16)),
+];
+
+function finish(instance, data, encoding) {
+  const result = instance.update(data, encoding);
+  if (instance instanceof crypto.Hash || instance instanceof crypto.Hmac)
+    return instance.digest();
+  if (instance instanceof crypto.Sign)
+    return instance.sign(privateKey);
+  if (instance instanceof crypto.Verify)
+    return instance.verify(publicKey, signature);
+  return Buffer.concat([result, instance.final()]);
+}
+
+for (const create of factories) {
+  const expected = finish(create(), bytes);
+  for (const encoding of ['bad', 'buffer']) {
+    const instance = create();
+    assert.throws(() => instance.update(data, encoding), {
+      code: 'ERR_UNKNOWN_ENCODING',
+      message: `Unknown encoding: ${encoding}`,
+    });
+    // Rejected input must not change the operation's state.
+    assert.deepStrictEqual(finish(instance, bytes), expected);
+  }
+  for (const encoding of [undefined, null, '', 'utf8', 'UTF-8']) {
+    assert.deepStrictEqual(finish(create(), data, encoding), expected);
+  }
+  assert.deepStrictEqual(finish(create(), bytes.toString('hex'), 'hex'), expected);
+  for (const input of [
+    bytes,
+    new Uint8Array(bytes),
+    new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength),
+  ]) {
+    assert.deepStrictEqual(finish(create(), input, 'bad'), expected);
+  }
+  assert.throws(() => create().update('a', 'hex'), {
+    code: 'ERR_INVALID_ARG_VALUE',
+  });
+}