security_box 0.6.0 → 0.7.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: 999342421a33fdaba7863952bafad78dc8e87bac98a1ec02269fb2903be9d414
4
- data.tar.gz: b119d9d14854ba07b2f99bd1c5d8c8e2e25db4f5f50370d1ecea41491ee8b673
3
+ metadata.gz: b5972e4ef120906a25c58bd1783e11091948929bd3fbdbd8aa4f738cd57a8819
4
+ data.tar.gz: b50d68cb20da70bcae520b0c5828391d11cf34c3f81839c7e9d458dde82d4f86
5
5
  SHA512:
6
- metadata.gz: 20c96a86adc927a31e1abbe16a6a6cae1b49b015f7078917a0cd1fccfab61e3f5afbc7e2b0a1f71bf546aa21bd40db7d063fb138f542f3c1d2c14f968cd93e05
7
- data.tar.gz: 8eddad9955d7801ce2ce93531ba92d581f1686f07288012501bc933d47034502dc3a7a4089f992340a2173ed2230e3698c1b5fdbb1b1e575465ccda0c53f3004
6
+ metadata.gz: 0221d3c37d4d5cb19c985b51088724ddd213d99ae52a9e3af305e1ed924e527c842e05bdebd0b5cfa330564bffea4571a6d28dc95f2fbed3ad8deb6a403f7bef
7
+ data.tar.gz: f5a71c1643e9543424e6b2767960d6cbb6d836ae8fa979dd653fcffe4bc14498c92352658a0de5c49f9263f45c5c6e37959f64ac8f6ed64268fe38424ced054f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.0] - 2026-11-02
9
+
10
+ ### Added
11
+
12
+ - RSpec is bundled into the sandbox image (`lib/security_box/guest_ext`), so
13
+ guest code can run spec suites from mounted projects. `SB.run_rspec` loads
14
+ RSpec lazily, runs it with `HOME`/cwd set to a writable directory and
15
+ returns the exit status instead of exiting the guest. The image grows by
16
+ ~2MB (about 52MB total); boot time is unchanged because RSpec is only
17
+ required on first use.
18
+ - `samples/rspec_sandbox`: a runnable sample that mounts a `ruby_src/` folder
19
+ read-only, runs its existing spec with `/work` as cwd through
20
+ `SB.run_rspec`, and includes a deliberately failing example to show how
21
+ failures reach the caller (`Result#value` + `Result#stdout`).
22
+
23
+ ### Changed
24
+
25
+ - `Rakefile`: the guest image freshness check now includes
26
+ `lib/security_box/guest_ext/Gemfile.lock`.
27
+
8
28
  ## [0.6.0] - 2026-09-21
9
29
 
10
30
  ### Added
data/README.md CHANGED
@@ -20,7 +20,8 @@ tries to escape.
20
20
  then packed with the guest entrypoint (`lib/security_box/guest/main.rb`)
21
21
  plus a hardening prelude (`lib/security_box/guest/prelude.rb`). The
22
22
  `sb_rpc` gem declares the `sb`/`call` wasm import used by the host RPC
23
- channel (see "Host RPC (code mode)" below).
23
+ channel (see "Host RPC (code mode)" below), and RSpec is bundled so guest
24
+ code can run spec suites (see "Running RSpec inside the sandbox").
24
25
  2. On every `#eval`, the host creates an exclusive tmpdir, writes the user code to it, and
25
26
  mounts it read-write as `/work` inside the sandbox. It also generates a per-eval
26
27
  random token and passes it to the guest via `SB_TOKEN`.
