Revision message is rendered unescaped into the migration docstring; triple quotes and backslashes break the generated script

Abierto Apto para principiantes
#1,879 3 comentarios 0 reacciones 0 asignados Ver en GitHub

@ApparentlyTejas ya está trabajando en esto.

Desde el 23/10/2026.

  • #1880 de @Eryx-Z — cerrado sin fusionar
  • #1881 de @Eryx-Z — cerrado sin fusionar

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
70/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python, sql
Área
tooling

Línea de trabajo

Empieza en alembic/script/base.py alrededor de la línea 743, donde generate_revision() pasa message a las plantillas script.py.mako, y revisa la línea 1 de esas plantillas para ver el docstring. Reproduce el problema con alembic revision -m usando un mensaje que contenga comillas triples o barras invertidas. Está terminado cuando el mensaje se escapa antes de renderizarse y las pruebas de ida y vuelta de RevisionCommandTest para script.doc y script.longdoc pasan junto con las pruebas existentes.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

bug command interface

Description

ScriptDirectory.generate_revision() passes the message argument directly into script.py.mako, where all built-in revision templates render it inside a triple-quoted docstring (line 1). This affects the generic, async, multidb, pyproject, and pyproject_async templates (alembic/script/base.py:743).

Because the message is not escaped before rendering, two issues arise:

  1. Docstring termination: A message containing """ prematurely closes the docstring, allowing the remainder of the message to be emitted as module-level Python code. When the generated revision is subsequently imported, this code executes.
  2. Backslash interpretation: Backslashes are interpreted as Python escape sequences. For example, \n becomes a newline, invalid escape sequences such as \p can trigger SyntaxWarning on Python 3.12+, and a trailing backslash can escape the newline following the message. Consequently, script.doc may not preserve the original message.

This was initially reported to Tidelift. The maintainers confirmed the behaviour but classified it as a developer-tool input issue rather than a security-boundary vulnerability, suggesting it be reported publicly as a defense-in-depth bug. I agree with that assessment.

Steps to Reproduce

  1. Initialise an Alembic environment:

    alembic init migrations
    
  2. Create a revision with a message that terminates the docstring:

    alembic revision -m 'add users"""
    print("this line is not part of the docstring")
    __doc__ = """'
    
  3. Run:

    alembic history
    

The generated revision is imported, and the injected print() statement executes.

The same behaviour can be triggered through command.revision(cfg, message=...).

Backslash handling can be reproduced with:

alembic revision -m 'C:\path\new'

The generated file may emit a SyntaxWarning for invalid escape sequences on import, and the revision's script.doc value does not round-trip to the original message.

Expected Behaviour

The generated revision should preserve the original message in script.doc and script.longdoc, without allowing the message to terminate the docstring or introduce executable Python code.

Affected Versions and Environment

  • Alembic: 1.20.1 (main at 1bc21b8)
  • Python: 3.12.13

Proposed Fix

Escape the message in generate_revision() before passing it to the template. This centralises the handling and covers both built-in templates and custom script.py.mako templates copied into user projects.

For example:

def _escape_docstring_message(message: str) -> str:
    return message.replace("\\", "\\\\").replace('"""', '\\"\\"\\"')

Apply this helper at alembic/script/base.py:743, before rendering the template.

I verified this approach locally. Two new tests in RevisionCommandTest, covering triple quotes and backslash round-tripping through script.doc and script.longdoc, pass alongside the existing 11 tests in that class.

Lenguaje dominante
Python
Estrellas
4.5k
Forks
391
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de sqlalchemy/alembic

Todos los issues de sqlalchemy/alembic

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.