Filtrando datos con jQuery

Filtrar elementos dentro de una lista o una tabla para realizar una búsqueda inline con jQuery es bastante simple, solo basta entender un poco donde va cada cosa. La función en cuestión que nos permite hacer esto es filter() que junto con un poco de trabajo para hacer un deep-search dentro del DOM nos da esta funcionalidad.

Por ejemplo si queremos mostrar los elementos de una lista que coinciden con una búsqueda deberíamos hacer algo como :

  v = 'some word';
  /* Ocultamos todos los LI */
  $('#list ul li').hide();
  /* Filtramos y mostramos los que coinciden */
  $('#list ul').contents().filter(function () {
    return innerSearch(this, v);
  }).show();

La función innerSearch la pueden ver en el ejemplo, y básicamente hace un recorrido por el DOM y devolviendo true en aquellos tags donde su contenido de texto contiene el patrón buscado.

Eso nos da muchas posibilidades, ya que por ejemplo en lugar de un simple show se podría aplicar alguna clase CSS para resaltar, modificar el color o lo que se les pueda ocurrir.

Por razones de performance no es muy útil para un DOM muy complejo, pero para el caso donde yo lo necesité funciona aceptablemente bien.

Dejo un ejemplo completo con filtrado en listas y tablas.

Anuncios

HTML5 : Guardar el contenido de en el servidor

Hoy tenía ganas de hacer algo distinto (vamos, no quería trabajar :P) así que me puse a jugar con HTML5, entre otras cosas con el tag <canvas> para poder dibujar. Luego de bajar varios ejemplos y tener un ‘paint’ andando lo que quería era guardar la imagen, y llegué a este post donde el autor deja guardar en PNG, JPG hasta incluso en BMP el contenido del canvas.

Pero que el usuario se pueda guardar la imagen no era mi idea, era más bien guardarle en el servidor. Por ejemplo para hacer una firma digital y usarla después en el sitio para firmar los posts como si fueran documentos :P.

Al principio pensé que iba a ser super complicado, pero me encontré que todo fue mucho más fácil de lo que imaginé. Así que vamos por partes.

Vamos a empezar por el server. Como era muy poco código para este ejemplo, hice una aplicación en Sinatra que me permite mostrar un canvas y luego hacer un POST con los datos. Es ideal en este caso, ya que el server queda de tan solo unas 20 líneas de código :

require 'sinatra'
require 'RMagick'

get '/' do
   erb :index
end

post '/' do
  # Ya completaremos ...
end

Como se puede ver tenemos una vista en el root y el POST lo manejamos también en la raiz. Para procesar la imagen decidí usar la única gema que conozco, RMagick, que buscando en la documentación de ImageMagick me encontré con grata sorpresa que sale leer ‘Base64-encoded inline image’ , que resulta que es justo el formato en que se obtienen los datos desde el canvas :). La única diferencia es que hay que preceder el string con ‘inline:’ y ya estamos, nuestro método POST queda así :

post '/' do
  data = 'inline:' + request.body.read

  begin
    image = Magick::Image.read(data).first

    image.write('image.png')
    "alert('Image saved successfully')"
  rescue
    "alert('Image format not recognized')"
  end
end

Habría que revisar la documentación y bugs de ImageMagick para ver que tipos de implicancia puede llegar a tener en la seguridad del sitio o del sistema, pero son cosas que no vienen al caso :).

La vista (reducida para el ejemplo) puede resumirse en el siguiente HTML :

  
    This text is displayed if your browser does not support HTML5 Canvas.
  

  

Y un poco de javascript :


  $(function () {
    $("input").click(function () {
      // jQuery todavía no habla bien con HTML5, hack para
      // no usar un plugin.
      var c = $("#example").get(0);
      $.post('/', c.toDataURL(), null, "script");
    });

    // Un dibujito estático para probar
    var example = document.getElementById('example');
    var context = example.getContext('2d');
    context.fillStyle = "rgb(255,0,0)";
    context.fillRect(30, 30, 50, 50);
  })

Y ya estamos, al presionar el botón se envían los datos de la imagen dibujada, si está todo bien ImageMagick creará el archivo en el filesystem y por último devolverá algo de javascript para informar al usuario como salió todo. Obviamente si tuviéramos un sistema de usuarios en el server podríamos guardar la imagen para cada uno, pero de nuevo, no viene al caso.

El resultado final :

$ identify -verbose image.png 
Image: image.png
  Format: PNG (Portable Network Graphics)
  Class: DirectClass
  Geometry: 200x200+0+0
  Resolution: 72x72
  Print size: 2.77778x2.77778
  Colorspace: RGB
  Depth: 8/1-bit
  Histogram:
     37500: (  0,  0,  0,  0) #00000000 none
      2500: (255,  0,  0,255) #FF0000 red
  Page geometry: 200x200+0+0
  Compression: Zip
  Properties:
    create-date: 2010-04-13T00:20:15-03:00
    modify-date: 2010-04-13T00:20:15-03:00
    signature: 0a42f3c85d6364e38f14dac554d26c62a90b84b06ac7f22264c9d964657ba8d6
  Tainted: False
  Filesize: 503b
  Number pixels: 39.1kb
  Version: ImageMagick 6.5.1-0 2009-08-27 Q16 OpenMP http://www.imagemagick.org

