style_capsule 1.0.2
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 +7 -0
- data/CHANGELOG.md +29 -0
- data/LICENSE.md +22 -0
- data/README.md +398 -0
- data/SECURITY.md +55 -0
- data/lib/style_capsule/component.rb +485 -0
- data/lib/style_capsule/component_styles_support.rb +73 -0
- data/lib/style_capsule/css_file_writer.rb +244 -0
- data/lib/style_capsule/css_processor.rb +182 -0
- data/lib/style_capsule/helper.rb +163 -0
- data/lib/style_capsule/phlex_helper.rb +66 -0
- data/lib/style_capsule/railtie.rb +68 -0
- data/lib/style_capsule/stylesheet_registry.rb +494 -0
- data/lib/style_capsule/version.rb +5 -0
- data/lib/style_capsule/view_component.rb +479 -0
- data/lib/style_capsule/view_component_helper.rb +53 -0
- data/lib/style_capsule.rb +93 -0
- data/lib/tasks/style_capsule.rake +89 -0
- data/sig/style_capsule.rbs +110 -0
- metadata +305 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1a5a88ff582594a22522f2b0349e54bfb235d42839a80b29a9118488a09f82df
|
|
4
|
+
data.tar.gz: 4bf81ae48a9cfe5ef9e0b7c5f8ae00abeb38dbdf7ea2dc930293036b1c375832
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a75e75f5c7d04616306d2ddaaea7a528eb2d4179071cb96ff6e360d854e940dddcfbfe271bf65fb3e6b4d5957961e5c116708f4017d5e9becd1ee3a76053bce3
|
|
7
|
+
data.tar.gz: b7981f706126585a59659ed0c6e3ec61c40a85767ac72c16a9908c6739a5956bcd2bb9c24f20593bc42402ecccfc5a747b042f85b13a07268dfe747555cb56c4
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## 1.0.2 (2025-11-21)
|
|
4
|
+
|
|
5
|
+
- Fix default output directory for CSS files to app/assets/builds/capsules/
|
|
6
|
+
|
|
7
|
+
## 1.0.1 (2025-11-21)
|
|
8
|
+
|
|
9
|
+
- Update ViewComponent dependency to version 4.0 and adjust compatibility in tests
|
|
10
|
+
- Enhance error handling in style_capsule Rake task for ViewComponent loading issues
|
|
11
|
+
- Remove Rails 6 support from Appraisals file and update Rails 7.2 dependencies
|
|
12
|
+
- Update Rails and ActiveSupport version requirements
|
|
13
|
+
- Add Codecov badge to README
|
|
14
|
+
|
|
15
|
+
## 1.0.0 (2025-11-20)
|
|
16
|
+
|
|
17
|
+
- Initial stable release
|
|
18
|
+
- Attribute-based CSS scoping for Phlex components, ViewComponent, and ERB templates
|
|
19
|
+
- Component-scoped CSS encapsulation using `[data-capsule="..."]` selectors (combined selector format: `[data-capsule="..."].class`)
|
|
20
|
+
- Per-component-type scope IDs (shared across all instances)
|
|
21
|
+
- Automatic HTML wrapping with scoped elements
|
|
22
|
+
- CSS processor with support for regular selectors, pseudo-classes, `@media` queries, `@keyframes`, and component-scoped `:host` selectors
|
|
23
|
+
- Thread-safe stylesheet registry for head rendering with namespace support
|
|
24
|
+
- Multiple caching strategies: no caching, time-based, custom proc, and file-based caching
|
|
25
|
+
- File-based caching with Rails asset pipeline integration and Rake tasks
|
|
26
|
+
- Security features: path traversal protection, CSS size limits (1MB), scope ID validation, filename validation
|
|
27
|
+
- Ruby >= 3.0 requirement
|
|
28
|
+
- Comprehensive test suite with > 93% coverage
|
|
29
|
+
|
data/LICENSE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# style_capsule
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/style_capsule) [](https://github.com/amkisko/style_capsule.rb/actions/workflows/test.yml) [](https://codecov.io/gh/amkisko/style_capsule.rb)
|
|
4
|
+
|
|
5
|
+
CSS scoping extension for Rails components. Provides attribute-based style encapsulation for Phlex, ViewComponent, and ERB templates to prevent style leakage between components. Includes configurable caching strategies for optimal performance.
|
|
6
|
+
|
|
7
|
+
Sponsored by [Kisko Labs](https://www.kiskolabs.com).
|
|
8
|
+
|
|
9
|
+
<a href="https://www.kiskolabs.com">
|
|
10
|
+
<img src="kisko.svg" width="200" alt="Sponsored by Kisko Labs" />
|
|
11
|
+
</a>
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "style_capsule"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then run `bundle install`.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **Attribute-based CSS scoping** (no class name renaming)
|
|
26
|
+
- **Phlex, ViewComponent, and ERB support** with automatic Rails integration
|
|
27
|
+
- **Per-component-type scope IDs** (shared across instances)
|
|
28
|
+
- **CSS Nesting support** (optional, more performant, requires modern browsers)
|
|
29
|
+
- **Stylesheet registry** with thread-safe head rendering, namespace support, and compatibility with Propshaft and other asset bundlers
|
|
30
|
+
- **Multiple cache strategies**: none, time-based, custom proc, and file-based (HTTP caching)
|
|
31
|
+
- **Security protections**: path traversal protection, input validation, size limits
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### Phlex Components
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
class MyComponent < ApplicationComponent
|
|
39
|
+
include StyleCapsule::Component
|
|
40
|
+
|
|
41
|
+
def component_styles
|
|
42
|
+
<<~CSS
|
|
43
|
+
.section { color: red; }
|
|
44
|
+
.heading:hover { opacity: 0.8; }
|
|
45
|
+
CSS
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def view_template
|
|
49
|
+
div(class: "section") do
|
|
50
|
+
h2(class: "heading") { "Hello" }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
CSS is automatically scoped with `[data-capsule="..."]` attributes and content is wrapped in a scoped element.
|
|
57
|
+
|
|
58
|
+
### ViewComponent
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
class MyComponent < ApplicationComponent
|
|
62
|
+
include StyleCapsule::ViewComponent
|
|
63
|
+
|
|
64
|
+
def component_styles
|
|
65
|
+
<<~CSS
|
|
66
|
+
.section { color: red; }
|
|
67
|
+
CSS
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def call
|
|
71
|
+
content_tag :div, class: "section" do
|
|
72
|
+
"Hello"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### ERB Templates
|
|
79
|
+
|
|
80
|
+
```erb
|
|
81
|
+
<%= style_capsule do %>
|
|
82
|
+
<style>
|
|
83
|
+
.section { color: red; }
|
|
84
|
+
</style>
|
|
85
|
+
<div class="section">Content</div>
|
|
86
|
+
<% end %>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## CSS Scoping Strategies
|
|
90
|
+
|
|
91
|
+
StyleCapsule supports two CSS scoping strategies:
|
|
92
|
+
|
|
93
|
+
1. **Selector Patching (default)**: Adds `[data-capsule="..."]` prefix to each selector
|
|
94
|
+
- Better browser support (all modern browsers)
|
|
95
|
+
- Output: `[data-capsule="abc123"] .section { color: red; }`
|
|
96
|
+
|
|
97
|
+
2. **CSS Nesting (optional)**: Wraps entire CSS in `[data-capsule="..."] { ... }`
|
|
98
|
+
- More performant (no CSS parsing needed)
|
|
99
|
+
- Requires CSS nesting support (Chrome 112+, Firefox 117+, Safari 16.5+)
|
|
100
|
+
- Output: `[data-capsule="abc123"] { .section { color: red; } }`
|
|
101
|
+
|
|
102
|
+
### Configuration
|
|
103
|
+
|
|
104
|
+
**Per-component:**
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
class MyComponent < ApplicationComponent
|
|
108
|
+
include StyleCapsule::Component
|
|
109
|
+
css_scoping_strategy :nesting # Use CSS nesting
|
|
110
|
+
end
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Global (in base component class):**
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
class ApplicationComponent < Phlex::HTML
|
|
117
|
+
include StyleCapsule::Component
|
|
118
|
+
css_scoping_strategy :nesting # Enable for all components
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Note:** If you change the strategy and it doesn't take effect, clear the CSS cache:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
MyComponent.clear_css_cache
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Stylesheet Registry
|
|
129
|
+
|
|
130
|
+
For better performance, register styles for head rendering instead of rendering `<style>` tags in the body:
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
class MyComponent < ApplicationComponent
|
|
134
|
+
include StyleCapsule::Component
|
|
135
|
+
stylesheet_registry namespace: :admin # Optional namespace
|
|
136
|
+
|
|
137
|
+
def component_styles
|
|
138
|
+
<<~CSS
|
|
139
|
+
.section { color: red; }
|
|
140
|
+
CSS
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
With cache strategy:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
class MyComponent < ApplicationComponent
|
|
149
|
+
include StyleCapsule::Component
|
|
150
|
+
stylesheet_registry namespace: :admin, cache_strategy: :time, cache_ttl: 1.hour
|
|
151
|
+
|
|
152
|
+
def component_styles
|
|
153
|
+
<<~CSS
|
|
154
|
+
.section { color: red; }
|
|
155
|
+
CSS
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Then in your layout:
|
|
161
|
+
|
|
162
|
+
```erb
|
|
163
|
+
<head>
|
|
164
|
+
<%= stylesheet_registrymap_tags %>
|
|
165
|
+
<%= stylesheet_registrymap_tags(namespace: :admin) %>
|
|
166
|
+
</head>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Or in Phlex (requires including `StyleCapsule::PhlexHelper`):
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
head do
|
|
173
|
+
stylesheet_registrymap_tags
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Registering Stylesheet Files
|
|
178
|
+
|
|
179
|
+
You can also register external stylesheet files (not inline CSS) for head rendering:
|
|
180
|
+
|
|
181
|
+
**In ERB:**
|
|
182
|
+
|
|
183
|
+
```erb
|
|
184
|
+
<% register_stylesheet("stylesheets/user/order_select_component", "data-turbo-track": "reload") %>
|
|
185
|
+
<% register_stylesheet("stylesheets/admin/dashboard", namespace: :admin) %>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**In Phlex (requires including `StyleCapsule::PhlexHelper`):**
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
def view_template
|
|
192
|
+
register_stylesheet("stylesheets/user/order_select_component", "data-turbo-track": "reload")
|
|
193
|
+
register_stylesheet("stylesheets/admin/dashboard", namespace: :admin)
|
|
194
|
+
div { "Content" }
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**In ViewComponent (requires including `StyleCapsule::ViewComponentHelper`):**
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
def call
|
|
202
|
+
register_stylesheet("stylesheets/user/order_select_component", "data-turbo-track": "reload")
|
|
203
|
+
register_stylesheet("stylesheets/admin/dashboard", namespace: :admin)
|
|
204
|
+
content_tag(:div, "Content")
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Registered files are rendered via `stylesheet_registrymap_tags` in your layout, just like inline CSS.
|
|
209
|
+
|
|
210
|
+
## Caching Strategies
|
|
211
|
+
|
|
212
|
+
### No Caching (Default)
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
class MyComponent < ApplicationComponent
|
|
216
|
+
include StyleCapsule::Component
|
|
217
|
+
stylesheet_registry # No cache strategy set (default: :none)
|
|
218
|
+
end
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Time-Based Caching
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
stylesheet_registry cache_strategy: :time, cache_ttl: 1.hour # Using ActiveSupport::Duration
|
|
225
|
+
# Or using integer seconds:
|
|
226
|
+
stylesheet_registry cache_strategy: :time, cache_ttl: 3600 # Cache for 1 hour
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Custom Proc Caching
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
stylesheet_registry cache_strategy: ->(css, capsule_id, namespace) {
|
|
233
|
+
cache_key = "css_#{capsule_id}_#{namespace}"
|
|
234
|
+
should_cache = css.length > 100
|
|
235
|
+
expires_at = Time.now + 1800
|
|
236
|
+
[cache_key, should_cache, expires_at]
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Note:** `cache_strategy` accepts Symbol (`:time`), String (`"time"`), or Proc. Strings are automatically converted to symbols.
|
|
241
|
+
|
|
242
|
+
### File-Based Caching (HTTP Caching)
|
|
243
|
+
|
|
244
|
+
Writes CSS to files for HTTP caching. **Requires class method `def self.component_styles`**:
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
class MyComponent < ApplicationComponent
|
|
248
|
+
include StyleCapsule::Component
|
|
249
|
+
stylesheet_registry cache_strategy: :file
|
|
250
|
+
|
|
251
|
+
# Must use class method for file caching
|
|
252
|
+
def self.component_styles
|
|
253
|
+
<<~CSS
|
|
254
|
+
.section { color: red; }
|
|
255
|
+
CSS
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Configuration:**
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
# config/initializers/style_capsule.rb
|
|
264
|
+
StyleCapsule::CssFileWriter.configure(
|
|
265
|
+
output_dir: Rails.root.join("app/assets/builds/capsules"),
|
|
266
|
+
filename_pattern: ->(component_class, capsule_id) {
|
|
267
|
+
"capsule-#{capsule_id}.css"
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Precompilation:**
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
bin/rails style_capsule:build # Build CSS files
|
|
276
|
+
bin/rails style_capsule:clear # Clear generated files
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Files are automatically built during `bin/rails assets:precompile`.
|
|
280
|
+
|
|
281
|
+
**Compatibility:** The stylesheet registry works with Propshaft, Sprockets, and other Rails asset bundlers. Static file paths are collected in a process-wide manifest (similar to Propshaft's approach), while inline CSS is stored per-request.
|
|
282
|
+
|
|
283
|
+
## Advanced Usage
|
|
284
|
+
|
|
285
|
+
### Database-Stored CSS
|
|
286
|
+
|
|
287
|
+
For CSS stored in a database (e.g., user-generated styles, themes), use StyleCapsule's CSS processor directly:
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
# app/models/theme.rb
|
|
291
|
+
class Theme < ApplicationRecord
|
|
292
|
+
def generate_capsule_id
|
|
293
|
+
return capsule_id if capsule_id.present?
|
|
294
|
+
scope_key = "theme_#{id}_#{name}"
|
|
295
|
+
self.capsule_id = "a#{Digest::SHA1.hexdigest(scope_key)}"[0, 8]
|
|
296
|
+
save! if persisted?
|
|
297
|
+
capsule_id
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def scoped_css
|
|
301
|
+
return scoped_css_cache if scoped_css_cache.present? &&
|
|
302
|
+
scoped_css_updated_at == updated_at
|
|
303
|
+
|
|
304
|
+
current_capsule_id = generate_capsule_id
|
|
305
|
+
scoped = StyleCapsule::CssProcessor.scope_selectors(css_content, current_capsule_id)
|
|
306
|
+
|
|
307
|
+
update_columns(
|
|
308
|
+
scoped_css_cache: scoped,
|
|
309
|
+
scoped_css_updated_at: updated_at,
|
|
310
|
+
capsule_id: current_capsule_id
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
scoped
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Usage:**
|
|
319
|
+
|
|
320
|
+
```erb
|
|
321
|
+
<div data-capsule="<%= theme.capsule_id %>">
|
|
322
|
+
<style><%= raw theme.scoped_css %></style>
|
|
323
|
+
<div class="header">Content</div>
|
|
324
|
+
</div>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## CSS Selector Support
|
|
328
|
+
|
|
329
|
+
- Regular selectors: `.section`, `#header`, `div.container`
|
|
330
|
+
- Pseudo-classes and pseudo-elements: `.button:hover`, `.item::before`
|
|
331
|
+
- Multiple selectors: `.a, .b, .c { color: red; }`
|
|
332
|
+
- Component-scoped selectors: `:host`, `:host(.active)`, `:host-context(.theme-dark)`
|
|
333
|
+
- Media queries: `@media (max-width: 768px) { ... }`
|
|
334
|
+
|
|
335
|
+
## How It Works
|
|
336
|
+
|
|
337
|
+
1. **Scope ID Generation**: Each component class gets a unique scope ID based on its class name (shared across all instances)
|
|
338
|
+
2. **CSS Rewriting**: CSS selectors are rewritten to include `[data-capsule="..."]` attribute selectors
|
|
339
|
+
3. **HTML Wrapping**: Component content is automatically wrapped in a scoped element
|
|
340
|
+
4. **No Class Renaming**: Class names remain unchanged (unlike Shadow DOM)
|
|
341
|
+
|
|
342
|
+
## Requirements
|
|
343
|
+
|
|
344
|
+
- Ruby >= 3.0
|
|
345
|
+
- Rails >= 7.0, < 9.0
|
|
346
|
+
- ActiveSupport >= 7.0, < 9.0
|
|
347
|
+
|
|
348
|
+
## Development
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
bundle install
|
|
352
|
+
bundle exec appraisal install
|
|
353
|
+
|
|
354
|
+
# Run tests
|
|
355
|
+
bundle exec rspec
|
|
356
|
+
|
|
357
|
+
# Run tests for all Rails versions
|
|
358
|
+
bundle exec appraisal rails72 rspec
|
|
359
|
+
bundle exec appraisal rails8ruby34 rspec
|
|
360
|
+
|
|
361
|
+
# Linting
|
|
362
|
+
bundle exec standardrb --fix
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Contributing
|
|
366
|
+
|
|
367
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/amkisko/style_capsule.rb
|
|
368
|
+
|
|
369
|
+
Contribution policy:
|
|
370
|
+
- New features are not necessarily added to the gem
|
|
371
|
+
- Pull requests should have test coverage and changelog entry
|
|
372
|
+
|
|
373
|
+
Review policy:
|
|
374
|
+
- Critical fixes: up to 2 calendar weeks
|
|
375
|
+
- Pull requests: up to 6 calendar months
|
|
376
|
+
- Issues: up to 1 calendar year
|
|
377
|
+
|
|
378
|
+
## Publishing
|
|
379
|
+
|
|
380
|
+
```sh
|
|
381
|
+
rm style_capsule-*.gem
|
|
382
|
+
gem build style_capsule.gemspec
|
|
383
|
+
gem push style_capsule-*.gem
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Security
|
|
387
|
+
|
|
388
|
+
StyleCapsule includes security protections:
|
|
389
|
+
- Path traversal protection
|
|
390
|
+
- Input validation
|
|
391
|
+
- Size limits (1MB per component)
|
|
392
|
+
- XSS prevention via Rails' HTML escaping
|
|
393
|
+
|
|
394
|
+
For detailed security information, see [SECURITY.md](SECURITY.md).
|
|
395
|
+
|
|
396
|
+
## License
|
|
397
|
+
|
|
398
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.md).
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# SECURITY
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 1.0.x | :white_check_mark: |
|
|
8
|
+
| < 1.0 | :x: |
|
|
9
|
+
|
|
10
|
+
## Security Guidelines
|
|
11
|
+
|
|
12
|
+
StyleCapsule provides minimal security protections to assist with automated CSS scoping. The gem does not validate, sanitize, or secure CSS content itself—this is the application's responsibility.
|
|
13
|
+
|
|
14
|
+
### What StyleCapsule Protects
|
|
15
|
+
|
|
16
|
+
- **Path Traversal**: Filenames are validated when writing CSS files to prevent directory traversal attacks
|
|
17
|
+
- **Input Size Limits**: CSS content is limited to 1MB per component to prevent resource exhaustion
|
|
18
|
+
- **Scope ID Validation**: Capsule IDs are validated to prevent injection into HTML attributes
|
|
19
|
+
|
|
20
|
+
### What StyleCapsule Does NOT Control
|
|
21
|
+
|
|
22
|
+
**Developer Responsibility:**
|
|
23
|
+
- **CSS Content**: StyleCapsule does not validate or sanitize CSS content. Malicious CSS (e.g., data exfiltration via `@import`, CSS injection attacks) is not prevented by the gem
|
|
24
|
+
- **User Input**: Applications must validate and sanitize user-provided CSS before passing it to StyleCapsule
|
|
25
|
+
- **Developer Intent**: The gem trusts that developers provide safe CSS content from trusted sources
|
|
26
|
+
|
|
27
|
+
**Rails Framework:**
|
|
28
|
+
- HTML escaping is handled by Rails' built-in helpers (`content_tag`, etc.)
|
|
29
|
+
- Content Security Policy (CSP) must be configured at the application level
|
|
30
|
+
- File system permissions and access control are managed by the application
|
|
31
|
+
|
|
32
|
+
### Security Best Practices
|
|
33
|
+
|
|
34
|
+
1. **Validate User Input**: Never pass untrusted CSS content to StyleCapsule without validation
|
|
35
|
+
2. **Use Content Security Policy**: Configure CSP headers to restrict inline styles and external resources
|
|
36
|
+
3. **Sanitize User-Generated CSS**: If allowing user input, sanitize CSS before processing
|
|
37
|
+
4. **Keep Dependencies Updated**: Use supported Ruby (>= 3.0) and Rails versions with security patches
|
|
38
|
+
5. **Review Generated Files**: Periodically review files in `app/assets/builds/capsules/` if using file-based caching
|
|
39
|
+
|
|
40
|
+
### Reporting a Vulnerability
|
|
41
|
+
|
|
42
|
+
If you discover a security vulnerability, please **do not** open a public issue. Instead:
|
|
43
|
+
|
|
44
|
+
1. **Email**: contact@kiskolabs.com
|
|
45
|
+
2. **Subject**: `[SECURITY] style_capsule vulnerability report`
|
|
46
|
+
3. **Include**: Description, steps to reproduce, potential impact, and suggested fix (if any)
|
|
47
|
+
|
|
48
|
+
We will acknowledge receipt within 48 hours and provide an initial assessment within 7 days.
|
|
49
|
+
|
|
50
|
+
### Security Updates
|
|
51
|
+
|
|
52
|
+
Security updates are released as patch versions (e.g., 1.0.1, 1.0.2) and announced via:
|
|
53
|
+
- GitHub Security Advisories
|
|
54
|
+
- RubyGems release notes
|
|
55
|
+
- CHANGELOG.md
|