jekyll_flexible_include 2.0.7 → 2.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38eeb054b62e4c999437208ce95cb7291174167839d344db3df48670954b32ef
4
- data.tar.gz: '0962b93b74c28d6521c655cea29f0646c11051d8f3ddaf690618b9ed91e7276b'
3
+ metadata.gz: d22302901e7a454d82515fd64525479b2932d8856ee572565ea2fae672b6bb28
4
+ data.tar.gz: 22c05d1fb04c976ebfb6a7e005e998bc572a21cb5fce7eed4ac2284c38840c2e
5
5
  SHA512:
6
- metadata.gz: 2e3ec9a8c269bda135c245a54b995abd6f2229658190a5f89e1bad97372e85f4a37af7c7227cbaa8b518f1ae90a6b70ecc3f0f9c881859329fdf241123047ca6
7
- data.tar.gz: b8f6497ee5505ac0f43f28a6c9d0bb639be7b7c2bc712375f8ae6774522ccaae6ecd0108be1bdcb0d72cb1656e0b1a268ffa52d3feb675722295aca6c698961d
6
+ metadata.gz: 04f0e0f7a4ecc3e929a2084311b4c6ee70483256117703a56642c78ab2962b8cfdbc01d3e027dc9fbae188ee8d2adeac202bb0dfa28759ff32fa46262d5c985c
7
+ data.tar.gz: 66b2a6c4383bcc86d5492a0c4155ecbf168e0dedd1c680d005a5f5cfca319b978158c5e71d2027f58109ae9ee02d449fd189044cb4860f793945fd2ce6d7971b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 2.0.10 / 2022-04-15
2
+ * Fixed nil pointer
3
+
4
+ ## 2.0.9 / 2022-04-15
5
+ * Changed how path matching was implemented.
6
+
7
+ ## 2.0.8 / 2022-04-14
8
+ * Added the ability to restrict arbitrary command execution, and specify the allowable directories to read from.
9
+
1
10
  ## 2.0.7 / 2022-04-14
2
11
  * Added `file=` option, so the included file or process is better defined. This option is not required; the file/process can be specified without it as before.
3
12
  * Documented `data-lt-active="false"`.
data/README.md CHANGED
@@ -48,6 +48,37 @@ The following options imply `pre`:
48
48
  * `label` specifies that an automatically generated label be placed above the contents. There is no need to specify this option if `download` or `copy_button` options are provided.
49
49
  * `label="blah blah"` specifies a label for the contents; this value overrides the default label. The value can be enclosed in single or double quotes.
50
50
 