Probado en :

  • Chromium 5.0.372.0 (44042) Ubuntu
  • Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.9) Gecko/20100401 Ubuntu/9.10 (karmic) Firefox/3.5.9

Código completo : html5_canvas_submit

Limpiando los mails de Yahoo Groups

Una de las cosas más molestas que tiene Yahoo Groups es la cantidad de basura que le mete a los emails que llegan desde el grupo. Como dicen, una imagen dice más que mil palabras :

Es realmente perturbante, sobre todo porque lo pone en cada email. La primer opción que busqué fue decirle a Gmail que me muestre los emails como plain-text, lo que no encontré. Después recordé que como uso Gmail para trabajar, me llegan emails con links en html-only :S, por lo que de todas formas no me servía.

Entonces dije “no hay drama, lo modifico con Stylish“, una extensión de Firefox que permite mediante CSS cambiar cosas (y un poco más liviana que Greasemonkey). La cosa no fue tan fácil, ya que el HTML que inserta Yahoo no tiene ni class ni id en los tags HTML, por lo que tuve que hacer un poco de magia con los CSS Selectors de Firefox, que por fortuna son varios :).

El resultado final obtenido es :

Todavía quedan algunos detalles, como sacar una imagen de 1×1 pixels al final del cuerpo que según parece usa Yahoo para geolocalizar desde donde se leen sus email.

El CSS aplicado :

@namespace url(http://www.w3.org/1999/xhtml);
@-moz-document domain(mail.google.com) {
  .ii > div > div > div > div > div + div {
    display: none !important;
  }
  .ii > div > div > div > div {
    display: none !important;
  }
  .ii > div > div > :first-child {
    width: 95% !important;
  }
  .ii > div > div > div > :first-child {
    display: block !important;
    width: 100% !important;
  }
}

Edit In Place con Prototype

Una de las cosas que me venían pidiendo en ¡Falta Uno! era que se le pudieran asignar nombre a los equipos de un partido. En un principio pensé solo en poner los campos en el formulario de “crear partido” pero después me pareció que quedaba piola que se puedan editar directamente desde el resumen, usando un “Edit in place”. Me puse a buscar si había algo hecho con Proptotype (que es lo que uso en este proyecto) y encontré esto que viene con varios ejemplos.

La biblioteca es muy fácil de usar y bastante flexible en cómo queremos que se comporte el edit (puede ser un input, un textarea, un combo). Suponiendo que los nombres de equipos estén siendo mostrados de la siguiente manera :

Para poder editar los títulos simplemente basta con ejecutar el siguiente código cuando se carga la página :

$('team_name_1').editInPlace({
  auto_adjust: true,
  select_text: true,
  save_url: ' 1) %>'
});
$('team_name_2').editInPlace({
  auto_adjust: true,
  select_text: true,
  save_url: ' 2) %>'
});

Es recomendable poner este código dentro de un método y usar un Observer para ejecutarlo, así mantenemos separada la lógica JS del HTML. Bastaría con un simple Event.observe(window, 'load', initEIP);.

Por el lado del servidor, el método que atiende el request (el definido el save_url) tiene que devolver el nuevo valor que va a tomar el campo. Por ejemplo, si el nombre no se pudo cambiar, deberíamos devolver el anterior. En este caso que es simple el template a devolver es el siguiente :


Para terminar, en caso de que el navegador del usuario no tenga JS queda el formulario de “Editar/Crear Partido” donde se pueden poner los nombres de manera tradicional.

Sobreviviendo ataques

Está terminando un día largo, de esos que uno espera que no le toquen, pero que tarde o temprano llegan. Ayer a la noche en 3DG fuimos víctimas de un pequeño ataque. Por suerte los atacantes super buena onda. Luego de que apagamos el primer incendio estuvimos chateando con ellos y nos dieron la data de por donde entraron, cómo, qué cosas modificaron y lo mejor de todo, donde nos habían dejado los backups que habían hecho :).

El ataque consistió en hacer que nuestros sitios (el target era el foro que es el que tiene más tráfico, pero afectó a otros sitios también) sean redirigidos a una página muy graciosa que no pienso linkear porque no es ATP. Para lograrlo (ya que el foro está aislado y no pudieron entrar por ahí) nos crackearon el sistema de publicidad e insertaron un banner javascript que hacía el redirect. Simple y efectivo.

Para lograr el acceso al server de ads fue más fácil, simplemente explotaron un SQL Injection que por la fecha de última modificación del script, estaba desde el 2001 :). Con eso consiguieron el password de admin para poder poner su publicidad. Nuestro segundo problema fue obvio, el usuario que usa ese script para acceder a la DB tenía demasiados privilegios y pudo leer y modificar una otra base de datos.

