Es la transparencia de Schrödinger: transparente y opaco a la vez. 🤷
El art. 344 de la Ley de Contratos del Sector Público (LCSP) establece que el ROLECE «será público y se podrá acceder de forma abierta previa identificación». Y que un reglamento «determinará modalidades y requisitos para la publicidad del Registro».
Pero no hay nada. 💩
✨ El Ministerio de Hacienda tiene este servicio para la consulta de expedientes de clasificación de contratistas. ¡Parece que toma sus datos del ROLECE!
🚫 Problema: tampoco permite la descarga completa del registro.
El buscador de Hacienda tiene seis campos. Solo uno es obligatorio: el nombre del contratista. Pero yo no quiero acceder al expediente de un contratista: ¡quiero acceder A TODOS!
Para cruzarlos informáticamente con la PLCSP y dar más transparencia a la contratación pública.
El buscador me obliga a consignar un mínimo de cuatro letras del nombre del contratista. ¿Por qué hacen esto? ¡Qué ganas de entorpecer el acceso a los datos públicos!
Puedo buscar «ZARA» o «IKEA». O Puedo teclear «FERRE» y encontrar todas las ferreterías.
Podría descargar todo el listado por fuerza bruta, automatizando búsquedas iterativas:
AAAA
AAAB
AAAC
…
ZZZX
ZZZY
ZZZZ
Pero sería una cochinada ineficiente:
26×26×26×26 = 456.976 búsquedas
Tiene que haber otra manera.
La validación de los datos de entrada de un formulario web puede hacerse en dos puntos del ciclo petición-respuesta:
1️⃣ En el cliente (el navegador del usuario); o,
2️⃣ En el servidor (la aplicación de Hacienda).
Siempre se recomienda validar los datos al menos en el servidor.
Observo que el buscador de Hacienda valida los datos en el cliente. Mi única esperanza es que no estén haciendo validación también en el servidor.
Juego todo a esta carta y comienzo a inspeccionar el campo de entrada que me interesa: el nombre del contratista.
Tiene asociados dos manejadores de eventos:
1️⃣ Uno al evento «change», que se dispara cuando el campo cambia.
2️⃣ Otro al evento «keypress», que se dispara cuando el usuario presiona una tecla con el foco en el campo.
Me interesa el evento «change», que se dispara después de «keypress».
Invoca una función ValidatorOnChange()
Exploro la función, voy trazando el código…
…y llego a una segunda función: ValidatorUpdateIsValid()
Que parece devolver un valor booleano:
— «verdadero» si todos los campos del formulario son válidos.
— «falso» si alguno de los seis campos tiene errores.
Quiero modificar esta función. Pero si está encapsulada en un módulo, una clase o un objeto, será complicado hacer nada con ella…
Afortunadamente para mí, está definida en el «scope» global 🎉. ¡Esto significa que puedo redefinirla desde el confort de mi navegador! 😜
Vamos a ver si hay suerte…
Asigno a AllValidatorsValid una nueva función que toma la misma entrada pero que siempre devuelve «verdadero».
Y en el campo del formulario consigno un valor ilegal: «A».
¿Colará?
…Y CUELAAAAAAaaaa!!!!!!111unouno
🎉 Con esto puedo sortear la validación del formulario. YEAH!
Ahora tengo que ver cómo iterar por las 26 letras del abecedario y bajarme todas las páginas del tirón. 🤓
Observo las peticiones al servidor y encuentro una llamada GET por XHR (Ajax).…
…a la que el servidor responde un JSON con los datos de mi interés.
✅ Son datos públicos.
✅ De un registro público.
✅ Que sirve Hacienda en internet.
Esto no es ningún crimen. ¡Es liberar datos públicos para que haya más transparencia en la contratación pública!
Replico la petición desde la línea de comandos, con cURL. Encuentro los parámetros que controlan el número de resultados por respuesta y la paginación de los resultados.
Con la letra «A» hay 9.953 registros, lo que son exactamente 100 páginas de 100 elementos cada una.
Hay mil formas de hacer esto, pero opto por seguir en la terminal y usar «seq»:
$ seq 0 100 10000
Genero así los valores del parámetro «iDisplayStart», que controla dónde comienza cada página.
Un poco de bricolaje y ya tengo un cerdoscript… 🐷
Curiosamente el dato más importante, el término de búsqueda, no viaja en ningún parámetro de las peticiones XHR. Parece que el servidor lo almacena en una variable de sesión y lo recupera de una cookie.
Es kafkiano, pero me da igual porque en el script paso la cookie… 🤷
Vuelvo al campo de búsqueda.
Puedo iterar 27 veces con todas las letras del abecedario. Y, para cada una de ellas, iterar a su vez por el número de páginas de resultados.
Pero no quiero iterar tanto.
¿Qué puedo hacer?
Tengo que darle al servidor al menos un caracter que me devuelva todos los registros. Pero, ¿cuál?
Pruebo con el espacio en blanco. ¿Funcionará?
¡No funciona!
La aplicación debe de estar haciendo un trim() de la cadena de texto que yo le envío, para eliminar los espacios en blanco antes de determinar si el valor está vacío.
Pero, ¿y si les envío algo que parezca un espacio pero no lo sea?
Esto es el caracter Unicode U+2800: un espacio vacío en Braille. Al ojo humano es indistinguible de un espacio tecleado con la barra espaciadora. Pero quizá trim() sí lo distinga…
Añado ocho filas más al cerdoscript y me reclino satisfecho en el estrado imaginario de mi putoamismo mientras descargan a mi portatil todos los registros.
¡Ahora tengo 10.747 expedientes más para cruzar con los datos de contratación! 🥳
He estudiado más de cien «amazon locales» puestos en marcha con dinero público. He intentado visibilizar las conclusiones en artículos como este. Sin mucho éxito, parece. 👇
👆 Mi artículo en el enlace del tuit de arriba contiene muchos enlaces a los amenos hilos de Twitter donde he ido destripando cada problema y proponiendo soluciones.
👇 Aquí en castellano, también con abundantes enlaces a mis investigaciones:
📈 Para mí es importante que nadie tenga que confiar en «mis» datos. ¡No son míos! Yo los extraigo de fuentes oficiales. Con sacacorchos, porque lo ponen difícil. Por eso acabo de añadir a mi herramienta algo imprescindible: el enlace de cada dato en las memorias del Gobierno. 😊
Creo que esto servirá para que mucha gente pueda buscar en mi herramienta fácilmente (¡y de forma divertida!) los datos de su interés. Y luego ir a tiro hecho a la infumable «fuente primaria» del Gobierno (PDF de más de 200 páginas) para confirmar la veracidad de los datos.
Creo que esto será útil…
✅ A la ciudadanía, para entender mejor su país.
✅ A los parlamentarios, para controlar mejor al Gobierno y detectar chanchullos.
✅ A los periodistas, para vigilar el reparto equitativo del dinero público.
🔴 La Ley 6/2010 de Publicidad y Comunicación de #Euskadi indica que el Gobierno Vasco tiene que remitir anualmente al Parlamento una memoria con el reparto del dinero público a los medios de comunicación.
Pero esas memorias tienen errores.
Por ejemplo: 👇
El Gobierno entrega los datos en un PDF que impide:
👉 A los diputados, controlar eficazmente el gasto del Gobierno.
👉 A la ciudadanía, entender cómo se reparte el dinero público.
👉 A los periodistas, comprobar si se cumplen las leyes.
¡Esto no puede ser!
📰 La última de estas memorias, del año 2022, acaba de ser publicada este mismo viernes.
Alguien en el Gobierno Vasco las escribe con Microsoft Word partiendo, parece, de una plantilla de 2017.
— Esto es sumamente ineficiente.
— Y produce un PDF que no da #transparencia real.
No tengo claro que ser la ciudad más subsidiada de España sea motivo de celebración. Quiero pensar que la métrica del éxito es otra. Pero he estudiado cien iniciativas similares a la suya y encuentro un denominador común: fracasan.
Este triunfal anuncio es compatible con un cuadro agudo de subvencionitis. Una pandemia que está causando estragos en alcaldes y concejales de toda bandera. El delirio: intentar salvar el comercio local con un «Amazon» municipal.
El virus lo inocula el Estado canalizando una línea de subvenciones con fondos europeos. Las entidades locales concurren porque hay dinero de Europa. Es la solución a la búsqueda de un problema. Y a alguien se le enciende la bombilla: un «marketplace». 🎉