March 19, 2022

Pretty and XSS-safe JSON rendering in the Django admin

A plethora of modules is available out there aiming to pretty-format a read-only JSON field in the Django admin. But do you really need to carry the burden of the extra features coming with such modules?

No, you don’t.

The original solution, whose only requirement is the well-established pygments module, is still worth checking out.

When integrating third-party code, a good practice is to run a security tool like Bandit to look for any security issue.

$ python3 -m bandit -r .
>> Issue: [B703:django_mark_safe] Potential XSS on mark_safe function.
   Severity: Medium   Confidence: High
   Location: ...
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b703_django_mark_safe.html
   18                  return mark_safe(style + response)

Uh-oh... we're notified of a potential XSS hole!

A look at the relevant bit on the Django documentation reveals that using mark_safe() was never officially encouraged in the first place.

Luckily, we can do without invoking mark_safe entirely and just pass the highlighted placeholder as the first argument to format_html(). The latter intelligently takes care of unescaping the passed HTML string while handling the JSON data parameter securely.

The updated working code in its simplest form is the following:

import json

from django.contrib import admin
from django.utils.html import format_html
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import JsonLexer
from myapp.models import MyModel

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    """My model admin."""

    readonly_fields = ("get_my_json_field",)

    def get_my_json_field(self, obj):
        """Return a formatted version of my JSON field."""
        return format_html(
            highlight("{}", JsonLexer(), HtmlFormatter()),
            json.dumps(obj.json_field, sort_keys=True, indent=2),
        )

Do have a look at the original post, lest you skip on the CSS-related bells and whistles.

Copyright © 2024 Niccolò Mineo
Some rights reserved: CC BY-NC 4.0