@lapxo/bound 0.2.4 → 0.2.6
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/CITATION.cff +1 -1
- package/README.md +20 -3
- package/TARGET.bound +85 -47
- package/dist/cli/accept.js +61 -590
- package/dist/cli/act-result.js +3 -103
- package/dist/cli/args.js +3 -44
- package/dist/cli/custodian.js +2 -29
- package/dist/cli/host.js +4 -32
- package/dist/cli/names.js +1 -12
- package/dist/cli/native-descendant.js +1 -19
- package/dist/cli/native.js +3 -60
- package/dist/cli/observe.js +1 -13
- package/dist/cli/owner.js +3 -139
- package/dist/cli/pass.js +22 -321
- package/dist/cli/place.js +6 -282
- package/dist/cli/release-result.js +3 -80
- package/dist/cli/retained-judgment.js +1 -25
- package/dist/cli/standing-result.js +4 -19
- package/dist/cli/takes.js +10 -112
- package/dist/cli/verb.d.ts +1 -2
- package/dist/cli/verb.js +85 -684
- package/dist/engine/computation.js +2 -0
- package/dist/engine/conformance.js +4 -0
- package/dist/engine/encoding.js +1 -2
- package/dist/engine/index.d.ts +1 -13
- package/dist/engine/index.js +2 -11
- package/dist/engine/object.js +1 -38
- package/dist/engine/ports.js +2 -117
- package/dist/fold/admission-history.js +1 -17
- package/dist/fold/bootstrap-anchor.js +1 -28
- package/dist/fold/ceilings.js +1 -142
- package/dist/fold/claims.js +2 -132
- package/dist/fold/closed.js +24 -235
- package/dist/fold/configures.js +1 -25
- package/dist/fold/digests.js +10 -226
- package/dist/fold/evidence.js +1 -22
- package/dist/fold/fold-worker.js +1 -13
- package/dist/fold/kept.js +12 -173
- package/dist/fold/keys.js +3 -91
- package/dist/fold/laid.js +3 -90
- package/dist/fold/nos.js +1 -48
- package/dist/fold/object.js +1 -2
- package/dist/fold/observed.js +28 -534
- package/dist/fold/opened.js +1 -33
- package/dist/fold/own.js +3 -217
- package/dist/fold/paid.js +1 -92
- package/dist/fold/place-inputs.js +1 -70
- package/dist/fold/places.js +3 -100
- package/dist/fold/rank.js +1 -9
- package/dist/fold/reach.js +1 -83
- package/dist/fold/readers.js +6 -142
- package/dist/fold/receipts.js +1 -47
- package/dist/fold/region-declarations.js +1 -13
- package/dist/fold/region.js +8 -286
- package/dist/fold/release-policy.js +1 -101
- package/dist/fold/resolved.js +6 -145
- package/dist/fold/roles.js +2 -59
- package/dist/fold/second.js +4 -71
- package/dist/fold/shards.js +12 -100
- package/dist/fold/signed.js +3 -81
- package/dist/fold/signers.js +1 -54
- package/dist/fold/stranger.js +6 -24
- package/dist/fold/views.js +1 -66
- package/dist/fold/wire.js +1 -46
- package/dist/fold/zoom.js +3 -81
- package/dist/host/act-cost.js +1 -25
- package/dist/host/adapters/keys/ed25519.js +1 -30
- package/dist/host/adapters/modules/javascript.js +1 -50
- package/dist/host/adapters/transport/http.js +1 -28
- package/dist/host/admitted-place.js +1 -35
- package/dist/host/archive.js +1 -152
- package/dist/host/call-context.js +1 -14
- package/dist/host/capsule-process.js +2 -62
- package/dist/host/capsule.js +9 -171
- package/dist/host/cell-receipts.js +12 -128
- package/dist/host/command-server.js +4 -0
- package/dist/host/commit-result.js +1 -5
- package/dist/host/content-needed.js +1 -8
- package/dist/host/content-store.js +1 -31
- package/dist/host/contextual-place.js +2 -72
- package/dist/host/digest.js +2 -15
- package/dist/host/hash.js +1 -9
- package/dist/host/host-controls.js +1 -19
- package/dist/host/in-process-act.js +3 -84
- package/dist/host/in-process-context.js +3 -91
- package/dist/host/in-process-root.js +6 -84
- package/dist/host/in-process.js +4 -55
- package/dist/host/index.d.ts +1 -15
- package/dist/host/index.js +1 -7
- package/dist/host/io.js +1 -11
- package/dist/host/lifetime.js +1 -79
- package/dist/host/located.js +6 -105
- package/dist/host/native/authority-card-view.js +5 -0
- package/dist/host/native/authority-card.js +1 -0
- package/dist/host/native/bounded-file.js +1 -32
- package/dist/host/native/command.js +1 -91
- package/dist/host/native/control-bridge-worker.js +2 -88
- package/dist/host/native/control-bridge.js +1 -110
- package/dist/host/native/control-selection.js +1 -32
- package/dist/host/native/custody.js +1 -91
- package/dist/host/native/deadline.js +1 -28
- package/dist/host/native/descendants.js +1 -528
- package/dist/host/native/generated-custodian.js +5 -267
- package/dist/host/native/generated.js +1 -379
- package/dist/host/native/image.js +1 -45
- package/dist/host/native/installed-package.d.ts +1 -0
- package/dist/host/native/installed-package.js +5 -0
- package/dist/host/native/jobs.js +2 -148
- package/dist/host/native/loader.js +1 -269
- package/dist/host/native/materialize.js +1 -175
- package/dist/host/native/module-closure.js +1 -130
- package/dist/host/native/module-delivery.d.ts +1 -62
- package/dist/host/native/module-delivery.js +1 -151
- package/dist/host/native/module-inventory.js +2 -0
- package/dist/host/native/module-syntax.js +1 -69
- package/dist/host/native/native-binary.js +1 -115
- package/dist/host/native/native-program.js +1 -72
- package/dist/host/native/node-jobs.js +3 -187
- package/dist/host/native/node-launch.js +1 -159
- package/dist/host/native/preload.js +1 -45
- package/dist/host/native/probes.js +1 -0
- package/dist/host/native/reading-witness-admission.js +5 -0
- package/dist/host/native/reading.js +1 -249
- package/dist/host/native/receipt-environment.js +1 -41
- package/dist/host/native/references.js +1 -30
- package/dist/host/native/root.js +1 -37
- package/dist/host/native/route.js +1 -216
- package/dist/host/native/runtime.js +1 -9
- package/dist/host/native-place-state.js +1 -0
- package/dist/host/native-reader-context.js +1 -8
- package/dist/host/native-receiver.js +1 -25
- package/dist/host/objects.js +1 -38
- package/dist/host/order.js +1 -2
- package/dist/host/persistent-host.js +1 -0
- package/dist/host/physical-lines.d.ts +1 -0
- package/dist/host/physical-lines.js +1 -0
- package/dist/host/place-act-transaction.js +4 -78
- package/dist/host/place-clock-selection.js +5 -124
- package/dist/host/ports/ingress.js +2 -57
- package/dist/host/ports/local-act.js +5 -183
- package/dist/host/ports/place-act.js +1 -134
- package/dist/host/ports/publication.js +1 -56
- package/dist/host/ports/signing.js +1 -74
- package/dist/host/ports/store.js +1 -18
- package/dist/host/ports/transport.js +1 -48
- package/dist/host/process-lifetime.js +1 -17
- package/dist/host/profile-composition.d.ts +1 -0
- package/dist/host/profile-composition.js +2 -0
- package/dist/host/provide.js +1 -183
- package/dist/host/read-only.js +1 -9
- package/dist/host/reading-closures.js +7 -227
- package/dist/host/reading-context.js +1 -53
- package/dist/host/reading-identity-types.js +0 -0
- package/dist/host/reading-identity.js +3 -0
- package/dist/host/reading-preparation.js +6 -0
- package/dist/host/reference-authenticated.d.ts +1 -40
- package/dist/host/reference-authenticated.js +3 -100
- package/dist/host/reference-contracts.js +1 -6
- package/dist/host/reference-export.js +6 -81
- package/dist/host/reference-native-api.d.ts +1 -3
- package/dist/host/reference-native-api.js +1 -3
- package/dist/host/reference-native.js +1 -74
- package/dist/host/reference-profile.js +1 -63
- package/dist/host/reference-standing.js +2 -18
- package/dist/host/reference.js +1 -24
- package/dist/host/refusals.js +2 -43
- package/dist/host/release.js +4 -256
- package/dist/host/root-clock-source.js +1 -307
- package/dist/host/selected-capsules.js +1 -50
- package/dist/host/selected-topos.js +1 -80
- package/dist/host/signature-era.js +1 -12
- package/dist/host/signing.js +2 -48
- package/dist/host/source-content.js +2 -111
- package/dist/host/standing-carrier.js +1 -18
- package/dist/host/standing-projection.js +1 -50
- package/dist/host/timing.js +1 -7
- package/dist/host/walk.js +20 -193
- package/dist/host/work-trace.js +1 -0
- package/dist/judge/late.js +1 -33
- package/dist/judge/take.js +2 -75
- package/dist/judge/widening.js +1 -25
- package/dist/land/act.js +8 -336
- package/dist/land/alias.js +1 -41
- package/dist/land/held.js +1 -23
- package/dist/land/inert.js +1 -33
- package/dist/land/leaving.js +1 -55
- package/dist/land/ledger.js +6 -188
- package/dist/land/owned.js +4 -66
- package/dist/land/sign.js +1 -53
- package/dist/land/standing-pin.js +1 -49
- package/dist/land/vouched.js +1 -112
- package/dist/land/withdraw.js +1 -24
- package/dist/observe/files.js +1 -88
- package/dist/observe/identity.js +1 -61
- package/dist/observe/match.js +1 -8
- package/dist/observe/regions.js +7 -306
- package/dist/observe/run.js +1 -41
- package/dist/observe/runner.js +4 -101
- package/dist/public-types.d.ts +2019 -0
- package/dist/render/capsule.js +1 -30
- package/dist/render/field.js +1 -40
- package/dist/render/names.js +1 -4
- package/dist/render/open.js +1 -61
- package/dist/render/page.js +1 -7
- package/dist/render/receipt-projection.js +1 -0
- package/dist/render/receipts.js +1 -47
- package/dist/render/shape.js +1 -74
- package/dist/render/target.js +4 -52
- package/dist/render/view.js +7 -197
- package/dist/vendor/bundle.json +1 -0
- package/dist/vendor/library14.js +1 -0
- package/dist/vendor/object-provider.js +42 -0
- package/dist/vendor/runtime.js +61 -0
- package/dist/wasm/adapter.d.ts +1 -51
- package/dist/wasm/adapter.js +1 -180
- package/dist/wasm/node-worker.js +1 -13
- package/dist/wasm/object.js +1 -76
- package/dist/wasm/worker.js +1 -7
- package/docs/contracts/authenticated-reference.publication.json +1 -1
- package/docs/contracts/authority-preflight.md +17 -0
- package/docs/contracts/authority-preflight.publication.json +25 -0
- package/docs/contracts/closure-delivery.md +1 -1
- package/docs/contracts/closure-delivery.publication.json +1 -1
- package/docs/contracts/conformance.json +142 -0
- package/docs/contracts/determinism.bound +9 -0
- package/docs/contracts/engine-object-projection.publication.json +1 -1
- package/docs/contracts/engine.md +21 -1
- package/docs/contracts/installed-package.md +21 -0
- package/docs/contracts/installed-package.publication.json +25 -0
- package/docs/contracts/materialized-leaf-sequence.md +15 -0
- package/docs/contracts/materialized-leaf-sequence.publication.json +25 -0
- package/docs/contracts/module-inventory.md +15 -0
- package/docs/contracts/module-inventory.publication.json +25 -0
- package/docs/contracts/native-host.md +29 -1
- package/docs/contracts/native-host.publication.json +4 -4
- package/docs/contracts/native-probes.md +13 -0
- package/docs/contracts/native-probes.publication.json +25 -0
- package/docs/contracts/native-publication.inventory.json +10 -1
- package/docs/contracts/native-view-batch.md +17 -0
- package/docs/contracts/native-view-batch.publication.json +25 -0
- package/docs/contracts/optional-wasm-adapter.publication.json +1 -1
- package/docs/contracts/physical-lines.md +19 -0
- package/docs/contracts/physical-lines.publication.json +25 -0
- package/docs/contracts/profile-composition.md +17 -0
- package/docs/contracts/profile-composition.publication.json +25 -0
- package/docs/contracts/profile-preparation-native-meaning.md +9 -0
- package/docs/contracts/publication.md +12 -0
- package/docs/contracts/reading-identity.md +61 -0
- package/docs/contracts/reading-identity.publication.json +25 -0
- package/docs/contracts/reading-witness-admission.md +49 -0
- package/docs/contracts/readme.publication.json +1 -1
- package/docs/contracts/reference-port.md +11 -0
- package/docs/contracts/reference-port.publication.json +1 -1
- package/docs/contracts/refusals.bound +346 -94
- package/docs/contracts/refusals.md +119 -39
- package/docs/contracts/refusals.publication.json +2 -2
- package/docs/contracts/views.md +8 -0
- package/docs/reference.md +9 -0
- package/licenses/wasm/{fast-text-encoding.txt → NOTICE.txt} +111 -0
- package/package.json +84 -34
- package/receipts.bound +12952 -10449
- package/samples/authority-preflight/no.json +6 -0
- package/samples/authority-preflight/yes.json +30 -0
- package/samples/engine-alphabet/reader.js +3 -1457
- package/samples/engine-alphabet/receipts.bound +7 -7
- package/samples/engine-interval/reader.js +3 -1457
- package/samples/engine-interval/receipts.bound +7 -7
- package/samples/installed-package/consumer.mjs +29 -0
- package/samples/installed-package/fixture.json +48 -0
- package/samples/installed-package/no.json +10 -0
- package/samples/installed-package/yes.json +52 -0
- package/samples/materialized-leaf-sequence/no.json +11 -0
- package/samples/materialized-leaf-sequence/yes.json +10 -0
- package/samples/module-inventory/yes.json +28 -0
- package/samples/native/physical-line-reader.mjs +1 -0
- package/samples/native/physical-line-reader.source.mjs +13 -0
- package/samples/native-host/no.json +6 -1
- package/samples/native-host/yes.json +18 -1
- package/samples/native-probes/inputs/inspection.json +28 -0
- package/samples/native-probes/no.json +7 -0
- package/samples/native-probes/yes.json +28 -0
- package/samples/native-view-batch/no.json +14 -0
- package/samples/native-view-batch/yes.json +10 -0
- package/samples/no/engine-order/reader.js +3 -1457
- package/samples/no/module-inventory/no.json +10 -0
- package/samples/physical-lines/no.json +5 -0
- package/samples/physical-lines/yes.json +9 -0
- package/samples/profile-composition/no.json +6 -0
- package/samples/profile-composition/yes.json +14 -0
- package/samples/reading-identity/no.json +26 -0
- package/samples/reading-identity/yes.json +69 -0
- package/vectors/native/authority-preflight.vector.json +55 -0
- package/vectors/native/installed-package.vector.json +31 -0
- package/vectors/native/materialized-leaf-sequence.vector.json +31 -0
- package/vectors/native/module-inventory.vector.json +56 -0
- package/vectors/native/native-host.vector.json +214 -0
- package/vectors/native/native-probes.vector.json +31 -0
- package/vectors/native/native-view-batch.vector.json +31 -0
- package/vectors/native/physical-lines.vector.json +105 -0
- package/vectors/native/profile-composition.vector.json +53 -0
- package/vectors/native/reading-identity.vector.json +139 -0
- package/vectors/native/refusals.vector.json +138 -0
- package/vendor/syntax/LICENSE.md +7 -0
- package/vendor/syntax/parser.js +1 -0
- package/.github/workflows/release.yml +0 -67
- package/.github/workflows/test.yml +0 -35
- package/dist/engine/object.d.ts +0 -17
- package/dist/engine/ports.d.ts +0 -37
- package/dist/fold/claims.d.ts +0 -29
- package/dist/host/call-context.d.ts +0 -20
- package/dist/host/commit-result.d.ts +0 -6
- package/dist/host/in-process-act.d.ts +0 -82
- package/dist/host/in-process-context.d.ts +0 -46
- package/dist/host/in-process-root.d.ts +0 -117
- package/dist/host/in-process.d.ts +0 -41
- package/dist/host/native/module-closure.d.ts +0 -35
- package/dist/host/native-receiver.d.ts +0 -19
- package/dist/host/place-act-transaction.d.ts +0 -24
- package/dist/host/place-clock-selection.d.ts +0 -55
- package/dist/host/ports/local-act.d.ts +0 -44
- package/dist/host/ports/place-act.d.ts +0 -49
- package/dist/host/ports/publication.d.ts +0 -10
- package/dist/host/reference-export.d.ts +0 -33
- package/dist/host/reference-native.d.ts +0 -30
- package/dist/host/reference-profile.d.ts +0 -23
- package/dist/host/refusals.d.ts +0 -14
- package/dist/host/root-clock-source.d.ts +0 -86
- package/licenses/wasm/jitl-quickjs-ffi-types.txt +0 -21
- package/licenses/wasm/jitl-quickjs-wasmfile-release-sync.txt +0 -47
- package/licenses/wasm/quickjs-emscripten-core.txt +0 -21
- package/licenses/wasm/topos.txt +0 -21
- /package/{licenses/wasm/bound.txt → dist/vendor/28cba430228db1c04f446c93f81b09a2705d6bf3e222d35adbe5a4cee4061884.LICENSE} +0 -0
- /package/{licenses/wasm/obligations.txt → dist/vendor/7aca161279d14bc0e054dcc281cd110aeb95be2e026b42fdf527a260e58b2aae.LICENSE} +0 -0
|
@@ -1,130 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { posix } from 'node:path';
|
|
4
|
-
import { moduleImportSyntax } from "./module-syntax.js";
|
|
5
|
-
import { members } from "../archive.js";
|
|
6
|
-
/** Host-selected archive and entry, never a verifier assertion from the program.
|
|
7
|
-
* This proves content identity only; standing admission and runtime capabilities
|
|
8
|
-
* are separate prerequisites. Binary resources are not executable text members.
|
|
9
|
-
*/
|
|
10
|
-
export function moduleSnapshot(archive, expected, entry) {
|
|
11
|
-
const identity = 'sha256:' + createHash('sha256').update(archive).digest('hex');
|
|
12
|
-
if (!/^sha256:[0-9a-f]{64}$/.test(expected) || identity !== expected)
|
|
13
|
-
throw Error('REFUSE·module closure archive bytes differ');
|
|
14
|
-
const textMembers = new Map();
|
|
15
|
-
for (const member of members(archive)) {
|
|
16
|
-
// Platform archive metadata remains covered by the archive digest. Its
|
|
17
|
-
// binary header, rather than a filename convention, identifies data.
|
|
18
|
-
if (member.bytes.length >= 4 && new DataView(member.bytes.buffer, member.bytes.byteOffset, 4).getUint32(0) === 0x00051607)
|
|
19
|
-
continue;
|
|
20
|
-
if (!['.js', '.mjs', '.cjs', '.ts', '.json'].includes(posix.extname(member.name)))
|
|
21
|
-
continue;
|
|
22
|
-
let source;
|
|
23
|
-
try {
|
|
24
|
-
source = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(member.bytes);
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
throw Error('REFUSE·module closure invalid UTF-8 bytes ' + member.name);
|
|
28
|
-
}
|
|
29
|
-
textMembers.set(member.name, { source, digest: 'sha256:' + createHash('sha256').update(member.bytes).digest('hex') });
|
|
30
|
-
}
|
|
31
|
-
return { identity, entry, members: textMembers };
|
|
32
|
-
}
|
|
33
|
-
/** The host supplies authenticated snapshot identities and its library resolver.
|
|
34
|
-
* Each reached text member is verified here against its exact UTF-8 bytes.
|
|
35
|
-
* Authentication of the snapshot envelope remains a separate host prerequisite.
|
|
36
|
-
* A capsule cannot select either verifier or resolver.
|
|
37
|
-
* This reads declared module edges, not arbitrary I/O or a JavaScript sandbox. Computed edges are unpaid, never absent.
|
|
38
|
-
* Activation requires an admitted host policy; existing inventory callers are not silently changed.
|
|
39
|
-
*/
|
|
40
|
-
export function moduleClosure(snapshot, library) {
|
|
41
|
-
return moduleClosures([snapshot], library);
|
|
42
|
-
}
|
|
43
|
-
/** Multiple needs share the existing traversal and one optional act budget. */
|
|
44
|
-
export function moduleClosures(snapshots, library, limits) {
|
|
45
|
-
if (limits && (!Number.isSafeInteger(limits.members) || limits.members < 1 || !Number.isSafeInteger(limits.bytes) || limits.bytes < 1))
|
|
46
|
-
throw Error('REFUSE·module closure missing input resources');
|
|
47
|
-
const seen = new Map();
|
|
48
|
-
let totalBytes = 0;
|
|
49
|
-
const coordinate = (name) => {
|
|
50
|
-
const normalized = posix.normalize(name);
|
|
51
|
-
if (!name || name.includes('\0') || name.includes('\\') || posix.isAbsolute(name) || normalized === '..' || normalized.startsWith('../'))
|
|
52
|
-
throw Error('REFUSE·module closure coordinate leaves snapshot ' + name);
|
|
53
|
-
return normalized;
|
|
54
|
-
};
|
|
55
|
-
const walk = (one, name) => {
|
|
56
|
-
if (limits?.signal?.aborted)
|
|
57
|
-
throw Error('REFUSE·module closure cancelled');
|
|
58
|
-
const at = coordinate(name), key = JSON.stringify([one.identity, at]);
|
|
59
|
-
if (limits?.portableESM && /[%?#]/.test(at))
|
|
60
|
-
throw Error('REFUSE·module closure unsupported URL coordinate ' + at);
|
|
61
|
-
const member = one.members.get(at);
|
|
62
|
-
if (member === undefined)
|
|
63
|
-
throw Error('REFUSE·module closure missing ' + one.identity + ' ' + at);
|
|
64
|
-
if (typeof member.source !== 'string' || !/^sha256:[0-9a-f]{64}$/.test(member.digest))
|
|
65
|
-
throw Error('REFUSE·module closure unnamed bytes ' + one.identity + ' ' + at);
|
|
66
|
-
const size = Buffer.byteLength(member.source, 'utf8');
|
|
67
|
-
if (limits && !seen.has(key) && (seen.size >= limits.members || size > limits.bytes - totalBytes))
|
|
68
|
-
throw Error('REFUSE·module closure input resources');
|
|
69
|
-
const bytes = Buffer.from(member.source, 'utf8');
|
|
70
|
-
if (bytes.toString('utf8') !== member.source)
|
|
71
|
-
throw Error('REFUSE·module closure invalid UTF-8 text ' + one.identity + ' ' + at);
|
|
72
|
-
if ('sha256:' + createHash('sha256').update(bytes).digest('hex') !== member.digest)
|
|
73
|
-
throw Error('REFUSE·module closure bytes differ ' + one.identity + ' ' + at);
|
|
74
|
-
const kept = seen.get(key);
|
|
75
|
-
if (kept) {
|
|
76
|
-
if (kept.digest !== member.digest)
|
|
77
|
-
throw Error('REFUSE·module closure conflicting member ' + one.identity + ' ' + at);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
totalBytes += size;
|
|
81
|
-
seen.set(key, { snapshot: one.identity, coordinate: at, digest: member.digest });
|
|
82
|
-
if (limits?.portableESM && !['.js', '.json'].includes(posix.extname(at)))
|
|
83
|
-
throw Error('REFUSE·module closure unsupported ESM member ' + at);
|
|
84
|
-
// JSON is data under the loader contract, not module syntax.
|
|
85
|
-
if (posix.extname(at) === '.json') {
|
|
86
|
-
if (limits?.portableESM) {
|
|
87
|
-
try {
|
|
88
|
-
JSON.parse(member.source);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
throw Error('REFUSE·module closure invalid JSON member ' + at);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
const syntax = moduleImportSyntax(member.source, at, limits?.portableESM ? { esmOnly: true } : undefined);
|
|
97
|
-
if (syntax.unresolved.length)
|
|
98
|
-
throw Error('REFUSE·module closure unresolved ' + at + ' ' + syntax.unresolved.join(' · '));
|
|
99
|
-
for (const specifier of syntax.imports) {
|
|
100
|
-
if (limits?.portableESM && /[%?#\\]/.test(specifier))
|
|
101
|
-
throw Error('REFUSE·module closure unsupported URL specifier ' + specifier + ' from ' + at);
|
|
102
|
-
// createRequire/registerHooks can select executable edges not present in this graph.
|
|
103
|
-
// Their runtime closure needs its own host contract; an arbitrary alias is not a literal edge.
|
|
104
|
-
if (specifier === 'node:module' || specifier === 'module')
|
|
105
|
-
throw Error('REFUSE·module closure runtime loader requires declared closure ' + specifier + ' from ' + at);
|
|
106
|
-
if (limits?.portableESM && (specifier.startsWith('node:') || builtinModules.includes(specifier)))
|
|
107
|
-
throw Error('REFUSE·module closure host builtin in portable delivery ' + specifier + ' from ' + at);
|
|
108
|
-
if (specifier.startsWith('node:')) {
|
|
109
|
-
if (!builtinModules.includes(specifier) && !builtinModules.includes(specifier.slice(5)))
|
|
110
|
-
throw Error('REFUSE·module closure unknown builtin ' + specifier + ' from ' + at);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (builtinModules.includes(specifier))
|
|
114
|
-
continue;
|
|
115
|
-
if (specifier.startsWith('.')) {
|
|
116
|
-
walk(one, posix.join(posix.dirname(at), specifier));
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
if (specifier.includes(':') || posix.isAbsolute(specifier))
|
|
120
|
-
throw Error('REFUSE·module closure unsupported location ' + specifier + ' from ' + at);
|
|
121
|
-
const pinned = library(specifier, one);
|
|
122
|
-
if (pinned === undefined)
|
|
123
|
-
throw Error('REFUSE·module closure unpinned library ' + specifier + ' from ' + at);
|
|
124
|
-
walk(pinned, pinned.entry);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
for (const snapshot of snapshots)
|
|
128
|
-
walk(snapshot, snapshot.entry);
|
|
129
|
-
return [...seen.values()];
|
|
130
|
-
}
|
|
1
|
+
var F=Object.defineProperty;var c=(u,i)=>F(u,"name",{value:i,configurable:!0});import{builtinModules as m}from"node:module";import{createHash as S}from"node:crypto";import{posix as l}from"node:path";import{moduleImportSyntax as R}from"./module-syntax.js";import{members as x}from"../archive.js";function B(u,i,t){const E="sha256:"+S("sha256").update(u).digest("hex");if(!/^sha256:[0-9a-f]{64}$/.test(i)||E!==i)throw Error("REFUSE\xB7module closure archive bytes differ");const d=new Map;for(const s of x(u)){if(s.bytes.length>=4&&new DataView(s.bytes.buffer,s.bytes.byteOffset,4).getUint32(0)===333319||![".js",".mjs",".cjs",".ts",".json"].includes(l.extname(s.name)))continue;let a;try{a=new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(s.bytes)}catch{throw Error("REFUSE\xB7module closure invalid UTF-8 bytes "+s.name)}d.set(s.name,{source:a,digest:"sha256:"+S("sha256").update(s.bytes).digest("hex")})}return{identity:E,entry:t,members:d}}c(B,"moduleSnapshot");function W(u,i,t){return M([u],i,void 0,t)}c(W,"moduleClosure");function M(u,i,t,E){if(t&&(!Number.isSafeInteger(t.members)||t.members<1||!Number.isSafeInteger(t.bytes)||t.bytes<1))throw Error("REFUSE\xB7module closure missing input resources");const d=new Map;let s=0;const a=c(r=>{const f=l.normalize(r);if(!r||r.includes("\0")||r.includes("\\")||l.isAbsolute(r)||f===".."||f.startsWith("../"))throw Error("REFUSE\xB7module closure coordinate leaves snapshot "+r);return f},"coordinate"),h=c((r,f)=>{if(t?.signal?.aborted)throw Error("REFUSE\xB7module closure cancelled");const e=a(f),b=JSON.stringify([r.identity,e]);if(t?.portableESM&&/[%?#]/.test(e))throw Error("REFUSE\xB7module closure unsupported URL coordinate "+e);const n=r.members.get(e);if(n===void 0)throw Error("REFUSE\xB7module closure missing "+r.identity+" "+e);if(typeof n.source!="string"||!/^sha256:[0-9a-f]{64}$/.test(n.digest))throw Error("REFUSE\xB7module closure unnamed bytes "+r.identity+" "+e);const w=Buffer.byteLength(n.source,"utf8");if(t&&!d.has(b)&&(d.size>=t.members||w>t.bytes-s))throw Error("REFUSE\xB7module closure input resources");const g=Buffer.from(n.source,"utf8");if(g.toString("utf8")!==n.source)throw Error("REFUSE\xB7module closure invalid UTF-8 text "+r.identity+" "+e);if("sha256:"+S("sha256").update(g).digest("hex")!==n.digest)throw Error("REFUSE\xB7module closure bytes differ "+r.identity+" "+e);const U=d.get(b);if(U){if(U.digest!==n.digest)throw Error("REFUSE\xB7module closure conflicting member "+r.identity+" "+e);return}if(s+=w,d.set(b,{snapshot:r.identity,coordinate:e,digest:n.digest}),t?.portableESM&&![".js",".json"].includes(l.extname(e)))throw Error("REFUSE\xB7module closure unsupported ESM member "+e);if(l.extname(e)===".json"){if(t?.portableESM)try{JSON.parse(n.source)}catch{throw Error("REFUSE\xB7module closure invalid JSON member "+e)}return}const p=R(n.source,e,t?.portableESM||E?.esmOnly?{esmOnly:!0}:void 0);if(p.unresolved.length)throw Error("REFUSE\xB7module closure unresolved "+e+" "+p.unresolved.join(" \xB7 "));for(const o of p.imports){if(t?.portableESM&&/[%?#\\]/.test(o))throw Error("REFUSE\xB7module closure unsupported URL specifier "+o+" from "+e);if(o==="node:module"||o==="module")throw Error("REFUSE\xB7module closure runtime loader requires declared closure "+o+" from "+e);if(t?.portableESM&&(o.startsWith("node:")||m.includes(o)))throw Error("REFUSE\xB7module closure host builtin in portable delivery "+o+" from "+e);if(o.startsWith("node:")){if(!m.includes(o)&&!m.includes(o.slice(5)))throw Error("REFUSE\xB7module closure unknown builtin "+o+" from "+e);continue}if(m.includes(o))continue;if(o.startsWith(".")){h(r,l.join(l.dirname(e),o));continue}if(o.includes(":")||l.isAbsolute(o))throw Error("REFUSE\xB7module closure unsupported location "+o+" from "+e);const y=i(o,r);if(y===void 0)throw Error("REFUSE\xB7module closure unpinned library "+o+" from "+e);h(y,y.entry)}},"walk");for(const r of u)h(r,r.entry);return[...d.values()]}c(M,"moduleClosures");export{W as moduleClosure,M as moduleClosures,B as moduleSnapshot};
|
|
@@ -1,62 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { moduleSnapshot, type ModuleClosureSnapshot } from './module-closure.ts';
|
|
3
|
-
export interface ClosureDeliveryLimits extends Omit<ModuleClosureLimits, 'portableESM'> {
|
|
4
|
-
readonly responseBytes: number;
|
|
5
|
-
}
|
|
6
|
-
export interface ClosureNotice {
|
|
7
|
-
readonly snapshot: string;
|
|
8
|
-
readonly coordinate: string;
|
|
9
|
-
readonly digest: string;
|
|
10
|
-
readonly source: string;
|
|
11
|
-
}
|
|
12
|
-
export interface ClosureDeliveryManifest {
|
|
13
|
-
readonly format: 'esm-file-closure/1';
|
|
14
|
-
readonly needs: readonly {
|
|
15
|
-
name: string;
|
|
16
|
-
snapshot: string;
|
|
17
|
-
entry: string;
|
|
18
|
-
}[];
|
|
19
|
-
readonly modules: readonly {
|
|
20
|
-
snapshot: string;
|
|
21
|
-
coordinate: string;
|
|
22
|
-
digest: string;
|
|
23
|
-
path: string;
|
|
24
|
-
}[];
|
|
25
|
-
readonly libraries: readonly {
|
|
26
|
-
parent: string;
|
|
27
|
-
specifier: string;
|
|
28
|
-
snapshot: string;
|
|
29
|
-
entry: string;
|
|
30
|
-
}[];
|
|
31
|
-
readonly notices: readonly {
|
|
32
|
-
snapshot: string;
|
|
33
|
-
coordinate: string;
|
|
34
|
-
digest: string;
|
|
35
|
-
path: string;
|
|
36
|
-
}[];
|
|
37
|
-
}
|
|
38
|
-
export interface ClosureDelivery {
|
|
39
|
-
readonly closure: string;
|
|
40
|
-
readonly digest: string;
|
|
41
|
-
readonly bytes: Uint8Array;
|
|
42
|
-
readonly manifest: ClosureDeliveryManifest;
|
|
43
|
-
readonly files: readonly {
|
|
44
|
-
path: string;
|
|
45
|
-
source: string;
|
|
46
|
-
}[];
|
|
47
|
-
readonly importMap: {
|
|
48
|
-
imports: Record<string, string>;
|
|
49
|
-
scopes: Record<string, Record<string, string>>;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
type Library = (specifier: string, parent: ModuleClosureSnapshot) => ModuleClosureSnapshot | undefined;
|
|
53
|
-
/** The host selects already authenticated snapshots and resolution, never the
|
|
54
|
-
* offered program. This prepares unchanged ESM files, not a JS sandbox, signer,
|
|
55
|
-
* browser host or symbol-pruning bundler. Existing native/Acorn routes remain.
|
|
56
|
-
*/
|
|
57
|
-
export declare function deliverModuleClosure(needs: readonly string[], entries: ReadonlyMap<string, ModuleClosureSnapshot>, library: Library, limits: ClosureDeliveryLimits, notices?: readonly ClosureNotice[]): ClosureDelivery;
|
|
58
|
-
/** Verify exact pinned delivery bytes and run the same pre-execution closure
|
|
59
|
-
* verifier again over the delivered set. No callbacks, install paths or verifier
|
|
60
|
-
* chosen by the payload. The returned import map is derived, never trusted input.
|
|
61
|
-
*/
|
|
62
|
-
export declare function verifyModuleDelivery(bytes: Uint8Array, expected: string, limits: ClosureDeliveryLimits): ClosureDelivery;
|
|
1
|
+
export {public4_ClosureDelivery as ClosureDelivery,public4_ClosureDeliveryLimits as ClosureDeliveryLimits,public4_ClosureDeliveryManifest as ClosureDeliveryManifest,public4_ClosureNotice as ClosureNotice,public4_ModuleClosureSnapshot as ModuleClosureSnapshot,public4_ModuleInventory as ModuleInventory,public4_ModuleInventoryContext as ModuleInventoryContext,public4_ModuleInventoryLine as ModuleInventoryLine,public4_ModuleInventorySource as ModuleInventorySource,public4_deliverModuleClosure as deliverModuleClosure,public4_moduleInventory as moduleInventory,public4_moduleSnapshot as moduleSnapshot,public4_verifyModuleDelivery as verifyModuleDelivery} from "../../public-types.js";
|
|
@@ -1,151 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { sha256, isSha256 } from "../hash.js";
|
|
3
|
-
import { archiveCoordinate } from "../archive.js";
|
|
4
|
-
import { compareStrings as compare } from "../order.js";
|
|
5
|
-
import { moduleClosures } from "./module-closure.js";
|
|
6
|
-
export { moduleSnapshot } from "./module-closure.js";
|
|
7
|
-
const fail = (why) => { throw Error('REFUSE·closure delivery ' + why); };
|
|
8
|
-
const key = (identity, coordinate) => JSON.stringify([identity, coordinate]);
|
|
9
|
-
const pathOf = (identity, coordinate) => {
|
|
10
|
-
if (!isSha256(identity))
|
|
11
|
-
return fail('snapshot identity is not SHA-256');
|
|
12
|
-
return 'modules/' + identity.slice(7) + '/' + coordinate;
|
|
13
|
-
};
|
|
14
|
-
const urlOf = (path) => './' + path.split('/').map(encodeURIComponent).join('/');
|
|
15
|
-
const resourceLimits = (limits) => {
|
|
16
|
-
if (!limits || !Number.isSafeInteger(limits.members) || limits.members < 1 || !Number.isSafeInteger(limits.bytes) || limits.bytes < 1 || !Number.isSafeInteger(limits.responseBytes) || limits.responseBytes < 1)
|
|
17
|
-
fail('missing resources');
|
|
18
|
-
if (limits.signal?.aborted)
|
|
19
|
-
fail('cancelled');
|
|
20
|
-
};
|
|
21
|
-
/** The host selects already authenticated snapshots and resolution, never the
|
|
22
|
-
* offered program. This prepares unchanged ESM files, not a JS sandbox, signer,
|
|
23
|
-
* browser host or symbol-pruning bundler. Existing native/Acorn routes remain.
|
|
24
|
-
*/
|
|
25
|
-
export function deliverModuleClosure(needs, entries, library, limits, notices = []) {
|
|
26
|
-
resourceLimits(limits);
|
|
27
|
-
if (!Array.isArray(notices) || notices.length >= limits.members)
|
|
28
|
-
fail('notice resources');
|
|
29
|
-
let noticeBytes = 0;
|
|
30
|
-
const noticePaths = new Set();
|
|
31
|
-
const retainedNotices = notices.map(n => {
|
|
32
|
-
if (!n || typeof n.coordinate !== 'string' || archiveCoordinate(n.coordinate) !== n.coordinate || typeof n.source !== 'string' || !isSha256(n.digest) || sha256(n.source) !== n.digest || (noticeBytes += Buffer.byteLength(n.source)) >= limits.bytes)
|
|
33
|
-
fail('notice bytes/resources');
|
|
34
|
-
const path = 'notices/' + pathOf(n.snapshot, n.coordinate).slice('modules/'.length);
|
|
35
|
-
if (noticePaths.has(path))
|
|
36
|
-
fail('duplicate notice');
|
|
37
|
-
noticePaths.add(path);
|
|
38
|
-
return { snapshot: n.snapshot, coordinate: n.coordinate, digest: n.digest, path };
|
|
39
|
-
}).sort((a, b) => compare(a.path, b.path));
|
|
40
|
-
if (!Array.isArray(needs) || !needs.length || needs.length > limits.members || needs.some(n => typeof n !== 'string' || !n || Buffer.byteLength(n) > limits.bytes))
|
|
41
|
-
fail('missing/oversized needs');
|
|
42
|
-
const requested = [...new Set(needs)].sort(compare), snapshots = new Map(), bindings = new Map();
|
|
43
|
-
const hold = (one) => {
|
|
44
|
-
if (!one || !isSha256(one.identity))
|
|
45
|
-
return fail('snapshot identity is not SHA-256');
|
|
46
|
-
const old = snapshots.get(one.identity);
|
|
47
|
-
// Different entry points may share one immutable snapshot, not different members.
|
|
48
|
-
if (old && old.members !== one.members)
|
|
49
|
-
fail('ambiguous snapshot members');
|
|
50
|
-
snapshots.set(one.identity, one);
|
|
51
|
-
return one;
|
|
52
|
-
};
|
|
53
|
-
const roots = requested.map(name => { const one = entries.get(name); if (!one)
|
|
54
|
-
return fail('unknown need ' + name); return hold(one); });
|
|
55
|
-
const reached = moduleClosures(roots, (specifier, parent) => {
|
|
56
|
-
const found = library(specifier, parent);
|
|
57
|
-
if (!found)
|
|
58
|
-
return;
|
|
59
|
-
const one = hold(found), at = key(parent.identity, specifier), binding = { parent: parent.identity, specifier, snapshot: one.identity, entry: one.entry }, old = bindings.get(at);
|
|
60
|
-
if (old && (old.snapshot !== one.identity || old.entry !== one.entry))
|
|
61
|
-
fail('ambiguous library resolution');
|
|
62
|
-
if (!old && bindings.size >= limits.members)
|
|
63
|
-
fail('library resources');
|
|
64
|
-
bindings.set(at, binding);
|
|
65
|
-
return one;
|
|
66
|
-
}, { ...limits, members: limits.members - notices.length, bytes: limits.bytes - noticeBytes, portableESM: true });
|
|
67
|
-
if (notices.some(n => !reached.some(r => r.snapshot === n.snapshot)))
|
|
68
|
-
fail('notice outside reached snapshots');
|
|
69
|
-
const modules = [...reached].sort((a, b) => compare(key(a.snapshot, a.coordinate), key(b.snapshot, b.coordinate))).map(r => ({ ...r, path: pathOf(r.snapshot, r.coordinate) }));
|
|
70
|
-
const files = modules.map(r => {
|
|
71
|
-
const source = snapshots.get(r.snapshot)?.members.get(r.coordinate)?.source;
|
|
72
|
-
if (source === undefined || sha256(source) !== r.digest)
|
|
73
|
-
fail('snapshot changed during delivery');
|
|
74
|
-
return { path: r.path, source: source };
|
|
75
|
-
});
|
|
76
|
-
for (const n of retainedNotices)
|
|
77
|
-
files.push({ path: n.path, source: notices.find(one => one.snapshot === n.snapshot && one.coordinate === n.coordinate).source });
|
|
78
|
-
const manifest = { format: 'esm-file-closure/1', needs: requested.map((name, i) => ({ name, snapshot: roots[i].identity, entry: posix.normalize(roots[i].entry) })), modules, libraries: [...bindings.values()].sort((a, b) => compare(key(a.parent, a.specifier), key(b.parent, b.specifier))), notices: retainedNotices };
|
|
79
|
-
const closure = sha256(JSON.stringify(manifest));
|
|
80
|
-
const bytes = Buffer.from(JSON.stringify({ manifest, files }), 'utf8');
|
|
81
|
-
if (bytes.length > limits.responseBytes)
|
|
82
|
-
fail('response resources');
|
|
83
|
-
if (limits.signal?.aborted)
|
|
84
|
-
fail('cancelled');
|
|
85
|
-
const imports = Object.fromEntries(manifest.needs.map(n => [n.name, urlOf(pathOf(n.snapshot, n.entry))]));
|
|
86
|
-
const scopes = {};
|
|
87
|
-
for (const b of manifest.libraries) {
|
|
88
|
-
const scope = './modules/' + b.parent.slice(7) + '/';
|
|
89
|
-
const values = scopes[scope] ?? (scopes[scope] = Object.create(null));
|
|
90
|
-
values[b.specifier] = urlOf(pathOf(b.snapshot, posix.normalize(b.entry)));
|
|
91
|
-
}
|
|
92
|
-
return { closure, digest: sha256(bytes), bytes: new Uint8Array(bytes), manifest, files, importMap: { imports, scopes } };
|
|
93
|
-
}
|
|
94
|
-
/** Verify exact pinned delivery bytes and run the same pre-execution closure
|
|
95
|
-
* verifier again over the delivered set. No callbacks, install paths or verifier
|
|
96
|
-
* chosen by the payload. The returned import map is derived, never trusted input.
|
|
97
|
-
*/
|
|
98
|
-
export function verifyModuleDelivery(bytes, expected, limits) {
|
|
99
|
-
resourceLimits(limits);
|
|
100
|
-
if (!(bytes instanceof Uint8Array) || bytes.byteLength > limits.responseBytes || !isSha256(expected) || sha256(bytes) !== expected)
|
|
101
|
-
fail('bundle bytes differ or exceed resources');
|
|
102
|
-
let value;
|
|
103
|
-
try {
|
|
104
|
-
value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return fail('invalid JSON bytes');
|
|
108
|
-
}
|
|
109
|
-
const manifest = value?.manifest;
|
|
110
|
-
if (manifest?.format !== 'esm-file-closure/1' || !Array.isArray(manifest.needs) || !Array.isArray(manifest.modules) || !Array.isArray(manifest.libraries) || !Array.isArray(manifest.notices) || !Array.isArray(value.files) || manifest.modules.length + manifest.notices.length > limits.members || value.files.length !== manifest.modules.length + manifest.notices.length || manifest.needs.length > limits.members || manifest.libraries.length > limits.members)
|
|
111
|
-
fail('invalid manifest resources');
|
|
112
|
-
const snapshots = new Map(), files = new Map();
|
|
113
|
-
let size = 0;
|
|
114
|
-
for (const file of value.files) {
|
|
115
|
-
if (typeof file?.path !== 'string' || typeof file.source !== 'string' || files.has(file.path) || (size += Buffer.byteLength(file.source)) > limits.bytes)
|
|
116
|
-
fail('invalid/duplicate file resources');
|
|
117
|
-
files.set(file.path, file.source);
|
|
118
|
-
}
|
|
119
|
-
for (const m of manifest.modules) {
|
|
120
|
-
if (!m || typeof m.coordinate !== 'string' || typeof m.path !== 'string' || m.path !== pathOf(m.snapshot, m.coordinate) || !isSha256(m.digest))
|
|
121
|
-
fail('invalid module');
|
|
122
|
-
const source = files.get(m.path);
|
|
123
|
-
if (source === undefined || sha256(source) !== m.digest)
|
|
124
|
-
fail('module bytes differ');
|
|
125
|
-
const members = snapshots.get(m.snapshot) ?? new Map();
|
|
126
|
-
if (members.has(m.coordinate))
|
|
127
|
-
fail('duplicate module');
|
|
128
|
-
members.set(m.coordinate, { source, digest: m.digest });
|
|
129
|
-
snapshots.set(m.snapshot, members);
|
|
130
|
-
}
|
|
131
|
-
const snapshot = (identity, entry) => { const members = snapshots.get(identity); if (!members || typeof entry !== 'string')
|
|
132
|
-
return fail('unknown snapshot/entry'); return { identity, entry, members }; };
|
|
133
|
-
const entries = new Map();
|
|
134
|
-
for (const n of manifest.needs) {
|
|
135
|
-
if (typeof n?.name !== 'string' || entries.has(n.name))
|
|
136
|
-
fail('invalid/duplicate need');
|
|
137
|
-
entries.set(n.name, snapshot(n.snapshot, n.entry));
|
|
138
|
-
}
|
|
139
|
-
const bindings = new Map();
|
|
140
|
-
for (const b of manifest.libraries) {
|
|
141
|
-
if (typeof b?.parent !== 'string' || typeof b.specifier !== 'string' || bindings.has(key(b.parent, b.specifier)))
|
|
142
|
-
fail('invalid/duplicate library');
|
|
143
|
-
bindings.set(key(b.parent, b.specifier), snapshot(b.snapshot, b.entry));
|
|
144
|
-
}
|
|
145
|
-
const notices = manifest.notices.map(n => { if (!n || typeof n.path !== 'string' || !files.has(n.path))
|
|
146
|
-
return fail('missing notice'); return { snapshot: n.snapshot, coordinate: n.coordinate, digest: n.digest, source: files.get(n.path) }; });
|
|
147
|
-
const prepared = deliverModuleClosure([...entries.keys()], entries, (specifier, parent) => bindings.get(key(parent.identity, specifier)), limits, notices);
|
|
148
|
-
if (prepared.digest !== expected)
|
|
149
|
-
fail('noncanonical, extra or unresolved delivery members');
|
|
150
|
-
return prepared;
|
|
151
|
-
}
|
|
1
|
+
var H=Object.defineProperty;var f=(r,a)=>H(r,"name",{value:a,configurable:!0});import{posix as L}from"node:path";import{sha256 as b,isSha256 as w}from"../hash.js";import{archiveCoordinate as R}from"../archive.js";import{compareStrings as z}from"../order.js";import{moduleClosures as q}from"./module-closure.js";import{moduleSnapshot as Z}from"./module-closure.js";import{moduleInventory as $}from"./module-inventory.js";const o=f(r=>{throw Error("REFUSE\xB7closure delivery "+r)},"fail"),m=f((r,a)=>JSON.stringify([r,a]),"key"),M=f((r,a)=>w(r)?"modules/"+r.slice(7)+"/"+a:o("snapshot identity is not SHA-256"),"pathOf"),k=f(r=>"./"+r.split("/").map(encodeURIComponent).join("/"),"urlOf"),C=f(r=>{(!r||!Number.isSafeInteger(r.members)||r.members<1||!Number.isSafeInteger(r.bytes)||r.bytes<1||!Number.isSafeInteger(r.responseBytes)||r.responseBytes<1)&&o("missing resources"),r.signal?.aborted&&o("cancelled")},"resourceLimits");function F(r,a,d,i,n=[]){C(i),(!Array.isArray(n)||n.length>=i.members)&&o("notice resources");let l=0;const u=new Set,B=n.map(e=>{(!e||typeof e.coordinate!="string"||R(e.coordinate)!==e.coordinate||typeof e.source!="string"||!w(e.digest)||b(e.source)!==e.digest||(l+=Buffer.byteLength(e.source))>=i.bytes)&&o("notice bytes/resources");const s="notices/"+M(e.snapshot,e.coordinate).slice(8);return u.has(s)&&o("duplicate notice"),u.add(s),{snapshot:e.snapshot,coordinate:e.coordinate,digest:e.digest,path:s}}).sort((e,s)=>z(e.path,s.path));(!Array.isArray(r)||!r.length||r.length>i.members||r.some(e=>typeof e!="string"||!e||Buffer.byteLength(e)>i.bytes))&&o("missing/oversized needs");const v=[...new Set(r)].sort(z),h=new Map,y=new Map,N=f(e=>{if(!e||!w(e.identity))return o("snapshot identity is not SHA-256");const s=h.get(e.identity);return s&&s.members!==e.members&&o("ambiguous snapshot members"),h.set(e.identity,e),e},"hold"),g=v.map(e=>{const s=a.get(e);return s?N(s):o("unknown need "+e)}),t=q(g,(e,s)=>{const O=d(e,s);if(!O)return;const S=N(O),J=m(s.identity,e),D={parent:s.identity,specifier:e,snapshot:S.identity,entry:S.entry},x=y.get(J);return x&&(x.snapshot!==S.identity||x.entry!==S.entry)&&o("ambiguous library resolution"),!x&&y.size>=i.members&&o("library resources"),y.set(J,D),S},{...i,members:i.members-n.length,bytes:i.bytes-l,portableESM:!0});n.some(e=>!t.some(s=>s.snapshot===e.snapshot))&&o("notice outside reached snapshots");const c=[...t].sort((e,s)=>z(m(e.snapshot,e.coordinate),m(s.snapshot,s.coordinate))).map(e=>({...e,path:M(e.snapshot,e.coordinate)})),p=c.map(e=>{const s=h.get(e.snapshot)?.members.get(e.coordinate)?.source;return(s===void 0||b(s)!==e.digest)&&o("snapshot changed during delivery"),{path:e.path,source:s}});for(const e of B)p.push({path:e.path,source:n.find(s=>s.snapshot===e.snapshot&&s.coordinate===e.coordinate).source});const A={format:"esm-file-closure/1",needs:v.map((e,s)=>({name:e,snapshot:g[s].identity,entry:L.normalize(g[s].entry)})),modules:c,libraries:[...y.values()].sort((e,s)=>z(m(e.parent,e.specifier),m(s.parent,s.specifier))),notices:B},U=b(JSON.stringify(A)),E=Buffer.from(JSON.stringify({manifest:A,files:p}),"utf8");E.length>i.responseBytes&&o("response resources"),i.signal?.aborted&&o("cancelled");const j=Object.fromEntries(A.needs.map(e=>[e.name,k(M(e.snapshot,e.entry))])),I={};for(const e of A.libraries){const s="./modules/"+e.parent.slice(7)+"/",O=I[s]??(I[s]=Object.create(null));O[e.specifier]=k(M(e.snapshot,L.normalize(e.entry)))}return{closure:U,digest:b(E),bytes:new Uint8Array(E),manifest:A,files:p,importMap:{imports:j,scopes:I}}}f(F,"deliverModuleClosure");function W(r,a,d){C(d),(!(r instanceof Uint8Array)||r.byteLength>d.responseBytes||!w(a)||b(r)!==a)&&o("bundle bytes differ or exceed resources");let i;try{i=JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(r))}catch{return o("invalid JSON bytes")}const n=i?.manifest;(n?.format!=="esm-file-closure/1"||!Array.isArray(n.needs)||!Array.isArray(n.modules)||!Array.isArray(n.libraries)||!Array.isArray(n.notices)||!Array.isArray(i.files)||n.modules.length+n.notices.length>d.members||i.files.length!==n.modules.length+n.notices.length||n.needs.length>d.members||n.libraries.length>d.members)&&o("invalid manifest resources");const l=new Map,u=new Map;let B=0;for(const t of i.files)(typeof t?.path!="string"||typeof t.source!="string"||u.has(t.path)||(B+=Buffer.byteLength(t.source))>d.bytes)&&o("invalid/duplicate file resources"),u.set(t.path,t.source);for(const t of n.modules){(!t||typeof t.coordinate!="string"||typeof t.path!="string"||t.path!==M(t.snapshot,t.coordinate)||!w(t.digest))&&o("invalid module");const c=u.get(t.path);(c===void 0||b(c)!==t.digest)&&o("module bytes differ");const p=l.get(t.snapshot)??new Map;p.has(t.coordinate)&&o("duplicate module"),p.set(t.coordinate,{source:c,digest:t.digest}),l.set(t.snapshot,p)}const v=f((t,c)=>{const p=l.get(t);return!p||typeof c!="string"?o("unknown snapshot/entry"):{identity:t,entry:c,members:p}},"snapshot"),h=new Map;for(const t of n.needs)(typeof t?.name!="string"||h.has(t.name))&&o("invalid/duplicate need"),h.set(t.name,v(t.snapshot,t.entry));const y=new Map;for(const t of n.libraries)(typeof t?.parent!="string"||typeof t.specifier!="string"||y.has(m(t.parent,t.specifier)))&&o("invalid/duplicate library"),y.set(m(t.parent,t.specifier),v(t.snapshot,t.entry));const N=n.notices.map(t=>!t||typeof t.path!="string"||!u.has(t.path)?o("missing notice"):{snapshot:t.snapshot,coordinate:t.coordinate,digest:t.digest,source:u.get(t.path)}),g=F([...h.keys()],h,(t,c)=>y.get(m(c.identity,t)),d,N);return g.digest!==a&&o("noncanonical, extra or unresolved delivery members"),g}f(W,"verifyModuleDelivery");export{F as deliverModuleClosure,$ as moduleInventory,Z as moduleSnapshot,W as verifyModuleDelivery};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var p=Object.defineProperty;var u=(n,i)=>p(n,"name",{value:i,configurable:!0});import{compareStrings as f}from"../order.js";import{library14_canonical as y}from"../../vendor/runtime.js";import{verifyModuleDelivery as h}from"./module-delivery.js";function w(n,i,c,t,e){const s=h(n,i,c),a=new Map,l=new Set(s.manifest.modules.map(o=>o.snapshot));if(!e||!e.scope||!e.by||!e.at)throw Error("REFUSE\xB7module inventory missing declaration context");if(!Array.isArray(t)||t.length!==l.size)throw Error("REFUSE\xB7module inventory source coverage");for(const o of t){if(!o||!l.has(o.snapshot)||typeof o.source!="string"||!o.source||typeof o.version!="string"||!o.version||a.has(o.snapshot))throw Error("REFUSE\xB7module inventory invalid/duplicate source");a.set(o.snapshot,o)}const d=s.manifest.modules.map(o=>{const r=a.get(o.snapshot);if(!r)throw Error("REFUSE\xB7module inventory missing source "+o.snapshot);return{unit:"module",source:r.source,version:r.version,snapshot:o.snapshot,module:o.coordinate,digest:o.digest,reachedFrom:s.closure}}).sort((o,r)=>f(JSON.stringify([o.snapshot,o.module]),JSON.stringify([r.snapshot,r.module]))),m=d.map(o=>y({scope:e.scope+"/"+o.snapshot.slice(7)+"/"+encodeURIComponent(o.module),role:"writes",form:"alphabet",measure:"digest",value:o.digest,by:e.by,at:e.at,about:JSON.stringify({format:"module-inventory-record/1",...o})})+`
|
|
2
|
+
`).join("");return{format:"verified-module-inventory/1",delivery:s.digest,closure:s.closure,modules:d,lines:m}}u(w,"moduleInventory");export{w as moduleInventory};
|
|
@@ -1,69 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as nodeModule from 'node:module';
|
|
3
|
-
import { extname } from 'node:path';
|
|
4
|
-
/** Host module syntax, never wire or domain interpretation. Reading imports executes no module. */
|
|
5
|
-
// The package's minimum Node runtime provides this API; older build-only Node types do not.
|
|
6
|
-
const require = nodeModule.createRequire(import.meta.url);
|
|
7
|
-
const { parse } = require('acorn');
|
|
8
|
-
const stripTypeScriptTypes = nodeModule.stripTypeScriptTypes;
|
|
9
|
-
/** Syntax inventory is not an execution-closure certificate: unresolved expressions stay explicit. */
|
|
10
|
-
export function moduleImportSyntax(source, coordinate, options) {
|
|
11
|
-
const code = extname(coordinate) === '.ts' ? stripTypeScriptTypes(source, { mode: 'transform' }) : source;
|
|
12
|
-
let tree;
|
|
13
|
-
try {
|
|
14
|
-
tree = parse(code, { ecmaVersion: 'latest', sourceType: 'module', allowReturnOutsideFunction: true, allowHashBang: true });
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
throw Error('REFUSE·source module syntax cannot be read: ' + coordinate);
|
|
18
|
-
}
|
|
19
|
-
const found = new Set(), unresolved = new Set();
|
|
20
|
-
const literal = stringLiteral;
|
|
21
|
-
const visit = (node, parent, key, reexportSource = false) => {
|
|
22
|
-
if (!node || typeof node !== 'object')
|
|
23
|
-
return;
|
|
24
|
-
if (Array.isArray(node)) {
|
|
25
|
-
for (const child of node)
|
|
26
|
-
visit(child, parent, key, reexportSource);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const n = node;
|
|
30
|
-
// Escaped loader references do not become an empty inventory. This is
|
|
31
|
-
// conservative syntax, not a complete JavaScript alias/data-flow proof.
|
|
32
|
-
if (n['type'] === 'Identifier' && n['name'] === 'require') {
|
|
33
|
-
const direct = parent?.['type'] === 'CallExpression' && key === 'callee';
|
|
34
|
-
const namedProperty = (parent?.['type'] === 'MemberExpression' && key === 'property' && !parent['computed']) || ((parent?.['type'] === 'Property' || parent?.['type'] === 'MethodDefinition') && key === 'key' && !parent['computed'] && !parent['shorthand']);
|
|
35
|
-
// Declaration names are not loader references. This does not resolve shadowing:
|
|
36
|
-
// references to a binding named require remain conservative, including local calls.
|
|
37
|
-
const declarationName = ((parent?.['type'] === 'ImportSpecifier' || parent?.['type'] === 'ImportDefaultSpecifier' || parent?.['type'] === 'ImportNamespaceSpecifier') && (key === 'imported' || key === 'local')) || (parent?.['type'] === 'FunctionDeclaration' && key === 'id');
|
|
38
|
-
const reexportName = parent?.['type'] === 'ExportSpecifier' && (key === 'exported' || (key === 'local' && reexportSource));
|
|
39
|
-
if (!direct && !namedProperty && !declarationName && !reexportName)
|
|
40
|
-
unresolved.add('require reference at ' + String(n['start']));
|
|
41
|
-
}
|
|
42
|
-
let specifier;
|
|
43
|
-
if (n['type'] === 'ImportDeclaration' || n['type'] === 'ExportNamedDeclaration' || n['type'] === 'ExportAllDeclaration' || n['type'] === 'ImportExpression') {
|
|
44
|
-
specifier = literal(n['source']);
|
|
45
|
-
if (n['type'] === 'ImportExpression' && specifier === undefined)
|
|
46
|
-
unresolved.add('import expression at ' + String(n['start']));
|
|
47
|
-
}
|
|
48
|
-
if (n['type'] === 'CallExpression') {
|
|
49
|
-
const callee = n['callee'];
|
|
50
|
-
if (callee?.['type'] === 'Identifier' && callee['name'] === 'require') {
|
|
51
|
-
if (options?.esmOnly)
|
|
52
|
-
throw Error('REFUSE·module closure CommonJS load in ESM delivery ' + coordinate);
|
|
53
|
-
specifier = literal(n['arguments'][0]);
|
|
54
|
-
if (specifier === undefined)
|
|
55
|
-
unresolved.add('require expression at ' + String(n['start']));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (specifier !== undefined)
|
|
59
|
-
found.add(specifier);
|
|
60
|
-
for (const [childKey, child] of Object.entries(n))
|
|
61
|
-
visit(child, n, childKey, n['type'] === 'ExportNamedDeclaration' ? n['source'] !== null && n['source'] !== undefined : reexportSource);
|
|
62
|
-
};
|
|
63
|
-
visit(tree);
|
|
64
|
-
return { imports: [...found], unresolved: [...unresolved] };
|
|
65
|
-
}
|
|
66
|
-
/** Existing inventory keeps its meaning; callers requiring closure must inspect unresolved edges. */
|
|
67
|
-
export function moduleImports(source, coordinate) {
|
|
68
|
-
return moduleImportSyntax(source, coordinate).imports;
|
|
69
|
-
}
|
|
1
|
+
var g=Object.defineProperty;var c=(o,r)=>g(o,"name",{value:r,configurable:!0});import{parse as b}from"../../../vendor/syntax/parser.js";import*as D from"node:module";import{extname as v}from"node:path";const N=D.stripTypeScriptTypes,T=c(o=>{if(!o||typeof o!="object")return;const r=o;return r.type==="Literal"&&typeof r.value=="string"?r.value:void 0},"stringLiteral");function j(o,r,u){const E=v(r)===".ts"?N(o,{mode:"transform"}):o;let m;try{m=b(E,{sourceType:"module",next:!0,ranges:{start:!0,end:!1,range:!1},lexical:!0,globalReturn:!0})}catch{throw Error("REFUSE\xB7source module syntax cannot be read: "+r)}const y=new Set,a=new Set,x=T,f=c((p,t,i,d=!1)=>{if(!p||typeof p!="object")return;if(Array.isArray(p)){for(const n of p)f(n,t,i,d);return}const e=p;if(e.type==="Identifier"&&e.name==="require"){const n=t?.type==="CallExpression"&&i==="callee",l=t?.type==="MemberExpression"&&i==="property"&&!t.computed||(t?.type==="Property"||t?.type==="MethodDefinition")&&i==="key"&&!t.computed&&!t.shorthand,I=(t?.type==="ImportSpecifier"||t?.type==="ImportDefaultSpecifier"||t?.type==="ImportNamespaceSpecifier")&&(i==="imported"||i==="local")||t?.type==="FunctionDeclaration"&&i==="id",h=t?.type==="ExportSpecifier"&&(i==="exported"||i==="local"&&d);!n&&!l&&!I&&!h&&a.add("require reference at "+String(e.start))}if(e.type==="Identifier"||e.type==="Literal")return;let s;if((e.type==="ImportDeclaration"||e.type==="ExportNamedDeclaration"||e.type==="ExportAllDeclaration"||e.type==="ImportExpression")&&(s=x(e.source),e.type==="ImportExpression"&&s===void 0&&a.add("import expression at "+String(e.start))),e.type==="CallExpression"){const n=e.callee;if(n?.type==="Identifier"&&n.name==="require"){if(u?.esmOnly)throw Error("REFUSE\xB7module closure CommonJS load in ESM delivery "+r);s=x(e.arguments[0]),s===void 0&&a.add("require expression at "+String(e.start))}}s!==void 0&&(!u?.staticOnly||e.type!=="ImportExpression"&&e.type!=="CallExpression")&&y.add(s);const S=e.type==="ExportNamedDeclaration"?e.source!==null&&e.source!==void 0:d;for(const n of Object.keys(e)){const l=e[n];l&&typeof l=="object"&&f(l,e,n,S)}},"visit");return f(m),{imports:[...y],unresolved:[...a]}}c(j,"moduleImportSyntax");function M(o,r){return j(o,r).imports}c(M,"moduleImports");export{j as moduleImportSyntax,M as moduleImports};
|
|
@@ -1,115 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isSha256 } from "../hash.js";
|
|
3
|
-
import { fieldNames } from "./reading.js";
|
|
4
|
-
import { sha256 } from "../hash.js";
|
|
5
|
-
/** Host-only native binary selection. No PATH lookup, Node impersonation,
|
|
6
|
-
* transitive OS-library seal or arbitrary-process sandbox is claimed. */
|
|
7
|
-
import { createHash } from 'node:crypto';
|
|
8
|
-
import { isAbsolute } from 'node:path';
|
|
9
|
-
import { realpathSync } from 'node:fs';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { authorityAtEpoch } from '@lapxo/topos/wire';
|
|
12
|
-
import { parsedLine as parse } from "../../fold/claims.js";
|
|
13
|
-
import { authorityFor } from "../../fold/signers.js";
|
|
14
|
-
import { authenticateSignedPort, nativeParentInputBytes, canonicalJson } from "./reading.js";
|
|
15
|
-
import { readBounded } from "./generated.js";
|
|
16
|
-
const sha = sha256, keys = fieldNames, validDigest = isSha256, fail = (why) => { throw Error('REFUSE·native binary ' + why); };
|
|
17
|
-
/** A Root-selected local origin is a location constraint, not a World hint. */
|
|
18
|
-
export function hostOriginPath(root, coordinate) {
|
|
19
|
-
if (!/^region\/[^\s]+$/.test(coordinate))
|
|
20
|
-
fail('origin is not a declared region');
|
|
21
|
-
const rows = root.live.filter(line => { const row = parse(line); return row.kind === 'fact' && row.value.fields.scope === coordinate && row.value.fields.measure === 'origin' && row.value.fields.value !== 'withdraw'; });
|
|
22
|
-
if (rows.length !== 1)
|
|
23
|
-
fail('missing/conflicting origin ' + coordinate);
|
|
24
|
-
const line = rows[0];
|
|
25
|
-
if (!root.history.includes(line))
|
|
26
|
-
fail('origin absent from authenticated history');
|
|
27
|
-
const row = parse(line);
|
|
28
|
-
if (row.kind !== 'fact')
|
|
29
|
-
return fail('origin not a wire record');
|
|
30
|
-
const f = row.value.fields;
|
|
31
|
-
if (f.role !== 'writes' || f.form !== 'alphabet')
|
|
32
|
-
fail('origin declaration meaning');
|
|
33
|
-
const auth = authorityFor(root.history, root.signer, root.eras), signed = auth.of(line);
|
|
34
|
-
if (signed.kind !== 'admitted' || signed.signer !== root.signer.id)
|
|
35
|
-
fail('origin must be Root signed');
|
|
36
|
-
const current = authorityAtEpoch(f, root.signer, root.epoch, coordinate, 'authority');
|
|
37
|
-
if (current.kind !== 'admitted')
|
|
38
|
-
fail('current origin authority ' + current.why);
|
|
39
|
-
let url, path;
|
|
40
|
-
try {
|
|
41
|
-
url = new URL(f.value);
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return fail('origin URL invalid');
|
|
45
|
-
}
|
|
46
|
-
if (url.protocol !== 'file:' || url.username || url.password || url.search || url.hash || (url.hostname && url.hostname !== 'localhost'))
|
|
47
|
-
fail('unsupported origin transport');
|
|
48
|
-
try {
|
|
49
|
-
path = fileURLToPath(url);
|
|
50
|
-
if (!isAbsolute(path) || path.includes('\0'))
|
|
51
|
-
return fail('origin path invalid');
|
|
52
|
-
path = realpathSync(path);
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
return fail('local origin absent/invalid');
|
|
56
|
-
}
|
|
57
|
-
return Object.freeze({ coordinate, line, path });
|
|
58
|
-
}
|
|
59
|
-
export function nativeBinaryOrigin(root, p, portLine) {
|
|
60
|
-
if (p.profile !== 'host-native-binary-origin@1')
|
|
61
|
-
return;
|
|
62
|
-
const signed = authorityFor(root.history, root.signer, root.eras).of(portLine);
|
|
63
|
-
if (signed.kind !== 'admitted' || signed.signer !== root.signer.id)
|
|
64
|
-
fail('origin selection must be Root signed');
|
|
65
|
-
const origin = hostOriginPath(root, p.origin);
|
|
66
|
-
const bytes = readBounded(origin.path, p.entry.bytes);
|
|
67
|
-
if (bytes.byteLength !== p.entry.bytes || sha(bytes) !== p.entry.digest)
|
|
68
|
-
fail('origin bytes differ');
|
|
69
|
-
return origin;
|
|
70
|
-
}
|
|
71
|
-
const verified = new WeakMap();
|
|
72
|
-
/** Call under the existing Root guard; privateBinding is from the already
|
|
73
|
-
* prepared host image, never the candidate profile's own expected digest. */
|
|
74
|
-
export function verifyNativeBinary(root, coordinate, parent, privateBinding) {
|
|
75
|
-
const auth = authenticateSignedPort(root, coordinate), p = auth.profile;
|
|
76
|
-
if (!p || keys(p) !== (p.profile === 'host-native-binary-origin@1' ? 'alias,condition,coordinate,entry,limits,origin,profile,selector' : 'alias,condition,coordinate,entry,limits,profile,selector') || !['host-native-binary@1', 'host-native-binary-origin@1'].includes(p.profile) || p.coordinate !== coordinate || typeof p.alias !== 'string' || !p.alias.length || p.alias.includes('\0') || typeof p.condition !== 'string' || !p.condition.length)
|
|
77
|
-
fail('selection absent/unknown');
|
|
78
|
-
if (keys(p.selector ?? {}) !== 'binding,implementation,port' || canonicalJson(p.selector) !== canonicalJson({ port: parent.port, binding: parent.binding, implementation: parent.implementation }))
|
|
79
|
-
fail('selection differs from parent');
|
|
80
|
-
if (!validDigest(parent.identity) || !validDigest(parent.instrument) || !validDigest(parent.policy) || !validDigest(parent.authority) || !validDigest(parent.implementation) || !Array.isArray(parent.inputs))
|
|
81
|
-
fail('parent identity absent');
|
|
82
|
-
if (keys(p.entry ?? {}) !== 'bytes,digest,path' || typeof p.entry.path !== 'string' || isAbsolute(p.entry.path) || p.entry.path.split('/').some(s => !s || s === '.' || s === '..') || !validDigest(p.entry.digest) || !Number.isSafeInteger(p.entry.bytes) || p.entry.bytes < 1 || keys(p.limits ?? {}) !== 'bytes' || !Number.isSafeInteger(p.limits.bytes) || p.limits.bytes < 1 || p.entry.bytes > p.limits.bytes || p.limits.bytes > nativeParentInputBytes(root, parent))
|
|
83
|
-
fail('entry byte resource absent/outside Root');
|
|
84
|
-
if (!privateBinding || typeof privateBinding.path !== 'string' || !isAbsolute(privateBinding.path) || !validDigest(privateBinding.digest) || p.entry.path !== privateBinding.coordinate || p.entry.digest !== privateBinding.digest || !parent.inputs.some(([_, digest]) => digest === privateBinding.digest))
|
|
85
|
-
fail('private image binding absent/differs');
|
|
86
|
-
const origin = nativeBinaryOrigin(root, p, auth.line);
|
|
87
|
-
let expected, actual;
|
|
88
|
-
try {
|
|
89
|
-
expected = realpathSync(privateBinding.path);
|
|
90
|
-
actual = realpathSync(privateBinding.path);
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
return fail('entry absent');
|
|
94
|
-
}
|
|
95
|
-
if (expected !== actual)
|
|
96
|
-
fail('entry path differs from private image');
|
|
97
|
-
let runtimePath;
|
|
98
|
-
try {
|
|
99
|
-
runtimePath = realpathSync(parent.runtime);
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return fail('runtime binding absent');
|
|
103
|
-
}
|
|
104
|
-
if (actual === runtimePath)
|
|
105
|
-
fail('binary aliases actual runtime');
|
|
106
|
-
if (origin && actual !== origin.path)
|
|
107
|
-
fail('entry differs from declared origin');
|
|
108
|
-
const bytes = readBounded(actual, p.entry.bytes);
|
|
109
|
-
if (bytes.byteLength !== p.entry.bytes || sha(bytes) !== privateBinding.digest)
|
|
110
|
-
fail('entry bytes differ');
|
|
111
|
-
const handle = Object.freeze({ identity: 'sha256:' + createHash('sha256').update(canonicalJson([auth.line, parent.identity, ...(origin ? [origin.line] : [])])).digest('hex') });
|
|
112
|
-
verified.set(handle, { kind: 'native-binary', alias: p.alias, entry: actual, entryDigest: privateBinding.digest, entryBytes: bytes.byteLength, condition: p.condition });
|
|
113
|
-
return handle;
|
|
114
|
-
}
|
|
115
|
-
export const nativeBinaryProjection = retainedProjection(verified, fail);
|
|
1
|
+
var S=Object.defineProperty;var h=(e,s)=>S(e,"name",{value:s,configurable:!0});import{retainedProjection as A}from"./reading.js";import{isSha256 as E}from"../hash.js";import{fieldNames as j}from"./reading.js";import{sha256 as N}from"../hash.js";import{createHash as O}from"node:crypto";import{isAbsolute as p}from"node:path";import{realpathSync as u}from"node:fs";import{fileURLToPath as U}from"node:url";import{library14_authorityAtEpoch as I}from"../../vendor/runtime.js";import{parsedLine as w}from"../../fold/claims.js";import{authorityFor as k}from"../../fold/signers.js";import{authenticateSignedPort as z,nativeParentInputBytes as D,canonicalJson as v}from"./reading.js";import{readBounded as L}from"./generated.js";const P=N,b=j,c=E,i=h(e=>{throw Error("REFUSE\xB7native binary "+e)},"fail");function F(e,s){/^region\/[^\s]+$/.test(s)||i("origin is not a declared region");const r=e.live.filter(g=>{const f=w(g);return f.kind==="fact"&&f.value.fields.scope===s&&f.value.fields.measure==="origin"&&f.value.fields.value!=="withdraw"});r.length!==1&&i("missing/conflicting origin "+s);const n=r[0];e.history.includes(n)||i("origin absent from authenticated history");const y=w(n);if(y.kind!=="fact")return i("origin not a wire record");const t=y.value.fields;(t.role!=="writes"||t.form!=="alphabet")&&i("origin declaration meaning");const d=k(e.history,e.signer,e.eras),m=d.of(n);(m.kind!=="admitted"||m.signer!==e.signer.id)&&i("origin must be Root signed");const l=I(t,e.signer,e.epoch,s,"authority");l.kind!=="admitted"&&i("current origin authority "+l.why);let o,a;try{o=new URL(t.value)}catch{return i("origin URL invalid")}(o.protocol!=="file:"||o.username||o.password||o.search||o.hash||o.hostname&&o.hostname!=="localhost")&&i("unsupported origin transport");try{if(a=U(o),!p(a)||a.includes("\0"))return i("origin path invalid");a=u(a)}catch{return i("local origin absent/invalid")}return Object.freeze({coordinate:s,line:n,path:a})}h(F,"hostOriginPath");function _(e,s,r){if(s.profile!=="host-native-binary-origin@1")return;const n=k(e.history,e.signer,e.eras).of(r);(n.kind!=="admitted"||n.signer!==e.signer.id)&&i("origin selection must be Root signed");const y=F(e,s.origin),t=L(y.path,s.entry.bytes);return(t.byteLength!==s.entry.bytes||P(t)!==s.entry.digest)&&i("origin bytes differ"),y}h(_,"nativeBinaryOrigin");const R=new WeakMap;function Z(e,s,r,n){const y=z(e,s),t=y.profile;(!t||b(t)!==(t.profile==="host-native-binary-origin@1"?"alias,condition,coordinate,entry,limits,origin,profile,selector":"alias,condition,coordinate,entry,limits,profile,selector")||!["host-native-binary@1","host-native-binary-origin@1"].includes(t.profile)||t.coordinate!==s||typeof t.alias!="string"||!t.alias.length||t.alias.includes("\0")||typeof t.condition!="string"||!t.condition.length)&&i("selection absent/unknown"),(b(t.selector??{})!=="binding,implementation,port"||v(t.selector)!==v({port:r.port,binding:r.binding,implementation:r.implementation}))&&i("selection differs from parent"),(!c(r.identity)||!c(r.instrument)||!c(r.policy)||!c(r.authority)||!c(r.implementation)||!Array.isArray(r.inputs))&&i("parent identity absent"),(b(t.entry??{})!=="bytes,digest,path"||typeof t.entry.path!="string"||p(t.entry.path)||t.entry.path.split("/").some(f=>!f||f==="."||f==="..")||!c(t.entry.digest)||!Number.isSafeInteger(t.entry.bytes)||t.entry.bytes<1||b(t.limits??{})!=="bytes"||!Number.isSafeInteger(t.limits.bytes)||t.limits.bytes<1||t.entry.bytes>t.limits.bytes||t.limits.bytes>D(e,r))&&i("entry byte resource absent/outside Root"),(!n||typeof n.path!="string"||!p(n.path)||!c(n.digest)||t.entry.path!==n.coordinate||t.entry.digest!==n.digest||!r.inputs.some(([f,x])=>x===n.digest))&&i("private image binding absent/differs");const d=_(e,t,y.line);let m,l;try{m=u(n.path),l=u(n.path)}catch{return i("entry absent")}m!==l&&i("entry path differs from private image");let o;try{o=u(r.runtime)}catch{return i("runtime binding absent")}l===o&&i("binary aliases actual runtime"),d&&l!==d.path&&i("entry differs from declared origin");const a=L(l,t.entry.bytes);(a.byteLength!==t.entry.bytes||P(a)!==n.digest)&&i("entry bytes differ");const g=Object.freeze({identity:"sha256:"+O("sha256").update(v([y.line,r.identity,...d?[d.line]:[]])).digest("hex")});return R.set(g,{kind:"native-binary",alias:t.alias,entry:l,entryDigest:n.digest,entryBytes:a.byteLength,condition:t.condition}),g}h(Z,"verifyNativeBinary");const B=A(R,i);export{F as hostOriginPath,_ as nativeBinaryOrigin,B as nativeBinaryProjection,Z as verifyNativeBinary};
|