kobako 0.9.1-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: fab93ea70f79595c4c5cd7f155bb1337d62e71c4579f75b70654141078543b86
4
- data.tar.gz: 70a69865ed1501dcf539261bfa6c09158c28214a6784b87aa3ac1a6d46ef2b4b
3
+ metadata.gz: f2b783a758091145f58f0a8e13b84c76c5c3a1420325ca2d272b5a58d31f7d38
4
+ data.tar.gz: b890067d1cc6af31a9cf74c8f3116504222ab1c64921715a3a9f35d6c60a9adb
5
5
  SHA512:
6
- metadata.gz: d7e682bbe2cde22e04c25fffa94b5c9f2e024f3d6c0f9b368a45ee05c4ecd3bcc8c3c7ced054609a9644f7e34cf42ea33592acbd008f8202dba4853e9a0a98b0
7
- data.tar.gz: 9cb3083092c7f596512c9cd3f8c9c55bfd4f3a137546e41b738ad02dce6a67e8e1452fa746e463a9be60443cac1c8ab4801e12bb04d4ee7b41ebc3a4425d151e
6
+ metadata.gz: ee3aa19299ca309a21b3ae63d3cfd38ac322c3ebd4bd0d7b8aecac2e893edfaff7cf36ec5878f0f4f42c2e1f50fa68ad5df07e38d3d27829ce442162896cf848
7
+ data.tar.gz: 511eeeaeeb466f7ce6b441e88cb8927de4c58da73e7596b327b5658bcffd8ccbbe6aec6877b3461d7f2de2b0eb1b544da4dedf939a37dbf340cc56cee153dd85
@@ -1 +1 @@
1
- {".":"0.9.1","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,16 @@
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
+
3
14
  ## [0.9.1](https://github.com/elct9620/kobako/compare/v0.9.0...v0.9.1) (2026-06-11)
4
15
 
5
16
 
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"
@@ -46,13 +46,28 @@ module Kobako
46
46
  # metaprogramming surface (+send+, +public_send+, +instance_eval+,
47
47
  # +method+, +tap+, +instance_variable_get+, ...) rather than Service
48
48
  # behaviour. A guest-supplied method name resolving to one of these is
49
- # rejected: the security contract is that only methods the bound object
50
- # itself defines are reachable, and +public_send(:send, ...)+ would
51
- # otherwise let a guest pivot through +send+ into the private
52
- # +Kernel#eval+ / +#system+ surface (host RCE).
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).
53
54
  META_OWNERS = [BasicObject, Kernel, Object, Module, Class].freeze
54
55
  private_constant :META_OWNERS
55
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
+
56
71
  # Dispatch a single transport request and return the encoded
57
72
  # Response bytes ({docs/behavior.md B-12}[link:../../../docs/behavior.md]).
58
73
  # Invoked from the +Runtime#on_dispatch+ Proc that
@@ -131,14 +146,18 @@ module Kobako
131
146
  end
132
147
 
133
148
  # Guard the +public_send+ below against ambient reflection methods
134
- # (see {META_OWNERS}). A concretely-defined public method whose owner
135
- # is a meta module is rejected; a name with no concrete public method
136
- # is allowed only when the target opts into it via +respond_to?+
137
- # (dynamic +method_missing+ Services), since the dangerous meta methods
138
- # are all concretely defined and therefore never reach that branch.
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.
139
156
  def reject_meta_method!(target, name)
140
157
  owner = target.public_method(name).owner
141
- return unless META_OWNERS.include?(owner)
158
+ gadget = GADGET_OWNERS.include?(owner)
159
+ return unless META_OWNERS.include?(owner) || gadget
160
+ return if gadget && CALLABLE_ALLOW.include?(name)
142
161
 
143
162
  raise UndefinedTargetError, "method #{name.inspect} is not a Service method"
144
163
  rescue NameError
@@ -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.1"
4
+ VERSION = "0.9.2"
5
5
  end
@@ -7,8 +7,7 @@
7
7
  "component": "kobako",
8
8
  "include-component-in-tag": false,
9
9
  "release-type": "ruby",
10
- "exclude-paths": ["wasm"],
11
- "release-as": "0.9.1"
10
+ "exclude-paths": ["wasm"]
12
11
  },
13
12
  "wasm/kobako-core": {
14
13
  "component": "kobako-core",
@@ -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
@@ -8,6 +8,10 @@ module Kobako
8
8
 
9
9
  META_OWNERS: Array[Module]
10
10
 
11
+ GADGET_OWNERS: Array[Module]
12
+
13
+ CALLABLE_ALLOW: Array[Symbol]
14
+
11
15
  def self?.dispatch: (String request_bytes, Kobako::Catalog::Namespaces namespaces, Kobako::Catalog::Handles handler, ^(String) -> String yield_to_guest) -> String
12
16
 
13
17
  def self?.resolve_call_args: (Kobako::Transport::Request request, Kobako::Catalog::Handles handler) -> [Array[untyped], Hash[Symbol, 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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kobako
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Aotokitsuruya