CVE-2020-14001: Missing Authorization (CWE-862)
value: The kramdown gem before 2.3.0 for Ruby processes the template option inside Kramdown documents by default, which allows unintended read access (such as template="/etc/passwd") or unintended embedded Ruby code execution (such as a string that begins with template="string://<%= `). NOTE: kramdown is used in Jekyll, GitLab Pages, GitHub Pages, and Thredded Forum.
CWE description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
CVSS v3 base score: 9.8
Published: 2020-07-17T16:15Z
Repository: https://github.com/gettalong/kramdown
Language: Ruby
Files changed: lib/kramdown/options.rb, lib/kramdown/parser/kramdown/extensions.rb, test/testcases/block/12_extension/options.text
Commit date: 2020-06-27 14:47:21 +0200
Commit message: Add option forbidden_inline_options

It is sometimes necessary to restrict the options that can be set
inline, ie. using the {::options ...} extensions.

By default, the template option is now forbidden to avoid possible
security problems. This addresses CVE-2020-14001.

Diff: diff --git a/lib/kramdown/options.rb b/lib/kramdown/options.rb
index ea67913..0214e28 100644
--- a/lib/kramdown/options.rb
+++ b/lib/kramdown/options.rb
@@ -587,8 +587,18 @@ module Kramdown
 
       Default: false
       Used by: HTML converter
     EOF
 
+    define(:forbidden_inline_options, Object, %w[template], <<~EOF) do |val|
+      Defines the options that may not be set using the {::options} extension
+
+      Default: template
+      Used by: HTML converter
+    EOF
+      val.map! {|item| item.kind_of?(String) ? str_to_sym(item) : item }
+      simple_array_validator(val, :forbidden_inline_options)
+    end
+
   end
 
 end
diff --git a/lib/kramdown/parser/kramdown/extensions.rb b/lib/kramdown/parser/kramdown/extensions.rb
index 493dcf7..637d0fa 100644
--- a/lib/kramdown/parser/kramdown/extensions.rb
+++ b/lib/kramdown/parser/kramdown/extensions.rb
@@ -108,10 +108,16 @@ module Kramdown
           true
         when 'options'
           opts.select do |k, v|
             k = k.to_sym
             if Kramdown::Options.defined?(k)
+              if @options[:forbidden_inline_options].include?(k) ||
+                  k == :forbidden_inline_options
+                warning("Option #{k} may not be set inline")
+                next false
+              end
+
               begin
                 val = Kramdown::Options.parse(k, v)
                 @options[k] = val
                 (@root.options[:options] ||= {})[k] = val
               rescue StandardError
diff --git a/test/testcases/block/12_extension/options.text b/test/testcases/block/12_extension/options.text
index 5991ab7..b63f34b 100644
--- a/test/testcases/block/12_extension/options.text
+++ b/test/testcases/block/12_extension/options.text
@@ -17,5 +17,7 @@ some <span>*para*</span>
 {::options footnote_nr="10" /}
 
 Some text[^ab].
 
 [^ab]: Some text.
+
+{::options template="/etc/passwd" /}

Fixed code:     define(:forbidden_inline_options, Object, %w[template], <<~EOF) do |val|
      Defines the options that may not be set using the {::options} extension

      Default: template
      Used by: HTML converter
    EOF
      val.map! {|item| item.kind_of?(String) ? str_to_sym(item) : item }
      simple_array_validator(val, :forbidden_inline_options)
    end

              if @options[:forbidden_inline_options].include?(k) ||
                  k == :forbidden_inline_options
                warning("Option #{k} may not be set inline")
                next false
              end


{::options template="/etc/passwd" /}