@markii/lua 0.3.1 → 0.4.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.
- package/README.md +1 -1
- package/dist/capabilities.js +46 -16
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# @markii/lua
|
|
2
2
|
|
|
3
|
-
A sandboxed Lua 5.4 (via `wasmoon`) execution runtime for [
|
|
3
|
+
A sandboxed Lua 5.4 (via `wasmoon`) execution runtime for [Markii](https://github.com/sadigaxund/markii)'s document scripting: an empty-environment global whitelist, capability-gated net/cache/bundle access, instruction-count and wall-clock/memory limits, and depth/size-capped Lua↔JS value marshaling. No React, no markdown parsing.
|
|
4
4
|
|
|
5
5
|
See the [repository](https://github.com/sadigaxund/markii) for the format spec and the reference library as a whole.
|
package/dist/capabilities.js
CHANGED
|
@@ -390,28 +390,58 @@ net.patch = function(url, body) return __smd_net_patch_blocked(url, body):await(
|
|
|
390
390
|
// below — so without this check, a 300k-element cached array reached
|
|
391
391
|
// the script completely uncapped even though the FETCH path was
|
|
392
392
|
// already capped.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
393
|
+
//
|
|
394
|
+
// An entry that fails the budget check, isn't a plain object at all,
|
|
395
|
+
// or carries a missing/non-finite `storedAtMs` (adversarial finding:
|
|
396
|
+
// a poisoned host snapshot like `{value: 'x'}` with no `storedAtMs`,
|
|
397
|
+
// or `storedAtMs: 'abc'`, would otherwise reach the Lua-side
|
|
398
|
+
// `now - storedAtMs` arithmetic in `cache.get` below and throw a
|
|
399
|
+
// script-error, wedging the note permanently instead of self-healing)
|
|
400
|
+
// — or that cannot be `JSON.stringify`'d at all (cyclic, or
|
|
401
|
+
// BigInt-bearing — something other than this sandbox's own WRITE
|
|
402
|
+
// side must have written it, since that side already rejects both) —
|
|
403
|
+
// is SELF-HEALED rather than denied (orchestrator decision, #6
|
|
404
|
+
// verification notes): it is treated as a cache MISS, exactly as if
|
|
405
|
+
// `key` had never been stored. `cache.get`'s Lua body (below) then
|
|
406
|
+
// calls `fn()` and writes the fresh, already-capped result back
|
|
407
|
+
// through `__smd_cache_set_raw`, which quietly repairs the stored
|
|
408
|
+
// entry for next time. No denial is recorded for this path — a
|
|
409
|
+
// capability denial is reserved for the fresh recompute itself
|
|
410
|
+
// failing the WRITE side's own caps, which is unchanged.
|
|
411
|
+
//
|
|
412
|
+
// Finiteness alone isn't enough (pentest finding N-1): a poisoned
|
|
413
|
+
// snapshot with a huge FUTURE `storedAtMs` (e.g.
|
|
414
|
+
// `Number.MAX_SAFE_INTEGER * 1000`) is finite, so it used to pass
|
|
415
|
+
// straight through to the Lua-side `(now - storedAtMs) < ttl*1000`
|
|
416
|
+
// freshness check below, which then went hugely negative and
|
|
417
|
+
// reported the poisoned value as fresh forever — served without
|
|
418
|
+
// `fn()` ever running. `storedAtMs` must additionally be an integer
|
|
419
|
+
// and plausible: not before the epoch, and not after "now" as this
|
|
420
|
+
// same call sees it. `nowMs` is read once, right before the check,
|
|
421
|
+
// and reused for the equality bound so a genuine entry written and
|
|
422
|
+
// read in the same millisecond (`storedAtMs === nowMs`) is still a
|
|
423
|
+
// HIT, not a false self-heal.
|
|
424
|
+
const nowMs = Date.now();
|
|
425
|
+
const storedAtMs = entry.storedAtMs;
|
|
426
|
+
if (typeof entry !== 'object' ||
|
|
427
|
+
entry === null ||
|
|
428
|
+
Array.isArray(entry) ||
|
|
429
|
+
typeof storedAtMs !== 'number' ||
|
|
430
|
+
!Number.isInteger(storedAtMs) ||
|
|
431
|
+
storedAtMs < 0 ||
|
|
432
|
+
storedAtMs > nowMs) {
|
|
433
|
+
return undefined;
|
|
398
434
|
}
|
|
435
|
+
const budgetCheck = checkJsonWithinLimits(entry.value, marshalLimits);
|
|
436
|
+
if (!budgetCheck.ok)
|
|
437
|
+
return undefined;
|
|
399
438
|
let text;
|
|
400
439
|
try {
|
|
401
|
-
// A cyclic or BigInt-bearing stored value throws here. This
|
|
402
|
-
// module's own WRITE side (`__smd_cache_set_raw` below) already
|
|
403
|
-
// rejects both before ever storing anything, but a `CacheProvider`
|
|
404
|
-
// is host-controlled storage that something other than this
|
|
405
|
-
// sandbox could have written — turn that into the same clean,
|
|
406
|
-
// catchable denial instead of an unclassified throw out of the
|
|
407
|
-
// capability layer.
|
|
408
440
|
const encoded = JSON.stringify(entry.value);
|
|
409
441
|
text = encoded === undefined ? 'null' : encoded;
|
|
410
442
|
}
|
|
411
|
-
catch
|
|
412
|
-
|
|
413
|
-
recordDenial('denied', message);
|
|
414
|
-
throw capabilityError(message);
|
|
443
|
+
catch {
|
|
444
|
+
return undefined;
|
|
415
445
|
}
|
|
416
446
|
return LuaMultiReturn.of(text, entry.storedAtMs);
|
|
417
447
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markii/lua",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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.
|
|
48
|
-
"@markii/runtime": "0.
|
|
47
|
+
"@markii/bundle": "0.4.0",
|
|
48
|
+
"@markii/runtime": "0.4.0",
|
|
49
49
|
"wasmoon": "^1.16.0"
|
|
50
50
|
}
|
|
51
51
|
}
|