csp_maker 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a645c19c334a4c1b70f46a7b7afec40bd91dd05205de493cd690ca97d254affb
4
+ data.tar.gz: 12ab705a8aa1ca92f4843e3355e068959bb39a234f90cb7e1a4bfc52b6d4c313
5
+ SHA512:
6
+ metadata.gz: 9a41bb604a093794b6ea224093e56e4bfd1272f31e1203ce0feb9673f3545cc39d093901412aeb7f5d036d881a1bbe11b2a43e8c1444fd97a0a91eef7f9439f9
7
+ data.tar.gz: 6a13a9fb9cee3bd78cee4fe158053c1fe75fd0439da1d437e183c31e3aeb19b6c17ec30fa7c2b69353af68f60dca4bd9e27109db852ffdd82b11accc191b0f29
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ ## [Unreleased]
data/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # CSP Maker
2
+
3
+ CSP Maker is a micro gem in Ruby for defining a [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy), extracted from Rails so it can be used standalone or in Rack apps.
4
+
5
+ The code is extracted from [ActionDispatch::ContentSecurityPolicy](https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/http/content_security_policy.rb) in Action Pack, with some minor modifications. The DSL as described in Rails docs should be usable 1:1 here. There is also an optional Rack middleware updated to not rely on Rails internals.
6
+
7
+
8
+ ## Installation
9
+
10
+ Add the gem to your app's `Gemfile`:
11
+
12
+ gem 'csp_maker', '~> 0.1'
13
+
14
+ Then, require it in your server code:
15
+
16
+ ```rb
17
+ require 'csp_maker'
18
+ ```
19
+
20
+ CSP Maker supports Ruby 3.0 and newer.
21
+
22
+
23
+ ## Basic usage
24
+
25
+ The simplest way is through the `CSPMaker.build_policy` method:
26
+
27
+ ```rb
28
+ headers['Content-Security-Policy'] = CSPMaker.build_policy do |p|
29
+ p.default_src :none
30
+ p.script_src :self, 'https://github.com', 'https://example.com'
31
+ p.style_src :self
32
+ p.font_src :self
33
+ p.img_src :self, :https, :data
34
+ p.connect_src :self, 'https://api.bsky.app'
35
+ p.base_uri :none
36
+ p.object_src :none
37
+ end
38
+ ```
39
+
40
+ This creates a `ContentSecurityPolicy` definition object and then immediately encodes it into the final string, which you can assign to the target header.
41
+
42
+ Alternatively, you can also pass a block without parameters and call the DSL methods on an implicit `self` like this:
43
+
44
+ ```rb
45
+ headers['Content-Security-Policy'] = CSPMaker.build_policy do
46
+ default_src :none
47
+ script_src :self, 'https://github.com', 'https://example.com'
48
+ style_src :self
49
+ font_src :self
50
+ img_src :self, :https, :data
51
+ connect_src :self, 'https://api.bsky.app'
52
+ base_uri :none
53
+ object_src :none
54
+ end
55
+ ```
56
+
57
+ Note, in this case (block without parameters) the block is run through `instance_eval` in the context of the policy object, so helper methods and instance vars from outside the block won't be accessible inside; if you want to use those, use the version with a parameter, or call the `ContentSecurityPolicy` constructor directly.
58
+
59
+ If you want to use nonces, you can generate a nonce yourself and pass it to the DSL using a new `nonce()` DSL method like this:
60
+
61
+ ```rb
62
+ asset_nonce = ...
63
+
64
+ headers['Content-Security-Policy'] = CSPMaker.build_policy do
65
+ default_src :none
66
+ script_src :self, nonce(asset_nonce)
67
+ end
68
+ ```
69
+
70
+ A second approach is to use `CSPMaker.define_policy` to create a policy definition once, and then reuse it on each request:
71
+
72
+ ```rb
73
+ CSP = CSPMaker.define_policy do
74
+ default_src :none
75
+ script_src :self, 'https://github.com', 'https://example.com'
76
+ style_src :self
77
+ ...
78
+ end
79
+ ```
80
+
81
+ You encode the policy into the header string by calling `#build(context, nonce, nonce_directives)`. The parameters (all optional) are:
82
+
83
+ - `context` – you can pass a Proc instead of a String/Symbol to a DSL directive, and that Proc will be called with this context object as `self` (it can be e.g. some kind of controller or request object)
84
+ - `nonce` – a nonce to be added to the designated directives
85
+ - `nonce_directives` – array of directives to which nonces should be added; if nil, the list is read from a global setting `CSPMaker.nonce_directives`, or the default `['script-src', 'style-src']`
86
+
87
+ So it can look like this:
88
+
89
+ ```rb
90
+ asset_nonce = ...
91
+
92
+ headers['Content-Security-Policy'] = CSP.build(nil, asset_nonce, ['script-src'])
93
+ ```
94
+
95
+
96
+ ## Rack middleware
97
+
98
+ A third way is to use the Rack middleware. The middleware class is also taken from the Rails code, but simplified to not rely on Rails internals.
99
+
100
+ Require the `csp_maker/rack` file instead of `csp_maker`:
101
+
102
+ ```rb
103
+ require 'csp_maker/rack'
104
+ ```
105
+
106
+ Define a policy, and pass it to the `CSPMaker::Middleware` when installing it into the Rack stack:
107
+
108
+ ```rb
109
+ policy = CSPMaker.define_policy {
110
+ ...
111
+ }
112
+
113
+ use CSPMaker::Middleware, policy
114
+ ```
115
+
116
+ The middleware will:
117
+
118
+ - automatically generate a nonce, append it to the directives and store it in the Rack `env`, if a nonce generator is configured (see below)
119
+ - store the generated nonce in the `env` under `csp_maker.nonce` (`CSPMaker::Middleware::NONCE_ENV_KEY`)
120
+ - create a `Rack::Request` object and use it as the block context for any Procs in the DSL
121
+ - encode the policy into a result string
122
+ - assign it to the `content-security-policy` header automatically
123
+
124
+
125
+ ## Using nonces
126
+
127
+ When using the Rack middleware, if you want it to append nonces, you need to assign a nonce generator Proc to `CSPMaker.nonce_generator` (equivalent of `config.content_security_policy_nonce_generator` in Rails), or pass it as a `nonce_generator:` option to the `Middleware` initializer. You can use `CSPMaker.default_generator`, which calls `SecureRandom.base64(16)`:
128
+
129
+ ```rb
130
+ CSPMaker.nonce_generator = CSPMaker.default_generator
131
+
132
+ # or:
133
+
134
+ CSPMaker.nonce_generator = -> { ... }
135
+ ```
136
+
137
+ Use `CSPMaker.nonce_directives` (equivalent of `config.content_security_policy_nonce_directives` in Rails) or `nonce_directives:` option in `Middleware` to configure which directives should have a nonce added to them (default is `['script-src', 'style-src']`):
138
+
139
+ ```rb
140
+ CSPMaker.nonce_directives = ['script-src']
141
+
142
+ # or:
143
+
144
+ use CSPMaker::Middleware, policy, nonce_generator: -> { ... }, nonce_directives: ['style-src']
145
+ ```
146
+
147
+ The generated nonce is stored in the `env` hash under `csp_maker.nonce` (`CSPMaker::Middleware::NONCE_ENV_KEY`). You will need it for the view template code to add it to the inline `script` / `style` tags, so you could add a helper like:
148
+
149
+ ```rb
150
+ def asset_nonce
151
+ request.env[CSPMaker::Middleware::NONCE_ENV_KEY]
152
+ end
153
+ ```
154
+
155
+ When using `build_policy` or `define_policy` standalone without a middleware, generating and storing the nonce is up to you – and you either pass it to `nonce(...)` inside a `build_policy` block, or as the second argument to `#build` on a `CSPMaker::ContentSecurityPolicy` object returned from `define_policy`. However, you can also assign `CSPMaker.nonce_generator` and call `CSPMaker.make_nonce` to run it if you want:
156
+
157
+ ```rb
158
+ CSPMaker.nonce_generator = CSPMaker.default_generator
159
+
160
+ policy = CSPMaker.define_policy { ... }
161
+ nonce = CSPMaker.make_nonce
162
+
163
+ headers['content-security-policy'] = policy.build(nil, nonce)
164
+ ```
165
+
166
+
167
+ ## Other options
168
+
169
+ You can also set `CSPMaker.report_only` (equivalent of `config.content_security_policy_report_only` in Rails) or `report_only:` option to `Middleware` to assign the policy to the `content-security-policy-report-only` header instead of `content-security-policy`; this makes the browser only report errors to the URL configured via `report_uri` in the DSL, but not actually enforce the policy in the web app (i.e. not block any scripts & styles from loading).
170
+
171
+
172
+ ## Full DSL API
173
+
174
+ Directives:
175
+
176
+ * directives that accept an array of sources: `base_uri`, `child_src`, `connect_src`, `default_src`, `font_src`, `form_action`, `frame_ancestors`, `frame_src`, `img_src`, `manifest_src`, `media_src`, `object_src`, `prefetch_src`, `require_trusted_types_for`, `script_src`, `script_src_attr`, `script_src_elem`, `style_src`, `style_src_attr`, `style_src_elem`, `trusted_types`, `worker_src`
177
+ * `block_all_mixed_content(enabled = true)`
178
+ * `plugin_types(*types)`
179
+ * `report_uri(uri)`
180
+ * `require_sri_for(*types)`
181
+ * `sandbox(enabled = true)` or `sandbox(*values)`
182
+ * `upgrade_insecure_requests(enabled = true)`
183
+
184
+ Source lists:
185
+
186
+ * special values: `:allow_duplicates`, `:none`, `:report_sample`, `:script`, `:self`, `:strict_dynamic`, `:unsafe_eval`, `:unsafe_hashes`, `:unsafe_inline`, `:wasm_unsafe_eval`
187
+ * protocols: `:http`, `:https`, `:data`, `:mediastream`, `:blob`, `:filesystem`, `:ws`, `:wss`
188
+ * content hashes: `"sha256-..."`, `"sha384-..."`, `"sha512-..."`
189
+ * `nonce(nonce_value)`
190
+
191
+
192
+ ## Credits
193
+
194
+ The original `content_security_policy.rb` was added to Rails ActionPack code by Andrew White in 2017, and had some updates since then by others.
195
+
196
+ Modifications for the purposes of this gem are © 2026 Kuba Suder ([@mackuba.eu](https://bsky.app/profile/did:plc:oio4hkxaop4ao4wz2pp3f4cr)).
197
+
198
+ The code is available under the terms of the [MIT license](https://choosealicense.com/licenses/mit/).
199
+
200
+ Bug reports and pull requests are welcome :)
@@ -0,0 +1,316 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configures the HTTP [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
4
+ # response header to help protect against XSS and injection attacks.
5
+ #
6
+ # Example global policy:
7
+ #
8
+ # CSPMaker.build_policy do |policy|
9
+ # policy.default_src :self, :https
10
+ # policy.font_src :self, :https, :data
11
+ # policy.img_src :self, :https, :data
12
+ # policy.object_src :none
13
+ # policy.script_src :self, :https
14
+ # policy.style_src :self, :https
15
+ #
16
+ # # Specify URI for violation reports
17
+ # policy.report_uri "/csp-violation-report-endpoint"
18
+ # end
19
+
20
+ module CSPMaker
21
+ class InvalidDirectiveError < StandardError
22
+ end
23
+
24
+ class ContentSecurityPolicy
25
+ MAPPINGS = {
26
+ self: "'self'",
27
+ unsafe_eval: "'unsafe-eval'",
28
+ wasm_unsafe_eval: "'wasm-unsafe-eval'",
29
+ unsafe_hashes: "'unsafe-hashes'",
30
+ unsafe_inline: "'unsafe-inline'",
31
+ none: "'none'",
32
+ http: "http:",
33
+ https: "https:",
34
+ data: "data:",
35
+ mediastream: "mediastream:",
36
+ allow_duplicates: "'allow-duplicates'",
37
+ blob: "blob:",
38
+ filesystem: "filesystem:",
39
+ report_sample: "'report-sample'",
40
+ script: "'script'",
41
+ strict_dynamic: "'strict-dynamic'",
42
+ ws: "ws:",
43
+ wss: "wss:"
44
+ }.freeze
45
+
46
+ DIRECTIVES = {
47
+ base_uri: "base-uri",
48
+ child_src: "child-src",
49
+ connect_src: "connect-src",
50
+ default_src: "default-src",
51
+ font_src: "font-src",
52
+ form_action: "form-action",
53
+ frame_ancestors: "frame-ancestors",
54
+ frame_src: "frame-src",
55
+ img_src: "img-src",
56
+ manifest_src: "manifest-src",
57
+ media_src: "media-src",
58
+ object_src: "object-src",
59
+ prefetch_src: "prefetch-src",
60
+ require_trusted_types_for: "require-trusted-types-for",
61
+ script_src: "script-src",
62
+ script_src_attr: "script-src-attr",
63
+ script_src_elem: "script-src-elem",
64
+ style_src: "style-src",
65
+ style_src_attr: "style-src-attr",
66
+ style_src_elem: "style-src-elem",
67
+ trusted_types: "trusted-types",
68
+ worker_src: "worker-src"
69
+ }.freeze
70
+
71
+ HASH_SOURCE_ALGORITHM_PREFIXES = ["sha256-", "sha384-", "sha512-"].freeze
72
+
73
+ DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
74
+
75
+ private_constant :MAPPINGS, :DIRECTIVES, :DEFAULT_NONCE_DIRECTIVES
76
+
77
+ attr_reader :directives
78
+
79
+ def initialize
80
+ @directives = {}
81
+ yield self if block_given?
82
+ end
83
+
84
+ def initialize_copy(other)
85
+ @directives = other.directives.to_h do |directive, sources|
86
+ if sources.is_a?(Array)
87
+ copied_sources = sources.map { |source| source.is_a?(String) ? source.dup : source }
88
+ [directive, copied_sources]
89
+ else
90
+ [directive, sources]
91
+ end
92
+ end
93
+ end
94
+
95
+ DIRECTIVES.each do |name, directive|
96
+ define_method(name) do |*sources|
97
+ if sources.first
98
+ @directives[directive] = apply_mappings(sources)
99
+ else
100
+ @directives.delete(directive)
101
+ end
102
+ end
103
+ end
104
+
105
+ # Specify whether to prevent the user agent from loading any assets over HTTP
106
+ # when the page uses HTTPS:
107
+ #
108
+ # policy.block_all_mixed_content
109
+ #
110
+ # Pass `false` to allow it again:
111
+ #
112
+ # policy.block_all_mixed_content false
113
+ #
114
+
115
+ def block_all_mixed_content(enabled = true)
116
+ if enabled
117
+ @directives["block-all-mixed-content"] = true
118
+ else
119
+ @directives.delete("block-all-mixed-content")
120
+ end
121
+ end
122
+
123
+ # Restricts the set of plugins that can be embedded:
124
+ #
125
+ # policy.plugin_types "application/x-shockwave-flash"
126
+ #
127
+ # Leave empty to allow all plugins:
128
+ #
129
+ # policy.plugin_types
130
+
131
+ def plugin_types(*types)
132
+ if types.first
133
+ @directives["plugin-types"] = types
134
+ else
135
+ @directives.delete("plugin-types")
136
+ end
137
+ end
138
+
139
+ # Enable the [report-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri)
140
+ # directive. Violation reports will be sent to the specified URI:
141
+ #
142
+ # policy.report_uri "/csp-violation-report-endpoint"
143
+
144
+ def report_uri(uri)
145
+ @directives["report-uri"] = [uri]
146
+ end
147
+
148
+ # Specify asset types for which [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
149
+ # is required:
150
+ #
151
+ # policy.require_sri_for :script, :style
152
+ #
153
+ # Leave empty to not require Subresource Integrity:
154
+ #
155
+ # policy.require_sri_for
156
+
157
+ def require_sri_for(*types)
158
+ if types.first
159
+ @directives["require-sri-for"] = types
160
+ else
161
+ @directives.delete("require-sri-for")
162
+ end
163
+ end
164
+
165
+ # Specify whether a [sandbox](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox)
166
+ # should be enabled for the requested resource:
167
+ #
168
+ # policy.sandbox
169
+ #
170
+ # Values can be passed as arguments:
171
+ #
172
+ # policy.sandbox "allow-scripts", "allow-modals"
173
+ #
174
+ # Pass `false` to disable the sandbox:
175
+ #
176
+ # policy.sandbox false
177
+
178
+ def sandbox(*values)
179
+ if values.empty?
180
+ @directives["sandbox"] = true
181
+ elsif values.first
182
+ @directives["sandbox"] = values
183
+ else
184
+ @directives.delete("sandbox")
185
+ end
186
+ end
187
+
188
+ # Specify whether user agents should treat any assets over HTTP as HTTPS:
189
+ #
190
+ # policy.upgrade_insecure_requests
191
+ #
192
+ # Pass `false` to disable it:
193
+ #
194
+ # policy.upgrade_insecure_requests false
195
+
196
+ def upgrade_insecure_requests(enabled = true)
197
+ if enabled
198
+ @directives["upgrade-insecure-requests"] = true
199
+ else
200
+ @directives.delete("upgrade-insecure-requests")
201
+ end
202
+ end
203
+
204
+ def nonce(nonce)
205
+ raise InvalidDirectiveError, "Nonce must be a non-empty string" unless nonce.is_a?(String) && nonce.length > 0
206
+
207
+ "'nonce-#{nonce}'"
208
+ end
209
+
210
+ def build(context = nil, nonce = nil, nonce_directives = nil)
211
+ nonce_directives ||= CSPMaker.nonce_directives || DEFAULT_NONCE_DIRECTIVES
212
+ build_directives(context, nonce, nonce_directives).compact.join("; ")
213
+ end
214
+
215
+
216
+ private
217
+
218
+ def apply_mappings(sources)
219
+ sources.map do |source|
220
+ case source
221
+ when Symbol
222
+ apply_mapping(source)
223
+ when String
224
+ if hash_source?(source)
225
+ "'#{source}'"
226
+ else
227
+ source
228
+ end
229
+ when Proc
230
+ source
231
+ else
232
+ raise ArgumentError, "Invalid content security policy source: #{source.inspect}"
233
+ end
234
+ end
235
+ end
236
+
237
+ def apply_mapping(source)
238
+ MAPPINGS.fetch(source) do
239
+ raise ArgumentError, "Unknown content security policy source mapping: #{source.inspect}"
240
+ end
241
+ end
242
+
243
+ def build_directives(context, nonce, nonce_directives)
244
+ @directives.map do |directive, sources|
245
+ if sources.is_a?(Array)
246
+ if nonce && nonce_directive?(directive, nonce_directives)
247
+ "#{directive} #{build_directive(directive, sources, context).join(' ')} 'nonce-#{nonce}'"
248
+ else
249
+ "#{directive} #{build_directive(directive, sources, context).join(' ')}"
250
+ end
251
+ elsif sources
252
+ directive
253
+ else
254
+ nil
255
+ end
256
+ end
257
+ end
258
+
259
+ def validate(directive, sources)
260
+ sources.each do |source|
261
+ if source.include?(";") || source =~ /[[:space:]]/
262
+ message =
263
+ "Invalid Content Security Policy #{directive}: #{source.inspect}. " +
264
+ "Directive values must not contain whitespace or semicolons. " +
265
+ "Please use multiple arguments or other directive methods instead."
266
+
267
+ raise InvalidDirectiveError, message
268
+ end
269
+ end
270
+ end
271
+
272
+ def build_directive(directive, sources, context)
273
+ resolved_sources = sources.flat_map { |source| resolve_source(source, context) }
274
+
275
+ validate(directive, resolved_sources)
276
+
277
+ resolved_sources
278
+ end
279
+
280
+ def resolve_source(source, context)
281
+ case source
282
+ when String
283
+ source
284
+ when Symbol
285
+ source.to_s
286
+ when Proc
287
+ if context.nil?
288
+ raise RuntimeError, "Missing context for the dynamic content security policy source: #{source.inspect}"
289
+ else
290
+ resolved = context.instance_exec(&source)
291
+ apply_mappings(wrap_array(resolved))
292
+ end
293
+ else
294
+ raise RuntimeError, "Unexpected content security policy source: #{source.inspect}"
295
+ end
296
+ end
297
+
298
+ def wrap_array(object)
299
+ if object.nil?
300
+ []
301
+ elsif object.respond_to?(:to_ary)
302
+ object.to_ary || [object]
303
+ else
304
+ [object]
305
+ end
306
+ end
307
+
308
+ def nonce_directive?(directive, nonce_directives)
309
+ nonce_directives.include?(directive)
310
+ end
311
+
312
+ def hash_source?(source)
313
+ source.start_with?(*HASH_SOURCE_ALGORITHM_PREFIXES)
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,55 @@
1
+ require_relative '../csp_maker'
2
+ require 'rack'
3
+
4
+ module CSPMaker
5
+ class Middleware
6
+ CONTENT_SECURITY_POLICY = "content-security-policy"
7
+ CONTENT_SECURITY_POLICY_REPORT_ONLY = "content-security-policy-report-only"
8
+ NONCE_ENV_KEY = "csp_maker.nonce"
9
+
10
+ def initialize(app, policy,
11
+ report_only: CSPMaker.report_only,
12
+ nonce_generator: CSPMaker.nonce_generator,
13
+ nonce_directives: CSPMaker.nonce_directives)
14
+
15
+ @app = app
16
+ @policy = policy
17
+ @report_only = report_only
18
+ @nonce_generator = nonce_generator
19
+ @nonce_directives = nonce_directives
20
+ end
21
+
22
+ def call(env)
23
+ nonce = make_nonce(env) if @nonce_generator
24
+
25
+ status, headers, _ = response = @app.call(env)
26
+
27
+ # Returning CSP headers with a 304 Not Modified is harmful, since nonces in the
28
+ # new CSP headers might not match nonces in the cached HTML.
29
+ return response if status == 304
30
+
31
+ return response if policy_present?(headers)
32
+
33
+ request = Rack::Request.new(env)
34
+ headers[header_name] = @policy.build(request, nonce, @nonce_directives)
35
+
36
+ response
37
+ end
38
+
39
+ private
40
+
41
+ def make_nonce(env)
42
+ if @nonce_generator
43
+ env[NONCE_ENV_KEY] ||= @nonce_generator.call
44
+ end
45
+ end
46
+
47
+ def header_name
48
+ @report_only ? CONTENT_SECURITY_POLICY_REPORT_ONLY : CONTENT_SECURITY_POLICY
49
+ end
50
+
51
+ def policy_present?(headers)
52
+ headers[CONTENT_SECURITY_POLICY] || headers[CONTENT_SECURITY_POLICY_REPORT_ONLY]
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CSPMaker
4
+ VERSION = '0.1.0'
5
+ end
data/lib/csp_maker.rb ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'csp_maker/csp'
4
+ require_relative 'csp_maker/version'
5
+
6
+ require 'securerandom'
7
+
8
+ module CSPMaker
9
+ class << self
10
+ attr_accessor :report_only, :nonce_generator, :nonce_directives
11
+
12
+ def build_policy(context = nil, &block)
13
+ raise ArgumentError, "block required" unless block_given?
14
+
15
+ policy = define_policy(&block)
16
+ policy.build(context)
17
+ end
18
+
19
+ def define_policy(&block)
20
+ raise ArgumentError, "block required" unless block_given?
21
+
22
+ policy = ContentSecurityPolicy.new
23
+
24
+ if block.parameters.empty?
25
+ policy.instance_eval(&block)
26
+ else
27
+ block.call(policy)
28
+ end
29
+
30
+ policy
31
+ end
32
+
33
+ def default_generator
34
+ proc { SecureRandom.base64(16) }
35
+ end
36
+
37
+ def make_nonce
38
+ nonce_generator.call
39
+ end
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: csp_maker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kuba Suder
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rack
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.0'
26
+ description: "\n CSP Maker lets you define a Content Security Policy using a DSL
27
+ and encode it into a header string.\n It has support for nonces, and includes
28
+ a Rack middleware that automates generating and assigning\n the header.\n\n The
29
+ code was extracted from ActionDispatch::ContentSecurityPolicy in Rails ActionPack,
30
+ with some\n minor modifications. The DSL as described in Rails docs should be
31
+ usable 1:1 here.\n "
32
+ email:
33
+ - jakub.suder@gmail.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - CHANGELOG.md
39
+ - README.md
40
+ - lib/csp_maker.rb
41
+ - lib/csp_maker/csp.rb
42
+ - lib/csp_maker/rack.rb
43
+ - lib/csp_maker/version.rb
44
+ homepage: https://tangled.org/mackuba.eu/csp_maker
45
+ licenses:
46
+ - MIT
47
+ metadata:
48
+ bug_tracker_uri: https://tangled.org/mackuba.eu/csp_maker/issues
49
+ changelog_uri: https://tangled.org/mackuba.eu/csp_maker/blob/master/CHANGELOG.md
50
+ source_code_uri: https://tangled.org/mackuba.eu/csp_maker
51
+ rubygems_mfa_required: 'true'
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 3.0.0
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubygems_version: 3.6.9
67
+ specification_version: 4
68
+ summary: A micro gem for defining a Content Security Policy using a DSL, extracted
69
+ from Rails
70
+ test_files: []