Más allá del trabajo que tuvimos que hacer para recuperar de nuestros backups cosas por las dudas (aunque nos hicieron backups tampoco confiar a ciegas :P) tuvimos que empezar a auditar cosas que teníamos pendiente hace rato. Para empezar necesitamos bajar los servicios completamente y la forma más linda que encontré fue usando un rewrite rule de apache, como sigue :

RewriteEngine on
RewriteCond %{REQUEST_URI} !^/imagenes
RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
RewriteCond %{REQUEST_URI} !/maintenance.html$

RewriteRule $ /maintenance.html [R=302,L] 

De esta manera cuando terminamos de hacer el mantenimiento simplemente borramos el maintenance.html y el sitio vuelve solo a la vida. De paso ya lo dejamos para cuando hagamos futuros updates.

Lo otro que nos entró en duda cuando arreglamos el problema fue : ¿lo arreglamos realmente? ¿tendremos otro agujero en algún lado?. Para poder revisar esto comencé a buscar herramientas para auditar SQL Injections y me encontré con un post donde habían varias soluciones. La que usamos finalmente fue sqlmap porque fue la que mejor nos pareció que andaba.

Fue una tarde divertida viendo que podíamos romper de nuestro viejo sitio. Para escanearlo simplemente corríamos :

#$ sqlmap -dbs -u "http://127.0.0.1/scriptbuggeado.php?Id=1"

El programa primero trata de ver si el parámetro Id es vulnerable a diferentes formas de hacer injection y si descubre alguna trata de obtener la lista de DBs. Es lindo ver cuando aparecen todas tus DBs en la consola :). Si le agregamos “-v 2” es más gracioso aún, porque los nombres van apareciendo letra a letra (parece que las va adivinando o algo, no me fijé en el código para ver como lo hace).

A esta hora cerramos ya el agujero del ataque y dos más que detectamos.

El último problema que encontramos fue que habían subido un shell. Para esto crackearon el password de programa para enviar newsletters y subieron un archivo php que tiene un shell re lindo que tiene funciones para escanear vulnerabilidades localmente. Acá el problema fue que el sysadmin anterior dejó permiso para ejecutar script en el directorio donde el programa guarda attachments que después se usan en el email (como imágenes en esos emails molestos HTML que nos llegan todos los días). Sacando los permisos para ejecutar cualquier tipo de script el shell ya no funciona.

Como sysadmin “temporal” fue una experiencia divertida, sobre todo porque no hubo pérdida de datos y la buena onda de los atacantes 🙂 (¿tendré el Síndrome de Estocolmo?).

Parseando HTML desde Ruby

Con todo este lío de la crisis mundial, corridas bancarias y demás en la oficina los días tranquilos jugamos a “Adiviná cuánto va a salir el dolay hoy”. Como es muy molesto entrar a “Dolar hoy dot com” donde miramos el valor oficial del juego, me puse a armar un script para robarme el valor actual y así ir tirando el dato minuto a minuto.

Siempre es una molestia tener que parsear HTML, sobre todo cuando es tan feo como el del sitio en cuestión, que no solo no tiene un solo class de CSS ni id, sino que usa el tag FONT, dios. Por suerte Hpricot está para ayudarnos :).

Hpricot es “a fast, flexible HTML parser written in C” con su interfaz a Ruby, obvio :). Hace cosas muy copadas, como por ejemplo agarrar un HTML, buscar todos los <a> que tengan el class custom, o todos los P seguidos de un DIV seguidos de un IMG, etc. Podemos consultar por ID, borrar, agregarle class a las cosas que encontramos y muchas cosas más, útiles para manipular HTML.

Lo primero que tuve que hacer fue saltear la “protección” del sitio, ya que para acceder a la página con las cotizaciones verifican que vengas del dominio principal. Un básico chequeo contra el HTTP_REFERER. Como suelo usar open-uri, a ésta le puedo pasar el referer que yo quiero enviar en el header, como cualquier biblioteca para manejar URIs que tenga auto-respeto :).

Para encontrar los valores fue fácil. Mirando el HTML de la página se ve que el valor del dolar está dentro de un “div/font/b/font”, por lo que solo tuve que buscarlo. Después hago un cleanup para sacarle un non-breaking space que me molestaba y los espacios que también quedan feos.

Y eso es todo. Ahora puedo consultar el valor del dolar desde mi consola :). Seguramente hay miles de formas más cómodas de hacerlo, algún widget para Gnome, página que te de un RSS, etc, pero fue un lindo ejercicio.

Dejo acá el script para el que quiera jugar un rato.

require 'rubygems'
# gem install hpricot si no lo tienen instalado
require 'hpricot'
require 'open-uri'


doc = Hpricot(open("http://dolarhoy.com/indexx.php", "Referer" => "http://dolarhoy.com/"))

div = (doc/"div/font/b/font")[1]

dolar = div.inner_html

dolar = dolar.gsub(/[ |$]/, "")
dolar.strip!

puts "Dolar Hoy : $#{dolar}"