envameleon 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71067c7187ac66066961bb5ae52346bdfa7dd72a11b7506b25f728ac4c81a64d
4
- data.tar.gz: d1dc5952b9fb0c501750c0655d905792b9bafe45a31c9a21af7f5ffe0b58cbdf
3
+ metadata.gz: f7597e5b1a9244904d1e811799087794525411e88ce6e00fccbdfb2e07a7bc36
4
+ data.tar.gz: d479fbcf8616eec177bfea1619003c164a262ed04e230620985ded862db78543
5
5
  SHA512:
6
- metadata.gz: b6492dd83fabd4f3d3e261bb4fcfa4fc16ee15c9d0c6f688b282b5a089dc4fff3512480904c32800ef6c9c7115e9c4d739f24ac2195587458b1a9d832feab392
7
- data.tar.gz: c98a6dd250a633190cd3326a8f425d62902023e8855982db5430c33b7844a22a9e21e29586b221326a3070345910c84f5a8916ca5879f9149aa2cf1f76b62fef
6
+ metadata.gz: 1e6c69a2cdd21f92abde6d1b34298020f3b8052152c3a89e2c99d25ef1591a6f52d19f35469a415bedd69f00e9141e9edc1dc8ef282fb0063532ec5088161a24
7
+ data.tar.gz: 5e5084c3dc7700201f8da011b1d9c41882481b3446a4c21ca65c8fc2fee7d1b2e8e60d4b1dda4f3af10aa5fd2531722a9f1a64f94168b84e04b0befb2c001b58
data/README.md CHANGED
@@ -19,6 +19,7 @@
19
19
 
