Tag: medium

  • GHSA-j656-3hf2-fvjc: DBHub: MySQL and MariaDB comment interpretation bypasses read-only SQL validation

    • Severity: MEDIUM
    • Ecosystems: npm
    • Packages: @bytebase/dbhub

    The read-only mode can be bypassed on the MySQL and MariaDB connectors. In read-only mode a caller can still execute arbitrary write and DDL statements (for example DROP TABLE), which defeats the protection that read-only mode is meant to provide when a database is exposed to an AI agent.

    Root cause

    The problem is a mismatch between how DBHub’s SQL parser handles single-line comments and how MySQL and MariaDB actually parse them.

    In src/utils/sql-parser.ts, scanSingleLineComment treats “–” as the start of a comment no matter what character follows it. MySQL and MariaDB only begin a “–” comment when the two dashes are followed by whitespace, a control character, or the end of the line. Otherwise the dashes are read as two minus signs and the rest of the line is ordinary SQL.

    The read-only check in src/tools/execute-sql.ts splits the input and validates each statement with isReadOnlySQL, but the connector then executes the original, unmodified string. The MySQL and MariaDB connectors open their connection pool with multipleStatements: true (src/connectors/mysql/index.ts:54 and src/connectors/mariadb/index.ts:53) and run the whole string in a single call.

    Because of this, the following payload:

    SELECT 1–1;DROP TABLE victim

    is seen by DBHub as a single SELECT statement, since everything after “–” is treated as a comment and the read-only check passes. MySQL and MariaDB instead parse it as “SELECT 1 – -1” followed by “DROP TABLE victim” and execute both statements.

    Pr…

    Source: GitHub Security Advisories · 2026-11-02

  • GHSA-g3hv-75wg-m249: minimatch has potential denial of service via globparts amplification

    • Severity: MEDIUM
    • CVE: CVE-2026-118764
    • Ecosystems: npm
    • Packages: minimatch

    minimatch 10.2.6 contains an attacker-controlled pattern amplification in firstPhasePreProcess() when optimizationLevel >= 2.

    researcher: Reid Downing (b1nv0id)
    files:

    – src/index.ts: 690-770
    – src/index.ts: 780-796

    issue:
    input-controlled growth of globParts during first-phase preprocessing, followed by quadratic second-phase processing.

    the **/../ transformation clones a globparts entry and appends the clone to the same globparts array:
    “`
    parts.splice(gs, 1)
    const other = parts.slice(0)
    other[gs] = ‘**’
    globParts.push(other)
    “`
    this occurs inside a do…while (didSomething) fixpoint loop, allowing generated entries to be processed again.

    the expanded globparts array is subsequently processed by secondPhasePreProcess(), which performs pairwise comparisons:
    “`
    for (let i = 0; i < globParts.length – 1; i++) {
    for (let j = i + 1; j < globParts.length; j++) {
    this.partsMatch(…)
    }
    }
    “`
    an attacker who can supply a glob pattern can therefore cause additional pattern entries to be generated during preprocessing, followed by quadratic pairwise processing, resulting in excessive cpu and memory consumption.

    Source: GitHub Security Advisories · 2026-10-30

  • GHSA-3g3m-2fjr-6pw4: Glob: AbortSignal cancellation bypass allows post-timeout filesystem traversal

    • Severity: MEDIUM
    • CVE: CVE-2026-118773
    • Ecosystems: npm
    • Packages: glob

    ### Summary

    In glob 13.0.6, an aborted async walk rejects or emits an error but continues recursively reading directories in the background.

    The root cause is that abort branches in src/walker.ts call cb() without returning. As a result, callers that rely on signal or AbortSignal.timeout() to bound glob traversal cost can still incur filesystem I/O and CPU work after the request has already been rejected.

    ### Details

    This was reproduced on glob v13.0.6 (commit e80cb38ae60d6cbff9e75f39032a994858994d35).

    The README documents that the signal option cancels the glob walk. However, in src/walker.ts, the following methods call cb() when this.signal?.aborted is true but do not return:

    – GlobUtil.walkCB()
    – GlobUtil.walkCB2()
    – GlobUtil.walkCBSync()
    – GlobUtil.walkCB2Sync()

    Example vulnerable pattern:

    if (this.signal?.aborted) cb()

    Because execution continues after cb(), the walker still reaches processor.processPatterns(), readdirCB(), and recursive walkCB2() calls.

    The issue was directly demonstrated against v13.0.6. A source-history review shows the same missing-return pattern is present in tagged releases from v9.0.0 through v13.0.6.

    The issue affects both glob() and globStream().

    Recommended fix:
    Change each abort branch to return immediately:

    if (this.signal?.aborted) return cb()

    ### Proof of concept

    Environment:
    – Linux x86_64
    – Node.js v22.23.3
    – npm 10.9.9
    – glob 13.0.6

    Reproduction steps:

    1. Install the affected package:
    npm init -y
    npm install glob@13.0.…

    Source: GitHub Security Advisories · 2026-10-30

  • GHSA-6wvx-3r32-xmcg: Forge: Prototype Pollution in forge.http cookie handling via a malicious Set-Cookie

    • Severity: MEDIUM
    • CVE: CVE-2026-118777
    • Ecosystems: npm
    • Packages: node-forge

    ### Summary
    The `forge.http` client stored cookies from `Set-Cookie` response headers in a plain object keyed by cookie name and path. A malicious server could use the cookie name `__proto__` to add properties to `Object.prototype`, use inherited names such as `constructor` to add properties to `Object`, and send an empty cookie value to delete `Object.prototype` properties. Fixed in node-forge 1.4.1.

    ### Details
    `client.setCookie()` in `lib/http.js` stored a cookie with `client.cookies[cookie.name][cookie.path] = cookie`, creating `client.cookies[cookie.name]` only when `!(cookie.name in client.cookies)`. Both values come from the server’s `Set-Cookie` header via `response.getCookies()`. Because `in` also sees inherited properties:

    – a cookie named `__proto__` skipped the initialization, `client.cookies[‘__proto__’]` returned `Object.prototype`, and the cookie path was written as a new `Object.prototype` property, visible on every object;
    – a cookie named `constructor` wrote onto the global `Object` function the same way;
    – a cookie with an empty value calls `client.removeCookie(name, path)`, which used the same `in` checks and then `delete client.cookies[name][path]`, so `Set-Cookie: __proto__=; path=toString` deleted `Object.prototype.toString`. This path skipped the cookie’s secure-flag and domain checks, so it also worked for `http://` clients.

    `forge.http` is not loaded by `require(‘node-forge’)`. It is included in the `forge.all` browser bundle and is used with a so…

    Source: GitHub Security Advisories · 2026-10-30

  • GHSA-525v-9gp9-w22h: Forge: TLS client crash when the server sends no certificate

    • Severity: MEDIUM
    • CVE: CVE-2026-118778
    • Ecosystems: npm
    • Packages: node-forge

    ### Summary
    The forge TLS client’s check for a missing server certificate read the wrong property and never ran. A malicious TLS server, or an on-path attacker who removes the server’s Certificate message, can make `connection.process()` throw a `TypeError`, which crashes applications that use the forge TLS client. Fixed in node-forge 1.4.1.

    ### Details
    After ServerHelloDone, `tls.handleServerHelloDone()` in `lib/tls.js` is meant to reject a server that sent no certificate with a fatal `insufficient_security` alert, after consulting the application’s `verify` callback. The check tested `c.serverCertificate`, which is never set (the certificate is stored as `c.session.serverCertificate`), so it never ran. The client state machine allows ServerHelloDone directly after ServerHello, so the handshake continued to `tls.createClientKeyExchange()`, which reads `c.session.serverCertificate.publicKey` and throws a `TypeError` on the null certificate. The exception propagates out of `connection.process()` without calling the `verify` or `error` callbacks.

    There is no authentication bypass: without a server certificate the client cannot build its RSA key exchange, so the handshake cannot complete.

    The fix reads `c.session.serverCertificate` and always fails the handshake in that case, still calling `verify` so the application can customize the alert. Failing even when `verify` returns `true` is needed because the key exchange requires the certificate.

    #### Patches
    Fixed in node-forge…

    Source: GitHub Security Advisories · 2026-10-30

  • GHSA-5cwg-6vw7-rj6p: Forge: AES-GCM authentication bypass via unvalidated tagLength option

    • Severity: MEDIUM
    • CVE: CVE-2026-119186
    • Ecosystems: npm
    • Packages: node-forge

    ### Summary

    The AES-GCM mode in forge.cipher does not validate the tagLength option passed to start(). With a tagLength of 0 or a value that converts to 0 (such as null, false, ” or ‘0’), the authentication tag is empty, and decipher.finish() returns true for any ciphertext and additional data when the supplied tag is empty or missing. Fixed in node-forge 1.4.1.

    ### Details

    `modes.gcm.prototype.start()` in `lib/cipherModes.js` stored the `tagLength` option without validation, and `lib/cipher.js` documented it as accepting “0-128”. After processing the data, `afterFinish()` truncates the 16-byte tag with `truncate(16 % (tagLength / 8))`. For a `tagLength` that converts to 0, this is `16 % 0 = NaN`, and truncating by `NaN` empties the tag. On decryption, the start-time check that the supplied `tag` has `tagLength / 8` bytes passes for an empty tag, and the final tag comparison compares two empty strings, so `decipher.finish()` returns `true` for any ciphertext and additional data.

    NIST SP 800-38D section 5.2.1.2 allows only tag lengths of 128, 120, 112, 104 and 96 bits, plus 64 and 32 bits for certain applications.

    The fix validates `tagLength` in `start()`, for both encryption and decryption. A missing, `null` or `undefined` `tagLength` uses the default of 128 bits. Any other value is converted with `Number()` and must be one of 128, 120, 112, 104, 96, 64 or 32; otherwise `start()` throws “Invalid tag length.”. Numeric strings such as `’128’` are still accepted. With a v…

    Source: GitHub Security Advisories · 2026-10-30