@@ -53,7 +54,7 @@ gem "security_box"
53
54
  ```
54
55
 
55
56
  The gem ships a prebuilt sandbox image (`lib/security_box/assets/security_box.wasm`,
56
- about 50MB), so no network access or build tools are needed at install or at
57
+ about 52MB), so no network access or build tools are needed at install or at
57
58
  runtime.
58
59
 
59
60
  ### Rebuilding the image (development only)
@@ -269,6 +270,50 @@ Security notes:
269
270
  - Mounted content can also be **written by the guest** in `mount_rw` mounts —
270
271
  treat a writable mount as part of the guest's blast radius.
271
272
 
273
+ ### Running RSpec inside the sandbox
274
+
275
+ The image bundles RSpec (`~> 3.13`), so guest code can run a spec suite from a
276
+ mounted project. `SB.run_rspec` loads RSpec lazily, runs it with `HOME` and cwd
277
+ set to a writable directory, and returns the exit status (`0` = all examples
278
+ passed) instead of exiting the guest:
279
+
280
+ ```ruby
281
+ box = SecurityBox.spawn(
282
+ timeout_ms: 30_000,
283
+ fuel_ms: 20_000,
284
+ mounts: [{ host: "./project", guest: "/project", mode: :read_only }]
285
+ )
286
+
287
+ result = box.eval(<<~RUBY)
288
+ SB.run_rspec("--no-color", "-I", "/project/lib", "/project/spec",
289
+ chdir: "/project")
290
+ RUBY
291
+
292
+ result.value # => 0
293
+ result.stdout # => RSpec output
294
+ ```
295
+
296
+ `chdir:` defaults to `/work` (the sandbox tmpdir). Use it when the suite needs
297
+ `.rspec`, relative paths or status files from the mounted project; mount the
298
+ project read-write if RSpec must persist them.
299
+
300
+ You can also call RSpec directly:
301
+
302
+ ```ruby
303
+ require "/bundle/setup.rb"
304
+ require "rspec/core"
305
+ RSpec::Core::Runner.run(["--no-color", "/work/spec"], $stderr, $stdout)
306
+ ```
307
+
308
+ Notes:
309
+
310
+ - Specs need a larger `timeout_ms`/`fuel_ms` than trivial evals; size them for
311
+ the whole suite, not for a single expression.
312
+ - The guest has no process APIs, so RSpec features that spawn processes
313
+ (external formatters, `--bisect`-style workflows) are unavailable.
314
+ - `/work` is the sandbox's own writable tmpdir and cannot be used as a mount
315
+ target; mount projects at another path (for example `/project`).
316
+
272
317
  ### Configuration
273
318
 
274
319
  `SecurityBox::Configuration` is immutable; use `.build` to create and `#with` to derive:
