Skip to content

Instantly share code, notes, and snippets.

@j15e
Last active August 31, 2015 14:05
Show Gist options
  • Select an option

  • Save j15e/95898dda12698eb6fd0e to your computer and use it in GitHub Desktop.

Select an option

Save j15e/95898dda12698eb6fd0e to your computer and use it in GitHub Desktop.

Revisions

  1. j15e revised this gist Aug 31, 2015. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion csp_reporter.rb
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,8 @@
    # Jean-Philippe Doyle, Copyright (c) 2015
    # Licence MIT
    # @see http://opensource.org/licenses/MIT

    # Logs CSP report in rails logs sent to REPORT_URI
    class CSPReportLogger
    BLOCKED_URI_KEY = 'blocked-uri'.freeze
    CSP_REPORT_KEY = 'csp-report'.freeze
    @@ -8,7 +13,7 @@ class CSPReportLogger
    POST_BODY = 'rack.input'.freeze
    REPORT_METHOD = 'POST'.freeze
    REPORT_URI = '/errors/csp'.freeze
    REQUEST_UUID = 'action_dispatch.request_id'.freeze # rails-only
    REQUEST_UUID = 'action_dispatch.request_id'.freeze # rails-only too

    def initialize(app)
    @app = app
  2. j15e created this gist Aug 31, 2015.
    53 changes: 53 additions & 0 deletions csp_reporter.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,53 @@
    class CSPReportLogger
    BLOCKED_URI_KEY = 'blocked-uri'.freeze
    CSP_REPORT_KEY = 'csp-report'.freeze
    IGNORE = %w{safari-extension://}.map { |src| /\A#{src}/.freeze }.freeze
    LOG_HEADER = '[SECURITY] CSP Violation Report'.freeze
    LOG_INVALID = 'INVALID FORMAT'.freeze
    LOG_KEYS = %w{blocked-uri document-uri line-number source-file violated-directive}.freeze
    POST_BODY = 'rack.input'.freeze
    REPORT_METHOD = 'POST'.freeze
    REPORT_URI = '/errors/csp'.freeze
    REQUEST_UUID = 'action_dispatch.request_id'.freeze # rails-only

    def initialize(app)
    @app = app
    end

    def call(env)
    if REPORT_METHOD == env['REQUEST_METHOD'] && REPORT_URI == env['REQUEST_PATH']
    report!(env)
    [204, {}, []]
    else
    @app.call(env)
    end
    end

    def report!(env)
    report = JSON.parse(env[POST_BODY])[CSP_REPORT_KEY]

    return log_invalid_body(env) if report.nil?
    return if ignored?(report)

    log(JSON.pretty_generate(report.slice(*LOG_KEYS)), env)
    rescue JSON::ParserError
    log_invalid_body(env)
    end


    private

    def log_invalid_body(env)
    log("#{LOG_INVALID}\n#{env[POST_BODY].read}", env)
    end

    def log(msg, env)
    Rails.logger.info("[#{env[REQUEST_UUID]}}] #{LOG_HEADER}:\n#{msg}")
    end

    def ignored?(report)
    return unless report[BLOCKED_URI_KEY]

    IGNORE.any? { |d| d =~ report[BLOCKED_URI_KEY] }
    end
    end