51
+ ### Restricting Directory Access
52
+ By default, `flexible_include` can read from all directories according to the permissions of the user account that launched the `jekyll` process.
53
+ For security-conscience environments, the accessible paths can be restricted.
54
+
55
+ Defining an environment variable called `FLEXIBLE_INCLUDE_PATHS` prior to launching Jekyll will restrict the paths that `flexible_include` will be able to read from.
56
+ This environment variable consists of a colon-delimited set of
57
+ [file and directory glob patterns](https://docs.ruby-lang.org/en/2.7.0/Dir.html#method-c-glob).
58
+ For example, the following restricts access to only the files within:
59
+ 1. The `~/my_dir` directory tree of the account of the user that launched Jekyll.
60
+ 2. The directory tree rooted at `/var/files`.
61
+ 3. The directory tree rooted at the expanded value of the `$work` environment variable.
62
+ ```shell
63
+ export FLEXIBLE_INCLUDE_PATHS='~/.*:$sites/.*:$work/.*'
64
+ ```
65
+ Note that the above matches dot (hidden) files as well as regular files.
66
+ To just match visible files:
67
+ ```shell
68
+ export FLEXIBLE_INCLUDE_PATHS='~/my_dir/**/*:/var/files/**/*:$work/**/*'
69
+ ```
70
+
71
+ #### Note
72
+ The specified directories are traversed when the plugin starts, and the filenames are stored in memory. Directories with lots of files might take a noticable amount to time to enumerate the files.
73
+
74
+
75
+ ### Restricting Arbitrary Processes
76
+ By default, `flexible_include` can execute any command. You can disable that by setting the environment variable `DISABLE_FLEXIBLE_INCLUDE` to any non-empty value.
77
+ ```shell
78
+ export DISABLE_FLEXIBLE_INCLUDE=true
79
+ ```
80
+
81
+ If a potential command execution is intercepted, a big red message will appear on the generated web page that says `Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.`, and a red error message will be logged on the console that says something like: `ERROR FlexibleInclude: _posts/2020/2020-10-03-jekyll-plugins.html - Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.`
51
82
 
52
83
 
53
84
  ## Installation
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllFlexibleIncludePluginVersion
4
- VERSION = "2.0.7"
4
+ VERSION = "2.0.10"
5
5
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "benchmark"
3
4
  require "jekyll"
4
5
  require "jekyll_plugin_logger"
5
6
  require "securerandom"
@@ -13,6 +14,36 @@ end
13
14
  class FlexibleInclude < Liquid::Tag
14
15
  FlexibleIncludeError = Class.new(Liquid::Error)
15
16
 
17
+ @read_regexes = nil
18
+
19
+ def self.normalize_path(path)
20
+ JekyllTagHelper.expand_env(path)
21
+ .gsub("~", Dir.home)
22
+ end
23
+
24
+ # If FLEXIBLE_INCLUDE_PATHS='~/lib/.*:.*:$WORK/.*'
25
+ # Then @read_regexes will be set to regexes of ["/home/my_user_id/lib/.*", "/pwd/.*", "/work/envar/path/.*"]
26
+ def self.security_check
27
+ @execution_denied = ENV['DISABLE_FLEXIBLE_INCLUDE']
28
+
29
+ unless @read_regexes
30
+ flexible_include_paths = ENV['FLEXIBLE_INCLUDE_PATHS']
31
+ read_paths = normalize_path(flexible_include_paths) if flexible_include_paths
32
+ if read_paths
33
+ @read_regexes = read_paths.split(":").map do |path|
34
+ abs_path = path.start_with?('/') ? path : (Pathname.new(Dir.pwd) + path).to_s
35
+ Regexp.new(abs_path)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.access_allowed(path)
42
+ return true unless @read_regexes
43
+
44
+ @read_regexes.find { |regex| regex.match(normalize_path(path)) }
45
+ end
46
+
16
47
  # @param tag_name [String] the name of the tag, which we already know.
17
48
  # @param markup [String] the arguments from the tag, as a single string.
18
49
  # @param parse_context [Liquid::ParseContext] hash that stores Liquid options.
@@ -24,6 +55,8 @@ class FlexibleInclude < Liquid::Tag
24
55
  super
25
56
  @logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
26
57
  @helper = JekyllTagHelper.new(tag_name, markup, @logger)
58
+
59
+ self.class.security_check
27
60
  end
28
61
 
29
62
  # @param liquid_context [Liquid::Context]
@@ -36,6 +69,7 @@ class FlexibleInclude < Liquid::Tag
36
69
  @label_specified = @label
37
70
  @copy_button = @helper.parameter_specified? "copyButton"
38
71
  @pre = @copy_button || @dark || @download || @label_specified || @helper.parameter_specified?("pre") # Download or label implies pre
72
+
39
73
  filename = @helper.parameter_specified? "file"
40
74
  filename ||= @helper.params.first # Do this after all options have been checked for
41
75
  @label ||= filename
@@ -48,13 +82,19 @@ class FlexibleInclude < Liquid::Tag
48
82
  path = JekyllTagHelper.expand_env(filename)
49
83
  case path
50
84
  when /\A\// # Absolute path
85
+ return denied("Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.") unless self.class.access_allowed(path)
86
+
51
87
  @logger.debug { "Absolute path=#{path}, filename=#{filename}" }
52
88
  when /\A~/ # Relative path to user's home directory
89
+ return denied("Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.") unless self.class.access_allowed(path)
90
+
53
91
  @logger.debug { "User home start filename=#{filename}, path=#{path}" }
54
92
  filename.slice! "~/"
55
93
  path = File.join(ENV['HOME'], filename)
56
94
  @logger.debug { "User home end filename=#{filename}, path=#{path}" }
57
95
  when /\A!/ # Run command and return response
96
+ return denied("Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.") if @execution_denied
97
+
58
98
  filename = JekyllTagHelper.remove_quotes(@helper.argv.first) if @helper.argv.first
59
99
  filename.slice! "!"
60
100
  contents = run(filename)
@@ -72,6 +112,11 @@ class FlexibleInclude < Liquid::Tag
72
112
 
73
113
  private
74
114
 
115
+ def denied(msg)
116
+ @logger.error("#{@helper.page.path} - #{msg}")
117
+ "<p style='color: white; background-color: red; padding: 2pt 1em 2pt 1em;'>#{msg}</p>"
118
+ end
119
+
75
120
  def read_file(file)
76
121
  File.read(file)
77
122
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shellwords"
4
+ require 'key_value_parser'
4
5
 
5
6
  class JekyllTagHelper
6
7
  attr_reader :argv, :liquid_context, :logger, :params, :tag_name
@@ -9,7 +10,7 @@ class JekyllTagHelper
9
10
  string.gsub("{", "&#123;").gsub("}", "&#125;").gsub("<", "&lt;")
10
11
  end
11
12
 
12
- # Expand environment variable references
13
+ # Expand a environment variable reference
13
14
  def self.expand_env(str)
14
15
  str.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) { ENV[Regexp.last_match(1)] }
