Mostrando las entradas con la etiqueta monkeypatch. Mostrar todas las entradas
Mostrando las entradas con la etiqueta monkeypatch. Mostrar todas las entradas

2009-07-05

Patch en Regexp para poder usarlas como clave en un Hash

Siguiendo mi proyecto de hacer mi wiki en Ruby, encontré un comportamiento muy raro.
Generé un hash (que se llama @rules) que no tiene un elemento que tiene. O sea, @rules[@rules.keys[2]] da nil, pero @rules.values[2] devuelve el objeto asociado a la clave @rules.keys[2]. Como este hash tiene como claves un montón de expresiones regulares, me imaginé que había un problema con el hash y el eql? de Regexp, así que los implementé de nuevo y monkeypatchié.
Este es el código:

class Regexp
alias_method :old_rapidito_inspect, :inspect

def inspect
@inspect = old_rapidito_inspect if @inspect.nil?
@inspect
end

def eql?( other )
false if other.class != Regexp
self.inspect == other.inspect
end

alias_method :"==", :eql?

def hash
self.inspect.hash
end
end


Esta corrección me anduvo con la siguiente versión de ruby:
$ ruby --version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]


Espero que les sirva.
Happy hacking,
Aureliano.

2009-07-03

Upcase con acentos

En ruby no se calculan los cambios minúscula mayúscula para las letras acentuadas. Por ejemplo:

irb(main):002:0> puts "áa".upcase
áA
=> nil

Para que ande un poco mejor (y tome las letras acentuadas de un montón de lenguajes), encontré un hack que en 10 líneas de código hace que puedan pasarse a mínusculas y mayúsculas los caracteres acentuados. Usando que todas las letras minúsculas y mayúsculas acentuadas están todas juntas, armé un par de gsubs que resuelven el problema.
class String
alias_method :old_rapidito_upcase, :upcase
def upcase
self.gsub( /\303[\240-\277]/ ) do
|match|
match[0].chr + (match[1] - 040).chr
end.old_rapidito_upcase
end

alias_method :old_rapidito_downcase, :downcase
def downcase
self.gsub( /\303[\200-\237]/ ) do
|match|
match[0].chr + (match[1] + 040).chr
end.old_rapidito_downcase
end
end

Lo que hago acá es reemplazar las funciones upcase y downcase para que manejen los acentos, sumando o restando 32 (40 en octal) al segundo byte. Pongo este código en lang_hacks.rb y sale todo con fritas. Mirando el irb
irb(main):001:0> require 'lang_hacks'
=> true
irb(main):002:0> "aÁ".upcase
=> "A\303\201"
irb(main):003:0> puts "aÁ".upcase

=> nil

Espero que les sirva.
Happy hacking,
Aureliano.

2008-03-09

Clases abiertas - Ejemplo práctico

Estoy mirando una biblioteca para acceder a bases de datos muy copada que se llama sequel. En la misma no hay que declarar nada y el mapeador solo sabe como hacer para acceder a todo (como debería ser). Y quise ver como hace el sql que va a la base, así que prendí el irb e hice un poquito de "aspect programming alla ruby". Primero importé la biblioteca y el driver para postgres y les agregué datos (todo usando la biblioteca sequel desde adentro del irb y sin escribir una línea de SQL).

Pero después me agarró la duda de cómo se generaba el SQL. ¿Tendría quizás alguna forma de hacer un SQL injection si uso la biblioteca para hacer una página web? Lo que siempre me gustó de bibliotecas como ActiveRecord e Hibernate, es que me quitan la responsabilidad por estas cosas. Así que puse manos a la obra.

Una mirada rápida por la documentación y el código no me llevó a la opción de mostrar todo el sql que se ejecuta, así que tuve que hacer un toque de magia.


irb(main):042:0> class Sequel::Postgres::Dataset
irb(main):043:1> alias_method :select_sql_old, :select_sql
irb(main):044:1> def select_sql(*p)
irb(main):045:2> sql = select_sql_old(*p)
irb(main):046:2> puts(sql)
irb(main):047:2> sql
irb(main):048:2> end
irb(main):049:1> end


¿Qué hice? Hice "aspect programming alla ruby". Cambié cómo se hace para generar el sql de las consultas en postgres para que lo muestre por pantalla (solo en mi sesión de irb).

A partir de este momento (y hasta que lo vuelva atrás o salga del irb) me muestra el SQL de cada SELECT que haga a la base.

Así que ahora puedo mirar fácil que pasa si trato de inyectar SQL:

irb(main):053:0> user_name = "aaaa ' or 1=1"
=> "aaaa ' or 1=1"
irb(main):054:0> users.where(:name => user_name).all
SELECT * FROM users WHERE ("name" = 'aaaa '' or 1=1')
=> []


Soy feliz, y veo que la biblioteca parece estar bien pensada (al menos para esto).

Happy hacking,
Aureliano.