HackIt’2022, Level1, EE30

Cada vez cuesta más escribir un post. Es más sencillo crear tuits. Hay menos fricción. Un tuit es algo rápido, algo donde no necesitas pensar y reescribir mucho. Sin embargo, creo que el arte de escribir posts en un blog nos hace mejores. Espero que, al menos una vez al año, vuelva esta sensaci´ón de estar haciendo lo que tengo que hacer.

Blast from the past. El primer nivel del HackIt 2022.

Julio de 2022. Después de dos años parados, volvemos al HackIt!. Retomamos buenas sensaciones. Salimos con nuevas ideas, conocimientos, propósitos y sentimientos. El esfuerzo dedicado al HackIt siempre, siempre, merece la pena. Por supuesto, hay cosas que mejorar. Lo primero, el nivel de las pruebas. Seguramente los autores dirán que no. Que somos unos failers (cierto), que no era para tanto (cierto). Pero de lo que no nos podemos esconder es de los datos. Que nuestro grupo haya quedado tercero en la clasificación del HackIt con solo una prueba superada es algo que llama la atención. Sólo dos equipos, wopr y NavarParty han superado más de dos pruebas (tres en total, y wopr sólo tenía dos pruebas superadas a pocos minutos del final). ¿Es responsabilidad única de los organizadores? No, los organizadores bastante hacen manteniendo el concurso durante años y generando pruebas sin ayuda durante muchos HackIts. Pero hay que darle una vuelta y echar un cable para la siguiente edición. Me la apunto como tarea. Espero que dentro de un año pueda enlazar este post y comentar las mejoras sugeridas y realizadas.

Al lío, level 1 (he hecho una copia de los levels en ee30.ikasten.io, pues mi petición anual de que marcan o imobilis los suban ha sido llevada a /dev/null sin piedad)

A simple vista nos encontramos con el típico nivel 1. Código en JavaScript que… un momento, no es JS, sino VBScript… vaya. Código en VBScript que cifra una clave y que basta con entender un poco el algoritmo para descifrarla (no). En el peor de los casos, para un nivel 1, con fuerza bruta seguro que se saca (no). Un nivel 1 que sólo se pasaron unos 13 equipos (seguramente alguno más, porque hice la copia de la web un par de horas antes del cierre) y que durante las primeras horas sólo 4 grupos lo consiguieron ¿Por qué? Porque hay que entender VBScript (fácil) y porque hay que invertir el algoritmo que calcula la clave. ¿Inversión de algoritmos con aritmética modular para un nivel 1? Pues sí. ¿Es fácil de resolver? Sí ¿Es adecuado para un level 1? No lo creo.

Lo primero que hicimos fue ejecutar el script original en algo que sepa interpretar VBScript. Una buena alternativa es hacerlo con el editor de macros de LibreOffice.

LibreOffice dispone de un buen IDE para editar macros en VBScript. En la imagen podemos ver que hemos puesto un punto de ruptura en la línea 37 para evaluar el valor del array «a» (en la caja de watches).

Para no volvernos locos con un lenguaje arcaico o trabajar con un IDE más user-friendly, también podemos convertir el script de VB a JS. Hay que tener cuidado con varias cosas: 1) al acabar la conversión ejecutar el script original con una valor cualquiera de password y la conversión con el mismo valor. Así veremos que no la hemos liado. 2) Los índices de los bucles for del script en VB parecen dar a entender que VB indexa los arrays desde la posición 1. FALSO. Indexa desde la posición 0, al igual que en JS. 3) Ojito con el index j si decidimos indexar desde 0 los arrays (véase el código JS adjunto). Nos podría quedar una solución como la siguiente:

let init = "pruebaprueba14";
let val = [...init]

    let a=[];
    let tmp;

    for (let i = 0; i <= 13; i++){ 

        a[i] = val[i].charCodeAt(0);
    }

    for (let j = 1; j <= 42 ; j++){
      for (let i = 0; i <= 13; i++) {
        a[i] = ( a[i] + j * a[ (i + 1) % 14 ] ) % 256
      }

        tmp = a[1]
        a[1] = a[2]
        a[2] = tmp

        a[3] = (a[3] * 49) % 256
        a[4] = (a[4] * ((j * 2) + 1)) % 256
        a[5] = 255 - a[5]

        tmp = a[0]
        for (let i = 0; i<= 12; i++){
            a[i] = a[i + 1]
        }
        a[13] = tmp

         console.log(j, a)
    }

    s = a.toString() + ","

    console.log(s)
    if (s != "101,107,164,102,76,232,0,57,122,139,112,36,17,205,"){
        console.log( "Wrong!" )
    }else{
        console.log("Nice!");
    }

