senren-ui 0.4.1 → 0.4.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 +4 -4
- data/CHANGELOG.md +15 -0
- data/lib/generators/senren/install/install_generator.rb +19 -0
- data/lib/senren/rails/asset_path_guard.rb +36 -33
- data/lib/senren/rails/version.rb +1 -1
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a7c6c06cf2fbc28902f954562926b0b5a75a5b22591074c2bd7bc94a398ebf0
|
|
4
|
+
data.tar.gz: 1c272c73f0008bca421d43a85ca55d217c10d563aa8ec0880083fad0c412dab0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b200e044d76d070cfa3657f967603adff7236a1b224a20ee08ed3e5a9e9ff35492d57a28782257b470df1c2901112ec6a60874556e292f5fc075a7ee5f29abb9
|
|
7
|
+
data.tar.gz: edee24c78567f1199468eebe25c676f23044fb622122a0bdb1658cbb4398fd3e8ac9d664812d7ccd3b13a52c8cfbe9039e61aa5e49ecbeeb124f01da13df24f6
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
v0.x is a pre-stable line: minor bumps may break things; patch bumps are
|
|
8
8
|
bug fixes only.
|
|
9
9
|
|
|
10
|
+
## [0.4.2] — 2026-10-26
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- Refuse `senren:install` destinations that resolve outside the host app,
|
|
15
|
+
including symlinked files, parent directories, and dangling links, before
|
|
16
|
+
creating or changing files.
|
|
17
|
+
- Detect component source exposed through symlinked asset paths before a
|
|
18
|
+
deployed app boots or precompiles assets. Dedicated sidecar asset directories
|
|
19
|
+
remain supported.
|
|
20
|
+
- Require Crass 1.0.8 and ViewComponent 4.14.0 or newer so consumer bundles
|
|
21
|
+
enforce the CSS sanitizer and render-state security fixes.
|
|
22
|
+
- Update development tooling's `source-map-js` to 1.2.2 for CVE-2026-93749
|
|
23
|
+
and add JavaScript dependency audits to local and GitHub CI.
|
|
24
|
+
|
|
10
25
|
## [0.4.1] — 2026-10-08
|
|
11
26
|
|
|
12
27
|
### Fixed
|
|
@@ -29,6 +29,25 @@ module Senren
|
|
|
29
29
|
class_option :force_locales, type: :boolean, default: false,
|
|
30
30
|
desc: 'Replace locale files that already exist with the shipped ones.'
|
|
31
31
|
|
|
32
|
+
# Thor's template/copy/gsub actions follow destination symlinks. Check
|
|
33
|
+
# every destination before the first directory is created, including
|
|
34
|
+
# files read before rewriting and links whose targets do not exist yet.
|
|
35
|
+
def validate_install_destinations
|
|
36
|
+
destinations = [
|
|
37
|
+
host_paths.senren_dir, host_paths.components_dir, host_paths.stimulus_dir,
|
|
38
|
+
host_paths.stylesheet_path, host_paths.stylesheet_path.dirname.join('senren_themes.css'),
|
|
39
|
+
host_paths.base_component_path, host_paths.conventions_file, host_paths.installed_components,
|
|
40
|
+
host_paths.registry_mirror, host_paths.skill_file, host_paths.agent_rules_file,
|
|
41
|
+
host_paths.codex_agents_md, host_paths.claude_md, host_paths.copilot_instructions,
|
|
42
|
+
host_paths.cursor_rule_file,
|
|
43
|
+
host_paths.root.join('app/javascript/controllers/index.js'), host_paths.root.join('config/importmap.rb')
|
|
44
|
+
]
|
|
45
|
+
destinations.concat(options[:locales].map { |locale| host_paths.locale_file(locale) })
|
|
46
|
+
destinations.each do |path|
|
|
47
|
+
Senren::Rails::SafeWrite.assert_inside!(path, host_paths.root, 'senren:install')
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
32
51
|
def create_senren_dir
|
|
33
52
|
empty_directory '.senren'
|
|
34
53
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'pathname'
|
|
4
|
+
|
|
3
5
|
module Senren
|
|
4
6
|
module Rails
|
|
5
7
|
# Refuses to let component source be served as a static asset.
|
|
@@ -58,23 +60,20 @@ module Senren
|
|
|
58
60
|
components = components_dir(app)
|
|
59
61
|
return [] unless components&.directory?
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
sources = component_sources(components)
|
|
64
|
+
return [] if sources.empty?
|
|
65
|
+
|
|
66
|
+
asset_paths(app).select { |path| publishes_source?(path, sources) }
|
|
62
67
|
end
|
|
63
68
|
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
# app/components/assets holding nothing but CSS looked identical to
|
|
69
|
-
# app/components/senren holding every component. Only the overlapping
|
|
70
|
-
# subtree can publish anything, and that subtree is the deeper of the two
|
|
71
|
-
# paths.
|
|
72
|
-
def publishes_source?(asset_path, components)
|
|
69
|
+
# Propshaft follows directory and file symlinks when collecting assets.
|
|
70
|
+
# Compare their real targets, including links nested below an asset path,
|
|
71
|
+
# so an alias cannot publish source past the production boot guard.
|
|
72
|
+
def publishes_source?(asset_path, sources)
|
|
73
73
|
asset = Pathname.new(asset_path.to_s).expand_path
|
|
74
|
-
|
|
75
|
-
return false unless overlap?(asset, components)
|
|
74
|
+
return false unless asset.directory?
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
each_file(asset).any? { |_, target| sources.key?(target) }
|
|
78
77
|
end
|
|
79
78
|
|
|
80
79
|
def components_dir(app)
|
|
@@ -83,8 +82,30 @@ module Senren
|
|
|
83
82
|
nil
|
|
84
83
|
end
|
|
85
84
|
|
|
86
|
-
def
|
|
87
|
-
|
|
85
|
+
def component_sources(components)
|
|
86
|
+
each_file(components).with_object({}) do |(path, target), sources|
|
|
87
|
+
sources[target] = true if [path.extname, target.extname].any? { |ext| SOURCE_EXTENSIONS.include?(ext) }
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Track real directory identities so aliases and cycles are visited once.
|
|
92
|
+
# Keep the original filename as well: widget.rb may point to a differently
|
|
93
|
+
# named file outside app/components, which is still component source.
|
|
94
|
+
def each_file(path, visited = {}, &block)
|
|
95
|
+
return enum_for(__method__, path, visited) unless block
|
|
96
|
+
|
|
97
|
+
target = path.realpath
|
|
98
|
+
if target.directory?
|
|
99
|
+
return if visited[target]
|
|
100
|
+
|
|
101
|
+
visited[target] = true
|
|
102
|
+
target.children.each { |child| each_file(child, visited, &block) }
|
|
103
|
+
else
|
|
104
|
+
block.call(path, target)
|
|
105
|
+
end
|
|
106
|
+
rescue Errno::ENOENT, Errno::ENOTDIR, Errno::ELOOP
|
|
107
|
+
# Broken links cannot publish content; visited directories stop cycles.
|
|
108
|
+
nil
|
|
88
109
|
end
|
|
89
110
|
|
|
90
111
|
def asset_paths(app)
|
|
@@ -93,24 +114,6 @@ module Senren
|
|
|
93
114
|
[]
|
|
94
115
|
end
|
|
95
116
|
|
|
96
|
-
# Two paths overlap when one contains the other, in EITHER direction, and
|
|
97
|
-
# the test has to be separator-aware:
|
|
98
|
-
#
|
|
99
|
-
# app/components is an ancestor -> publishes everything
|
|
100
|
-
# app/components/senren is a descendant -> publishes the components
|
|
101
|
-
# app/comp shares a prefix -> publishes nothing
|
|
102
|
-
#
|
|
103
|
-
# The first version tested only the ancestor direction with a bare
|
|
104
|
-
# start_with?, so it missed the descendant case — which is what a
|
|
105
|
-
# developer writes after reading this module's own remediation text — and
|
|
106
|
-
# it blocked production boots over an unrelated app/comp directory.
|
|
107
|
-
def overlap?(one, other)
|
|
108
|
-
a = "#{one}#{File::SEPARATOR}"
|
|
109
|
-
b = "#{other}#{File::SEPARATOR}"
|
|
110
|
-
|
|
111
|
-
a.start_with?(b) || b.start_with?(a)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
117
|
def message_for(offenders)
|
|
115
118
|
<<~MESSAGE.strip
|
|
116
119
|
app/components is on the asset load path (#{offenders.join(', ')}).
|
data/lib/senren/rails/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: senren-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- vutt
|
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: crass
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 1.0.8
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 1.0.8
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: rails
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -29,14 +43,14 @@ dependencies:
|
|
|
29
43
|
requirements:
|
|
30
44
|
- - ">="
|
|
31
45
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 4.
|
|
46
|
+
version: 4.14.0
|
|
33
47
|
type: :runtime
|
|
34
48
|
prerelease: false
|
|
35
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
50
|
requirements:
|
|
37
51
|
- - ">="
|
|
38
52
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 4.
|
|
53
|
+
version: 4.14.0
|
|
40
54
|
description: |
|
|
41
55
|
Senren UI is a Rails-native UI component library inspired by the developer
|
|
42
56
|
experience of shadcn/ui. It ships generators, a registry, and a centralized
|