grover 1.1.7 → 1.1.9

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: e791010e46d99d4347edc987fc2a7b90b08d038a1449cf927b57b828e58f1d59
4
- data.tar.gz: 62837cb867ec1728c000c91b33352f01a1c4beb5b0823647dff74946d09c0cbd
3
+ metadata.gz: 82b9a3edaa4f0d20c326f3e79d61b1d2a0190454ac9ffb6867191a9607ec1ea0
4
+ data.tar.gz: 4e71234c41bf19c88a5b44c0a043a1c2ae5a74f94d0606fb0cd627c02e8511a2
5
5
  SHA512:
6
- metadata.gz: 1471dda6f9817f921ae1f7ef88cbdf0ffab143448b1057ef1e3f71f5d91a9fce95b77fbece05f10c0823b51fbc181a4138ae5a6c6db437074c2d95143e90fa4d
7
- data.tar.gz: fad1c63de120fb414aa23ffcf0b2adec6e419550ec2add1c5008ce3e1d855343934bbae4de1e9a14f8c74e974699ad7a74aad2a22bc2d6ef7ba1cd8850a73b36
6
+ metadata.gz: 737079399ecd8ef4c6a88c535495a2aa52fcb9e2e8fa0ca171d3453b58134d795237e606487a9f3dd06afad73baac8fac834a76a33445a1099e168f929f13835
7
+ data.tar.gz: 767e6ebf33ce60336d9afd71859e004b54765910136387ed81b44a12489de7728ff88d0b5f33890dea45314a58476972d31a1f3e9e5e7834f6d68979acf0eb19
@@ -7,7 +7,7 @@ class Grover
7
7
  class Configuration
8
8
  attr_accessor :options, :meta_tag_prefix, :ignore_path, :ignore_request,
9
9
  :root_url, :use_pdf_middleware, :use_png_middleware,
10
- :use_jpeg_middleware
10
+ :use_jpeg_middleware, :node_env_vars, :allow_file_uris
11
11
 
12
12
  def initialize
13
13
  @options = {}
@@ -18,6 +18,8 @@ class Grover
18
18
  @use_pdf_middleware = true
19
19
  @use_png_middleware = false
20
20
  @use_jpeg_middleware = false
21
+ @node_env_vars = {}
22
+ @allow_file_uris = false
21
23
  end
22
24
  end
23
25
  end
data/lib/grover/errors.rb CHANGED
@@ -15,4 +15,5 @@ class Grover
15
15
  const_set name, Class.new(Error)
16
16
  end
17
17
  end
18
+ UnsafeConfigurationError = Class.new(Error)
18
19
  end
@@ -26,7 +26,7 @@ const fs = require('fs');
26
26
  const os = require('os');
27
27
  const path = require('path');
28
28
 
