2009-12-31 6 views
4

Je souhaite écrire une fonction permettant aux utilisateurs de faire correspondre des données en fonction d'une expression rationnelle, mais je suis préoccupé par l'assainissement des chaînes utilisateur. Je sais qu'avec les requêtes SQL, vous pouvez utiliser des variables de liaison pour éviter les attaques par injection SQL, mais je ne suis pas sûr qu'il existe un tel mécanisme pour les expressions rationnelles. Je vois qu'il y a Regexp.escape, mais je veux autoriser les expressions rationnelles valides.Assainissement de l'expression utilisateur

Voici est la fonction de l'échantillon:

def tagged?(text) 
    tags.each do |tag| 
     return true if text =~ /#{tag.name}/i 
    end 
    return false 
    end 

Depuis que je suis qu'aligner directement sur tag.name est-il une chance que quelqu'un pourrait insérer un appel Proc ou quelque chose pour sortir de l'expression rationnelle et causer des ravages?

Tout conseil sur les meilleures pratiques serait apprécié.

Répondre

5

cordes interpolés dans une expression rationnelle ne sont pas exécutées, mais ne génèrent des avertissements ennuyeux:

/#{exit -3}/.match('test') 
# => exits 

foo = '#{exit -3}' 
/#{foo}/.match('test') 
# => warning: regexp has invalid interval 
# => warning: regexp has `}' without escape 

Les deux avertissements semblent se rapporter à l'# d'ouverture {et la fermeture} respectivement, et sont indépendants.

Comme stratégie plus efficace, vous pouvez désinfecter la liste de balises dans une expression rationnelle combinée que vous pouvez exécuter une seule fois. Il est généralement beaucoup moins efficace de construire et de tester contre N expressions régulières que 1 avec N parties.

Peut-être quelque chose le long des lignes de ce:

class Taggable 
    def tags 
    @tags 
    end 

    def tags=(value) 
    @tags = value 

    @tag_regexp = Regexp.new(
     [ 
     '^(?:', 
     @tags.collect do |tag| 
      '(?:' + tag.sub(/\#\{/, '\\#\\{').sub(/([^\\])\}/, '\1\\}') + ')' 
     end.join('|'), 
     ')$' 
     ].to_s, 
     Regexp::IGNORECASE 
    ) 
    end 

    def tagged?(text) 
    !!text.match(@tag_regexp) 
    end 
end 

Ceci peut être utilisé comme ceci:

e = Taggable.new 
e.tags = %w[ #{exit-3} .*\.gif .*\.png .*\.jpe?g ] 

puts e.tagged?('foo.gif').inspect 

Si l'appel de sortie a été exécuté, le programme arrêterait là, mais il seulement interprète cela en tant que chaîne littérale. Pour éviter les avertissements, il est échappé avec des antislashs.

+0

Je testais en essayant d'insérer un proc et j'ai eu les mêmes erreurs. J'ai l'impression qu'il est échappé lorsqu'il est affecté à un objet activerecord: # Et je reçois les mêmes erreurs que vous avez eu en essayant de l'utiliser dans le cadre de une regex. Je ne savais pas s'il y avait quelque chose qui me manquait. –

1

Vous devez probablement créer une instance de la classe Regexp à la place.

def tagged?(text) 
    return tags.any? { |tag| text =~ Regexp.new(tag.name, Regexp::IGNORECASE) } 
end 
Questions connexes