kobako 0.9.0-x86_64-linux → 0.9.2-x86_64-linux

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: f3200b8962a6710eeb92c4e293caf6153617334c42945589bb825b8602c72cc5
4
- data.tar.gz: 5a37c3f1010128d74afadb8193bb76c73d561611bdd50d3a2f2db69b45956d24
3
+ metadata.gz: f2b783a758091145f58f0a8e13b84c76c5c3a1420325ca2d272b5a58d31f7d38
4
+ data.tar.gz: b890067d1cc6af31a9cf74c8f3116504222ab1c64921715a3a9f35d6c60a9adb
5
5
  SHA512:
6
- metadata.gz: 8bb785cb47fbca434bf31c6a70116482cf87c9dae4671c24dc8872b706efe756c2b432865e95dfaa2df0fed121c5b254f7adf26d363a5e77fc1617813987aeae
7
- data.tar.gz: 7327b9ea8268ac98eb3431ef0bdcc7c65a896b78ca343d8d26b8f96be8bd31fe0236a5e13a86ec42aa528d0672c032edc8eb85bcc3341b7e1712cb47387364ea
6
+ metadata.gz: ee3aa19299ca309a21b3ae63d3cfd38ac322c3ebd4bd0d7b8aecac2e893edfaff7cf36ec5878f0f4f42c2e1f50fa68ad5df07e38d3d27829ce442162896cf848
7
+ data.tar.gz: 511eeeaeeb466f7ce6b441e88cb8927de4c58da73e7596b327b5658bcffd8ccbbe6aec6877b3461d7f2de2b0eb1b544da4dedf939a37dbf340cc56cee153dd85
@@ -1 +1 @@
1
- {".":"0.9.0","wasm/kobako-core":"0.4.0","wasm/kobako":"0.4.0","wasm/kobako-io":"0.4.0","wasm/kobako-regexp":"0.4.0"}
1
+ {".":"0.9.2","wasm/kobako-core":"0.4.1","wasm/kobako":"0.4.1","wasm/kobako-io":"0.4.1","wasm/kobako-regexp":"0.4.1"}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.2](https://github.com/elct9620/kobako/compare/v0.9.1...v0.9.2) (2026-06-11)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **catalog:** never mint a Capability Handle for a reflective gadget ([6c2d29d](https://github.com/elct9620/kobako/commit/6c2d29d0fbcced5187df5538c2c6c437705fd6d8))
9
+ * **ext:** cap stdin frames at 16 MiB like the run envelope ([a94099a](https://github.com/elct9620/kobako/commit/a94099a03830929c55fcb266e227073db9c5a624))
10
+ * **ext:** deny guest ambient clock and entropy at the WASI layer ([1275b35](https://github.com/elct9620/kobako/commit/1275b35264813628a2aeb396a3546faf1f6d9d0c))
11
+ * **transport:** reject reflective gadget methods in guest dispatch ([948fb9e](https://github.com/elct9620/kobako/commit/948fb9ea7d6c0d6bd91f6e261d3263743974388b))
12
+ * **wasm:** mirror the reflection rejection in the guest proxy ([f6ead3b](https://github.com/elct9620/kobako/commit/f6ead3b91f1ac92c3c075397d177edb4b82cd15d))
13
+
14
+ ## [0.9.1](https://github.com/elct9620/kobako/compare/v0.9.0...v0.9.1) (2026-06-11)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **transport:** block ambient reflection in guest dispatch (GHSA-7pwq-q9jf-539h) ([dd08166](https://github.com/elct9620/kobako/commit/dd081665f368f7ba54e476c3ad045ee1aa8ed703))
20
+
3
21
  ## [0.9.0](https://github.com/elct9620/kobako/compare/v0.8.0...v0.9.0) (2026-06-10)
4
22
 
5
23
 
data/data/kobako.wasm CHANGED
Binary file
Binary file
Binary file
Binary file
@@ -30,6 +30,13 @@ module Kobako
30
30
  # +0x7fff_ffff+ (2³¹ − 1). Allocation beyond the cap raises
31
31
  # immediately — no silent truncation, no wrap, no ID reuse.
32
32
  class Handles
33
+ # Reflective gadget types that are never minted as a Capability Handle
34
+ # ({docs/behavior.md B-43}[link:../../../docs/behavior.md]): wrapping a
35
+ # +Binding+ / +Method+ / +UnboundMethod+ would hand the guest a callable
36
+ # proxy onto host reflection (a returned +Binding+ reaches +Binding#eval+).
37
+ UNWRAPPABLE_TYPES = [Binding, Method, UnboundMethod].freeze
38
+ private_constant :UNWRAPPABLE_TYPES
39
+
33
40
  # Build a fresh, empty table. +next_id+ is an internal seam that
34
41
  # sets the starting value of the monotonic counter (defaults to 1 per
35
42
  # B-15); tests pass a value near +Kobako::Handle::MAX_ID+ to exercise
@@ -53,14 +60,9 @@ module Kobako
53
60
  # is reserved for the codec's wire-decode path, where the id is
54
61
  # the only thing the bytes carry.
55
62
  def alloc(object)
63
+ reject_unwrappable!(object)
64
+ ensure_capacity!
56
65
  id = @next_id
57
- cap = Kobako::Handle::MAX_ID
58
- if id > cap
59
- raise HandlerExhaustedError,
60
- "Out of handle allocations: too many host objects were referenced " \
61
- "in a single invocation (limit #{cap})"
62
- end
63
-
64
66
  @entries[id] = object
65
67
  @next_id = id + 1
66
68
  Kobako::Handle.restore(id)
@@ -94,6 +96,27 @@ module Kobako
94
96
 
95
97
  private
96
98
 
99
+ # Refuse to mint a Capability Handle for a reflective gadget
100
+ # ({UNWRAPPABLE_TYPES}, B-43). Raising here keeps the rule at the single
101
+ # mint point, so it holds on both the Service-return (B-14) and the
102
+ # +#run+ host→guest auto-wrap (B-34) paths.
103
+ def reject_unwrappable!(object)
104
+ return unless UNWRAPPABLE_TYPES.any? { |type| object.is_a?(type) }
105
+
106
+ raise SandboxError, "a #{object.class} cannot cross as a Capability Handle"
107
+ end
108
+
109
+ # Guard {#alloc} against issuing an ID past the B-21 cap. Returns +nil+
110
+ # on success; raises +Kobako::HandlerExhaustedError+ at exhaustion.
111
+ def ensure_capacity!
112
+ cap = Kobako::Handle::MAX_ID
113
+ return unless @next_id > cap
114
+
115
+ raise HandlerExhaustedError,
116
+ "Out of handle allocations: too many host objects were referenced " \
117
+ "in a single invocation (limit #{cap})"
118
+ end
119
+
97
120
  # Single source of truth for the "unknown Handle id" raise used by
98
121
  # {#fetch}. Returns +nil+ on success; raises +Kobako::SandboxError+
99
122
  # when +id+ is not currently bound.
@@ -3,7 +3,6 @@
3
3
  require_relative "handles"
4
4
  require_relative "../codec"
5
5
  require_relative "../errors"
6
- require_relative "../transport/request"
7
6
  require_relative "../namespace"
8
7
 
9
8
  module Kobako
data/lib/kobako/codec.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "codec/error"
4
+ require_relative "codec/utils"
5
+ require_relative "codec/factory"
6
+ require_relative "codec/encoder"
7
+ require_relative "codec/decoder"
4
8
 
5
9
  module Kobako
6
10
  # Host-side MessagePack codec for the kobako wire contract — the
@@ -17,15 +21,10 @@ module Kobako
17
21
  # {Decoder} are thin wrappers that register the three kobako-specific
18
22
  # ext types (0x00 Symbol, 0x01 Capability Handle, 0x02 Exception
19
23
  # envelope) on a single +MessagePack::Factory+ instance. The Rust side
20
- # mirrors this layer as the +codec+ module in the +kobako-wasm+ crate;
24
+ # mirrors this layer as the +codec+ module in the +kobako-core+ crate;
21
25
  # the ext-code constants live as module-private values on {Factory}
22
26
  # alongside +codec::EXT_SYMBOL+ / +codec::EXT_HANDLE+ /
23
27
  # +codec::EXT_ERRENV+ on that side.
24
28
  module Codec
25
29
  end
26
30
  end
27
-
28
- require_relative "codec/utils"
29
- require_relative "codec/factory"
30
- require_relative "codec/encoder"
31
- require_relative "codec/decoder"
@@ -42,6 +42,32 @@ module Kobako
42
42
  # ({docs/behavior.md E-12}[link:../../../docs/behavior.md]).
43
43
  class UndefinedTargetError < StandardError; end
44
44
 
45
+ # Modules whose instance methods are ambient Ruby reflection /
46
+ # metaprogramming surface (+send+, +public_send+, +instance_eval+,
47
+ # +method+, +tap+, +instance_variable_get+, ...) rather than Service
48
+ # behaviour. A guest-supplied method name resolving to one of these is
49
+ # rejected ({docs/behavior.md B-42}[link:../../../docs/behavior.md]):
50
+ # only methods the bound object itself exposes as Service behaviour are
51
+ # reachable, and +public_send(:send, ...)+ would otherwise let a guest
52
+ # pivot through +send+ into the private +Kernel#eval+ / +#system+
53
+ # surface (host RCE).
54
+ META_OWNERS = [BasicObject, Kernel, Object, Module, Class].freeze
55
+ private_constant :META_OWNERS
56
+
57
+ # Callable gadget types whose own public methods are reflection surface
58
+ # (+Proc#binding+ reaches +Binding#eval+, +Method#receiver+ / +#unbind+
59
+ # hand back the underlying object) rather than Service behaviour. Only
60
+ # {CALLABLE_ALLOW} is reachable on a target of these types; a bound
61
+ # lambda stays invocable, its reflective surface does not (B-42).
62
+ GADGET_OWNERS = [Proc, Method, UnboundMethod, Binding].freeze
63
+ private_constant :GADGET_OWNERS
64
+
65
+ # The sole methods reachable on a {GADGET_OWNERS} target: invoking it
66
+ # (+call+ / +[]+ / +yield+) and the harmless +arity+ / +lambda?+
67
+ # describers that aid guest-side debugging.
68
+ CALLABLE_ALLOW = %i[call [] yield arity lambda?].freeze
69
+ private_constant :CALLABLE_ALLOW
70
+
45
71
  # Dispatch a single transport request and return the encoded
46
72
  # Response bytes ({docs/behavior.md B-12}[link:../../../docs/behavior.md]).
47
73
  # Invoked from the +Runtime#on_dispatch+ Proc that
@@ -109,14 +135,37 @@ module Kobako
109
135
  # so the same call site handles both cases without an explicit
110
136
  # conditional.
111
137
  def invoke(target, method, args, kwargs, yielder = nil)
138
+ name = method.to_sym
139
+ reject_meta_method!(target, name)
112
140
  block = yielder&.to_proc
113
141
  if kwargs.empty?
114
- target.public_send(method.to_sym, *args, &block)
142
+ target.public_send(name, *args, &block)
115
143
  else
116
- target.public_send(method.to_sym, *args, **kwargs, &block)
144
+ target.public_send(name, *args, **kwargs, &block)
117
145
  end
118
146
  end
119
147
 
148
+ # Guard the +public_send+ below against ambient reflection methods
149
+ # ({docs/behavior.md B-42}[link:../../../docs/behavior.md]). A public
150
+ # method whose owner is a {META_OWNERS} or {GADGET_OWNERS} module is
151
+ # rejected, except {CALLABLE_ALLOW} on a gadget target (a bound lambda
152
+ # stays invocable). A name with no concrete public method is allowed
153
+ # only when the target opts into it via +respond_to?+ (dynamic
154
+ # +method_missing+ Services), since the dangerous methods are all
155
+ # concretely defined and therefore never reach that branch.
156
+ def reject_meta_method!(target, name)
157
+ owner = target.public_method(name).owner
158
+ gadget = GADGET_OWNERS.include?(owner)
159
+ return unless META_OWNERS.include?(owner) || gadget
160
+ return if gadget && CALLABLE_ALLOW.include?(name)
161
+
162
+ raise UndefinedTargetError, "method #{name.inspect} is not a Service method"
163
+ rescue NameError
164
+ return if target.respond_to?(name)
165
+
166
+ raise UndefinedTargetError, "no public method #{name.inspect} on target"
167
+ end
168
+
120
169
  # {docs/behavior.md B-16}[link:../../../docs/behavior.md] — A Kobako::Handle arriving as a positional or keyword
121
170
  # argument identifies a host-side object previously allocated by a prior
122
171
  # transport call's Handle wrap (B-14). Resolve it back to the Ruby object before
@@ -5,16 +5,8 @@ require_relative "../codec"
5
5
 
6
6
  module Kobako
7
7
  # See lib/kobako/transport.rb for the umbrella module doc; this file
8
- # owns the Request value object and its +#encode+ / +.decode+ codec,
9
- # plus the +STATUS_OK+ / +STATUS_ERROR+ constants shared with Response.
8
+ # owns the Request value object and its +#encode+ / +.decode+ codec.
10
9
  module Transport
11
- # ---------------- Response status bytes (docs/wire-contract.md § Response Shape) ---
12
-
13
- # Response variant marker for the success branch.
14
- STATUS_OK = 0
15
- # Response variant marker for the fault branch.
16
- STATUS_ERROR = 1
17
-
18
10
  # Value object for a single guest-initiated Transport Request
19
11
  # ({docs/wire-codec.md Envelope Encoding → Request}[link:../../../docs/wire-codec.md]).
20
12
  #
@@ -2,12 +2,19 @@
2
2
 
3
3
  require_relative "../codec"
4
4
  require_relative "../fault"
5
- require_relative "request"
6
5
 
7
6
  module Kobako
8
7
  # See lib/kobako/transport.rb for the umbrella module doc; this file
9
- # owns the Response value object and its +#encode+ / +.decode+ codec.
8
+ # owns the Response value object and its +#encode+ / +.decode+ codec,
9
+ # plus the +STATUS_OK+ / +STATUS_ERROR+ status bytes.
10
10
  module Transport
11
+ # ---------------- Response status bytes (docs/wire-contract.md § Response Shape) ---
12
+
13
+ # Response variant marker for the success branch.
14
+ STATUS_OK = 0
15
+ # Response variant marker for the fault branch.
16
+ STATUS_ERROR = 1
17
+
11
18
  # Value object for a single host-side Transport Response
12
19
  # ({docs/wire-codec.md Envelope Encoding → Response}[link:../../../docs/wire-codec.md]).
13
20
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- VERSION = "0.9.0"
4
+ VERSION = "0.9.2"
5
5
  end
@@ -1,6 +1,8 @@
1
1
  module Kobako
2
2
  module Catalog
3
3
  class Handles
4
+ UNWRAPPABLE_TYPES: Array[Module]
5
+
4
6
  def initialize: (?next_id: Integer) -> void
5
7
 
6
8
  def alloc: (untyped object) -> Kobako::Handle
@@ -13,6 +15,10 @@ module Kobako
13
15
 
14
16
  private
15
17
 
18
+ def reject_unwrappable!: (untyped object) -> void
19
+
20
+ def ensure_capacity!: () -> void
21
+
16
22
  def require_bound!: (Integer id) -> void
17
23
  end
18
24
  end
@@ -6,6 +6,12 @@ module Kobako
6
6
 
7
7
  BREAK_THROW: Symbol
8
8
 
9
+ META_OWNERS: Array[Module]
10
+
11
+ GADGET_OWNERS: Array[Module]
12
+
13
+ CALLABLE_ALLOW: Array[Symbol]
14
+
9
15
  def self?.dispatch: (String request_bytes, Kobako::Catalog::Namespaces namespaces, Kobako::Catalog::Handles handler, ^(String) -> String yield_to_guest) -> String
10
16
 
11
17
  def self?.resolve_call_args: (Kobako::Transport::Request request, Kobako::Catalog::Handles handler) -> [Array[untyped], Hash[Symbol, untyped]]
@@ -14,6 +20,8 @@ module Kobako
14
20
 
15
21
  def self?.invoke: (untyped target, String method, Array[untyped] args, Hash[Symbol, untyped] kwargs, ?Kobako::Transport::Yielder? yielder) -> untyped
16
22
 
23
+ def self?.reject_meta_method!: (untyped target, Symbol name) -> void
24
+
17
25
  def self?.resolve_arg: (untyped value, Kobako::Catalog::Handles handler) -> untyped
18
26
 
19
27
  def self?.resolve_target: (String | Kobako::Handle target, Kobako::Catalog::Namespaces namespaces, Kobako::Catalog::Handles handler) -> untyped
@@ -1,8 +1,5 @@
1
1
  module Kobako
2
2
  module Transport
3
- STATUS_OK: Integer
4
- STATUS_ERROR: Integer
5
-
6
3
  class Request < Data
7
4
  attr_reader target: String | Kobako::Handle
8
5
  attr_reader method_name: String
@@ -1,5 +1,8 @@
1
1
  module Kobako
2
2
  module Transport
3
+ STATUS_OK: Integer
4
+ STATUS_ERROR: Integer
5
+
3
6
  class Response < Data
4
7
  attr_reader status: Integer
5
8
  attr_reader payload: untyped
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kobako
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.2
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Aotokitsuruya
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-10 00:00:00.000000000 Z
11
+ date: 2026-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack