envameleon 0.2.0-universal-mingw

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: 76320666462ab75a0de8ccec9a4f51f2a0bf5d25342fa798a647c6e53f7c2b66
4
+ data.tar.gz: b31c3bc6f236daf24c859f43450a794cd6fbcb7d2d42ce44e9eb6f683e0512b5
5
+ SHA512:
6
+ metadata.gz: 06f34cc1a6b2d3143e56b474d3e5aab7e368259f211dc9db459cf2d60bcdda1d4022ddef2e1df21b87f76bfe0e7b0cb186fa90a264de264a6fbcdeb9944563d1
7
+ data.tar.gz: ef6f91088ba51fe266904665a77cef33f2b2d54de2773867017a509399a719638f412d3969ae2495bad1e5210a20938a55eb1b45b69488f450fd5c49ee0b9f1d
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dmytro Shteflyuk
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.
data/README.md ADDED
@@ -0,0 +1,279 @@
1
+ <a id="readme-top"></a>
2
+
3
+ <div align="center">
4
+ <img src="assets/envameleon.png" alt="ENVameleon — Leave no ENVidence." width="800">
5
+
6
+ <h1>ENVameleon</h1>
7
+ <p><strong>Leave no ENVidence.</strong></p>
8
+ <p>Hide the first process environment shown by Linux without changing Ruby's <code>ENV</code>.</p>
9
+ <p>
10
+ <a href="#about">About</a> ·
11
+ <a href="#getting-started">Get started</a> ·
12
+ <a href="#usage">Usage</a> ·
13
+ <a href="#how-it-works">How it works</a> ·
14
+ <a href="#development">Development</a>
15
+ </p>
16
+ </div>
17
+
18
+ ## Table of Contents
19
+
20
+ - [About](#about)
21
+ - [Getting Started](#getting-started)
22
+ - [Native Gems](#native-gems)
23
+ - [Usage](#usage)
24
+ - [How It Works](#how-it-works)
25
+ - [Process Inheritance](#process-inheritance)
26
+ - [Security Limits](#security-limits)
27
+ - [Development](#development)
28
+ - [License](#license)
29
+
30
+ ## About
31
+
32
+ Linux exposes the environment passed at process start through
33
+ `/proc/self/environ`. ENVameleon lets a Ruby process mask, scrub, or drop that
34
+ view after boot. Ruby's live `ENV` stays intact.
35
+
36
+ ### Why this matters
37
+
38
+ Suppose your app keeps its credentials in an encrypted file. Your container
39
+ runner passes the unlock key through the process environment. The encrypted
40
+ file may seem safe even if an attacker can read any file that the app can
41
+ access.
42
+
43
+ Guess again. Linux also exposes `/proc/self/environ` as a file. It may hold the
44
+ unlock key passed at process launch. An attacker who reads both files can
45
+ decrypt the credentials, recover a session signing key, and forge sessions.
46
+
47
+ ENVameleon removes this copy of the key from the proc view after app boot.
48
+
49
+ The gem has no runtime dependencies. Loading it does not change anything. Your
50
+ app chooses when to call one of its three methods.
51
+
52
+ ## Getting Started
53
+
54
+ ### Requirements
55
+
56
+ - CRuby 3.2 or newer
57
+ - Linux for changes to `/proc/self/environ`
58
+ - The Linux `CAP_SYS_RESOURCE` right only for `ENV.drop_proc_data`
59
+
60
+ On macOS and Windows, all three methods are safe no-ops.
61
+
62
+ ### Installation
63
+
64
+ Install the gem:
65
+
66
+ ```console
67
+ gem install envameleon
68
+ ```
69
+
70
+ Or add it to your `Gemfile`:
71
+
72
+ ```ruby
73
+ gem "envameleon"
74
+ ```
75
+
76
+ Then load it:
77
+
78
+ ```ruby
79
+ require "envameleon"
80
+ ```
81
+
82
+ [Back to top](#readme-top).
83
+
84
+ ## Native Gems
85
+
86
+ ENVameleon publishes precompiled native gems for its supported CRuby minors on:
87
+
88
+ - GNU/Linux and musl Linux on x86-64 and AArch64
89
+
90
+ The macOS and Windows gems contain the documented pure-Ruby no-ops. There is no
91
+ native code to compile on those systems. Linux is split into `-linux-gnu` and
92
+ `-linux-musl` gems so a glibc binary is never mistaken for a musl binary. These
93
+ native gems require RubyGems 3.3.22 or newer; musl users should use Bundler
94
+ 2.5.6 or newer.
95
+
96
+ Each precompiled Linux gem contains a separate binary for every supported Ruby
97
+ minor and has no extension build hook. When Linux runs a newer Ruby API,
98
+ RubyGems or Bundler selects the generic source gem instead. That gem runs
99
+ `extconf.rb` and requires a C compiler, Make, and the Ruby headers. macOS and
100
+ Windows continue to select their compiler-free no-op gems. If neither a native
101
+ nor source fallback applies on Linux, `require "envameleon"` fails closed
102
+ instead of silently exposing the process environment.
103
+
104
+ Every GitHub release includes the gems and `SHA256SUMS`. Release gems also have
105
+ GitHub build-provenance attestations, which can be checked with:
106
+
107
+ ```console
108
+ gh attestation verify envameleon-0.2.0-x86_64-linux-gnu.gem --repo kpumuk/envameleon
109
+ ```
110
+
111
+ [Back to top](#readme-top).
112
+
113
+ ## Usage
114
+
115
+ ```ruby
116
+ require "envameleon"
117
+
118
+ ENV.mask_proc_data
119
+ # SECRET=example becomes SECRET=e*****e in /proc/self/environ
120
+
121
+ ENV.scrub_proc_data
122
+ # /proc/self/environ now holds only NUL bytes
123
+
124
+ ENV.drop_proc_data
125
+ # /proc/self/environ is now zero bytes long
126
+ ```
127
+
128
+ | Method | Result in `/proc/self/environ` | File length | Permissions |
129
+ | --- | --- | --- | --- |
130
+ | `ENV.mask_proc_data` | Names plus the first and last value byte | Unchanged | None |
131
+ | `ENV.scrub_proc_data` | NUL bytes | Unchanged | None |
132
+ | `ENV.drop_proc_data` | Empty | Zero | `CAP_SYS_RESOURCE` |
133
+
134
+ Choose the least power you need.
135
+
136
+ > [!IMPORTANT]
137
+ > `ENV.drop_proc_data` requires `CAP_SYS_RESOURCE` on Linux. Without it, the
138
+ > method raises `Errno::EPERM`.
139
+
140
+ Masking works on raw bytes, not characters. It replaces all value bytes except
141
+ the first and last with `*`. Values shorter than three bytes stay unchanged.
142
+
143
+ [Back to top](#readme-top).
144
+
145
+ ## How It Works
146
+
147
+ CRuby moves its active environment during startup. Linux still keeps the old
148
+ range used by `/proc/self/environ`. ENVameleon reads that range from
149
+ `/proc/self/stat`.
150
+
151
+ Masking and scrubbing first check that Ruby no longer uses the old range. They
152
+ then change those bytes in place. If Ruby still uses any of them, the method
153
+ raises an error and changes nothing.
154
+
155
+ Dropping uses `PR_SET_MM_ENV_END` to move the end of the kernel's view to its
156
+ start. This makes `/proc/self/environ` truly zero-length. Linux requires the
157
+ `CAP_SYS_RESOURCE` right for that call.
158
+
159
+ ## Process Inheritance
160
+
161
+ Call a method before `fork` and each child inherits the changed proc view. Ruby
162
+ `ENV` remains available in the parent and children. The test suite checks this
163
+ with a second fork for all three methods.
164
+
165
+ `spawn`, `system`, and `exec` are different. They give the new program a fresh
166
+ proc environment. That program must load ENVameleon and call a method itself.
167
+
168
+ ### Forking servers
169
+
170
+ You may call a method in the parent before it forks. You may also call it from a
171
+ worker boot hook. These examples use scrubbing, which needs no extra right.
172
+
173
+ #### Unicorn
174
+
175
+ ```ruby
176
+ require "envameleon"
177
+
178
+ after_fork do |_server, _worker|
179
+ ENV.scrub_proc_data
180
+ end
181
+ ```
182
+
183
+ #### Puma
184
+
185
+ Use `before_worker_boot` for cluster workers. In single mode, call the method
186
+ during app boot.
187
+
188
+ ```ruby
189
+ require "envameleon"
190
+
191
+ before_worker_boot do
192
+ ENV.scrub_proc_data
193
+ end
194
+ ```
195
+
196
+ #### Sidekiq
197
+
198
+ Standard Sidekiq uses threads. Its startup hook runs before work begins. Sidekiq
199
+ Enterprise Swarm runs the hook in each child.
200
+
201
+ ```ruby
202
+ require "envameleon"
203
+
204
+ Sidekiq.configure_server do |config|
205
+ config.on(:startup) { ENV.scrub_proc_data }
206
+ end
207
+ ```
208
+
209
+ #### Karafka
210
+
211
+ The `app.running` event runs in the server process. In Swarm mode, it runs in
212
+ each forked node.
213
+
214
+ ```ruby
215
+ require "envameleon"
216
+
217
+ Karafka::App.monitor.subscribe("app.running") do
218
+ ENV.scrub_proc_data
219
+ end
220
+ ```
221
+
222
+ If a child calls `ENV.drop_proc_data`, it must still have the needed Linux right.
223
+
224
+ [Back to top](#readme-top).
225
+
226
+ ## Security Limits
227
+
228
+ > [!WARNING]
229
+ > **`CAP_SYS_RESOURCE` grants broad powers outside ENVameleon.**
230
+ >
231
+ > `ENV.drop_proc_data` needs this Linux capability. Grant it only if you accept
232
+ > its wider access. Masking and scrubbing do not need it.
233
+
234
+ ENVameleon changes only what `/proc/self/environ` shows. It does not erase every
235
+ copy of a secret. It gives no protection from memory disclosure, debuggers, or
236
+ core dumps.
237
+
238
+ Dropping leaves the old bytes in memory. Masking keeps variable names and the
239
+ outer bytes of each value. Short values remain fully visible. Scrubbing
240
+ overwrites the old proc range, but a secret may still exist elsewhere.
241
+
242
+ [Back to top](#readme-top).
243
+
244
+ ## Development
245
+
246
+ Install the checksum-locked development bundle and run the `test/unit` suite.
247
+ The test task compiles the extension on Linux and exercises the pure-Ruby no-ops
248
+ elsewhere:
249
+
250
+ ```console
251
+ bundle install
252
+ bundle exec rake test
253
+ ```
254
+
255
+ On Linux, the drop test runs when `CAP_SYS_RESOURCE` is available. Otherwise,
256
+ that one test is omitted.
257
+
258
+ Build the macOS and Windows no-op gems and the Linux source fallback with:
259
+
260
+ ```console
261
+ bundle exec rake gem
262
+ ```
263
+
264
+ Native builds run inside the versioned image selected by the
265
+ checksum-locked `rake-compiler-dock` gem. The image installs the exact Bundler
266
+ version recorded in `Gemfile.lock`; Bundler then validates its own locked
267
+ checksum and installs every other dependency from the prepared local cache:
268
+
269
+ ```console
270
+ bundle cache --all-platforms
271
+ bundle exec rake gem:x86_64-linux-gnu
272
+ ruby .github/scripts/verify_native_gem.rb pkg/envameleon-0.2.0-x86_64-linux-gnu.gem x86_64-linux-gnu
273
+ ```
274
+
275
+ ## License
276
+
277
+ Distributed under the MIT License. See `LICENSE.txt`.
278
+
279
+ [Back to top](#readme-top).
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV.define_singleton_method(:scrub_proc_data) { nil }
4
+ ENV.define_singleton_method(:mask_proc_data) { nil }
5
+ ENV.define_singleton_method(:drop_proc_data) { nil }
data/lib/envameleon.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbconfig"
4
+
5
+ ruby_api_version = RUBY_VERSION[/\A\d+\.\d+/]
6
+ native_extension = File.expand_path(
7
+ "envameleon/#{ruby_api_version}/envameleon.#{RbConfig::CONFIG.fetch("DLEXT")}",
8
+ __dir__
9
+ )
10
+ development_extension = File.expand_path(
11
+ "envameleon/envameleon.#{RbConfig::CONFIG.fetch("DLEXT")}",
12
+ __dir__
13
+ )
14
+
15
+ if RbConfig::CONFIG.fetch("host_os").include?("linux")
16
+ extension = [native_extension, development_extension].find { |path| File.file?(path) }
17
+ if extension
18
+ require extension
19
+ else
20
+ begin
21
+ require "envameleon/envameleon"
22
+ rescue LoadError => error
23
+ raise unless error.path == "envameleon/envameleon"
24
+
25
+ raise LoadError, "ENVameleon has no native extension for Ruby #{ruby_api_version} on #{RUBY_PLATFORM}"
26
+ end
27
+ end
28
+ else
29
+ require_relative "envameleon/noop"
30
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: envameleon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: universal-mingw
6
+ authors:
7
+ - Dmytro Shteflyuk
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: rake
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '13.2'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '13.2'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake-compiler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.3.1
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.3.1
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake-compiler-dock
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.12'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.12'
54
+ - !ruby/object:Gem::Dependency
55
+ name: test-unit
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.7'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.7'
68
+ description: Scrub, mask, or drop Linux's /proc/self/environ view without changing
69
+ Ruby's ENV.
70
+ executables: []
71
+ extensions: []
72
+ extra_rdoc_files: []
73
+ files:
74
+ - LICENSE.txt
75
+ - README.md
76
+ - lib/envameleon.rb
77
+ - lib/envameleon/noop.rb
78
+ homepage: https://github.com/kpumuk/envameleon
79
+ licenses:
80
+ - MIT
81
+ metadata:
82
+ bug_tracker_uri: https://github.com/kpumuk/envameleon/issues
83
+ rubygems_mfa_required: 'true'
84
+ source_code_uri: https://github.com/kpumuk/envameleon
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '3.2'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubygems_version: 4.0.16
100
+ specification_version: 4
101
+ summary: Leave no ENVidence.
102
+ test_files: []