Skip to content

DQL Injection via unvalidated regexp filter argument in GraphQL query rewriter

High
matthewmcneely published GHSA-33p8-wc97-5qcj Jul 17, 2026

Package

gomod github.com/dgraph-io/dgraph/v25 (Go)

Affected versions

<= 25.3.7

Patched versions

v25.3.8

Description

Summary

The maybeQuoteArg function in graphql/resolve/query_rewriter.go explicitly skips sanitization for regexp filter arguments, returning user input raw. This allows DQL (Dgraph Query Language) injection through any GraphQL query or mutation that uses a regexp filter on a @search(by: [regexp]) field.

Root Cause

At query_rewriter.go:2349:

func maybeQuoteArg(fn string, arg interface{}) string {
    switch arg := arg.(type) {
    case string:
        if fn == "regexp" {
            return arg  // <-- NO SANITIZATION
        }
        return fmt.Sprintf("%q", arg)
    ...
}

The raw value is written into the DQL query string by writeFilterArguments in graphquery.go:185 with zero escaping.

Attack Vector

An unauthenticated attacker sends a GraphQL query with a crafted regexp filter value that:

  1. Closes the regex literal with a second /
  2. Closes the regexp() function call with )
  3. Injects arbitrary DQL filter logic (OR, has(), eq(), uid())

The DQL lexer tokenizes /pattern/flags as a single token, then processes the injected content as valid DQL syntax.

Exploitation

Filter Bypass (Queries)

An attacker can bypass intended query filters to retrieve all nodes of a type:

query {
  queryUser(filter: { name: { regexp: "/x/i) OR has(User.name" }}) {
    name
    email
  }
}

This produces DQL: @filter(regexp(User.name, /x/i) OR has(User.name)) — returning ALL users regardless of the original filter intent.

Mass Data Modification (Mutations)

The same injection works in update and delete mutation filters via mutation_rewriter.go:990:

mutation {
  deleteUser(filter: { name: { regexp: "/x/i) OR has(User.name" }}) {
    numUids
  }
}

This expands the deletion set to ALL nodes of the type (subject to @auth rules if defined).

Impact

  • Data disclosure: Retrieve all nodes of any type with a regexp-indexed field, bypassing filter constraints
  • Mass data modification/deletion: Expand mutation targets beyond intended scope
  • No authentication required for query-based exploitation
  • Mutations require user-level auth but bypass intended scope restrictions

Affected Code Paths

  1. graphql/resolve/query_rewriter.go:2167 → maybeQuoteArg(fn, val) in filter construction
  2. graphql/resolve/mutation_rewriter.go:990 → addFilter(dgQuery[0], m.MutatedType(), filter)
  3. graphql/dgraph/graphquery.go:185 → b.WriteString(arg.Value) serialization

Suggested Fix

Validate regexp filter arguments before passing to DQL. Either:

  1. Validate that the input matches the expected DQL regex format (/pattern/flags) and contains no unescaped ) or DQL operators
  2. Use DQL variable bindings for regexp values (as was done for passwords in the GHSA-q2m9-6jp9-c6mc fix)

Prerequisites

  • Target schema must have at least one type with @search(by: [regexp]) or @search(by: [trigram]) on a String field
  • This is a common configuration in Dgraph applications

Reported by zx (Jace)

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
Low
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L

CVE ID

CVE-2026-63637

Weaknesses

Improper Neutralization of Special Elements in Data Query Logic

The product generates a query intended to access or manipulate data in a data store such as a database, but it does not neutralize or incorrectly neutralizes special elements that can modify the intended logic of the query. Learn more on MITRE.

Credits