@markii/lua 0.17.0-next.2 → 0.17.0-next.4
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.
- package/dist/capabilities.js +13 -15
- package/dist/json-decode.d.ts +2 -3
- package/dist/json-decode.js +4 -5
- package/dist/json-table.d.ts +2 -2
- package/dist/json-table.js +2 -2
- package/dist/marshal.d.ts +1 -2
- package/dist/marshal.js +4 -5
- package/dist/sandbox.js +1 -2
- package/package.json +3 -3
package/dist/capabilities.js
CHANGED
|
@@ -199,9 +199,9 @@ export function buildCapabilities(config) {
|
|
|
199
199
|
// this function nested POST/PATCH wiring inside "if GET is granted",
|
|
200
200
|
// which silently produced no `net.post` at all for a POST-only grant.
|
|
201
201
|
//
|
|
202
|
-
// The `net` table itself, and every one of its three methods, is
|
|
203
|
-
// ALWAYS wired, regardless of grants, provider wiring, or tier
|
|
204
|
-
//
|
|
202
|
+
// The `net` table itself, and every one of its three methods, is
|
|
203
|
+
// ALWAYS wired, regardless of grants, provider wiring, or tier. Were it
|
|
204
|
+
// not, an ungranted
|
|
205
205
|
// script saw a missing global (`net` itself omitted when nothing was
|
|
206
206
|
// granted, or a missing method when a specific host/verb wasn't) and
|
|
207
207
|
// failed with an ordinary Lua "attempt to index/call a nil value" —
|
|
@@ -270,8 +270,8 @@ local __smd_net_get = __smd_net_get_raw
|
|
|
270
270
|
-- a dynamic global lookup inside \`net.fetch_json\`'s own body. Otherwise a
|
|
271
271
|
-- script could do \`__smd_json_decode = function(t) return t end\` before a
|
|
272
272
|
-- LATER \`net.fetch_json\` call and neuter the decoder's own depth guard and
|
|
273
|
-
-- array-marker stripping entirely
|
|
274
|
-
-- rebinding risk \`./json-decode\`'s own doc comment
|
|
273
|
+
-- array-marker stripping entirely -- the same
|
|
274
|
+
-- rebinding risk \`./json-decode\`'s own doc comment already
|
|
275
275
|
-- closes for the primitives the decoder uses internally.
|
|
276
276
|
local __smd_net_get_json_decode = __smd_json_decode
|
|
277
277
|
__smd_net_get_raw = nil
|
|
@@ -415,7 +415,7 @@ end
|
|
|
415
415
|
return undefined;
|
|
416
416
|
// Depth/node budget, checked BEFORE the stored value is ever encoded
|
|
417
417
|
// to text and handed to Lua — mirrors `net.fetch_json`'s own
|
|
418
|
-
// pre-check exactly
|
|
418
|
+
// pre-check exactly. A host-stored value is
|
|
419
419
|
// exactly as untrusted as a remote fetch body — a bundle's
|
|
420
420
|
// `.cache/*.json` file, for instance, can be edited by anything with
|
|
421
421
|
// write access to the bundle, not just this sandbox's own WRITE side
|
|
@@ -424,16 +424,14 @@ end
|
|
|
424
424
|
// already capped.
|
|
425
425
|
//
|
|
426
426
|
// An entry that fails the budget check, isn't a plain object at all,
|
|
427
|
-
// or carries a missing/non-finite `storedAtMs` (
|
|
428
|
-
// a poisoned host snapshot like `{value: 'x'}` with no `storedAtMs`,
|
|
427
|
+
// or carries a missing/non-finite `storedAtMs` (a poisoned host snapshot like `{value: 'x'}` with no `storedAtMs`,
|
|
429
428
|
// or `storedAtMs: 'abc'`, would otherwise reach the Lua-side
|
|
430
429
|
// `now - storedAtMs` arithmetic in `cache.get` below and throw a
|
|
431
430
|
// script-error, wedging the note permanently instead of self-healing)
|
|
432
431
|
// — or that cannot be `JSON.stringify`'d at all (cyclic, or
|
|
433
432
|
// BigInt-bearing — something other than this sandbox's own WRITE
|
|
434
433
|
// side must have written it, since that side already rejects both) —
|
|
435
|
-
// is SELF-HEALED rather than denied (
|
|
436
|
-
// verification notes): it is treated as a cache MISS, exactly as if
|
|
434
|
+
// is SELF-HEALED rather than denied (by design): it is treated as a cache MISS, exactly as if
|
|
437
435
|
// `key` had never been stored. `cache.get`'s Lua body (below) then
|
|
438
436
|
// calls `fn()` and writes the fresh, already-capped result back
|
|
439
437
|
// through `__smd_cache_set_raw`, which quietly repairs the stored
|
|
@@ -441,7 +439,7 @@ end
|
|
|
441
439
|
// capability denial is reserved for the fresh recompute itself
|
|
442
440
|
// failing the WRITE side's own caps, which is unchanged.
|
|
443
441
|
//
|
|
444
|
-
// Finiteness alone isn't enough
|
|
442
|
+
// Finiteness alone isn't enough: a poisoned
|
|
445
443
|
// snapshot with a huge FUTURE `storedAtMs` (e.g.
|
|
446
444
|
// `Number.MAX_SAFE_INTEGER * 1000`) is finite, so it used to pass
|
|
447
445
|
// straight through to the Lua-side `(now - storedAtMs) < ttl*1000`
|
|
@@ -501,10 +499,10 @@ local __smd_cache_get = __smd_cache_get_raw
|
|
|
501
499
|
local __smd_cache_set = __smd_cache_set_raw
|
|
502
500
|
local __smd_now_ms = __smd_now_ms_raw
|
|
503
501
|
-- Captured into locals HERE, at prelude-definition time -- NOT resolved as
|
|
504
|
-
-- dynamic globals inside cache.get's own body
|
|
505
|
-
--
|
|
506
|
-
-- return t end\`
|
|
507
|
-
-- return v end\`
|
|
502
|
+
-- dynamic globals inside cache.get's own body. Without this, a script
|
|
503
|
+
-- could do \`__smd_json_decode = function(t)
|
|
504
|
+
-- return t end\` or, more seriously, \`__smd_marshal_root = function(v)
|
|
505
|
+
-- return v end\` before a LATER cache.get call: the marshal-root
|
|
508
506
|
-- rebind would send an UNBOUNDED, uncapped table straight through
|
|
509
507
|
-- wasmoon's own eager table->JS conversion into the store the moment the
|
|
510
508
|
-- rebound "identity" function handed it back, since the write side
|
package/dist/json-decode.d.ts
CHANGED
|
@@ -68,8 +68,7 @@ import { type MarshalLimits } from './marshal.js';
|
|
|
68
68
|
* cap that already bounds the JS-side parse.
|
|
69
69
|
*
|
|
70
70
|
* ## Every Lua primitive this decoder uses is pinned at PRELUDE-DEFINITION
|
|
71
|
-
* ## time, not resolved as a dynamic global at call time
|
|
72
|
-
* ## finding A1)
|
|
71
|
+
* ## time, not resolved as a dynamic global at call time
|
|
73
72
|
*
|
|
74
73
|
* `string.byte`/`sub`/`find`/`char`, `table.concat`, `tonumber`,
|
|
75
74
|
* `math.floor`, and `error` are all captured into locals ONCE, at the top
|
|
@@ -84,6 +83,6 @@ import { type MarshalLimits } from './marshal.js';
|
|
|
84
83
|
* the depth guard entirely, since `fail()` would then no longer actually
|
|
85
84
|
* raise). This mirrors `./marshal`'s own `buildMarshalPrelude`, which
|
|
86
85
|
* already does exactly this for the same reason (see that module's doc
|
|
87
|
-
* comment
|
|
86
|
+
* comment).
|
|
88
87
|
*/
|
|
89
88
|
export declare function buildJsonDecodePrelude(limits: MarshalLimits): string;
|
package/dist/json-decode.js
CHANGED
|
@@ -69,8 +69,7 @@ import { ARRAY_MARKER } from './marshal.js';
|
|
|
69
69
|
* cap that already bounds the JS-side parse.
|
|
70
70
|
*
|
|
71
71
|
* ## Every Lua primitive this decoder uses is pinned at PRELUDE-DEFINITION
|
|
72
|
-
* ## time, not resolved as a dynamic global at call time
|
|
73
|
-
* ## finding A1)
|
|
72
|
+
* ## time, not resolved as a dynamic global at call time
|
|
74
73
|
*
|
|
75
74
|
* `string.byte`/`sub`/`find`/`char`, `table.concat`, `tonumber`,
|
|
76
75
|
* `math.floor`, and `error` are all captured into locals ONCE, at the top
|
|
@@ -85,7 +84,7 @@ import { ARRAY_MARKER } from './marshal.js';
|
|
|
85
84
|
* the depth guard entirely, since `fail()` would then no longer actually
|
|
86
85
|
* raise). This mirrors `./marshal`'s own `buildMarshalPrelude`, which
|
|
87
86
|
* already does exactly this for the same reason (see that module's doc
|
|
88
|
-
* comment
|
|
87
|
+
* comment).
|
|
89
88
|
*/
|
|
90
89
|
export function buildJsonDecodePrelude(limits) {
|
|
91
90
|
return `
|
|
@@ -246,7 +245,7 @@ function __smd_json_decode(__smd_json_text)
|
|
|
246
245
|
-- \`{"${ARRAY_MARKER}":true,"1":"a","2":"b","x":"kept"}\` would decode
|
|
247
246
|
-- to a Lua table carrying that same key, and later marshal to the
|
|
248
247
|
-- host as a JS ARRAY -- silently reshaping attacker-controlled data
|
|
249
|
-
-- and dropping its real keys (
|
|
248
|
+
-- and dropping its real keys (see marshal.ts). Dropping
|
|
250
249
|
-- (rather than rejecting the whole response) keeps an otherwise
|
|
251
250
|
-- ordinary API response usable even if it happens to use this exact
|
|
252
251
|
-- field name for something unrelated.
|
|
@@ -281,7 +280,7 @@ function __smd_json_decode(__smd_json_text)
|
|
|
281
280
|
local value = decode_value(depth + 1)
|
|
282
281
|
n = n + 1
|
|
283
282
|
-- A JSON null ARRAY ELEMENT decodes to Lua \`false\`, not an absent
|
|
284
|
-
-- slot (
|
|
283
|
+
-- slot (by design): an absent
|
|
285
284
|
-- slot would leave a hole (\`#\`/\`ipairs\` stop early, and returning
|
|
286
285
|
-- the table to the host fails \`MARK_MARSHAL:key-type\` since the walk
|
|
287
286
|
-- no longer sees sequential \`1..n\` keys) -- reproducing issue #6's
|
package/dist/json-table.d.ts
CHANGED
|
@@ -78,8 +78,8 @@ export interface JsonTableConfig {
|
|
|
78
78
|
* PRELUDE-DEFINITION TIME -- before any untrusted script code runs -- so a
|
|
79
79
|
* later script reassigning any of those globals (or `json` itself) cannot
|
|
80
80
|
* neuter the type check or reach past the reused decoder/marshal walk. This
|
|
81
|
-
* is the same discipline `./json-decode`
|
|
82
|
-
*
|
|
81
|
+
* is the same discipline `./json-decode` and `./capabilities`
|
|
82
|
+
* already apply to their own internal calls into these
|
|
83
83
|
* exact two functions.
|
|
84
84
|
*/
|
|
85
85
|
export declare function buildJsonTable(config: JsonTableConfig): {
|
package/dist/json-table.js
CHANGED
|
@@ -64,8 +64,8 @@ import { checkJsonWithinLimits, finalizeMarshaledValue, } from './marshal.js';
|
|
|
64
64
|
* PRELUDE-DEFINITION TIME -- before any untrusted script code runs -- so a
|
|
65
65
|
* later script reassigning any of those globals (or `json` itself) cannot
|
|
66
66
|
* neuter the type check or reach past the reused decoder/marshal walk. This
|
|
67
|
-
* is the same discipline `./json-decode`
|
|
68
|
-
*
|
|
67
|
+
* is the same discipline `./json-decode` and `./capabilities`
|
|
68
|
+
* already apply to their own internal calls into these
|
|
69
69
|
* exact two functions.
|
|
70
70
|
*/
|
|
71
71
|
export function buildJsonTable(config) {
|
package/dist/marshal.d.ts
CHANGED
|
@@ -121,8 +121,7 @@ export declare function checkJsonWithinLimits(value: unknown, limits: MarshalLim
|
|
|
121
121
|
* True array marker set by the Lua-side marshal walk (see
|
|
122
122
|
* `buildMarshalPrelude`). Exported (not just module-private) so
|
|
123
123
|
* `./json-decode`'s JSON decoder can recognize and DROP this exact key if
|
|
124
|
-
* it ever appears as an object key in attacker-controlled JSON
|
|
125
|
-
* finding A4): without that, a remote response body like
|
|
124
|
+
* it ever appears as an object key in attacker-controlled JSON: without that, a remote response body like
|
|
126
125
|
* `{"__smd_is_array":true,"1":"a","2":"b","x":"kept"}` would decode to an
|
|
127
126
|
* ordinary Lua table carrying this same marker key, and `finalizeMarshaledValue`
|
|
128
127
|
* below — which has no way to tell "the trusted marshal walk set this" apart
|
package/dist/marshal.js
CHANGED
|
@@ -78,9 +78,9 @@ export function buildMarshalPrelude(limits) {
|
|
|
78
78
|
-- once per engine, before any untrusted user code runs), so the walk below
|
|
79
79
|
-- closes over these as upvalues fixed to the genuine primitives -- immune to
|
|
80
80
|
-- a later script doing e.g. \`error = function() end\` in the shared globals
|
|
81
|
-
-- table before this walk runs.
|
|
82
|
-
--
|
|
83
|
-
--
|
|
81
|
+
-- table before this walk runs. Resolving error/type/pairs/math.floor as
|
|
82
|
+
-- DYNAMIC GLOBAL lookups would let a script rebind them to neuter its own
|
|
83
|
+
-- caps.
|
|
84
84
|
local error, type, pairs, floor, sfind = error, type, pairs, math.floor, string.find
|
|
85
85
|
|
|
86
86
|
local function __smd_marshal(value, seen, depth, budget)
|
|
@@ -222,8 +222,7 @@ export function checkJsonWithinLimits(value, limits) {
|
|
|
222
222
|
* True array marker set by the Lua-side marshal walk (see
|
|
223
223
|
* `buildMarshalPrelude`). Exported (not just module-private) so
|
|
224
224
|
* `./json-decode`'s JSON decoder can recognize and DROP this exact key if
|
|
225
|
-
* it ever appears as an object key in attacker-controlled JSON
|
|
226
|
-
* finding A4): without that, a remote response body like
|
|
225
|
+
* it ever appears as an object key in attacker-controlled JSON: without that, a remote response body like
|
|
227
226
|
* `{"__smd_is_array":true,"1":"a","2":"b","x":"kept"}` would decode to an
|
|
228
227
|
* ordinary Lua table carrying this same marker key, and `finalizeMarshaledValue`
|
|
229
228
|
* below — which has no way to tell "the trusted marshal walk set this" apart
|
package/dist/sandbox.js
CHANGED
|
@@ -476,8 +476,7 @@ export async function runScript(options) {
|
|
|
476
476
|
// throws synchronously inside the try — e.g. `finalizeMarshaledValue`
|
|
477
477
|
// recursing deep enough to overflow the JS call stack, which today is
|
|
478
478
|
// safe only INCIDENTALLY because wasmoon's own `getValue` conversion
|
|
479
|
-
// overflows first on sufficiently deep input
|
|
480
|
-
// finding F-1 "also recommended" note) — so that guarantee no longer
|
|
479
|
+
// overflows first on sufficiently deep input — so that guarantee no longer
|
|
481
480
|
// depends on that ordering holding forever. Classified the same as any
|
|
482
481
|
// other unclassified failure: a plain `'runtime'` failure, never a raw
|
|
483
482
|
// throw out of `runScript`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markii/lua",
|
|
3
|
-
"version": "0.17.0-next.
|
|
3
|
+
"version": "0.17.0-next.4",
|
|
4
4
|
"description": "Sandboxed Lua 5.4 (wasmoon) execution runtime for Mark's document scripting: an empty-env global whitelist, two-tier capability-gated net/cache/bundle access, instruction-count/wall-clock/memory limits, and depth/size-capped Lua<->JS marshaling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"lint": "eslint ."
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@markii/bundle": "^0.17.0-next.
|
|
48
|
-
"@markii/runtime": "^0.17.0-next.
|
|
47
|
+
"@markii/bundle": "^0.17.0-next.4",
|
|
48
|
+
"@markii/runtime": "^0.17.0-next.4",
|
|
49
49
|
"wasmoon": "^1.16.0"
|
|
50
50
|
}
|
|
51
51
|
}
|