Eugene Komissarov

TILOctober 03, 2019by Eugene Komissarov

Save your links from phishers.

While Pull Request fixing issue with OWASP Tabnabbing is still open... since Feb 16, 2017, our shiny Rails applications are in danger. But wait no longer! Just put code like this:


# frozen_string_literal: true



module ActionView

  module Helpers #:nodoc:

      module UrlHelper

      # Same as #link_to, but also adds rel="nofollow" and rel="noopener" if target="_blank"

      #   rel='noopener' is added to mitigate OWASP Reverse Tabnabbing

      #

      #   external_link_to "External link", "http://www.rubyonrails.org/", target: "_blank"

      #   # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow noopener">External link</a>

      def external_link_to(name = nil, options = nil, html_options = nil, &block)

        html_options, options, name = options, name, yield if block_given?

        html_options ||= {}

        html_options.stringify_keys!



        html_options['rel'.freeze] = "#{html_options['rel'.freeze]} nofollow".lstrip

        html_options['rel'.freeze] = "#{html_options['rel'.freeze]} noopener".lstrip if html_options['target'.freeze] == '_blank'.freeze

        link_to(name, options, html_options)

      end

    end

  end

end

into one of initializers and enjoy bit of safety. This will add new url helper external_link_to to your disposal, that will mitigate Reverse Tabnabbing endangering your application.

Or, if you feel adventurous today... lets patch link_to itself!


# frozen_string_literal: true



module ActionView

  module Helpers #:nodoc:

    module UrlHelper



      def link_to(name = nil, options = nil, html_options = nil, &block)

        html_options, options, name = options, name, block if block_given?

        options ||= {}





        html_options = convert_options_to_data_attributes(options, html_options)



        html_options['rel'.freeze] = "#{html_options['rel'.freeze]} nofollow".lstrip

        html_options['rel'.freeze] = "#{html_options['rel'.freeze]} noopener".lstrip if html_options['target'.freeze] == '_blank'.freeze



        url = url_for(options)

        html_options["href".freeze] ||= url



        content_tag("a".freeze, name || url, html_options, &block)

      end

    end

  end

end

Even better! Now all your existing links with target="_blank" gonna be safe from nasty fishers.

Happy coding and stay safe.

TILAugust 23, 2019by Eugene Komissarov

Rubyists life made easier with composition operators.

If you write Ruby code and wandered into FP world you might just started writing those little tiny methods inside your classes/modules. And that was awesome to write code like this:


class Import

  # Some code goes here...



  def find_record(row)

    [ Msa.find_or_initialize_by(region_name: row[:region_name], city: row[:city], state: row[:state], metro: row[:metro] ), row ]

  end



  # record is one of:

  # Object - when record was found

  # false - when record was not found

  def update_record(record, attributes)

    record.attributes = attributes

    record

  end



  # record is one of:

  # false

  # ZipRegion

  def validate_record(record)

    case record

    when false

      [:error, nil]

    else

      validate_record!(record)

    end

  end



  # record is ZipRegion object

  def validate_record!(record)

    if record.valid?

      [:ok, record]

    else

      error(record.id, record.errors.messages)

      [:error, record]

    end

  end



  def persist_record!(validation, record)

    case validation

    when :ok

      record.save

    when :error

      false

    end

  end

end

Yeah, I know there is YARD, and argument types are somewhat weird but at the time of coding, I was fascinated with Gregor Kiczales's HTDP courses (that was a ton of fun, sincerely recommend for every adventurous soul).

And next comes dreadful composition:


def process(row, index)

    return if header_row?(row)



    success(row[:region_name], persist_record!(*validate_record(update_record(*find_record(parse_row(row))))))

  end

The pipeline is quite short but already hard to read. Luckily, in Ruby 2.6 we now have 2 composition operators: Proc#>> and its reverse sibling Proc#<<.

And, with a bit of refactoring composition method becomes:


def process(row, index)

    return if header_row?(row)



    composition = method(:parse_row) >>

                             method(:find_record) >>

                             method(:update_record) >>

                             method(:validate_record) >>

                             method(:persist_record!) >>

                             method(:success).curry[row[:region_name]]



    composition.call(row)

Much nicier, don't you think? Ruby just became one step closer to FP-friendly languages family, let's hope there'll be more!