Pieza 19 de 64
TrámitePieza nº 0019 de 64

Se sustituye eval por motivos de seguridad

La sede de la Agencia Tributaria tiene un buscador de epígrafes del IAE que un trámite puede abrir en una ventana aparte, para que se elija uno y se le devuelva. Para devolverlo, la ventana está programada para escribir una línea de programa como texto y ejecutarla. Encima lo explica un comentario: se sustituye eval por new Function por motivos de seguridad. 24 líneas más abajo, new Function está desactivado y el texto se ejecuta por un tercer camino.

Dominio
sede.agenciatributaria.gob.es
Guardado
31 AGO 2026
Comprobado en vivo
15 SEP 2026
Citas
6 en 1 fichero guardado
Lectura
unos 4 min
Captura de https://sede.agenciatributaria.gob.es/
foto 1La portada de sede.agenciatributaria.gob.es el 23 de septiembre de 2026: la sede electrónica de la Agencia Tributaria.Captura del 23 de septiembre de 2026, 10:16 (hora peninsular)

La operación tiene su intríngulis. Cuando un trámite abre el buscador, la ventana del buscador y la del trámite son dos páginas distintas, y para hablarse usan el único hilo que las une: la ventana hija sabe quién la abrió y puede llamar a una función de la madre. Qué función hay que llamar no lo sabe el buscador de antemano, porque depende del trámite, así que el nombre le llega escrito en la dirección, en un parámetro, uno de los datos que viajan al final de ella, llamado callback.

Captura de https://sede.agenciatributaria.gob.es/Sede/buscador-iae.html
foto 2El buscador de actividades y epígrafes del IAE en la sede de la Agencia Tributaria, el 23 de septiembre de 2026, abierto como página suelta, sin escribir ni pulsar nada. Cuando lo abre un trámite en una ventana aparte, las tarjetas de resultados llevan además un botón para devolver el epígrafe elegido: de ese encargo se ocupa la función de esta pieza.Captura del 23 de septiembre de 2026, 14:17 (hora peninsular) · el recuadro es nuestro

Con ese nombre y con los datos del epígrafe elegido hay que montar la llamada. Y aquí aparece el comentario, justo debajo de otro que explica el propósito de la función. Dice, con todas las letras, que se sustituye eval por new Function por motivos de seguridad, y añade entre paréntesis la razón técnica: los privilegios de eval. Es un comentario bien escrito, que dice qué se cambió y por qué, en una línea.