Tanto si lo queremos hacer con el código VB o con JS, ahora viene lo bueno. Para solucionar este level hay que revertir el algoritmo. Es decir, partiendo de la solución que se busca ("101,107,164,102,76,...") ir ejecutando hacia atrás. Bueno, al principio parece fácil. Por ejemplo, si el último bucle rota los elementos del array hacia la izquierda:

Lo que tendremos que hacer al invertir es rotar hacia la derecha:

Sigamos con la inversión. Tenemos el siguiente trozo:

Las dos últimas líneas (recuerda, estamos invirtiendo, hay que empezar desde el final hacia el principio) son fáciles:

Pero con la línea del módulo 256 empieza a complicarse el tema. Nosotros partimos de un a(5) conocido y tenemos que saber como invertir la operación para descubir cuál era el valor anterior de a(5). Es decir, buscamos un número x tal que x * ((j * 2) + 1)) Mod 256 = a(5). El valor de la variable j es conocido (el valor del índice j es conocido en cada vuelta del for). El problema es que la ecuación x mod 256 = y tiene múltiples soluciones para x. Por ejemplo, si y=3, entonces una posible solución es x= 3, pero otra es x=256+3. En general x=256*n+3 es una solución (con n = cualquier entero positivo)

Pero sabemos que en cada paso, el valor de a(5) no pasará de 256, así que bastaría con buscar el valor de x iterando por todos los posibles valores:

Aplicamos la misma lógica al resto del programa y encontramos la ansiada clave original.

Agradecer como siempre a @imobilis y a @marcan42 todo el curro realizado y por seguir al pie del cañón a pesar de nuestros rants :)

Saludos a NavarParty, w0pr, Sauronealo e insomnia, por haber conseguido subirse al podium en el HackIt/SolveIt. Y un gran hats-off a NavarParty, por haber conseguido destronar a w0pr después de ♾️ años.

Y cañas para mis compañeros Kotxerra, Ochoto y Cuartango, que un año más me han vuelto a enseñar cómo jugar un buen HackIt y saborearlo con unas buenas cervezas :)

Continuará...

Cómo borrar repositorios GitHub de forma masiva

Problema: tengo más de 100 repositorios que quiero borrar en GitHub. Están creados bajo la organización X. Borrar uno por uno es un infierno: Settings / Danger Zone / Erase / Confirmar borrado tecleando el nombre del repo. Necesito algo más rápido. gh-cli, por ejemplo.

gh cli : GitHub’s official command line tool
https://github.com/cli/cli
Pero no tiene soporte directo para borrar repositorios y menos en batch. It’s not a bug, it’s a feature:

https://github.com/cli/cli/issues/2461

Pero… leyendo los comentarios de varios usuarios, sí que se puede «programar» usando el comando `gh api`

Puedes ver un listado de todos los proyectos (sí, la org X en la que quiero hacer limpieza de repos viejos es mi universidad)

$ gh api -X GET ‘orgs/UPV-EHU-Bilbao/repos’ -F per_page=100 –paginate –cache 1h | jq ‘.[]|.full_name’

Preparar el alias de borrado:
$ gh alias set repo-delete ‘api -X DELETE «repos/$1″‘
e intentar la eliminación:
$ gh repo-delete UPV-EHU-Bilbao/codespacephp
… para llegar a este error de permisos:

Es necesario actualizar el token para añadirle permiso de borrado de repos. Basta con este comando:
$ gh auth refresh -h http://github.com -s delete_repo

Y ahora sí, el alias funcionará.

$ gh repo-delete UPV-EHU-Bilbao/codespacephp
$