15
16
  end
data/spec/glob_spec.rb ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lib/flexible_include"
4
+
5
+ RSpec.describe(FlexibleInclude) do
6
+ it "controls access to files" do
7
+ ENV['FLEXIBLE_INCLUDE_PATHS'] = '~/.*:spec/.*'
8
+
9
+ FlexibleInclude.send(:new, 'my_tag', "", Liquid::ParseContext.new)
10
+ FlexibleInclude.security_check
11
+ expect(FlexibleInclude.access_allowed(__FILE__)).to be_truthy
12
+
13
+ expect(FlexibleInclude.access_allowed("~/.mem_settings.yaml")).to be_truthy
14
+
15
+ home_file = JekyllTagHelper.expand_env("$HOME/.mem_settings.yaml")
16
+ expect(FlexibleInclude.access_allowed(home_file)).to be_truthy
17
+
18
+ expect(FlexibleInclude.access_allowed('/asdf')).to be_falsey
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+
5
+ require_relative "../lib/flexible_include"
6
+
7
+ Jekyll.logger.log_level = :info
8
+
9
+ RSpec.configure do |config|
10
+ config.filter_run :focus
11
+ config.order = "random"
12
+ config.run_all_when_everything_filtered = true
13
+
14
+ # See https://relishapp.com/rspec/rspec-core/docs/command-line/only-failures
15
+ config.example_status_persistence_file_path = "spec/status_persistence.txt"
16
+ end
@@ -0,0 +1,3 @@
1
+ example_id | status | run_time |
2
+ ------------------------ | ------ | --------------- |
3
+ ./spec/glob_spec.rb[1:1] | passed | 0.01217 seconds |
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll_flexible_include
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.7
4
+ version: 2.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Slinn
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2022-04-14 00:00:00.000000000 Z
13
+ date: 2022-04-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: jekyll
@@ -88,6 +88,9 @@ files:
88
88
  - lib/flexible_include.rb
89
89
  - lib/flexible_include/version.rb
90
90
  - lib/jekyll_tag_helper.rb
91
+ - spec/glob_spec.rb
92
+ - spec/spec_helper.rb
93
+ - spec/status_persistence.txt
91
94
  homepage: https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#flexibleInclude
92
95
  licenses:
93
96
  - MIT
@@ -120,4 +123,7 @@ signing_key:
120
123
  specification_version: 4
121
124
  summary: Jekyll plugin supports various ways to include content into the generated
122
125
  site.
123
- test_files: []
126
+ test_files:
127
+ - spec/glob_spec.rb
128
+ - spec/spec_helper.rb
129
+ - spec/status_persistence.txt