@@ -433,6 +478,17 @@ bundle exec ruby bin/spike.rb
433
478
  LLM agent built with [RubyLLM](https://github.com/crmne/ruby_llm) whose code-execution
434
479
  tool runs model-generated Ruby inside the sandbox, demonstrating folder mounts
435
480
  (read-only vs read-write) end to end.
481
+ - [`samples/ruby_llm/context7_code_mode`](samples/ruby_llm/context7_code_mode/README.md) —
482
+ a RubyLLM code-mode agent whose sandbox code reaches the hosted
483
+ [Context7](https://context7.com) MCP server through `SB.call`: host RPC
484
+ handlers proxy the MCP tools (streamable HTTP + bearer auth), so the API
485
+ key never crosses the sandbox boundary. Includes a no-LLM smoke script
486
+ that exercises the RPC↔MCP wiring.
487
+ - [`samples/rspec_sandbox`](samples/rspec_sandbox/README.md) — runs an existing
488
+ RSpec suite inside the sandbox: the library and its spec live in `ruby_src/`,
489
+ are mounted read-only and run with `/work` as cwd via `SB.run_rspec`. The
490
+ suite includes one deliberately failing example, showing how failures reach
491
+ the caller through `Result#value` and `Result#stdout`.
436
492
 
437
493
  ## Documentation
438
494
 
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/security_box/assets/security_box.wasm
3
- size: 52249212 bytes
4
- sha256: 6a6b265e2c1e79f871ccee925a8ea63310cdf433fcffb061369c72f64fd5301c
3
+ size: 53871847 bytes
4
+ sha256: a866ea3c387155f5bce26a4a3f109b54c778297a1ad45680bbf63d38001fc636
@@ -100,4 +100,8 @@ end
100
100
  # above; must load after it.
101
101
  require_relative "rpc"
102
102
 
103
+ # Guest RSpec runner (SB.run_rspec) — also reopens SB; RSpec itself is
104
+ # required lazily on the first call.
105
+ require_relative "rspec"
106
+
103
107
  SB.run
@@ -0,0 +1,59 @@
1
+ # Guest-side RSpec runner (loaded by main.rb before user code).
2
+ #
3
+ # The image bundles RSpec (see lib/security_box/guest_ext/Gemfile), so guest
4
+ # code can run a spec suite written to /work or mounted at another path:
5
+ #
6
+ # SB.run_rspec("--no-color", "/work/spec")
7
+ # SB.run_rspec("-I", "/project/lib", "/project/spec", chdir: "/project")
8
+ #
9
+ # RSpec is required lazily on the first call, so ordinary evals do not pay
10
+ # the load cost. The runner returns RSpec's exit status (0 = all examples
11
+ # passed) instead of exiting the process, keeping the envelope channel intact.
12
+ module SB
13
+ module_function
14
+
15
+ # Runs RSpec with `args` (an argv-style list) and returns its exit status.
16
+ # The suite runs with cwd and HOME set to `chdir` (default /work) so
17
+ # relative paths, `.rspec` discovery and RSpec's status/cache files stay
18
+ # inside a writable directory.
19
+ def run_rspec(*args, chdir: "/work")
20
+ previous_home = ENV["HOME"]
21
+ previous_pwd = Dir.pwd
22
+ load_rspec!
23
+
24
+ work = if File.directory?(chdir)
25
+ chdir
26
+ elsif File.directory?("/work")
27
+ "/work"
28
+ else
29
+ previous_pwd
30
+ end
31
+ ENV["HOME"] = work
32
+ Dir.chdir(work)
33
+
34
+ RSpec::Core::Runner.run(args.flatten, $stderr, $stdout)
35
+ ensure
36
+ Dir.chdir(previous_pwd) if previous_pwd && File.directory?(previous_pwd)
37
+ if previous_home.nil?
38
+ ENV.delete("HOME")
39
+ else
40
+ ENV["HOME"] = previous_home
41
+ end
42
+ end
43
+
44
+ def load_rspec!
45
+ require "/bundle/setup.rb"
46
+ require "rspec/core"
47
+ # Hide the embedded guest protocol frames (/src/main.rb, /src/rspec.rb,
48
+ # ...) from RSpec's printed failure backtraces; the sandbox reserves /src,
49
+ # so this cannot hide user files.
50
+ RSpec.configure do |config|
51
+ config.backtrace_exclusion_patterns << %r{\A/src/}
52
+ end
53
+ rescue LoadError => e
54
+ raise LoadError,
55
+ "rspec is not available in this image (#{e.message}); " \
56
+ "rebuild it with `bundle exec rake security_box:build_image`"
57
+ end
58
+ private_class_method :load_rspec!
59
+ end
@@ -8,3 +8,8 @@ gem "ruby_wasm", "~> 2.10.1"
8
8
  # Rakefile). Only guest-safe dependencies belong here — host-only gems
9
9
  # (wasmtime) must stay in the project Gemfile.
10
10
  gem "sb_rpc", path: "sb_rpc"
11
+
12
+ # Bundled so guest code can run spec suites mounted at /work (see
13
+ # lib/security_box/guest/rspec.rb and SB.run_rspec). RSpec is pure Ruby and
14
+ # adds no native extensions, so it does not change the Ruby build cache key.
15
+ gem "rspec", "~> 3.13"
@@ -6,7 +6,21 @@ PATH
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ diff-lcs (1.6.2)
9
10
  logger (1.7.0)
11
+ rspec (3.13.2)
12
+ rspec-core (~> 3.13.0)
13
+ rspec-expectations (~> 3.13.0)
14
+ rspec-mocks (~> 3.13.0)
15
+ rspec-core (3.13.6)
16
+ rspec-support (~> 3.13.0)
17
+ rspec-expectations (3.13.5)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.13.0)
20
+ rspec-mocks (3.13.8)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.13.0)
23
+ rspec-support (3.13.7)
10
24
  ruby_wasm (2.10.1)
11
25
  logger
12
26
  ruby_wasm (2.10.1-x86_64-linux)
@@ -17,12 +31,19 @@ PLATFORMS
17
31
  x86_64-linux
18
32
 
19
33
  DEPENDENCIES
34
+ rspec (~> 3.13)
20
35
  ruby_wasm (~> 2.10.1)
21
36
  sb_rpc!
22
37
 
23
38
  CHECKSUMS
24
39
  bundler (4.0.20) sha256=7978a8ac648767f5e635bc522445b79e80a52b907a39a36c2d8085ed6bc762ae
40
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
25
41
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
42
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
43
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
44
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
45
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
46
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
26
47
  ruby_wasm (2.10.1) sha256=874e0acb783a8326bfb0db892004816c26963a4276a9f719791a8828e53a05f1
27
48
  ruby_wasm (2.10.1-x86_64-linux) sha256=728e29dc688246572b56dcef1c7b131431d07865320c9b8434dd1ccdfd63db2b
28
49
  sb_rpc (0.1.0)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SecurityBox
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: security_box
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcelo Junior
@@ -45,6 +45,7 @@ files:
45
45
  - lib/security_box/guest/main.rb
46
46
  - lib/security_box/guest/prelude.rb
47
47
  - lib/security_box/guest/rpc.rb
48
+ - lib/security_box/guest/rspec.rb
48
49
  - lib/security_box/guest_ext/Gemfile
49
50
  - lib/security_box/guest_ext/Gemfile.lock
50
51
  - lib/security_box/guest_ext/sb_rpc/ext/sb_rpc/extconf.rb