Ojo, no pide confirmación, ejecuta la orden de borrado inmediatamente. Así que si lo metes en un bucle para iterar sobre todos los repos, revisa varias veces el script antes de cargarte todo 🙂

Originally tweeted by juanan (@juanan) on 30 August, 2021.

Dynamic updates in Overleaf

How do you keep updated in your LaTeX document a number that changes every often in its original source?…

I was writing a document in @overleaf that included some numbers extracted from an online spreadsheet that was updated regularly. 🧵

I needed to keep my LaTeX document in sync with those shared spreadsheet values. But AFAIK there wasn’t any option available neither in Overleaf nor in any other LaTeX package for implementing «dynamic update» behavior. So I created one 🙂

First, we need to publish a link to the source spreadsheet so we can access the value of any cell through an endpoint like:
(Share it at least for read access. Then, publish it: File / Publish to the web)

https://spreadsheets.google.com/feeds/cells/1xhWskXv1qCDIHSAxSzEaQBeFKP1vfuR5ZLdx7nsgBg4/default/public/full/R3C3?alt=json

(R3C3 = Row 3, Column 3 = Row 3, Column C)

If you need further explanations about how to obtain the URL shown here, use this step-by-step guide:
https://www.freecodecamp.org/news/cjn-google-sheets-as-json-endpoint/)

Then, we need to define a LaTeX command to access the endpoint and insert the cell value from the spreadsheet in our LaTeX document. Something like

