La viñeta que vive en otro sitio
En el Boletín Oficial de la Provincia de Granada, un fichero de JavaScript guarda una función que, según el comentario que lleva encima, prepara el sumario del boletín para convertirlo en PDF. A cada anuncio le pone una viñeta, y la dirección de esa viñeta es la ruta de una carpeta del disco del servidor. En los tres sumarios en PDF que hemos abierto, el último del 25 de septiembre de 2026, no hay ninguna.
- Dominio
- bop.dipgra.es
- Guardado
- 31 AGO 2026
- Citas
- 5 en 1 fichero guardado
- Lectura
- unos 4 min

El sumario ordena los anuncios por el organismo que los manda: la Diputación, cada ayuntamiento, otras entidades. El fichero tiene cinco funciones que hacen casi lo mismo: recorren los anuncios y los escriben uno detrás de otro, cada una en un formato. La primera escribe la lista de la web. Las otras cuatro llevan encima el mismo comentario con una palabra cambiada: «Este método es utilizado desde JAVA para generar» el HTML, el XML o el JSON del sumario, tres formas de escribir la misma lista para que la lea otro programa.
O sea que esas cuatro no las usa el navegador de quien entra en la web. Las llama el programa, escrito en Java, que corre en el servidor, el ordenador que entrega el boletín, para fabricar el sumario en otros formatos. La primera de las cuatro dice además para qué: el HTML «para el PDF del sumario del BOP». Es el sumario que la página del boletín ofrece para descargar, junto al rótulo Extracto, con un botón que dice PDF.

- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 17,9 kB
- sha256
- d36c297617ef…
| 88 | return 'Saludos desde la función test con texto: ' + txt; |
| 89 | } |
| 90 | |
| 91 | |
| 92 | /** |
| 93 | * Este método es utilizado desde JAVA para generar el HTML del sumario para el PDF del sumario del BOP |
| 94 | * @param elemento |
| 95 | * @param tieneHijos |
| 96 | * @param nivel |
| 97 | * @returns {string} |
| 98 | */ |
| 99 | function obtener |
| 100 | var htmlResultante = ''; |
| 101 | //console.log("Funcion obtener |
| 102 | // Comrobamos que en elemento tenemos un JSON |
| 103 | if (!elemento["entidad"]) { |
| 104 | elemento = JSON.parse(elemento); |
| 105 | } |
| 106 | var htmlResultante = ''; |
| 107 | |
| 108 | htmlResultante += '' + |
| 109 | '<div class="elemento_listado nivel_' + nivel + '' + (nivel == 4 ? " active" : "") + '">'; |
| 82 | // |
| 83 | return htmlResultante; |
| 84 | } |
| 85 | |
| 86 | // TEST |
| 87 | function test(txt) { |
| 88 | return 'Saludos desde la función test con texto: ' + txt; |
| 89 | } |
| 90 | |
| 91 | |
| 92 | /** |
| 93 | * Este método es utilizado desde JAVA para generar el HTML del sumario para el PDF del sumario del BOP |
| 94 | * @param elemento |
| 95 | * @param tieneHijos |
| 96 | * @param nivel |
| 97 | * @returns {string} |
| 98 | */ |
| 99 | function obtener |
| 100 | var htmlResultante = ''; |
| 101 | //console.log("Funcion obtener |
| 102 | // Comrobamos que en elemento tenemos un JSON |
| 103 | if (!elemento["entidad"]) { |
| 104 | elemento = JSON.parse(elemento); |
Esa función es la única de las cinco que pone una viñeta delante de cada anuncio: una imagen minúscula, de cinco píxeles por cinco, de esas que hacen que una lista parezca una lista. Lleva su ancho y su alto escritos a mano, y una dirección. Esa dirección empieza por una barra y sigue así: opt, tomcat, webapps, ROOT, WEB-INF y una carpeta más, con la imagen dentro; esa última parte la tapamos. Eso que parece una dirección de internet es un camino por dentro del disco del servidor.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 17,9 kB
- sha256
- d36c297617ef…
- Tapado
- 3 tramos ▒
| 133 | ' <div class="contenido">'; |
| 134 | |
| 135 | var anunciosElemento = elemento.anuncios; |
| 136 | if (anunciosElemento != undefined && anunciosElemento != null) { |
| 137 | htmlResultante += '' + |
| 138 | '<div class="listado_anuncios">'; |
| 139 | anuncios |
| 140 | htmlResultante += '' + |
| 141 | '<div class="elemento_listado">' + |
| 142 | ' <div class="etiqueta">' + |
| 143 | ' <div class="texto anuncio">' + |
| 144 | ' <span class="bullet"><img src="/ |
| 145 | ' ' + valor.titulo + |
| 146 | ' </a>' + |
| 147 | ' </div>' + |
| 148 | ' </div> ' + |
| 149 | '</div>'; |
| 150 | }); |
| 151 | htmlResultante += '' + |
| 152 | '</div>'; |
| 153 | } |
| 154 | |
| 155 | if (tieneHijos) { |
Traducido: /opt es donde se instalan los programas en un servidor Linux; tomcat es el programa que sirve las aplicaciones Java; webapps es donde Tomcat guarda las que atiende; y ROOT es la que contesta cuando no se pide ninguna en concreto. Es el camino que habría que recorrer por dentro del disco de la máquina donde vive el boletín para llegar a ese fichero de imagen.
Justo antes de la carpeta de las imágenes aparece WEB-INF. En el mundo Java es la carpeta privada de una aplicación: la norma que define cómo funcionan estos servidores dice que su contenido no se entrega nunca a quien lo pida por la web. Ahí van la configuración, las librerías y las clases compiladas. Un programa que corre dentro del propio servidor sí puede leerla; lo prohibido es servirla hacia fuera.
Así que la ruta tiene sentido para un programa que lee del disco de la máquina, y ninguno para uno que pide las imágenes por la web, como un navegador. Nosotros la pedimos por la web el 15 de septiembre de 2026, con una sola petición de lectura, y el servidor contestó que no existe. Cómo busca las imágenes el programa que fabrica el PDF no lo sabemos: no está en nada que hayamos guardado.
El resultado sí se puede mirar. El 23 y el 26 de septiembre de 2026 descargamos los sumarios en PDF de los boletines del 23 y del 25, los que enlaza la página de cada uno. Su lista tiene la forma que escribe la función, con un guion delante de cada organismo y sus anuncios debajo. Pero no hay ninguna viñeta: esos PDF no llevan más imágenes que los fondos de sus páginas. En el sumario del 10 de julio de 2024, uno de los boletines que guardamos, tampoco.
Las otras cuatro funciones escriben la misma lista sin viñeta, cada una a su manera, con sus bucles idénticos y sus mismas comprobaciones repetidas una detrás de otra. Son cinco copias del mismo recorrido en un solo fichero, y la viñeta, con su ruta de disco, está solo en una: la del PDF. Cuando el mismo código vive cinco veces, lo que se escribe en una copia no llega a las demás.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 17,9 kB
- sha256
- d36c297617ef…
| 265 | // |
| 266 | return htmlResultante; |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * Este método es utilizado desde JAVA para generar el XML del sumario del BOP |
| 271 | * @param elemento |
| 272 | * @param tieneHijos |
| 273 | * @param nivel |
| 274 | * @returns {string} |
| 275 | */ |
| 276 | function obtener |
| 277 | var xmlResultante = ''; |
| 278 | //console.log("Funcion obtener |
| 279 | // Comrobamos que en elemento tenemos un JSON |
| 280 | if (!elemento["entidad"]) { |
| 281 | elemento = JSON.parse(elemento); |
| 282 | } |
| 283 | |
| 284 | var anunciosElemento = elemento.anuncios; |
| 285 | var xmlResultante; |
| 286 | if (nivel === 1) { |
| 287 | xmlResultante += '<administracion nombre=\"' + elemento["entidad"] + '\">'; |
| 346 | // |
| 347 | return xmlResultante; |
| 348 | } |
| 349 | |
| 350 | /** |
| 351 | * Este método es utilizado desde JAVA para generar el JSON del sumario del BOP |
| 352 | * @param elemento |
| 353 | * @param tieneHijos |
| 354 | * @param nivel |
| 355 | * @returns {string} |
| 356 | */ |
| 357 | function obtener |
| 358 | var xmlResultante = ''; |
| 359 | //console.log("Funcion obtener |
| 360 | // Comrobamos que en elemento tenemos un JSON |
| 361 | if (!elemento["entidad"]) { |
| 362 | elemento = JSON.parse(elemento); |
| 363 | } |
| 364 | |
| 365 | var anunciosElemento = elemento.anuncios; |
| 366 | var xmlResultante; |
| 367 | if (nivel === 1) { |
| 368 | xmlResultante += '{ \"nombre\": \"' + elemento["entidad"] + '\",'; |
La viñeta tampoco lleva texto alternativo, la descripción que enseña el navegador cuando una imagen no carga y la que lee en voz alta un lector de pantalla. Aquí daría igual, porque es decorativa y no diría nada que no diga el título del anuncio que va al lado; es de los pocos sitios donde dejarlo vacío es lo correcto.
Y sigue igual. El fichero que pedimos el 15 de septiembre de 2026, y otra vez el 23 y el 26, tiene la misma huella que el que guardamos el 31 de agosto: los 17.888 bytes son idénticos, uno por uno. El servidor dice que lo escribió por última vez el 15 de septiembre, pero lo que escribió es lo mismo, con la viñeta y su ruta dentro.
La viñeta de cada anuncio tiene su dirección en el disco del servidor, en la carpeta que no se entrega a nadie. En los PDF que hemos abierto, ningún anuncio la lleva.
- En el fichero detalle_bop_html.js de bop.dipgra.es, guardado el 31 de agosto de 2026, la función
obtenerNivelListadoDetalleSumario, cuyo comentario dice «Este método es utilizado desde JAVA para generar el HTML del sumario para el PDF del sumario del BOP», escribe delante de cada anuncio una etiqueta de imagen cuya dirección es /opt/tomcat/webapps/ROOT/WEB-INF/▒ (lo que sigue lo tapamos), con ancho y alto de cinco píxeles y sin texto alternativo. - Es la única imagen que escribe ese fichero.
- Otras tres funciones llevan el mismo comentario para el HTML, el XML y el JSON del sumario, sin viñeta; la quinta escribe la lista de la web, también sin ella.
- El 15, el 23 y el 26 de septiembre de 2026 el mismo fichero se sirve con la misma huella sha256 y los mismos 17.888 bytes; el 15, una petición de lectura a esa ruta como dirección web contestó que no existe.
- Los sumarios en PDF de los boletines del 23 y del 25 de septiembre de 2026, descargados el 23 y el 26 desde la página de cada boletín, que carga el fichero, no contienen más imágenes que los fondos de sus páginas; el del 10 de julio de 2024, tampoco.
- El fichero lo cargan 41 páginas del boletín en nuestro archivo.
- No decimos que esa carpeta se pueda leer desde fuera: no existe como dirección web y no hemos intentado nada más que pedirla una vez.
- No decimos que el sumario esté roto: se ve en la web, se descarga en PDF y sus anuncios están.
- No decimos por qué no sale la viñeta en el PDF: no sabemos cómo busca las imágenes el programa del servidor, ni si ese PDF lo fabrica esta función u otra parecida; decimos lo que dice su comentario y lo que lleva el PDF.
- Y no decimos desde cuándo está así ni si la viñeta llegó a verse alguna vez, porque no consta en nada que tengamos.
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.
- https://bop.dipgra.es/export/system/modules/com.saga.inpro.theme.bop/resources/js/detalle_bop_html.js31 de agosto de 2026, 16:32archivosha256 d36c297617efa3987085a59bdf1cf076c6e04e70a0547dc4fd88103688b15c66