jekyll-remote-theme 0.5.2 → 0.6.0
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/lib/jekyll-remote-theme/downloader.rb +46 -54
- data/lib/jekyll-remote-theme/http.rb +48 -0
- data/lib/jekyll-remote-theme/munger.rb +6 -2
- data/lib/jekyll-remote-theme/theme.rb +1 -5
- data/lib/jekyll-remote-theme/version.rb +1 -1
- data/lib/jekyll-remote-theme.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e8b60407212cb023740ee7d4258e9a02c862bed37246db406607af4d3e6228e8
|
|
4
|
+
data.tar.gz: 448d9d30e4f0e0a2efa300c1ab7bdd8199ff82fcc4afd97264227892d6ab5fe3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 36eacae972f56890144e128ec284067fa33f03f528b4f70047b0c60e4f90e80f04426990a6abb6ea87d1d6cbecac1b12effec5dcafbe89c7eb6abd9d1160c357
|
|
7
|
+
data.tar.gz: '0871deb358244f78aa294b171af43415eb0bed1367c3224554992c1723dbae092d48d0bc321c116fd0297b1e22d7847e030599c748a6fef5b5192a9f1ccdfa93'
|
|
@@ -6,6 +6,9 @@ module Jekyll
|
|
|
6
6
|
PROJECT_URL = "https://github.com/benbalter/jekyll-remote-theme"
|
|
7
7
|
USER_AGENT = "Jekyll Remote Theme/#{VERSION} (+#{PROJECT_URL})"
|
|
8
8
|
MAX_FILE_SIZE = 1 * (1024 * 1024 * 1024) # Size in bytes (1 GB)
|
|
9
|
+
# Limit on the theme's total size once extracted, to stop zip bombs
|
|
10
|
+
MAX_EXTRACTED_SIZE = 2 * (1024 * 1024 * 1024) # Size in bytes (2 GB)
|
|
11
|
+
EXTRACT_CHUNK_SIZE = 64 * 1024
|
|
9
12
|
NET_HTTP_ERRORS = [
|
|
10
13
|
Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::OpenTimeout,
|
|
11
14
|
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
|
|
@@ -45,23 +48,30 @@ module Jekyll
|
|
|
45
48
|
@zip_file ||= Tempfile.new([TEMP_PREFIX, ".zip"], :binmode => true)
|
|
46
49
|
end
|
|
47
50
|
|
|
48
|
-
# rubocop:disable Metrics/AbcSize
|
|
49
51
|
def download
|
|
50
52
|
Jekyll.logger.debug LOG_KEY, "Downloading #{zip_url} to #{zip_file.path}"
|
|
51
|
-
|
|
53
|
+
HTTP.start(zip_url, :use_ssl => true) do |http|
|
|
52
54
|
http.request(request) do |response|
|
|
53
55
|
raise_unless_success(response)
|
|
54
56
|
enforce_max_file_size(response.content_length)
|
|
55
|
-
response
|
|
56
|
-
zip_file.write chunk
|
|
57
|
-
end
|
|
57
|
+
write_body(response)
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
@downloaded = true
|
|
61
61
|
rescue *NET_HTTP_ERRORS => e
|
|
62
62
|
raise DownloadError, e.message
|
|
63
63
|
end
|
|
64
|
-
|
|
64
|
+
|
|
65
|
+
# Content-Length is optional (e.g. chunked responses), so also count the
|
|
66
|
+
# bytes as they arrive and stop once the limit is exceeded.
|
|
67
|
+
def write_body(response)
|
|
68
|
+
bytes = 0
|
|
69
|
+
response.read_body do |chunk|
|
|
70
|
+
bytes += chunk.bytesize
|
|
71
|
+
enforce_max_file_size(bytes)
|
|
72
|
+
zip_file.write chunk
|
|
73
|
+
end
|
|
74
|
+
end
|
|
65
75
|
|
|
66
76
|
def request
|
|
67
77
|
return @request if defined? @request
|
|
@@ -102,6 +112,7 @@ module Jekyll
|
|
|
102
112
|
# is now resolved relative to a `destination_directory:` keyword,
|
|
103
113
|
# mangling the absolute paths we pass). Reading each entry's stream and
|
|
104
114
|
# writing it ourselves behaves identically across rubyzip 1.x–3.x.
|
|
115
|
+
@extracted_size = 0
|
|
105
116
|
Zip::File.open(zip_file) do |archive|
|
|
106
117
|
archive.each { |entry| extract_entry(entry) }
|
|
107
118
|
end
|
|
@@ -116,64 +127,45 @@ module Jekyll
|
|
|
116
127
|
def extract_entry(entry)
|
|
117
128
|
return if entry.name.end_with?("/") # skip directory entries
|
|
118
129
|
|
|
130
|
+
enforce_max_extracted_size(@extracted_size + entry.size)
|
|
119
131
|
dest = path_without_name_and_ref(entry.name)
|
|
120
132
|
FileUtils.mkdir_p File.dirname(dest)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# Full URL to codeload zip download endpoint for the given theme
|
|
125
|
-
def zip_url
|
|
126
|
-
@zip_url ||= Addressable::URI.new(
|
|
127
|
-
:scheme => theme.scheme,
|
|
128
|
-
:host => "codeload.#{theme.host}",
|
|
129
|
-
:path => [theme.owner, theme.name, "zip", theme.git_ref].join("/")
|
|
130
|
-
).normalize
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Returns an HTTP class that respects proxy environment variables
|
|
134
|
-
def http_class
|
|
135
|
-
@http_class ||= Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Extracts proxy settings from environment variables
|
|
139
|
-
def proxy_uri
|
|
140
|
-
return @proxy_uri if defined?(@proxy_uri)
|
|
141
|
-
|
|
142
|
-
proxy_env = find_proxy_env_var
|
|
143
|
-
@proxy_uri = parse_proxy_uri(proxy_env)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def find_proxy_env_var
|
|
147
|
-
# Check for HTTPS proxy first if the URL uses HTTPS
|
|
148
|
-
if theme.scheme == "https"
|
|
149
|
-
ENV["https_proxy"] || ENV["HTTPS_PROXY"] || ENV["http_proxy"] || ENV["HTTP_PROXY"]
|
|
150
|
-
else
|
|
151
|
-
ENV["http_proxy"] || ENV["HTTP_PROXY"]
|
|
133
|
+
File.open(dest, "wb") do |output|
|
|
134
|
+
entry.get_input_stream { |input| copy_entry(entry, input, output) }
|
|
152
135
|
end
|
|
153
136
|
end
|
|
154
137
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
138
|
+
# Copies an entry in chunks, counting the bytes actually inflated rather
|
|
139
|
+
# than trusting the sizes in the archive. Zip.validate_entry_sizes only
|
|
140
|
+
# applies to Zip::Entry#extract, so the same check is made here: an entry
|
|
141
|
+
# may not inflate to more than its declared size.
|
|
142
|
+
def copy_entry(entry, input, output)
|
|
143
|
+
written = 0
|
|
144
|
+
while (chunk = input.read(EXTRACT_CHUNK_SIZE))
|
|
145
|
+
written += chunk.bytesize
|
|
146
|
+
if written > entry.size
|
|
147
|
+
raise DownloadError, "Zip entry #{entry.name} is larger than its declared size"
|
|
148
|
+
end
|
|
162
149
|
|
|
163
|
-
|
|
164
|
-
|
|
150
|
+
@extracted_size += chunk.bytesize
|
|
151
|
+
enforce_max_extracted_size(@extracted_size)
|
|
152
|
+
output.write(chunk)
|
|
153
|
+
end
|
|
165
154
|
end
|
|
166
155
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
end
|
|
156
|
+
def enforce_max_extracted_size(size)
|
|
157
|
+
return unless size > MAX_EXTRACTED_SIZE
|
|
170
158
|
|
|
171
|
-
|
|
172
|
-
proxy_uri&.user
|
|
159
|
+
raise DownloadError, "Maximum extracted theme size of #{MAX_EXTRACTED_SIZE} bytes exceeded"
|
|
173
160
|
end
|
|
174
161
|
|
|
175
|
-
|
|
176
|
-
|
|
162
|
+
# Full URL to codeload zip download endpoint for the given theme
|
|
163
|
+
def zip_url
|
|
164
|
+
@zip_url ||= Addressable::URI.new(
|
|
165
|
+
:scheme => theme.scheme,
|
|
166
|
+
:host => "codeload.#{theme.host}",
|
|
167
|
+
:path => [theme.owner, theme.name, "zip", theme.git_ref].join("/")
|
|
168
|
+
).normalize
|
|
177
169
|
end
|
|
178
170
|
|
|
179
171
|
def theme_dir_exists?
|
|
@@ -181,7 +173,7 @@ module Jekyll
|
|
|
181
173
|
end
|
|
182
174
|
|
|
183
175
|
def theme_dir_empty?
|
|
184
|
-
Dir
|
|
176
|
+
Dir.empty?(theme.root)
|
|
185
177
|
end
|
|
186
178
|
|
|
187
179
|
# Codeload generated zip files contain a top level folder in the form of
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module RemoteTheme
|
|
5
|
+
# Net::HTTP setup shared by the theme download and the GitHub API lookup
|
|
6
|
+
# for @latest, so both use the same proxy settings and timeouts.
|
|
7
|
+
module HTTP
|
|
8
|
+
OPEN_TIMEOUT = 10 # seconds
|
|
9
|
+
READ_TIMEOUT = 60 # seconds
|
|
10
|
+
# Proxy environment variables to try, by request scheme
|
|
11
|
+
PROXY_SCHEMES = {
|
|
12
|
+
"https" => %w(https http).freeze,
|
|
13
|
+
"http" => %w(http).freeze,
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# Opens a connection to the host of `uri` (an Addressable::URI) and
|
|
19
|
+
# yields the Net::HTTP session.
|
|
20
|
+
def start(uri, use_ssl: uri.scheme == "https", env: ENV, &block)
|
|
21
|
+
proxy = proxy_uri(uri, env)
|
|
22
|
+
Net::HTTP.start(
|
|
23
|
+
uri.host, uri.port,
|
|
24
|
+
proxy&.host, proxy&.port, proxy&.user, proxy&.password,
|
|
25
|
+
:use_ssl => use_ssl,
|
|
26
|
+
:open_timeout => OPEN_TIMEOUT,
|
|
27
|
+
:read_timeout => READ_TIMEOUT,
|
|
28
|
+
&block
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The proxy to use for `uri`, from the environment, or nil. HTTPS
|
|
33
|
+
# requests use https_proxy, falling back to http_proxy. Hosts listed in
|
|
34
|
+
# no_proxy/NO_PROXY, and loopback addresses, bypass the proxy.
|
|
35
|
+
def proxy_uri(uri, env = ENV)
|
|
36
|
+
PROXY_SCHEMES.fetch(uri.scheme, PROXY_SCHEMES["http"]).each do |scheme|
|
|
37
|
+
target = URI::Generic.build(:scheme => scheme, :host => uri.host,
|
|
38
|
+
:port => uri.inferred_port)
|
|
39
|
+
proxy = target.find_proxy(env)
|
|
40
|
+
return proxy if proxy
|
|
41
|
+
end
|
|
42
|
+
nil
|
|
43
|
+
rescue URI::Error
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -20,11 +20,13 @@ module Jekyll
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
Jekyll.logger.info LOG_KEY, "Using theme #{theme.name_with_owner}"
|
|
23
|
+
# after_reset fires on every rebuild (e.g. `jekyll serve`). Only the
|
|
24
|
+
# first run downloads the theme, so only it registers the cleanup.
|
|
23
25
|
unless munged?
|
|
24
26
|
downloader.run
|
|
25
27
|
configure_theme
|
|
28
|
+
enqueue_theme_cleanup
|
|
26
29
|
end
|
|
27
|
-
enqueue_theme_cleanup
|
|
28
30
|
|
|
29
31
|
theme
|
|
30
32
|
end
|
|
@@ -35,8 +37,10 @@ module Jekyll
|
|
|
35
37
|
site.theme&.is_a?(Jekyll::RemoteTheme::Theme)
|
|
36
38
|
end
|
|
37
39
|
|
|
40
|
+
# On a rebuild, reuse the theme configured by the first run rather than
|
|
41
|
+
# building a new one, which would create another temp directory.
|
|
38
42
|
def theme
|
|
39
|
-
@theme ||= Theme.new(raw_theme)
|
|
43
|
+
@theme ||= munged? ? site.theme : Theme.new(raw_theme)
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
def raw_theme
|
|
@@ -171,11 +171,7 @@ module Jekyll
|
|
|
171
171
|
end
|
|
172
172
|
|
|
173
173
|
def make_api_request(api_url)
|
|
174
|
-
|
|
175
|
-
api_url.host,
|
|
176
|
-
api_url.port,
|
|
177
|
-
:use_ssl => api_url.scheme == "https"
|
|
178
|
-
) do |http|
|
|
174
|
+
HTTP.start(api_url) do |http|
|
|
179
175
|
request = Net::HTTP::Get.new(api_url.request_uri)
|
|
180
176
|
request["Accept"] = "application/vnd.github.v3+json"
|
|
181
177
|
request["User-Agent"] = Downloader::USER_AGENT
|
data/lib/jekyll-remote-theme.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Jekyll
|
|
|
16
16
|
class DownloadError < StandardError; end
|
|
17
17
|
|
|
18
18
|
autoload :Downloader, "jekyll-remote-theme/downloader"
|
|
19
|
+
autoload :HTTP, "jekyll-remote-theme/http"
|
|
19
20
|
autoload :MockGemspec, "jekyll-remote-theme/mock_gemspec"
|
|
20
21
|
autoload :Munger, "jekyll-remote-theme/munger"
|
|
21
22
|
autoload :Theme, "jekyll-remote-theme/theme"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-remote-theme
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben Balter
|
|
@@ -279,6 +279,7 @@ extra_rdoc_files: []
|
|
|
279
279
|
files:
|
|
280
280
|
- lib/jekyll-remote-theme.rb
|
|
281
281
|
- lib/jekyll-remote-theme/downloader.rb
|
|
282
|
+
- lib/jekyll-remote-theme/http.rb
|
|
282
283
|
- lib/jekyll-remote-theme/mock_gemspec.rb
|
|
283
284
|
- lib/jekyll-remote-theme/munger.rb
|
|
284
285
|
- lib/jekyll-remote-theme/theme.rb
|
|
@@ -294,14 +295,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
294
295
|
requirements:
|
|
295
296
|
- - ">="
|
|
296
297
|
- !ruby/object:Gem::Version
|
|
297
|
-
version:
|
|
298
|
+
version: '3.0'
|
|
298
299
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
299
300
|
requirements:
|
|
300
301
|
- - ">="
|
|
301
302
|
- !ruby/object:Gem::Version
|
|
302
303
|
version: '0'
|
|
303
304
|
requirements: []
|
|
304
|
-
rubygems_version:
|
|
305
|
+
rubygems_version: 3.6.9
|
|
305
306
|
specification_version: 4
|
|
306
307
|
summary: Jekyll plugin for building Jekyll sites with any GitHub-hosted theme
|
|
307
308
|
test_files: []
|