jekyll_flexible_include 2.0.7 → 2.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +31 -0
- data/lib/flexible_include/version.rb +1 -1
- data/lib/flexible_include.rb +45 -0
- data/lib/jekyll_tag_helper.rb +2 -1
- data/spec/glob_spec.rb +20 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/status_persistence.txt +3 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d22302901e7a454d82515fd64525479b2932d8856ee572565ea2fae672b6bb28
|
4
|
+
data.tar.gz: 22c05d1fb04c976ebfb6a7e005e998bc572a21cb5fce7eed4ac2284c38840c2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/flexible_include.rb
CHANGED
@@ -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
|
data/lib/jekyll_tag_helper.rb
CHANGED
@@ -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("{", "{").gsub("}", "}").gsub("<", "<")
|
10
11
|
end
|
11
12
|
|
12
|
-
# Expand environment variable
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
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.
|
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-
|
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
|