Gem cell gem 默认不进行 html escape 问题

heroyct · 2018年10月01日 · 最后由 heroyct 回复于 2018年11月14日 · 3220 次阅读

现在的项目使用了 cell 这个 gem
https://github.com/trailblazer/cells

4.0 以前

在 4.0 以前的时候使用的是 rails 的 render,默认会进行 html escape

比如下面的代码是没有问题的

@text = '<script>alert("hello")</script>'
<%= @text %>

4.0 以后

目前打算升级到 4.0

但是 4.0 以后,cell 采用了其他的 render 方法,默认不进行 html escape, 必须像下面这样传递 escape 过后的值

@text = '<script>alert("hello")</script>'
<%= h @text %>

http://trailblazer.to/gems/cells/cells4.html

Cells per default does not escape HTML. However, you may run into problems when using Rails helpers. Internally, those helpers often blindly escape. This is not Cells’ fault but a design flaw in Rails. Everything related to #capture will cause problems - check this as an example. As you can see, this is Rails swinging the escape hammer. Please don’t blame us for escapes where they shouldn’t be. Rather open an issue on Rails and tell them to make their code better overrideable for us.

但是每个字段自己写代码 escape 感觉很麻烦,而且万一忘记了,会出现安全问题

问题

目前把需要 escape 的都加了 html_escape 方法暂时对应了 (估计有漏的地方)

没找到其他的好的方法,有遇到相同的问题的吗?

建议去掉这个 gem。

@Rei 目前我也是这么想的,因为感觉这个 gem 不太稳定 (突然设计风格就变了)
只是目前所有页面都是 cell 写的,这改造的活量有点大
所以问问看有没有类似问题的人,借鉴一下经验

暂时把 cell 的 render 改成了 rails 的 render

class CellBase < Cell::ViewModel
  attr_accessor :response_body

  include AbstractController::Rendering
  include AbstractController::Helpers
  include ActionView::Layouts

  helper ApplicationHelper
  include ApplicationHelper

  self.view_paths = ['app/cells']

  def self.supports_path?
    true
  end

  def action_name
    caller(5, 1)[0].match(/`(\w+)/)[1]
  end

  # 让view中可以访问cell class里面的方法
  def self.inherited(klass)
    def klass.method_added(name)
      helper_method(name)
    end
  end
end

终于把 cell gem 删除了

为了尽量少改动代码,扩展 render partial 来实现了类似的接口

1. 添加一个 helper

# app/helpers/cell_helper.rb

module CellHelper
  def cell(cell_name, options = {})
    cell_class = class_from_cell_name(cell_name)
    cell_class.new(self, options)
  end

  private

  def class_from_cell_name(name)
    "#{name}_cell".camelize.constantize
  end
end

2. 添加一个 cell 的 base 类

# app/cells/cell_base.rb

class CellBase
  attr_reader :context

  def initialize(context, options = {})
    @context = context
    @options = options
  end

  def call(method_name, options = {})
    send(method_name, options)
  end

  protected

  def render(options = {})
    locals = options.fetch(:locals, {})
    locals = locals.merge(cell: self)
    called_method_name = caller_locations(1, 1)[0].label

    context.render(
      partial: template_file_path(called_method_name, options),
      locals: locals
    )
  end

  def template_file_path(method_name, options)
    file_name = options[:file].present? ? options[:file] : method_name

    folder = self.class.to_s.underscore[0..-6]
    "#{folder}/#{file_name}"
  end
end

3. 让所有的 cell 类都继承 CellBase

class SampleCell < CellBase
end

4. 把 xxx.erb 文件改成_xxx.html.erb 文件 (partial file)

heroyct 关闭了讨论 11月14日 17:58
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册