1de 6sede.agenciatributaria.gob.es/static_files/common/script/buscadorIAE-cards.jslínea 2093 de 2.807
Copia
archivo
Guardado
31 ago 2026
Peso
103,8 kB
sha256
67a5e0ae7cd7…
2090try{
2091    if(callbk != null){
2092        // Si viene de aplicación se monta el paso de variables según los parámetros solicitados
2093        // Se sustituye eval por new Function por motivos de seguridad (tiene que ver con los privilegios de eval)
2094        let code = ``;
2095        const dtipo = node.attr(`data-tipo`);
2096        const dseccion = node.attr(`data-seccion`);
1La función entera empieza documentada como manda el oficio. La última línea de la vitrina es el comentario que da título a la pieza.

Traducido: eval es la orden que coge un texto y lo ejecuta como si fuera programa, y lo hace ahí mismo, dentro de la función que la llama, con acceso a todas sus variables, los datos con los que esa función está trabajando. new Function hace algo parecido desde fuera: fabrica una función con ese texto y la ejecuta sin ver las variables de quien la llama, solo las que son de toda la página. Esos son los privilegios de eval que se querían quitar. El diagnóstico es correcto, y el cambio, razonable.

El texto que se va a ejecutar se arma pegando cachos: la llamada a la función de la ventana madre, y entre comillas el tipo, la sección y el número de epígrafe, que el programa lee de los atributos de la tarjeta que el usuario acaba de pulsar. Si hay más parámetros, se añaden en un bucle, con sus comillas. Al final, un paréntesis y un punto y coma: una línea de programa escrita en el momento. El nombre que llega en la dirección no se pega en el texto: el texto dice callbk, y el nombre se busca al ejecutarlo.

2de 6sede.agenciatributaria.gob.es/static_files/common/script/buscadorIAE-cards.jslínea 2102 de 2.807
Copia
archivo
Guardado
31 ago 2026
Peso
103,8 kB
sha256
67a5e0ae7cd7…
2099let objSelection = {};
2100objSelection[`tipo`] = dtipo;
2101if(node.attr(`data-epigrafe`)){
2102    code = `window.opener[callbk]("${dtipo}", "${dseccion}", "${epigrafe}"`;
2103    objSelection[`seccion`] = dseccion;
2104    objSelection[`epigrafe`] = epigrafe;
2105}else{
2La llamada, montada como texto con los datos de la tarjeta pulsada. El nombre de la función no está: el texto dice callbk. El paréntesis se cierra catorce líneas más abajo, en la vitrina siguiente, tras añadir los parámetros extra.

Y llega el momento de ejecutarla. Las dos líneas que hacen lo que anuncia el comentario, crear la función con new Function y llamarla, están ahí, una debajo de otra, desactivadas con dos barras delante, que convierten una línea de programa en un comentario. No se ejecutan. Debajo hay cuatro líneas nuevas: se crea un elemento script, la etiqueta con la que una página lleva su programa, se le mete dentro el texto, se cuelga de la cabecera del documento y se quita acto seguido, como quien entra a dejar un recado y sale sin sentarse.

3de 6sede.agenciatributaria.gob.es/static_files/common/script/buscadorIAE-cards.jslínea 2117 de 2.807
Copia
archivo
Guardado
31 ago 2026
Peso
103,8 kB
sha256
67a5e0ae7cd7…
2114    }
2115}
2116code += `${dparams});`;
2117//const _call = new Function(code);
2118//_call();
2119const script = document.createElement('script');
2120script.textContent = code;
3El cambio anunciado, desactivado con dos barras, y debajo lo que se ejecuta de verdad: un script que se cuelga del documento y se retira.

Colgar un script del documento ejecuta su contenido: es lo que hace el navegador con cada script de cada página. El texto corre, pues, como correría con eval o con new Function. Y corre desde fuera de la función, viendo solo las variables de toda la página, igual que con new Function. En lo que explica el comentario, los privilegios de eval, el tercer camino está del mismo lado que el segundo. Lo que ya no cuadra es el comentario, que anuncia un new Function que no se ejecuta. Por qué se cambió otra vez, el fichero no lo dice.

Los tres caminos comparten una cosa: la llamada se escribe primero como texto y después se ejecuta. Una vez ejecutada, esa línea hace una sola cosa, llamar a la función de la ventana madre con el tipo, la sección, el epígrafe y los parámetros. JavaScript, el lenguaje de los programas que corren en el navegador, permite también hacer esa llamada directamente, con los valores, sin texto de por medio. El comentario habla de cómo ejecutar el texto; de escribirlo no dice nada.

Falta saber de dónde salen los valores y cuándo se llega aquí. El tipo, la sección, el epígrafe y los parámetros se leen de la tarjeta que pinta el propio buscador con los datos de su búsqueda. El nombre de la función llega en la dirección. Y el botón que pone todo esto en marcha solo se pinta en las tarjetas con cuatro condiciones. La dirección tiene que traer ese nombre y no traer el identificador de convenio, que es la marca del asistente. El navegador tiene que decir de qué página se viene. Y la ventana madre tiene que tener de verdad una función con ese nombre.

4de 6sede.agenciatributaria.gob.es/static_files/common/script/buscadorIAE-cards.jslínea 2489 de 2.807
Copia
archivo
Guardado
31 ago 2026
Peso
103,8 kB
sha256
67a5e0ae7cd7…
2486let fromAssistant = false;
2487let fromApp = false;
2488const pepigrafe = searchParams.pepigrafe;
2489const callbk = searchParams.callback;
2490if(callbk != null && (idConv != null && idConv != ``) && document.referrer != null && document.referrer != ``){
2491    fromAssistant = true;
2492}else if(callbk != null && (idConv == null || (idConv !== null && idConv == ``)) && document.referrer != null && document.referrer != ``){
4Cuatrocientas líneas más abajo, el nombre de la función, leído de la dirección. Debajo, dos casos: con identificador de convenio se viene del asistente; sin él, y con página de origen, se viene de un trámite, y solo entonces se puede devolver el epígrafe.
6de 6sede.agenciatributaria.gob.es/static_files/common/script/buscadorIAE-cards.jslínea 1089 de 2.807sigue en el mismo fichero · la línea de al lado
1086</li>`;
1087
1088let btnSelection = ``;
1089if(callbk != null && fromApp && window.opener != null && window.opener[callbk] != null){
1090    // Si hay callbk, existe la funcion de callbk y no viene del asistente... hay que poner un botón en la card al que hay que agregar los atributos con la información necesaria para su uso al hacer click
1091    const dataSeccion = _isIAE ? ` data-seccion="${_source[`seccion`]}"` : ``;
1092    let epiId = _source[`epigrafe`];
6Donde se decide si la tarjeta lleva el botón que devuelve el epígrafe: hace falta el nombre, venir de un trámite y que la ventana madre tenga esa función. Los valores de la tarjeta salen de la búsqueda.

Todo el bloque, además, va envuelto en un try, una red que recoge los errores: si algo falla, lo escribe en la consola del navegador con el nombre del fichero delante, y la ventana se queda abierta. Si todo va bien, después de la llamada se mandan los datos de la elección para las estadísticas del buscador y la ventana se cierra sola, con el epígrafe ya entregado al trámite que la abrió.

5de 6sede.agenciatributaria.gob.es/static_files/common/script/buscadorIAE-cards.jslínea 2127 de 2.807
Copia
archivo
Guardado
31 ago 2026
Peso
103,8 kB
sha256
67a5e0ae7cd7…
2124            window.close();
2125        }
2126    }catch(e){
2127        console.log(`buscadorIAE-cards.js --> closePopup ERROR: ${e.message}`);
2128    }
2129}
5El final: se cierra la ventana y, si algo ha fallado, se cuenta en la consola del navegador.

El comentario sigue explicando por qué se cambió eval por new Function. 24 líneas más abajo, new Function también está cambiado, y de eso no hay comentario.

Lo que decimos
  • En el buscador de epígrafes del IAE de sede.agenciatributaria.gob.es (fichero buscadorIAE-cards.js guardado el 31 de agosto de 2026 e idéntico el 15 de septiembre), la función closePopup lleva en su línea 2093 el comentario «Se sustituye eval por new Function por motivos de seguridad (tiene que ver con los privilegios de eval)».
  • Las dos líneas que usarían new Function, 24 más abajo, están comentadas.
  • Lo que se ejecuta en su lugar es un elemento script creado al vuelo con ese mismo texto, añadido a la cabecera del documento y retirado después; un script así se ejecuta en el ámbito global de la página, como una función creada con new Function, y no en el de la función que lo crea, como eval.
  • El texto se arma con los atributos data de la tarjeta pulsada; el nombre de la función se lee del parámetro callback de la dirección y se usa como window.opener[callbk].
  • El botón que llama a closePopup solo se pinta si hay callback, si no hay identificador de convenio, si hay página de origen y si la ventana madre tiene esa función.
  • El 23 de septiembre la sede sirve una versión del fichero con cambios en otras partes y estas líneas iguales.
Lo que no decimos
  • No decimos que esto se pueda aprovechar: los valores que se pegan dentro del texto salen de las tarjetas que pinta el propio buscador con los datos de su búsqueda, y el botón que lo pone en marcha solo aparece si la ventana que abrió el buscador tiene una función con el nombre que llega en la dirección.
  • No decimos que el script sea peor que new Function en lo que explica el comentario: en eso son iguales.
  • No decimos por qué se desactivó new Function: el fichero no lo explica.
  • No lo hemos probado contra el servidor ni vamos a hacerlo: descargamos el fichero, que es público, y lo leímos.
  • Y no decimos que el buscador funcione mal.
Procedencia

De dónde sale cada cita

Cada trozo de código de esta ficha está en un fichero guardado tal como lo entregó el servidor, con su huella (sha256) y su hora. Las citas entre «» se comprueban por programa contra ese fichero antes de publicar.

Desgobierno Digital · https://desgobiernodigital.com/piezas/aeat-eval-sustituido/ · copia impresa de la edición generada el 19 de octubre de 2026 · Un proyecto de BQuant · correcciones: @Gsnchez