VYPR
High severityOSV Advisory· Published Feb 3, 2026· Updated Feb 3, 2026

Potential SQL injection via raster lookups on PostGIS

CVE-2026-1207

Description

An issue was discovered in 6.0 before 6.0.2, 5.2 before 5.2.11, and 4.2 before 4.2.28. Raster lookups on `RasterField` (only implemented on PostGIS) allows remote attackers to inject SQL via the band index parameter. Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected. Django would like to thank Tarek Nakkouch for reporting this issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
>= 6.0a1, < 6.0.26.0.2
DjangoPyPI
>= 5.2a1, < 5.2.115.2.11
DjangoPyPI
>= 4.2a1, < 4.2.284.2.28

Affected products

1

Patches

1
81aa5292967c

Fixed CVE-2026-1207 -- Prevented SQL injections in RasterField lookups via band index.

https://github.com/django/djangoJacob WallsJan 19, 2026via ghsa
5 files changed · +88 1
  • django/contrib/gis/db/backends/postgis/operations.py+6 0 modified
    @@ -51,6 +51,9 @@ def check_raster(self, lookup, template_params):
     
             # Look for band indices and inject them if provided.
             if lookup.band_lhs is not None and lhs_is_raster:
    +            if not isinstance(lookup.band_lhs, int):
    +                name = lookup.band_lhs.__class__.__name__
    +                raise TypeError(f"Band index must be an integer, but got {name!r}.")
                 if not self.func:
                     raise ValueError(
                         "Band indices are not allowed for this operator, it works on bbox "
    @@ -62,6 +65,9 @@ def check_raster(self, lookup, template_params):
                 )
     
             if lookup.band_rhs is not None and rhs_is_raster:
    +            if not isinstance(lookup.band_rhs, int):
    +                name = lookup.band_rhs.__class__.__name__
    +                raise TypeError(f"Band index must be an integer, but got {name!r}.")
                 if not self.func:
                     raise ValueError(
                         "Band indices are not allowed for this operator, it works on bbox "
    
  • docs/releases/4.2.28.txt+12 0 modified
    @@ -29,3 +29,15 @@ produced super-linear computation resulting in service degradation or outage.
     
     This issue has severity "moderate" according to the :ref:`Django security
     policy <security-disclosure>`.
    +
    +CVE-2026-1207: Potential SQL injection via raster lookups on PostGIS
    +====================================================================
    +
    +:ref:`Raster lookups <spatial-lookup-raster>` on GIS fields (only implemented
    +on PostGIS) were subject to SQL injection if untrusted data was used as a band
    +index.
    +
    +As a reminder, all untrusted user input should be validated before use.
    +
    +This issue has severity "high" according to the :ref:`Django security policy
    +<security-disclosure>`.
    
  • docs/releases/5.2.11.txt+12 0 modified
    @@ -29,3 +29,15 @@ produced super-linear computation resulting in service degradation or outage.
     
     This issue has severity "moderate" according to the :ref:`Django security
     policy <security-disclosure>`.
    +
    +CVE-2026-1207: Potential SQL injection via raster lookups on PostGIS
    +====================================================================
    +
    +:ref:`Raster lookups <spatial-lookup-raster>` on GIS fields (only implemented
    +on PostGIS) were subject to SQL injection if untrusted data was used as a band
    +index.
    +
    +As a reminder, all untrusted user input should be validated before use.
    +
    +This issue has severity "high" according to the :ref:`Django security policy
    +<security-disclosure>`.
    
  • docs/releases/6.0.2.txt+12 0 modified
    @@ -30,6 +30,18 @@ produced super-linear computation resulting in service degradation or outage.
     This issue has severity "moderate" according to the :ref:`Django security
     policy <security-disclosure>`.
     
    +CVE-2026-1207: Potential SQL injection via raster lookups on PostGIS
    +====================================================================
    +
    +:ref:`Raster lookups <spatial-lookup-raster>` on GIS fields (only implemented
    +on PostGIS) were subject to SQL injection if untrusted data was used as a band
    +index.
    +
    +As a reminder, all untrusted user input should be validated before use.
    +
    +This issue has severity "high" according to the :ref:`Django security policy
    +<security-disclosure>`.
    +
     Bugfixes
     ========
     
    
  • tests/gis_tests/rasterapp/test_rasterfield.py+46 1 modified
    @@ -2,7 +2,11 @@
     
     from django.contrib.gis.db.models.fields import BaseSpatialField
     from django.contrib.gis.db.models.functions import Distance
    -from django.contrib.gis.db.models.lookups import DistanceLookupBase, GISLookup
    +from django.contrib.gis.db.models.lookups import (
    +    DistanceLookupBase,
    +    GISLookup,
    +    RasterBandTransform,
    +)
     from django.contrib.gis.gdal import GDALRaster
     from django.contrib.gis.geos import GEOSGeometry
     from django.contrib.gis.measure import D
    @@ -356,6 +360,47 @@ def test_lookup_input_band_not_allowed(self):
             with self.assertRaisesMessage(ValueError, msg):
                 qs.count()
     
    +    def test_lookup_invalid_band_rhs(self):
    +        rast = GDALRaster(json.loads(JSON_RASTER))
    +        qs = RasterModel.objects.filter(rast__contains=(rast, "evil"))
    +        msg = "Band index must be an integer, but got 'str'."
    +        with self.assertRaisesMessage(TypeError, msg):
    +            qs.count()
    +
    +    def test_lookup_invalid_band_lhs(self):
    +        """
    +        Typical left-hand side usage is protected against non-integers, but for
    +        defense-in-depth purposes, construct custom lookups that evade the
    +        `int()` and `+ 1` checks in the lookups shipped by django.contrib.gis.
    +        """
    +
    +        # Evade the int() call in RasterField.get_transform().
    +        class MyRasterBandTransform(RasterBandTransform):
    +            band_index = "evil"
    +
    +            def process_band_indices(self, *args, **kwargs):
    +                self.band_lhs = self.lhs.band_index
    +                self.band_rhs, *self.rhs_params = self.rhs_params
    +
    +        # Evade the `+ 1` call in BaseSpatialField.process_band_indices().
    +        ContainsLookup = RasterModel._meta.get_field("rast").get_lookup("contains")
    +
    +        class MyContainsLookup(ContainsLookup):
    +            def process_band_indices(self, *args, **kwargs):
    +                self.band_lhs = self.lhs.band_index
    +                self.band_rhs, *self.rhs_params = self.rhs_params
    +
    +        RasterField = RasterModel._meta.get_field("rast")
    +        RasterField.register_lookup(MyContainsLookup, "contains")
    +        self.addCleanup(RasterField.register_lookup, ContainsLookup, "contains")
    +
    +        qs = RasterModel.objects.annotate(
    +            transformed=MyRasterBandTransform("rast")
    +        ).filter(transformed__contains=(F("transformed"), 1))
    +        msg = "Band index must be an integer, but got 'str'."
    +        with self.assertRaisesMessage(TypeError, msg):
    +            list(qs)
    +
         def test_isvalid_lookup_with_raster_error(self):
             qs = RasterModel.objects.filter(rast__isvalid=True)
             msg = (
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

8

News mentions

0

No linked articles in our index yet.