20
20
  - [About](#about)
21
21
  - [Getting Started](#getting-started)
22
+ - [Native Gems](#native-gems)
22
23
  - [Usage](#usage)
23
24
  - [How It Works](#how-it-works)
24
25
  - [Process Inheritance](#process-inheritance)
@@ -56,7 +57,7 @@ app chooses when to call one of its three methods.
56
57
  - Linux for changes to `/proc/self/environ`
57
58
  - The Linux `CAP_SYS_RESOURCE` right only for `ENV.drop_proc_data`
58
59
 
59
- On other systems, all three methods are safe no-ops.
60
+ On macOS and Windows, all three methods are safe no-ops.
60
61
 
61
62
  ### Installation
62
63
 
@@ -80,6 +81,35 @@ require "envameleon"
80
81
 
81
82
  [Back to top](#readme-top).
82
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
+
83
113
  ## Usage
84
114
 
85
115
  ```ruby
@@ -213,21 +243,33 @@ overwrites the old proc range, but a secret may still exist elsewhere.
213
243
 
214
244
  ## Development
215
245
 
216
- Build the native extension and run the `test/unit` suite:
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:
217
249
 
218
250
  ```console
219
- ruby -Cext/envameleon extconf.rb
220
- make -C ext/envameleon
221
- ruby -Ilib -Iext test/test_envameleon.rb
251
+ bundle install
252
+ bundle exec rake test
222
253
  ```
223
254
 
224
255
  On Linux, the drop test runs when `CAP_SYS_RESOURCE` is available. Otherwise,
225
256
  that one test is omitted.
226
257
 
227
- Build the gem with:
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:
228
268
 
229
269
  ```console
230
- gem build envameleon.gemspec
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
231
273
  ```
232
274
 
233
275
  ## License
@@ -7,7 +7,6 @@
7
7
  #include <stdlib.h>
8
8
  #include <string.h>
9
9
  #include <sys/prctl.h>
10
- #include <linux/prctl.h>
11
10
 
12
11
  static void
13
12
  read_env_range(unsigned long *env_start, unsigned long *env_end)
@@ -16,22 +15,26 @@ read_env_range(unsigned long *env_start, unsigned long *env_end)
16
15
  char *field;
17
16
  char *end;
18
17
  FILE *file = fopen("/proc/self/stat", "r");
18
+ int error = 0;
19
19
  int number;
20
+ size_t length;
20
21
 
21
22
  if (file == NULL)
22
23
  rb_sys_fail("fopen(/proc/self/stat)");
23
24
 
24
- if (fgets(stat, sizeof(stat), file) == NULL) {
25
- int error = errno;
26
- fclose(file);
27
- if (error != 0) {
28
- errno = error;
29
- rb_sys_fail("fgets(/proc/self/stat)");
30
- }
31
- rb_raise(rb_eRuntimeError, "empty /proc/self/stat");
32
- }
25
+ length = fread(stat, 1, sizeof(stat), file);
26
+ if (ferror(file))
27
+ error = errno == 0 ? EIO : errno;
33
28
  fclose(file);
34
29
 
30
+ if (error != 0)
31
+ rb_syserr_fail(error, "fread(/proc/self/stat)");
32
+ if (length == sizeof(stat))
33
+ rb_raise(rb_eRuntimeError, "/proc/self/stat is too large");
34
+ if (length == 0)
35
+ rb_raise(rb_eRuntimeError, "empty /proc/self/stat");
36
+ stat[length] = '\0';
37
+
35
38
  field = strrchr(stat, ')');
36
39
  if (field == NULL || field[1] != ' ')
37
40
  rb_raise(rb_eRuntimeError, "invalid /proc/self/stat");
@@ -98,16 +101,15 @@ mask_proc_data(VALUE environment)
98
101
  size_t remaining = (char *)env_end - entry;
99
102
  char *terminator = memchr(entry, '\0', remaining);
100
103
  char *equals;
101
- char *value;
102
- size_t value_length;
103
104
 
104
105
  if (terminator == NULL)
105
106
  rb_raise(rb_eRuntimeError, "invalid environment data");
106
107
 
107
108
  equals = memchr(entry, '=', terminator - entry);
108
109
  if (equals != NULL) {
109
- value = equals + 1;
110
- value_length = terminator - value;
110
+ char *value = equals + 1;
111
+ size_t value_length = terminator - value;
112
+
111
113
  if (value_length > 2)
112
114
  memset(value + 1, '*', value_length - 2);
113
115
  }
@@ -154,7 +156,7 @@ drop_proc_data(VALUE environment)
154
156
  }
155
157
  #endif
156
158
 
157
- void
159
+ RUBY_FUNC_EXPORTED void
158
160
  Init_envameleon(void)
159
161
  {
160
162
  ID environment_id = rb_intern2("ENV", 3);
@@ -2,4 +2,24 @@
2
2
 
3
3
  require "mkmf"
4
4
 
5
+ append_cflags %w[
6
+ -Wall
7
+ -Wextra
8
+ -Wformat=2
9
+ -Werror=format-security
10
+ -Werror=implicit-function-declaration
11
+ -fstack-protector-strong
12
+ -fvisibility=hidden
13
+ ]
14
+
15
+ if RUBY_PLATFORM.include?("linux")
16
+ append_cflags "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"
17
+
18
+ append_ldflags %w[
19
+ -Wl,-z,noexecstack
20
+ -Wl,-z,now
21
+ -Wl,-z,relro
22
+ ]
23
+ end
24
+
5
25
  create_makefile("envameleon/envameleon")
data/lib/envameleon.rb CHANGED
@@ -1,7 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ENVameleon
4
- VERSION = "0.1.0"
5
- end
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
+ )
6
14
 
7
- require "envameleon/envameleon"
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 CHANGED
@@ -1,14 +1,70 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: envameleon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmytro Shteflyuk
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
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'
12
68
  description: Scrub, mask, or drop Linux's /proc/self/environ view without changing
13
69
  Ruby's ENV.
14
70
  executables: []
@@ -18,13 +74,16 @@ extra_rdoc_files: []
18
74
  files:
19
75
  - LICENSE.txt
20
76
  - README.md
21
- - assets/envameleon.png
22
77
  - ext/envameleon/envameleon.c
23
78
  - ext/envameleon/extconf.rb
24
79
  - lib/envameleon.rb
80
+ homepage: https://github.com/kpumuk/envameleon
25
81
  licenses:
26
82
  - MIT
27
- metadata: {}
83
+ metadata:
84
+ bug_tracker_uri: https://github.com/kpumuk/envameleon/issues
85
+ rubygems_mfa_required: 'true'
86
+ source_code_uri: https://github.com/kpumuk/envameleon
28
87
  rdoc_options: []
29
88
  require_paths:
30
89
  - lib
@@ -32,14 +91,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
32
91
  requirements:
33
92
  - - ">="
34
93
  - !ruby/object:Gem::Version
35
- version: '3.2'
94
+ version: 4.1.0.dev
36
95
  required_rubygems_version: !ruby/object:Gem::Requirement
37
96
  requirements:
38
97
  - - ">="
39
98
  - !ruby/object:Gem::Version
40
99
  version: '0'
41
100
  requirements: []
42
- rubygems_version: 3.6.9
101
+ rubygems_version: 4.0.16
43
102
  specification_version: 4
44
103
  summary: Leave no ENVidence.
45
104
  test_files: []
Binary file