diff --git a/bin/fix-os-hashes.ts b/bin/fix-os-hashes.ts new file mode 100644 index 00000000..a89f0938 --- /dev/null +++ b/bin/fix-os-hashes.ts @@ -0,0 +1,189 @@ +/* + * Copyright 2025 The NATS Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { parse } from "https://deno.land/std@0.221.0/flags/mod.ts"; +import { ObjectStoreImpl, ServerObjectInfo } from "../jetstream/objectstore.ts"; +import { + connect, + ConnectionOptions, + credsAuthenticator, +} from "https://raw.githubusercontent.com/nats-io/nats.deno/main/src/mod.ts"; +import { Base64UrlPaddedCodec } from "../nats-base-client/base64.ts"; +import { + SHA256 as BAD_SHA256, +} from "https://raw.githubusercontent.com/nats-io/nats.deno/refs/tags/v1.29.1/nats-base-client/sha256.js"; +import { consumerOpts } from "../jetstream/mod.ts"; +import { sha256 } from "https://raw.githubusercontent.com/nats-io/nats.deno/refs/tags/v1.29.1/nats-base-client/sha256.js"; +import { checkSha256, parseSha256 } from "../jetstream/sha_digest.parser.ts"; + +const argv = parse( + Deno.args, + { + alias: { + "s": ["server"], + "f": ["creds"], + "b": ["bucket"], + }, + default: { + s: "127.0.0.1:4222", + c: 1, + i: 0, + }, + boolean: ["check"], + string: ["server", "creds", "bucket"], + }, +); + +const copts = { servers: argv.s } as ConnectionOptions; + +if (argv.h || argv.help) { + console.log( + "Usage: fix-os [-s server] [--creds=/path/file.creds] [--check] --bucket=name", + ); + console.log( + "\nThis tool fixes metadata entries in an object store that were written", + ); + console.log( + "with hashes that were calculated incorrectly due to a bug in the sha256 library.", + ); + console.log("Please backup your object stores before using this tool."); + + Deno.exit(1); +} + +if (argv.creds) { + const data = await Deno.readFile(argv.creds); + copts.authenticator = credsAuthenticator(data); +} + +if (!argv.bucket) { + console.log("--bucket is required"); + Deno.exit(1); +} + +const nc = await connect(copts); + +const js = nc.jetstream(); +const jsm = await nc.jetstreamManager(); +const lister = jsm.streams.listObjectStores(); +let found = false; +const streamName = `OBJ_${argv.bucket}`; +for await (const oss of lister) { + if (oss.streamInfo.config.name === streamName) { + found = true; + break; + } +} +if (!found) { + console.log(`bucket '${argv.bucket}' was not found`); + Deno.exit(1); +} +const os = await js.views.os(argv.bucket) as ObjectStoreImpl; +await fixDigests(os); + +async function fixDigests(os: ObjectStoreImpl): Promise { + let fixes = 0; + const entries = await os.list(); + for (const entry of entries) { + if (!entry.digest.startsWith("SHA-256=")) { + console.error( + `ignoring entry ${entry.name} - unknown objectstore digest:`, + entry.digest, + ); + continue; + } + // plain digest string + const digest = entry.digest.substring(8); + const parsedDigest = parseSha256(digest); + if (parsedDigest === null) { + console.error( + `ignoring entry ${entry.name} - unable to parse digest:`, + digest, + ); + continue; + } + + const badSha = new BAD_SHA256(); + const sha = sha256.create(); + let badHash = new Uint8Array(0); + let hash = new Uint8Array(0); + + const oc = consumerOpts(); + oc.orderedConsumer(); + + const subj = `$O.${os.name}.C.${entry.nuid}`; + let needsFixing = false; + + const sub = await js.subscribe(subj, oc); + for await (const m of sub) { + if (m.data.length > 0) { + badSha.update(m.data); + sha.update(m.data); + } + if (m.info.pending === 0) { + badHash = badSha.digest(); + hash = sha.digest(); + break; + } + } + sub.unsubscribe(); + + if (checkSha256(parsedDigest, badHash)) { + // this one could be bad + if (!checkSha256(badHash, hash)) { + console.log( + `[WARN] entry ${entry.name} has a bad hash: ${ + Base64UrlPaddedCodec.encode(badHash) + } - should be ${Base64UrlPaddedCodec.encode(hash)}`, + ); + needsFixing = true; + fixes++; + } + } + + if (argv.check) { + continue; + } + if (needsFixing) { + const metaSubject = os._metaSubject(entry.name); + const m = await os.jsm.streams.getMessage(os.stream, { + last_by_subj: metaSubject, + }); + const info = m.json(); + const digest = Base64UrlPaddedCodec.encode(hash); + info.digest = `SHA-256=${digest}`; + try { + await js.publish(metaSubject, JSON.stringify(info)); + } catch (err) { + console.error(`[ERR] failed to update ${metaSubject}: ${err.message}`); + continue; + } + try { + const seq = m.seq; + await jsm.streams.deleteMessage(os.stream, seq); + } catch (err) { + console.error( + `[WARN] failed to delete bad entry ${metaSubject}: ${err.message} - new entry was added`, + ); + } + } + } + + const verb = argv.check ? "are" : "were"; + console.log( + `${fixes} digest fixes ${verb} required on bucket ${argv.bucket}`, + ); +} + +await nc.drain(); diff --git a/jetstream/objectstore.ts b/jetstream/objectstore.ts index beb12a01..8c110899 100644 --- a/jetstream/objectstore.ts +++ b/jetstream/objectstore.ts @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 The NATS Authors + * Copyright 2022-2025 The NATS Authors * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -36,7 +36,7 @@ import { PubAck, } from "./types.ts"; import { QueuedIteratorImpl } from "../nats-base-client/queued_iterator.ts"; -import { SHA256 } from "../nats-base-client/sha256.js"; +import { sha256 } from "../nats-base-client/js-sha256.js"; import { MsgHdrs, @@ -55,6 +55,7 @@ import { } from "./jsapi_types.ts"; import { JsMsg } from "./jsmsg.ts"; import { PubHeaders } from "./jsclient.ts"; +import { checkSha256, parseSha256 } from "./sha_digest.parser.ts"; export const osPrefix = "OBJ_"; export const digestType = "SHA-256="; @@ -357,7 +358,7 @@ export class ObjectStoreImpl implements ObjectStore { const db = new DataBuffer(); try { const reader = rs ? rs.getReader() : null; - const sha = new SHA256(); + const sha = sha256.create(); while (true) { const { done, value } = reader @@ -378,10 +379,8 @@ export class ObjectStoreImpl implements ObjectStore { // prepare the metadata info.mtime = new Date().toISOString(); - const digest = sha.digest("base64"); - const pad = digest.length % 3; - const padding = pad > 0 ? "=".repeat(pad) : ""; - info.digest = `${digestType}${digest}${padding}`; + const digest = Base64UrlPaddedCodec.encode(sha.digest()); + info.digest = `${digestType}${digest}`; info.deleted = false; // trailing md for the object @@ -527,6 +526,16 @@ export class ObjectStoreImpl implements ObjectStore { return os.get(ln); } + if (!info.digest.startsWith(digestType)) { + return Promise.reject(new Error(`unknown digest type: ${info.digest}`)); + } + const digest = parseSha256(info.digest.substring(8)); + if (digest === null) { + return Promise.reject( + new Error(`unable to parse digest: ${info.digest}`), + ); + } + const d = deferred(); const r: Partial = { @@ -543,7 +552,7 @@ export class ObjectStoreImpl implements ObjectStore { const oc = consumerOpts(); oc.orderedConsumer(); - const sha = new SHA256(); + const sha = sha256.create(); const subj = `$O.${this.name}.C.${info.nuid}`; const sub = await this.js.subscribe(subj, oc); (async () => { @@ -553,12 +562,7 @@ export class ObjectStoreImpl implements ObjectStore { controller!.enqueue(jm.data); } if (jm.info.pending === 0) { - const hash = sha.digest("base64"); - // go pads the hash - which should be multiple of 3 - otherwise pads with '=' - const pad = hash.length % 3; - const padding = pad > 0 ? "=".repeat(pad) : ""; - const digest = `${digestType}${hash}${padding}`; - if (digest !== info.digest) { + if (!checkSha256(digest, sha.digest())) { controller!.error( new Error( `received a corrupt object, digests do not match received: ${info.digest} calculated ${digest}`, diff --git a/jetstream/sha_digest.parser.ts b/jetstream/sha_digest.parser.ts new file mode 100644 index 00000000..d663b491 --- /dev/null +++ b/jetstream/sha_digest.parser.ts @@ -0,0 +1,104 @@ +/* + * Copyright 2025 The NATS Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function parseSha256(s: string): Uint8Array | null { + return toByteArray(s); +} + +function isHex(s: string): boolean { + // contains valid hex characters only + const hexRegex = /^[0-9A-Fa-f]+$/; + if (!hexRegex.test(s)) { + // non-hex characters + return false; + } + + // check for mixed-case strings - paranoid base64 sneaked in + const isAllUpperCase = /^[0-9A-F]+$/.test(s); + const isAllLowerCase = /^[0-9a-f]+$/.test(s); + if (!(isAllUpperCase || isAllLowerCase)) { + return false; + } + + // ensure the input string length is even + return s.length % 2 === 0; +} + +function isBase64(s: string): boolean { + // test for padded or normal base64 + return /^[A-Za-z0-9\-_]*(={0,2})?$/.test(s) || + /^[A-Za-z0-9+/]*(={0,2})?$/.test(s); +} + +function detectEncoding(input: string): "hex" | "b64" | "" { + // hex is more reliable to flush out... + if (isHex(input)) { + return "hex"; + } else if (isBase64(input)) { + return "b64"; + } + return ""; +} + +function hexToByteArray(s: string): Uint8Array { + if (s.length % 2 !== 0) { + throw new Error("hex string must have an even length"); + } + const a = new Uint8Array(s.length / 2); + for (let i = 0; i < s.length; i += 2) { + // parse hex two chars at a time + a[i / 2] = parseInt(s.substring(i, i + 2), 16); + } + return a; +} + +function base64ToByteArray(s: string): Uint8Array { + // could be url friendly + s = s.replace(/-/g, "+"); + s = s.replace(/_/g, "/"); + const sbin = atob(s); + return Uint8Array.from(sbin, (c) => c.charCodeAt(0)); +} + +function toByteArray(input: string): Uint8Array | null { + const encoding = detectEncoding(input); + switch (encoding) { + case "hex": + return hexToByteArray(input); + case "b64": + return base64ToByteArray(input); + } + return null; +} + +export function checkSha256( + a: string | Uint8Array, + b: string | Uint8Array, +): boolean { + const aBytes = typeof a === "string" ? parseSha256(a) : a; + const bBytes = typeof b === "string" ? parseSha256(b) : b; + if (aBytes === null || bBytes === null) { + return false; + } + if (aBytes.length !== bBytes.length) { + return false; + } + for (let i = 0; i < aBytes.length; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + return true; +} diff --git a/jetstream/tests/consumers_ordered_test.ts b/jetstream/tests/consumers_ordered_test.ts index a2e914f3..2e80f361 100644 --- a/jetstream/tests/consumers_ordered_test.ts +++ b/jetstream/tests/consumers_ordered_test.ts @@ -979,7 +979,7 @@ Deno.test("ordered consumers - consume reset", async () => { const c = await js.consumers.get("A") as OrderedPullConsumerImpl; // after the first message others will get published - let iter = await c.consume({ max_messages: 11, expires: 5000 }); + const iter = await c.consume({ max_messages: 11, expires: 5000 }); countResets(iter).catch(); const sequences = []; for await (const m of iter) { diff --git a/jetstream/tests/kv_test.ts b/jetstream/tests/kv_test.ts index dbe055f5..b2f0dcc7 100644 --- a/jetstream/tests/kv_test.ts +++ b/jetstream/tests/kv_test.ts @@ -2166,7 +2166,7 @@ Deno.test("kv - watcher on server restart", async () => { }); Deno.test("kv - maxBucketSize doesn't override max_bytes", async () => { - let { ns, nc } = await setup( + const { ns, nc } = await setup( jetstreamServerConf({}), ); const js = nc.jetstream(); diff --git a/jetstream/tests/objectstore_test.ts b/jetstream/tests/objectstore_test.ts index 4c2dd6c6..b257ff19 100644 --- a/jetstream/tests/objectstore_test.ts +++ b/jetstream/tests/objectstore_test.ts @@ -32,7 +32,7 @@ import { crypto } from "https://deno.land/std@0.221.0/crypto/mod.ts"; import { ObjectInfo, ObjectStoreMeta, StorageType } from "../mod.ts"; import { connect, Empty, headers, nanos, StringCodec } from "../../src/mod.ts"; import { equals } from "https://deno.land/std@0.221.0/bytes/mod.ts"; -import { SHA256 } from "../../nats-base-client/sha256.js"; +import { sha256 } from "../../nats-base-client/js-sha256.js"; import { Base64UrlPaddedCodec } from "../../nats-base-client/base64.ts"; import { digestType } from "../objectstore.ts"; @@ -80,12 +80,10 @@ function makeData(n: number): Uint8Array { } function digest(data: Uint8Array): string { - const sha = new SHA256(); + const sha = sha256.create(); sha.update(data); - const digest = sha.digest("base64"); - const pad = digest.length % 3; - const padding = pad > 0 ? "=".repeat(pad) : ""; - return `${digestType}${digest}${padding}`; + const digest = Base64UrlPaddedCodec.encode(sha.digest()); + return `${digestType}${digest}`; } Deno.test("objectstore - basics", async () => { diff --git a/nats-base-client/js-sha256.js b/nats-base-client/js-sha256.js new file mode 100644 index 00000000..b8988058 --- /dev/null +++ b/nats-base-client/js-sha256.js @@ -0,0 +1,404 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file +// This code was bundled using `deno bundle` and it's not recommended to edit it manually +// https://github.com/emn178/js-sha256 (MIT) + +function t(t, e) { + return e.forEach(function(e) { + e && "string" != typeof e && !Array.isArray(e) && Object.keys(e).forEach(function(r) { + if ("default" !== r && !(r in t)) { + var i = Object.getOwnPropertyDescriptor(e, r); + Object.defineProperty(t, r, i.get ? i : { + enumerable: !0, + get: function() { + return e[r]; + } + }); + } + }); + }), Object.freeze(t); +} +var e = "undefined" != typeof global ? global : "undefined" != typeof self ? self : "undefined" != typeof window ? window : {}; +function r() { + throw new Error("setTimeout has not been defined"); +} +function i() { + throw new Error("clearTimeout has not been defined"); +} +var h = r, s = i; +function n(t) { + if (h === setTimeout) return setTimeout(t, 0); + if ((h === r || !h) && setTimeout) return h = setTimeout, setTimeout(t, 0); + try { + return h(t, 0); + } catch (e) { + try { + return h.call(null, t, 0); + } catch (e) { + return h.call(this, t, 0); + } + } +} +"function" == typeof e.setTimeout && (h = setTimeout), "function" == typeof e.clearTimeout && (s = clearTimeout); +var o, a = [], f = !1, u = -1; +function c() { + f && o && (f = !1, o.length ? a = o.concat(a) : u = -1, a.length && l()); +} +function l() { + if (!f) { + var t = n(c); + f = !0; + for(var e = a.length; e;){ + for(o = a, a = []; ++u < e;)o && o[u].run(); + u = -1, e = a.length; + } + o = null, f = !1, function(t) { + if (s === clearTimeout) return clearTimeout(t); + if ((s === i || !s) && clearTimeout) return s = clearTimeout, clearTimeout(t); + try { + return s(t); + } catch (e) { + try { + return s.call(null, t); + } catch (e) { + return s.call(this, t); + } + } + }(t); + } +} +function y(t, e) { + this.fun = t, this.array = e; +} +y.prototype.run = function() { + this.fun.apply(null, this.array); +}; +function p() {} +var d = p, w = p, b = p, v = p, A = p, g = p, _ = p; +var m = e.performance || {}, O = m.now || m.mozNow || m.msNow || m.oNow || m.webkitNow || function() { + return (new Date).getTime(); +}; +var B = new Date; +var E = { + nextTick: function(t) { + var e = new Array(arguments.length - 1); + if (arguments.length > 1) for(var r = 1; r < arguments.length; r++)e[r - 1] = arguments[r]; + a.push(new y(t, e)), 1 !== a.length || f || n(l); + }, + title: "browser", + browser: !0, + env: {}, + argv: [], + version: "", + versions: {}, + on: d, + addListener: w, + once: b, + off: v, + removeListener: A, + removeAllListeners: g, + emit: _, + binding: function(t) { + throw new Error("process.binding is not supported"); + }, + cwd: function() { + return "/"; + }, + chdir: function(t) { + throw new Error("process.chdir is not supported"); + }, + umask: function() { + return 0; + }, + hrtime: function(t) { + var e = .001 * O.call(m), r = Math.floor(e), i = Math.floor(e % 1 * 1e9); + return t && (r -= t[0], (i -= t[1]) < 0 && (r--, i += 1e9)), [ + r, + i + ]; + }, + platform: "browser", + release: {}, + config: {}, + uptime: function() { + return (new Date - B) / 1e3; + } +}, S = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {}; +function T(t) { + if (t.__esModule) return t; + var e = Object.defineProperty({}, "__esModule", { + value: !0 + }); + return Object.keys(t).forEach(function(r) { + var i = Object.getOwnPropertyDescriptor(t, r); + Object.defineProperty(e, r, i.get ? i : { + enumerable: !0, + get: function() { + return t[r]; + } + }); + }), e; +} +var k, x = { + exports: {} +}, j = {}, N = T(t({ + __proto__: null, + default: j +}, [ + j +])); +k = x, function() { + var t = "input is invalid type", e = "object" == typeof window, r = e ? window : {}; + r.JS_SHA256_NO_WINDOW && (e = !1); + var i = !e && "object" == typeof self, h = !r.JS_SHA256_NO_NODE_JS && E.versions && E.versions.node; + h ? r = S : i && (r = self); + var s = !r.JS_SHA256_NO_COMMON_JS && k.exports, n = !r.JS_SHA256_NO_ARRAY_BUFFER && "undefined" != typeof ArrayBuffer, o = "0123456789abcdef".split(""), a = [ + -2147483648, + 8388608, + 32768, + 128 + ], f = [ + 24, + 16, + 8, + 0 + ], u = [ + 1116352408, + 1899447441, + 3049323471, + 3921009573, + 961987163, + 1508970993, + 2453635748, + 2870763221, + 3624381080, + 310598401, + 607225278, + 1426881987, + 1925078388, + 2162078206, + 2614888103, + 3248222580, + 3835390401, + 4022224774, + 264347078, + 604807628, + 770255983, + 1249150122, + 1555081692, + 1996064986, + 2554220882, + 2821834349, + 2952996808, + 3210313671, + 3336571891, + 3584528711, + 113926993, + 338241895, + 666307205, + 773529912, + 1294757372, + 1396182291, + 1695183700, + 1986661051, + 2177026350, + 2456956037, + 2730485921, + 2820302411, + 3259730800, + 3345764771, + 3516065817, + 3600352804, + 4094571909, + 275423344, + 430227734, + 506948616, + 659060556, + 883997877, + 958139571, + 1322822218, + 1537002063, + 1747873779, + 1955562222, + 2024104815, + 2227730452, + 2361852424, + 2428436474, + 2756734187, + 3204031479, + 3329325298 + ], c = [ + "hex", + "array", + "digest", + "arrayBuffer" + ], l = []; + !r.JS_SHA256_NO_NODE_JS && Array.isArray || (Array.isArray = function(t) { + return "[object Array]" === Object.prototype.toString.call(t); + }), !n || !r.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW && ArrayBuffer.isView || (ArrayBuffer.isView = function(t) { + return "object" == typeof t && t.buffer && t.buffer.constructor === ArrayBuffer; + }); + var y = function(t, e) { + return function(r) { + return new v(e, !0).update(r)[t](); + }; + }, p = function(t) { + var e = y("hex", t); + h && (e = d(e, t)), e.create = function() { + return new v(t); + }, e.update = function(t) { + return e.create().update(t); + }; + for(var r = 0; r < c.length; ++r){ + var i = c[r]; + e[i] = y(i, t); + } + return e; + }, d = function(e, i) { + var h, s = N, n = N.Buffer, o = i ? "sha224" : "sha256"; + return h = n.from && !r.JS_SHA256_NO_BUFFER_FROM ? n.from : function(t) { + return new n(t); + }, function(r) { + if ("string" == typeof r) return s.createHash(o).update(r, "utf8").digest("hex"); + if (null == r) throw new Error(t); + return r.constructor === ArrayBuffer && (r = new Uint8Array(r)), Array.isArray(r) || ArrayBuffer.isView(r) || r.constructor === n ? s.createHash(o).update(h(r)).digest("hex") : e(r); + }; + }, w = function(t, e) { + return function(r, i) { + return new A(r, e, !0).update(i)[t](); + }; + }, b = function(t) { + var e = w("hex", t); + e.create = function(e) { + return new A(e, t); + }, e.update = function(t, r) { + return e.create(t).update(r); + }; + for(var r = 0; r < c.length; ++r){ + var i = c[r]; + e[i] = w(i, t); + } + return e; + }; + function v(t, e) { + e ? (l[0] = l[16] = l[1] = l[2] = l[3] = l[4] = l[5] = l[6] = l[7] = l[8] = l[9] = l[10] = l[11] = l[12] = l[13] = l[14] = l[15] = 0, this.blocks = l) : this.blocks = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], t ? (this.h0 = 3238371032, this.h1 = 914150663, this.h2 = 812702999, this.h3 = 4144912697, this.h4 = 4290775857, this.h5 = 1750603025, this.h6 = 1694076839, this.h7 = 3204075428) : (this.h0 = 1779033703, this.h1 = 3144134277, this.h2 = 1013904242, this.h3 = 2773480762, this.h4 = 1359893119, this.h5 = 2600822924, this.h6 = 528734635, this.h7 = 1541459225), this.block = this.start = this.bytes = this.hBytes = 0, this.finalized = this.hashed = !1, this.first = !0, this.is224 = t; + } + function A(e, r, i) { + var h, s = typeof e; + if ("string" === s) { + var o, a = [], f = e.length, u = 0; + for(h = 0; h < f; ++h)(o = e.charCodeAt(h)) < 128 ? a[u++] = o : o < 2048 ? (a[u++] = 192 | o >>> 6, a[u++] = 128 | 63 & o) : o < 55296 || o >= 57344 ? (a[u++] = 224 | o >>> 12, a[u++] = 128 | o >>> 6 & 63, a[u++] = 128 | 63 & o) : (o = 65536 + ((1023 & o) << 10 | 1023 & e.charCodeAt(++h)), a[u++] = 240 | o >>> 18, a[u++] = 128 | o >>> 12 & 63, a[u++] = 128 | o >>> 6 & 63, a[u++] = 128 | 63 & o); + e = a; + } else { + if ("object" !== s) throw new Error(t); + if (null === e) throw new Error(t); + if (n && e.constructor === ArrayBuffer) e = new Uint8Array(e); + else if (!(Array.isArray(e) || n && ArrayBuffer.isView(e))) throw new Error(t); + } + e.length > 64 && (e = new v(r, !0).update(e).array()); + var c = [], l = []; + for(h = 0; h < 64; ++h){ + var y = e[h] || 0; + c[h] = 92 ^ y, l[h] = 54 ^ y; + } + v.call(this, r, i), this.update(l), this.oKeyPad = c, this.inner = !0, this.sharedMemory = i; + } + v.prototype.update = function(e) { + if (!this.finalized) { + var r, i = typeof e; + if ("string" !== i) { + if ("object" !== i) throw new Error(t); + if (null === e) throw new Error(t); + if (n && e.constructor === ArrayBuffer) e = new Uint8Array(e); + else if (!(Array.isArray(e) || n && ArrayBuffer.isView(e))) throw new Error(t); + r = !0; + } + for(var h, s, o = 0, a = e.length, u = this.blocks; o < a;){ + if (this.hashed && (this.hashed = !1, u[0] = this.block, this.block = u[16] = u[1] = u[2] = u[3] = u[4] = u[5] = u[6] = u[7] = u[8] = u[9] = u[10] = u[11] = u[12] = u[13] = u[14] = u[15] = 0), r) for(s = this.start; o < a && s < 64; ++o)u[s >>> 2] |= e[o] << f[3 & s++]; + else for(s = this.start; o < a && s < 64; ++o)(h = e.charCodeAt(o)) < 128 ? u[s >>> 2] |= h << f[3 & s++] : h < 2048 ? (u[s >>> 2] |= (192 | h >>> 6) << f[3 & s++], u[s >>> 2] |= (128 | 63 & h) << f[3 & s++]) : h < 55296 || h >= 57344 ? (u[s >>> 2] |= (224 | h >>> 12) << f[3 & s++], u[s >>> 2] |= (128 | h >>> 6 & 63) << f[3 & s++], u[s >>> 2] |= (128 | 63 & h) << f[3 & s++]) : (h = 65536 + ((1023 & h) << 10 | 1023 & e.charCodeAt(++o)), u[s >>> 2] |= (240 | h >>> 18) << f[3 & s++], u[s >>> 2] |= (128 | h >>> 12 & 63) << f[3 & s++], u[s >>> 2] |= (128 | h >>> 6 & 63) << f[3 & s++], u[s >>> 2] |= (128 | 63 & h) << f[3 & s++]); + this.lastByteIndex = s, this.bytes += s - this.start, s >= 64 ? (this.block = u[16], this.start = s - 64, this.hash(), this.hashed = !0) : this.start = s; + } + return this.bytes > 4294967295 && (this.hBytes += this.bytes / 4294967296 | 0, this.bytes = this.bytes % 4294967296), this; + } + }, v.prototype.finalize = function() { + if (!this.finalized) { + this.finalized = !0; + var t = this.blocks, e = this.lastByteIndex; + t[16] = this.block, t[e >>> 2] |= a[3 & e], this.block = t[16], e >= 56 && (this.hashed || this.hash(), t[0] = this.block, t[16] = t[1] = t[2] = t[3] = t[4] = t[5] = t[6] = t[7] = t[8] = t[9] = t[10] = t[11] = t[12] = t[13] = t[14] = t[15] = 0), t[14] = this.hBytes << 3 | this.bytes >>> 29, t[15] = this.bytes << 3, this.hash(); + } + }, v.prototype.hash = function() { + var t, e, r, i, h, s, n, o, a, f = this.h0, c = this.h1, l = this.h2, y = this.h3, p = this.h4, d = this.h5, w = this.h6, b = this.h7, v = this.blocks; + for(t = 16; t < 64; ++t)e = ((h = v[t - 15]) >>> 7 | h << 25) ^ (h >>> 18 | h << 14) ^ h >>> 3, r = ((h = v[t - 2]) >>> 17 | h << 15) ^ (h >>> 19 | h << 13) ^ h >>> 10, v[t] = v[t - 16] + e + v[t - 7] + r | 0; + for(a = c & l, t = 0; t < 64; t += 4)this.first ? (this.is224 ? (s = 300032, b = (h = v[0] - 1413257819) - 150054599 | 0, y = h + 24177077 | 0) : (s = 704751109, b = (h = v[0] - 210244248) - 1521486534 | 0, y = h + 143694565 | 0), this.first = !1) : (e = (f >>> 2 | f << 30) ^ (f >>> 13 | f << 19) ^ (f >>> 22 | f << 10), i = (s = f & c) ^ f & l ^ a, b = y + (h = b + (r = (p >>> 6 | p << 26) ^ (p >>> 11 | p << 21) ^ (p >>> 25 | p << 7)) + (p & d ^ ~p & w) + u[t] + v[t]) | 0, y = h + (e + i) | 0), e = (y >>> 2 | y << 30) ^ (y >>> 13 | y << 19) ^ (y >>> 22 | y << 10), i = (n = y & f) ^ y & c ^ s, w = l + (h = w + (r = (b >>> 6 | b << 26) ^ (b >>> 11 | b << 21) ^ (b >>> 25 | b << 7)) + (b & p ^ ~b & d) + u[t + 1] + v[t + 1]) | 0, e = ((l = h + (e + i) | 0) >>> 2 | l << 30) ^ (l >>> 13 | l << 19) ^ (l >>> 22 | l << 10), i = (o = l & y) ^ l & f ^ n, d = c + (h = d + (r = (w >>> 6 | w << 26) ^ (w >>> 11 | w << 21) ^ (w >>> 25 | w << 7)) + (w & b ^ ~w & p) + u[t + 2] + v[t + 2]) | 0, e = ((c = h + (e + i) | 0) >>> 2 | c << 30) ^ (c >>> 13 | c << 19) ^ (c >>> 22 | c << 10), i = (a = c & l) ^ c & y ^ o, p = f + (h = p + (r = (d >>> 6 | d << 26) ^ (d >>> 11 | d << 21) ^ (d >>> 25 | d << 7)) + (d & w ^ ~d & b) + u[t + 3] + v[t + 3]) | 0, f = h + (e + i) | 0, this.chromeBugWorkAround = !0; + this.h0 = this.h0 + f | 0, this.h1 = this.h1 + c | 0, this.h2 = this.h2 + l | 0, this.h3 = this.h3 + y | 0, this.h4 = this.h4 + p | 0, this.h5 = this.h5 + d | 0, this.h6 = this.h6 + w | 0, this.h7 = this.h7 + b | 0; + }, v.prototype.hex = function() { + this.finalize(); + var t = this.h0, e = this.h1, r = this.h2, i = this.h3, h = this.h4, s = this.h5, n = this.h6, a = this.h7, f = o[t >>> 28 & 15] + o[t >>> 24 & 15] + o[t >>> 20 & 15] + o[t >>> 16 & 15] + o[t >>> 12 & 15] + o[t >>> 8 & 15] + o[t >>> 4 & 15] + o[15 & t] + o[e >>> 28 & 15] + o[e >>> 24 & 15] + o[e >>> 20 & 15] + o[e >>> 16 & 15] + o[e >>> 12 & 15] + o[e >>> 8 & 15] + o[e >>> 4 & 15] + o[15 & e] + o[r >>> 28 & 15] + o[r >>> 24 & 15] + o[r >>> 20 & 15] + o[r >>> 16 & 15] + o[r >>> 12 & 15] + o[r >>> 8 & 15] + o[r >>> 4 & 15] + o[15 & r] + o[i >>> 28 & 15] + o[i >>> 24 & 15] + o[i >>> 20 & 15] + o[i >>> 16 & 15] + o[i >>> 12 & 15] + o[i >>> 8 & 15] + o[i >>> 4 & 15] + o[15 & i] + o[h >>> 28 & 15] + o[h >>> 24 & 15] + o[h >>> 20 & 15] + o[h >>> 16 & 15] + o[h >>> 12 & 15] + o[h >>> 8 & 15] + o[h >>> 4 & 15] + o[15 & h] + o[s >>> 28 & 15] + o[s >>> 24 & 15] + o[s >>> 20 & 15] + o[s >>> 16 & 15] + o[s >>> 12 & 15] + o[s >>> 8 & 15] + o[s >>> 4 & 15] + o[15 & s] + o[n >>> 28 & 15] + o[n >>> 24 & 15] + o[n >>> 20 & 15] + o[n >>> 16 & 15] + o[n >>> 12 & 15] + o[n >>> 8 & 15] + o[n >>> 4 & 15] + o[15 & n]; + return this.is224 || (f += o[a >>> 28 & 15] + o[a >>> 24 & 15] + o[a >>> 20 & 15] + o[a >>> 16 & 15] + o[a >>> 12 & 15] + o[a >>> 8 & 15] + o[a >>> 4 & 15] + o[15 & a]), f; + }, v.prototype.toString = v.prototype.hex, v.prototype.digest = function() { + this.finalize(); + var t = this.h0, e = this.h1, r = this.h2, i = this.h3, h = this.h4, s = this.h5, n = this.h6, o = this.h7, a = [ + t >>> 24 & 255, + t >>> 16 & 255, + t >>> 8 & 255, + 255 & t, + e >>> 24 & 255, + e >>> 16 & 255, + e >>> 8 & 255, + 255 & e, + r >>> 24 & 255, + r >>> 16 & 255, + r >>> 8 & 255, + 255 & r, + i >>> 24 & 255, + i >>> 16 & 255, + i >>> 8 & 255, + 255 & i, + h >>> 24 & 255, + h >>> 16 & 255, + h >>> 8 & 255, + 255 & h, + s >>> 24 & 255, + s >>> 16 & 255, + s >>> 8 & 255, + 255 & s, + n >>> 24 & 255, + n >>> 16 & 255, + n >>> 8 & 255, + 255 & n + ]; + return this.is224 || a.push(o >>> 24 & 255, o >>> 16 & 255, o >>> 8 & 255, 255 & o), a; + }, v.prototype.array = v.prototype.digest, v.prototype.arrayBuffer = function() { + this.finalize(); + var t = new ArrayBuffer(this.is224 ? 28 : 32), e = new DataView(t); + return e.setUint32(0, this.h0), e.setUint32(4, this.h1), e.setUint32(8, this.h2), e.setUint32(12, this.h3), e.setUint32(16, this.h4), e.setUint32(20, this.h5), e.setUint32(24, this.h6), this.is224 || e.setUint32(28, this.h7), t; + }, A.prototype = new v, A.prototype.finalize = function() { + if (v.prototype.finalize.call(this), this.inner) { + this.inner = !1; + var t = this.array(); + v.call(this, this.is224, this.sharedMemory), this.update(this.oKeyPad), this.update(t), v.prototype.finalize.call(this); + } + }; + var g = p(); + g.sha256 = g, g.sha224 = p(!0), g.sha256.hmac = b(), g.sha224.hmac = b(!0), s ? k.exports = g : (r.sha256 = g.sha256, r.sha224 = g.sha224); +}(); +var U = x.exports, z = x.exports.sha224, J = x.exports.sha256; +export { U as default, z as sha224, J as sha256 }; diff --git a/nats-base-client/sha256.js b/nats-base-client/sha256.js deleted file mode 100644 index e862c05b..00000000 --- a/nats-base-client/sha256.js +++ /dev/null @@ -1,360 +0,0 @@ -// deno-fmt-ignore-file -// deno-lint-ignore-file -// This code was bundled using `deno bundle` and it's not recommended to edit it manually - -// deno bundle https://deno.land/x/sha256@v1.0.2/mod.ts - -// The MIT License (MIT) -// -// Original work (c) Marco Paland (marco@paland.com) 2015-2018, PALANDesign Hannover, Germany -// -// Deno port Copyright (c) 2019 Noah Anabiik Schwarz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -function getLengths(b64) { - const len = b64.length; - let validLen = b64.indexOf("="); - if (validLen === -1) { - validLen = len; - } - const placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4; - return [ - validLen, - placeHoldersLen - ]; -} -function init(lookup, revLookup, urlsafe = false) { - function _byteLength(validLen, placeHoldersLen) { - return Math.floor((validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen); - } - function tripletToBase64(num) { - return lookup[num >> 18 & 0x3f] + lookup[num >> 12 & 0x3f] + lookup[num >> 6 & 0x3f] + lookup[num & 0x3f]; - } - function encodeChunk(buf, start, end) { - const out = new Array((end - start) / 3); - for(let i = start, curTriplet = 0; i < end; i += 3){ - out[curTriplet++] = tripletToBase64((buf[i] << 16) + (buf[i + 1] << 8) + buf[i + 2]); - } - return out.join(""); - } - return { - byteLength (b64) { - return _byteLength.apply(null, getLengths(b64)); - }, - toUint8Array (b64) { - const [validLen, placeHoldersLen] = getLengths(b64); - const buf = new Uint8Array(_byteLength(validLen, placeHoldersLen)); - const len = placeHoldersLen ? validLen - 4 : validLen; - let tmp; - let curByte = 0; - let i; - for(i = 0; i < len; i += 4){ - tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)]; - buf[curByte++] = tmp >> 16 & 0xff; - buf[curByte++] = tmp >> 8 & 0xff; - buf[curByte++] = tmp & 0xff; - } - if (placeHoldersLen === 2) { - tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4; - buf[curByte++] = tmp & 0xff; - } else if (placeHoldersLen === 1) { - tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2; - buf[curByte++] = tmp >> 8 & 0xff; - buf[curByte++] = tmp & 0xff; - } - return buf; - }, - fromUint8Array (buf) { - const maxChunkLength = 16383; - const len = buf.length; - const extraBytes = len % 3; - const len2 = len - extraBytes; - const parts = new Array(Math.ceil(len2 / 16383) + (extraBytes ? 1 : 0)); - let curChunk = 0; - let chunkEnd; - for(let i = 0; i < len2; i += maxChunkLength){ - chunkEnd = i + maxChunkLength; - parts[curChunk++] = encodeChunk(buf, i, chunkEnd > len2 ? len2 : chunkEnd); - } - let tmp; - if (extraBytes === 1) { - tmp = buf[len2]; - parts[curChunk] = lookup[tmp >> 2] + lookup[tmp << 4 & 0x3f]; - if (!urlsafe) parts[curChunk] += "=="; - } else if (extraBytes === 2) { - tmp = buf[len2] << 8 | buf[len2 + 1] & 0xff; - parts[curChunk] = lookup[tmp >> 10] + lookup[tmp >> 4 & 0x3f] + lookup[tmp << 2 & 0x3f]; - if (!urlsafe) parts[curChunk] += "="; - } - return parts.join(""); - } - }; -} -const lookup = []; -const revLookup = []; -const code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -for(let i = 0, l = code.length; i < l; ++i){ - lookup[i] = code[i]; - revLookup[code.charCodeAt(i)] = i; -} -const { byteLength , toUint8Array , fromUint8Array } = init(lookup, revLookup, true); -const decoder = new TextDecoder(); -const encoder = new TextEncoder(); -function toHexString(buf) { - return buf.reduce((hex, __byte)=>`${hex}${__byte < 16 ? "0" : ""}${__byte.toString(16)}`, ""); -} -function fromHexString(hex) { - const len = hex.length; - if (len % 2 || !/^[0-9a-fA-F]+$/.test(hex)) { - throw new TypeError("Invalid hex string."); - } - hex = hex.toLowerCase(); - const buf = new Uint8Array(Math.floor(len / 2)); - const end = len / 2; - for(let i = 0; i < end; ++i){ - buf[i] = parseInt(hex.substr(i * 2, 2), 16); - } - return buf; -} -function decode(buf, encoding = "utf8") { - if (/^utf-?8$/i.test(encoding)) { - return decoder.decode(buf); - } else if (/^base64$/i.test(encoding)) { - return fromUint8Array(buf); - } else if (/^hex(?:adecimal)?$/i.test(encoding)) { - return toHexString(buf); - } else { - throw new TypeError("Unsupported string encoding."); - } -} -function encode(str, encoding = "utf8") { - if (/^utf-?8$/i.test(encoding)) { - return encoder.encode(str); - } else if (/^base64$/i.test(encoding)) { - return toUint8Array(str); - } else if (/^hex(?:adecimal)?$/i.test(encoding)) { - return fromHexString(str); - } else { - throw new TypeError("Unsupported string encoding."); - } -} -const BYTES = 32; -class SHA256 { - hashSize = 32; - _buf; - _bufIdx; - _count; - _K; - _H; - _finalized; - constructor(){ - this._buf = new Uint8Array(64); - this._K = new Uint32Array([ - 0x428a2f98, - 0x71374491, - 0xb5c0fbcf, - 0xe9b5dba5, - 0x3956c25b, - 0x59f111f1, - 0x923f82a4, - 0xab1c5ed5, - 0xd807aa98, - 0x12835b01, - 0x243185be, - 0x550c7dc3, - 0x72be5d74, - 0x80deb1fe, - 0x9bdc06a7, - 0xc19bf174, - 0xe49b69c1, - 0xefbe4786, - 0x0fc19dc6, - 0x240ca1cc, - 0x2de92c6f, - 0x4a7484aa, - 0x5cb0a9dc, - 0x76f988da, - 0x983e5152, - 0xa831c66d, - 0xb00327c8, - 0xbf597fc7, - 0xc6e00bf3, - 0xd5a79147, - 0x06ca6351, - 0x14292967, - 0x27b70a85, - 0x2e1b2138, - 0x4d2c6dfc, - 0x53380d13, - 0x650a7354, - 0x766a0abb, - 0x81c2c92e, - 0x92722c85, - 0xa2bfe8a1, - 0xa81a664b, - 0xc24b8b70, - 0xc76c51a3, - 0xd192e819, - 0xd6990624, - 0xf40e3585, - 0x106aa070, - 0x19a4c116, - 0x1e376c08, - 0x2748774c, - 0x34b0bcb5, - 0x391c0cb3, - 0x4ed8aa4a, - 0x5b9cca4f, - 0x682e6ff3, - 0x748f82ee, - 0x78a5636f, - 0x84c87814, - 0x8cc70208, - 0x90befffa, - 0xa4506ceb, - 0xbef9a3f7, - 0xc67178f2 - ]); - this.init(); - } - init() { - this._H = new Uint32Array([ - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 - ]); - this._bufIdx = 0; - this._count = new Uint32Array(2); - this._buf.fill(0); - this._finalized = false; - return this; - } - update(msg, inputEncoding) { - if (msg === null) { - throw new TypeError("msg must be a string or Uint8Array."); - } else if (typeof msg === "string") { - msg = encode(msg, inputEncoding); - } - for(let i = 0, len = msg.length; i < len; i++){ - this._buf[this._bufIdx++] = msg[i]; - if (this._bufIdx === 64) { - this._transform(); - this._bufIdx = 0; - } - } - const c = this._count; - if ((c[0] += msg.length << 3) < msg.length << 3) { - c[1]++; - } - c[1] += msg.length >>> 29; - return this; - } - digest(outputEncoding) { - if (this._finalized) { - throw new Error("digest has already been called."); - } - this._finalized = true; - const b = this._buf; - let idx = this._bufIdx; - b[idx++] = 0x80; - while(idx !== 56){ - if (idx === 64) { - this._transform(); - idx = 0; - } - b[idx++] = 0; - } - const c = this._count; - b[56] = c[1] >>> 24 & 0xff; - b[57] = c[1] >>> 16 & 0xff; - b[58] = c[1] >>> 8 & 0xff; - b[59] = c[1] >>> 0 & 0xff; - b[60] = c[0] >>> 24 & 0xff; - b[61] = c[0] >>> 16 & 0xff; - b[62] = c[0] >>> 8 & 0xff; - b[63] = c[0] >>> 0 & 0xff; - this._transform(); - const hash = new Uint8Array(32); - for(let i = 0; i < 8; i++){ - hash[(i << 2) + 0] = this._H[i] >>> 24 & 0xff; - hash[(i << 2) + 1] = this._H[i] >>> 16 & 0xff; - hash[(i << 2) + 2] = this._H[i] >>> 8 & 0xff; - hash[(i << 2) + 3] = this._H[i] >>> 0 & 0xff; - } - this.init(); - return outputEncoding ? decode(hash, outputEncoding) : hash; - } - _transform() { - const h = this._H; - let h0 = h[0]; - let h1 = h[1]; - let h2 = h[2]; - let h3 = h[3]; - let h4 = h[4]; - let h5 = h[5]; - let h6 = h[6]; - let h7 = h[7]; - const w = new Uint32Array(16); - let i; - for(i = 0; i < 16; i++){ - w[i] = this._buf[(i << 2) + 3] | this._buf[(i << 2) + 2] << 8 | this._buf[(i << 2) + 1] << 16 | this._buf[i << 2] << 24; - } - for(i = 0; i < 64; i++){ - let tmp; - if (i < 16) { - tmp = w[i]; - } else { - let a = w[i + 1 & 15]; - let b = w[i + 14 & 15]; - tmp = w[i & 15] = (a >>> 7 ^ a >>> 18 ^ a >>> 3 ^ a << 25 ^ a << 14) + (b >>> 17 ^ b >>> 19 ^ b >>> 10 ^ b << 15 ^ b << 13) + w[i & 15] + w[i + 9 & 15] | 0; - } - tmp = tmp + h7 + (h4 >>> 6 ^ h4 >>> 11 ^ h4 >>> 25 ^ h4 << 26 ^ h4 << 21 ^ h4 << 7) + (h6 ^ h4 & (h5 ^ h6)) + this._K[i] | 0; - h7 = h6; - h6 = h5; - h5 = h4; - h4 = h3 + tmp; - h3 = h2; - h2 = h1; - h1 = h0; - h0 = tmp + (h1 & h2 ^ h3 & (h1 ^ h2)) + (h1 >>> 2 ^ h1 >>> 13 ^ h1 >>> 22 ^ h1 << 30 ^ h1 << 19 ^ h1 << 10) | 0; - } - h[0] = h[0] + h0 | 0; - h[1] = h[1] + h1 | 0; - h[2] = h[2] + h2 | 0; - h[3] = h[3] + h3 | 0; - h[4] = h[4] + h4 | 0; - h[5] = h[5] + h5 | 0; - h[6] = h[6] + h6 | 0; - h[7] = h[7] + h7 | 0; - } -} -function sha256(msg, inputEncoding, outputEncoding) { - return new SHA256().update(msg, inputEncoding).digest(outputEncoding); -} -export { BYTES as BYTES }; -export { SHA256 as SHA256 }; -export { sha256 as sha256 }; - - diff --git a/tests/auth_test.ts b/tests/auth_test.ts index a5b6392f..991c6fb1 100644 --- a/tests/auth_test.ts +++ b/tests/auth_test.ts @@ -1273,7 +1273,7 @@ Deno.test("auth - sub permission queue", async () => { const qA = deferred(); nc.subscribe("q", { queue: "A", - callback: (err, msg) => { + callback: (err, _msg) => { if (err) { qA.reject(err); } @@ -1283,7 +1283,7 @@ Deno.test("auth - sub permission queue", async () => { const qBad = deferred(); nc.subscribe("q", { queue: "bad", - callback: (err, msg) => { + callback: (err, _msg) => { if (err) { qBad.resolve(err); }