\get{https://spreadsheets.google.com/feeds/cells/1xhWskXv1qCDIHSAxSzEaQBeFKP1vfuR5ZLdx7nsgBg4/default/public/full/R3C3?alt=json}{XXX}

(The XXX value is the value by default)

We will define the \get command in our LaTeX document as follows:

But how can we instruct Overleaf to execute that \get command to access the JSON value from the endpoint and insert it online in our current document?

Well, we first need to know how to parse an Overleaf document programmatically.

Then we need to know how to search for the \get command in our LaTex document.

Internally, Overleaf uses the Ace editor

https://ace.c9.io/

Ace is an embeddable code editor written in JavaScript. It offers a simple yet complete JS API to access and manipulate the content of the current document.

Let’s try to use the Ace API offered by Overleaf to get the number of lines of the current LaTeX document.

Open DevTools in Chrome (or the web console in Firefox) and run this script:

As you can see, we obtained the reference to Ace and calculated the number of lines of the current document programmatically (25, in our case)

Now, using the same API we are now in a good position to find the next occurrence of our \get command in the document:

Once our \get command is selected, we need to parse it and obtain the URL of the endpoint and the dynamic value of the cell (we will store it in the newValue variable)

Something like this:

Finally, we only need to replace the current \get command and value with the new value… and iterate, finding the next occurrence and repeating the process.

I have published a gist with the full source code 🙂

let ace = document.querySelector(«.ace_editor»);
// ace.env.editor.session.getLength(); // number of lines
let previousRow = 0
let r = ace.env.editor.find(‘\\get\{(.*?)\}\{(.*?)\}’,{
backwards: false,
wrap: false,
caseSensitive: false,
wholeWord: false,
regExp: true
});
while (r?.end.row >= previousRow){
previousRow = r.end.row
const original = ace.env.editor.getSelectedText();
const values = original.match(/\{.*?\}/g)
const jsonvalue = await fetch(values[0].substring(1, values[0].length-1))
const cell = await jsonvalue.json()
const newValue = parseFloat(cell.entry.content.$t)
const range = ace.env.editor.selection.getRange();
ace.env.editor.session.replace(range, `\get${values[0]}{${newValue}}`);
r = ace.env.editor.find(‘\\get\{(.*?)\}\{(.*?)\}’,{
backwards: false,
wrap: false,
caseSensitive: false,
wholeWord: false,
regExp: true
});
}

Here, you can see the script in action. Hope it helps!

A link to the LaTeX document used in the video:

https://www.overleaf.com/read/nvqpgknvmbrj

Originally tweeted by juanan (@juanan) on 12 July, 2021.

Revisando TFGs (I)

Ahora que tenemos RepoSearch funcionando, estos días me gustaría destacar algunos TFGs que me han llamado la atención. El primero de ellos lleva por título:
«Diseño de máquina arcade con servidor Web e identificación NFC» y es una delicia para los amantes de los juegos retro

https://riunet.upv.es/handle/10251/165377

Publicado en abril de 2021, su autor, Alejandro Grau, nos muestra una implementación completa de una máquina arcade con una Raspberry Pi.
Para su desarrollo, se ha basado en RetroPie

https://retropie.org.uk

RetroPie permite emular una gran variedad de consolas, desde las más antiguas como Atari, hasta las más actuales como PlayStation 1, PlayStation 2 o PlayStation Portable.

Como añadido extra, se incluyen también instrucciones de conexión y programación de un módulo RFID que permite al usuario identificarse para jugar la partida y guardar las puntuaciones 🙂

Me gusta que el autor no se haya quedado sólo en la parte de desarrollo software sino que nos regala también el diseño de la carcasa hardware

Como prueba de concepto el autor ha integrado el juego Pang (1989) publicado por Mitchel Corporation en Europa y Capcom en USA (bajo el título Buster Bros)
Tengo debilidad por este juego y fue uno de los que implementamos en la asignatura #DAWE en el curso 19/20 🙂

Originally tweeted by juanan (@juanan) on 7 May, 2021.

RepoSearch, un buscador de TFGs de Ingeniería Informática

¿Alguna vez te has preguntado si el TFG de Informática que vas a llevar a cabo no lo ha hecho ya alguien en alguna otra facultad? Si es así, o si estudias Ingeniería Informática y estás decidiendo qué TFG llevar a cabo, sigue leyendo 🧵👇

Los repositorios institucionales de las universidades guardan las memorias de los TFGs del Grado en Ingeniería Informática de todas las facultades. No había un buscador que permitiera una búsqueda simultánea en todos ellos. Así que he programado uno 🙂

https://reposearch.ikasten.io/

RepoSearch permite buscar en los metadatos de las memorias de los TFG de Informática que estén publicadas en repositorios con licencia Creative Commons (suele ser cc-by-nc-nd).
¿Qué facultades de informática se indexan en RepoSearch? Estas.

Una vez localizadas las facultades de interés, ¿cuáles son las URL de los repositorios donde guardan sus memorias? Para responder a esta pregunta tenemos a nuestra disposición el directorio de repositorios de REBIUN (Red de Bibliotecas Universitarias)

Por ejemplo, ADDI https://addi.ehu.es/ es el repositorio de mi Universidad, la UPV/EHU, e incluye las memorias del grado en ingeniería informática tanto de la Facultad de Informática de Donostia como de la Escuela de Ingeniería de Bilbao.

Bien, pero ¿cómo extraemos los resultados? Y antes de eso, ¿cómo buscamos las memorias de TFG filtrando por grado o por facultad? Bueno, podemos usar el protocolo OAI-PMH para buscar y obtener metadatos de repositorios abiertos.

OAI-PMH son las siglas de Open Archives Initiative Protocol for Metadata Harvesting. Puedes leer al respecto aquí: https://www.openarchives.org/pmh/ Si controlas Python, hay un módulo que te ayudará a trabajar con OAI.

He de decir que inicialmente me lié la manta a la cabeza y fui a lo bruto, con mis conocimientos de Python + Scrapy, a realizar web scraping como si no hubiera un mañana 🤷‍♂️ Tras un café relajado pensé que estaba haciendo el tonto (sí), y que habría otra forma (oai-pmh) 🙂

En fin, el problema de los repositorios es que cada uno organiza los TFGs como le viene en gana. Algunos los organizan por facultad o escuela, otros por departamento, otros por palabras clave… Para programar Reposearch tuve que indagar, uno por uno, en el método de organización

A partir de ahí, mezclando Python+Sickle+SQLAlchemy con un poco de BeautifulSoup, me permitieron capturar todos los metadatos que buscaba y guardarlos en una BBDD MySQL. A día de hoy se guardan 9832 registros

Para finalizar, si quieres estar al tanto de las novedades, de nuevas memorias de TFG que se publiquen, etc. puedes seguir la cuenta @BuscaRepo. Cuando se añada una nueva memoria en https://reposearch.ikasten.io esa cuenta tuiteará automáticamente la novedad.