29
- const _processPage = (async (convertAction, urlOrHtml, options) => {
29
+ const _processPage = (async (convertAction, uriOrHtml, options) => {
30
30
  let browser, page, errors = [], tmpDir, wsConnection = false;
31
31
 
32
32
  try {
@@ -100,6 +100,7 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
100
100
  }
101
101
 
102
102
  // Setup timeout option (if provided)
103
+ if (options.timeout === null) delete options.timeout;
103
104
  let requestOptions = {};
104
105
  let requestTimeout = options.requestTimeout; delete options.requestTimeout;
105
106
  if (requestTimeout === undefined) requestTimeout = options.timeout;
@@ -172,10 +173,12 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
172
173
  }
173
174
 
174
175
  const waitUntil = options.waitUntil; delete options.waitUntil;
175
- if (urlOrHtml.match(/^http/i)) {
176
+ const allowFileUri = options.allowFileUri; delete options.allowFileUri;
177
+ const uriRegex = allowFileUri ? /^(https?|file):\/\//i : /^https?:\/\//i;
178
+ if (uriOrHtml.match(uriRegex)) {
176
179
  // Request is for a URL, so request it
177
180
  requestOptions.waitUntil = waitUntil || 'networkidle2';
178
- await page.goto(urlOrHtml, requestOptions);
181
+ await page.goto(uriOrHtml, requestOptions);
179
182
  } else {
180
183
  // Request is some HTML content. Use request interception to assign the body
181
184
  requestOptions.waitUntil = waitUntil || 'networkidle0';
@@ -187,7 +190,7 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
187
190
  request.continue();
188
191
  else {
189
192
  htmlIntercepted = true
190
- request.respond({ body: urlOrHtml === '' ? ' ' : urlOrHtml });
193
+ request.respond({ body: uriOrHtml === '' ? ' ' : uriOrHtml });
191
194
  }
192
195
  });
193
196
  const displayUrl = options.displayUrl; delete options.displayUrl;
@@ -24,11 +24,14 @@ class Grover
24
24
  dup._call(env)
25
25
  end
26
26
 
27
- def _call(env)
27
+ def _call(env) # rubocop:disable Metrics/MethodLength
28
28
  @request = Rack::Request.new(env)
29
29
  identify_request_type
30
30
 
31
- configure_env_for_grover_request(env) if grover_request?
31
+ if grover_request?
32
+ check_file_uri_configuration
33
+ configure_env_for_grover_request(env)
34
+ end
32
35
  status, headers, response = @app.call(env)
33
36
  response = update_response response, headers if grover_request? && html_content?(headers)
34
37
 
@@ -45,6 +48,14 @@ class Grover
45
48
 
46
49
  attr_reader :pdf_request, :png_request, :jpeg_request
47
50
 
51
+ def check_file_uri_configuration
52
+ return unless Grover.configuration.allow_file_uris
53
+
54
+ # The combination of middleware and allowing file URLs is exceptionally
55
+ # unsafe as it can lead to data exfiltration from the host system.
56
+ raise UnsafeConfigurationError, 'using `allow_file_uris` configuration with middleware is exceptionally unsafe'
57
+ end
58
+
48
59
  def identify_request_type
49
60
  @pdf_request = Grover.configuration.use_pdf_middleware && path_matches?(PDF_REGEX)
50
61
  @png_request = Grover.configuration.use_png_middleware && path_matches?(PNG_REGEX)
@@ -8,12 +8,12 @@ class Grover
8
8
  # Build options from Grover.configuration, meta_options, and passed-in options
9
9
  #
10
10
  class OptionsBuilder < Hash
11
- def initialize(options, url)
11
+ def initialize(options, uri)
12
12
  super()
13
- @url = url
13
+ @uri = uri
14
14
  combined = grover_configuration
15
15
  Utils.deep_merge! combined, Utils.deep_stringify_keys(options)
16
- Utils.deep_merge! combined, meta_options unless url_source?
16
+ Utils.deep_merge! combined, meta_options unless uri_source?
17
17
 
18
18
  update OptionsFixer.new(combined).run
19
19
  end
@@ -41,11 +41,11 @@ class Grover
41
41
  end
42
42
 
43
43
  def meta_tags
44
- Nokogiri::HTML(@url).xpath('//meta')
44
+ Nokogiri::HTML(@uri).xpath('//meta')
45
45
  end
46
46
 
47
- def url_source?
48
- @url.match(/\Ahttp/i)
47
+ def uri_source?
48
+ @uri.match?(%r{\A(https?|file)://}i)
49
49
  end
50
50
  end
51
51
  end
@@ -32,6 +32,7 @@ class Grover
32
32
 
33
33
  def spawn_process
34
34
  @stdin, @stdout, @stderr, @wait_thr = Open3.popen3(
35
+ Grover.configuration.node_env_vars,
35
36
  'node',
36
37
  File.expand_path(File.join(__dir__, 'js/processor.cjs')),
37
38
  chdir: app_root
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Grover
4
- VERSION = '1.1.7'
4
+ VERSION = '1.1.9'
5
5
  end
data/lib/grover.rb CHANGED
@@ -28,40 +28,40 @@ class Grover
28
28
  attr_reader :front_cover_path, :back_cover_path
29
29
 
30
30
  #
31
- # @param [String] url URL of the page to convert
31
+ # @param [String] uri URI of the page to convert
32
32
  # @param [Hash] options Optional parameters to pass to PDF processor
33
33
  # see https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.pdfoptions.md
34
34
  # and https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.screenshotoptions.md
35
35
  #
36
- def initialize(url, **options)
37
- @url = url.to_s
38
- @options = OptionsBuilder.new(options, @url)
36
+ def initialize(uri, **options)
37
+ @uri = uri.to_s
38
+ @options = OptionsBuilder.new(options, @uri)
39
39
  @root_path = @options.delete 'root_path'
40
40
  @front_cover_path = @options.delete 'front_cover_path'
41
41
  @back_cover_path = @options.delete 'back_cover_path'
42
42
  end
43
43
 
44
44
  #
45
- # Request URL with provided options and create PDF
45
+ # Request URI with provided options and create PDF
46
46
  #
47
47
  # @param [String] path Optional path to write the PDF to
48
48
  # @return [String] The resulting PDF data
49
49
  #
50
50
  def to_pdf(path = nil)
51
- processor.convert :pdf, @url, normalized_options(path: path)
51
+ processor.convert :pdf, @uri, normalized_options(path: path)
52
52
  end
53
53
 
54
54
  #
55
- # Request URL with provided options and render HTML
55
+ # Request URI with provided options and render HTML
56
56
  #
57
57
  # @return [String] The resulting HTML string
58
58
  #
59
59
  def to_html
60
- processor.convert :content, @url, normalized_options(path: nil)
60
+ processor.convert :content, @uri, normalized_options(path: nil)
61
61
  end
62
62
 
63
63
  #
64
- # Request URL with provided options and create screenshot
64
+ # Request URI with provided options and create screenshot
65
65
  #
66
66
  # @param [String] path Optional path to write the screenshot to
67
67
  # @param [String] format Optional format of the screenshot
@@ -70,11 +70,11 @@ class Grover
70
70
  def screenshot(path: nil, format: nil)
71
71
  options = normalized_options(path: path)
72
72
  options['type'] = format if %w[png jpeg].include? format
73
- processor.convert :screenshot, @url, options
73
+ processor.convert :screenshot, @uri, options
74
74
  end
75
75
 
76
76
  #
77
- # Request URL with provided options and create PNG
77
+ # Request URI with provided options and create PNG
78
78
  #
79
79
  # @param [String] path Optional path to write the screenshot to
80
80
  # @return [String] The resulting PNG data
@@ -84,7 +84,7 @@ class Grover
84
84
  end
85
85
 
86
86
  #
87
- # Request URL with provided options and create JPEG
87
+ # Request URI with provided options and create JPEG
88
88
  #
89
89
  # @param [String] path Optional path to write the screenshot to
90
90
  # @return [String] The resulting JPEG data
@@ -116,10 +116,10 @@ class Grover
116
116
  #
117
117
  def inspect
118
118
  format(
119
- '#<%<class_name>s:0x%<object_id>p @url="%<url>s">',
119
+ '#<%<class_name>s:0x%<object_id>p @uri="%<uri>s">',
120
120
  class_name: self.class.name,
121
121
  object_id: object_id,
122
- url: @url
122
+ uri: @uri
123
123
  )
124
124
  end
125
125
 
@@ -147,6 +147,7 @@ class Grover
147
147
  def normalized_options(path:)
148
148
  normalized_options = Utils.normalize_object @options, excluding: ['extraHTTPHeaders']
149
149
  normalized_options['path'] = path if path.is_a? ::String
150
+ normalized_options['allowFileUri'] = Grover.configuration.allow_file_uris == true
150
151
  normalized_options
151
152
  end
152
153
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grover
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.7
4
+ version: 1.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Bromwich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-09 00:00:00.000000000 Z
11
+ date: 2024-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: combine_pdf
@@ -38,180 +38,6 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
- - !ruby/object:Gem::Dependency
42
- name: childprocess
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: mini_magick
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '4.12'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '4.12'
69
- - !ruby/object:Gem::Dependency
70
- name: pdf-reader
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '2.11'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '2.11'
83
- - !ruby/object:Gem::Dependency
84
- name: puma
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '6.4'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '6.4'
97
- - !ruby/object:Gem::Dependency
98
- name: rack-test
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '1.1'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '1.1'
111
- - !ruby/object:Gem::Dependency
112
- name: rake
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '13.0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '13.0'
125
- - !ruby/object:Gem::Dependency
126
- name: rspec
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '3.12'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: '3.12'
139
- - !ruby/object:Gem::Dependency
140
- name: rubocop
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: '1.43'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - "~>"
151
- - !ruby/object:Gem::Version
152
- version: '1.43'
153
- - !ruby/object:Gem::Dependency
154
- name: rubocop-rake
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - "~>"
158
- - !ruby/object:Gem::Version
159
- version: '0.6'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - "~>"
165
- - !ruby/object:Gem::Version
166
- version: '0.6'
167
- - !ruby/object:Gem::Dependency
168
- name: rubocop-rspec
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - "~>"
172
- - !ruby/object:Gem::Version
173
- version: '2.18'
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - "~>"
179
- - !ruby/object:Gem::Version
180
- version: '2.18'
181
- - !ruby/object:Gem::Dependency
182
- name: sinatra
183
- requirement: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - "~>"
186
- - !ruby/object:Gem::Version
187
- version: '3.2'
188
- type: :development
189
- prerelease: false
190
- version_requirements: !ruby/object:Gem::Requirement
191
- requirements:
192
- - - "~>"
193
- - !ruby/object:Gem::Version
194
- version: '3.2'
195
- - !ruby/object:Gem::Dependency
196
- name: simplecov
197
- requirement: !ruby/object:Gem::Requirement
198
- requirements:
199
- - - "~>"
200
- - !ruby/object:Gem::Version
201
- version: '0.17'
202
- - - "<"
203
- - !ruby/object:Gem::Version
204
- version: '0.18'
205
- type: :development
206
- prerelease: false
207
- version_requirements: !ruby/object:Gem::Requirement
208
- requirements:
209
- - - "~>"
210
- - !ruby/object:Gem::Version
211
- version: '0.17'
212
- - - "<"
213
- - !ruby/object:Gem::Version
214
- version: '0.18'
215
41
  description: Transform HTML into PDF/PNG/JPEG using Google Puppeteer/Chromium
216
42
  email:
217
43
  - abromwich@studiosity.com