@bounded-sh/server 0.0.55 → 0.0.57
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/index.js +55 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +56 -22
- package/dist/index.mjs.map +1 -1
- package/dist/sanitize-headers.d.ts +1 -0
- package/dist/wallet-client.d.ts +2 -1
- package/dist/webhooks.d.ts +15 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -74,8 +74,8 @@ async function getIdToken() {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const PRESIGNED_BLOCKHASH_ERROR = 'Server signedTransaction blockhash is stale or expired';
|
|
77
|
-
const BOUNDED_PROGRAM_MAINNET = '
|
|
78
|
-
const BOUNDED_PROGRAM_DEVNET = '
|
|
77
|
+
const BOUNDED_PROGRAM_MAINNET = 'openTv7fbpYSseNHYmCZFZ1CZgj4r8D9fKNgEz1qo6F';
|
|
78
|
+
const BOUNDED_PROGRAM_DEVNET = 'openTv7fbpYSseNHYmCZFZ1CZgj4r8D9fKNgEz1qo6F';
|
|
79
79
|
const COMPUTE_BUDGET_PROGRAM = 'ComputeBudget111111111111111111111111111111';
|
|
80
80
|
const SYSTEM_PROGRAM_ID = '11111111111111111111111111111111';
|
|
81
81
|
const SUPPORTED_SOLANA_NETWORKS = ['solana_devnet', 'solana_mainnet'];
|
|
@@ -810,17 +810,23 @@ class EvmKeypairProvider {
|
|
|
810
810
|
}
|
|
811
811
|
}
|
|
812
812
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
813
|
+
// #023: strip caller-supplied auth headers so an operation runs as the isolated wallet, not
|
|
814
|
+
// as whatever headers were forwarded in. HTTP header names are case-insensitive on the wire,
|
|
815
|
+
// so a fixed `Authorization`/`authorization` destructure let a mixed-case `AUTHORIZATION`
|
|
816
|
+
// survive and ride along. Drop every header whose lowercased name is authorization (plus
|
|
817
|
+
// proxy-authorization / cookie, which would likewise leak a caller identity into the session).
|
|
818
|
+
const STRIP_HEADER_NAMES = new Set(["authorization", "proxy-authorization", "cookie"]);
|
|
819
|
+
function stripCallerAuthHeaders(headers) {
|
|
820
|
+
if (!headers)
|
|
821
|
+
return undefined;
|
|
822
|
+
const rest = {};
|
|
823
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
824
|
+
if (!STRIP_HEADER_NAMES.has(name.toLowerCase()))
|
|
825
|
+
rest[name] = value;
|
|
826
|
+
}
|
|
827
|
+
return Object.keys(rest).length > 0 ? rest : undefined;
|
|
828
|
+
}
|
|
829
|
+
|
|
824
830
|
/* ------------------------------------------------------------------ */
|
|
825
831
|
/* Keypair parsing */
|
|
826
832
|
/* ------------------------------------------------------------------ */
|
|
@@ -868,7 +874,11 @@ class WalletClient {
|
|
|
868
874
|
this.evmTransactionPolicy = evmTransactionPolicy;
|
|
869
875
|
this.live = {
|
|
870
876
|
intent: (roomPath, intent, opts = {}) => core.live.intent(roomPath, intent, this.mergeLiveIntentOptions(opts)),
|
|
871
|
-
|
|
877
|
+
// Route status through the SAME auth merge intent uses so a server-keypair
|
|
878
|
+
// agent's session token rides the request. Without it the agent sends the
|
|
879
|
+
// public app id only and the server returns the slim anonymous shape rather
|
|
880
|
+
// than the full diagnostics the authenticated agent is entitled to (#102).
|
|
881
|
+
status: (roomPath, opts = {}) => core.live.status(roomPath, this.mergeLiveStatusOptions(opts)),
|
|
872
882
|
// Agent-as-player view stream: subscribe to THIS wallet's per-player view
|
|
873
883
|
// under the wallet's own session (per-subscription overrides — no global
|
|
874
884
|
// auth needed). The view id defaults to this wallet's @user.id (== its
|
|
@@ -908,12 +918,9 @@ class WalletClient {
|
|
|
908
918
|
this.sessionManager.clearSession();
|
|
909
919
|
}, _walletAddress: this.address }, (this.evmTransactionPolicy ? { _evmTransactionPolicy: this.evmTransactionPolicy } : {}));
|
|
910
920
|
}
|
|
911
|
-
/**
|
|
921
|
+
/** #023: strip caller Authorization (any casing) so operations run as the isolated wallet. */
|
|
912
922
|
sanitizeHeaders(headers) {
|
|
913
|
-
|
|
914
|
-
return undefined;
|
|
915
|
-
const _a = headers, { Authorization, authorization } = _a, rest = __rest(_a, ["Authorization", "authorization"]);
|
|
916
|
-
return Object.keys(rest).length > 0 ? rest : undefined;
|
|
923
|
+
return stripCallerAuthHeaders(headers);
|
|
917
924
|
}
|
|
918
925
|
mergeSetOverrides(opts) {
|
|
919
926
|
var _a;
|
|
@@ -930,6 +937,12 @@ class WalletClient {
|
|
|
930
937
|
const merged = Object.assign(Object.assign({}, opts), { headers: this.sanitizeHeaders(opts === null || opts === void 0 ? void 0 : opts.headers), _overrides: Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts._overrides), { headers: this.sanitizeHeaders((_a = opts === null || opts === void 0 ? void 0 : opts._overrides) === null || _a === void 0 ? void 0 : _a.headers), _getAuthHeaders: ovr._getAuthHeaders, _clearAuth: ovr._clearAuth, _walletAddress: ovr._walletAddress }) });
|
|
931
938
|
return merged;
|
|
932
939
|
}
|
|
940
|
+
mergeLiveStatusOptions(opts) {
|
|
941
|
+
var _a;
|
|
942
|
+
const ovr = this.buildOverrides();
|
|
943
|
+
const merged = Object.assign(Object.assign({}, opts), { headers: this.sanitizeHeaders(opts === null || opts === void 0 ? void 0 : opts.headers), _overrides: Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts._overrides), { headers: this.sanitizeHeaders((_a = opts === null || opts === void 0 ? void 0 : opts._overrides) === null || _a === void 0 ? void 0 : _a.headers), _getAuthHeaders: ovr._getAuthHeaders, _clearAuth: ovr._clearAuth, _walletAddress: ovr._walletAddress }) });
|
|
944
|
+
return merged;
|
|
945
|
+
}
|
|
933
946
|
/* ---- Data operations ---- */
|
|
934
947
|
async set(path, document, options) {
|
|
935
948
|
return core.set(path, document, this.mergeSetOverrides(options));
|
|
@@ -1344,8 +1357,29 @@ async function verifyWebhook(rawBody, headers, opts = {}) {
|
|
|
1344
1357
|
if (Math.abs(nowSeconds - payload.timestamp) > maxSkew) {
|
|
1345
1358
|
throw new WebhookVerificationError(`Webhook timestamp outside allowed skew of ${maxSkew}s (delta ${Math.abs(nowSeconds - payload.timestamp)}s).`);
|
|
1346
1359
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1360
|
+
// #072: bind the delivery to an app by DEFAULT. The shared platform signing key means a
|
|
1361
|
+
// valid signature proves "from Bounded", not "from your app"; payload.appId is the ONLY
|
|
1362
|
+
// per-app separation. Previously the bind ran only when expectedAppId was passed, so the
|
|
1363
|
+
// secure check was opt-in and a malicious app owner could point their app's webhook at a
|
|
1364
|
+
// victim receiver. Now: an explicit expectedAppId is authoritative; otherwise bind to the
|
|
1365
|
+
// init()-time app id; a multi-app receiver must set allowAnyAppId to accept any app; and
|
|
1366
|
+
// when no app id is resolvable we FAIL CLOSED rather than silently trust any app.
|
|
1367
|
+
if (opts.expectedAppId !== undefined) {
|
|
1368
|
+
if (payload.appId !== opts.expectedAppId) {
|
|
1369
|
+
throw new WebhookVerificationError(`Webhook appId "${payload.appId}" does not match expected appId "${opts.expectedAppId}".`);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
else if (!opts.allowAnyAppId) {
|
|
1373
|
+
const configuredAppId = core.getConfiguredAppId();
|
|
1374
|
+
if (!configuredAppId) {
|
|
1375
|
+
throw new WebhookVerificationError("verifyWebhook cannot determine which app this webhook is for: no expectedAppId was " +
|
|
1376
|
+
"passed and the SDK has no init()-time app id. Pass expectedAppId, or set " +
|
|
1377
|
+
"allowAnyAppId: true to intentionally accept webhooks from any app.");
|
|
1378
|
+
}
|
|
1379
|
+
if (payload.appId !== configuredAppId) {
|
|
1380
|
+
throw new WebhookVerificationError(`Webhook appId "${payload.appId}" does not match the configured app id "${configuredAppId}". ` +
|
|
1381
|
+
"Pass allowAnyAppId: true to accept webhooks from any app.");
|
|
1382
|
+
}
|
|
1349
1383
|
}
|
|
1350
1384
|
// Replay cache: reject a previously-seen signed delivery within the skew
|
|
1351
1385
|
// window. The worker's payload.id is the document id/path, so using it alone
|