@ai-ecoverse/slicc-kernel 1.177.0 → 1.178.1
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 +23 -4
- package/dist/driver-worker.js +4 -1
- package/dist/driver-worker.js.map +3 -3
- package/dist/index.js +47 -3
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +200 -74
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +4 -1
- package/dist/node-driver-worker.js.map +3 -3
- package/dist/node-process-worker.cjs +8 -4
- package/dist/node-process-worker.cjs.map +2 -2
- package/dist/node-process-worker.js +8 -4
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +237 -70
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +8 -4
- package/dist/process-worker.js.map +2 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/kernel/net/http-syscalls.d.ts +1 -1
- package/dist/types/kernel/net/remote-transport.d.ts +3 -3
- package/dist/types/kernel/net/transport.d.ts +1 -0
- package/dist/types/kernel/sealed-oauth.d.ts +74 -0
- package/dist/types/kernel/sealed.d.ts +1 -0
- package/dist/types/node.d.ts +1 -0
- package/dist/types/realm/sync-fs-wire.d.ts +1 -1
- package/dist/types/transport.d.ts +2 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -969,9 +969,9 @@ function changeCred(cur, change) {
|
|
|
969
969
|
}
|
|
970
970
|
const privileged = cur.euid === 0;
|
|
971
971
|
const next3 = copyCred(cur);
|
|
972
|
-
for (const
|
|
973
|
-
const own =
|
|
974
|
-
for (const key of
|
|
972
|
+
for (const keys2 of [UIDS, GIDS]) {
|
|
973
|
+
const own = keys2.map((key) => cur[key]);
|
|
974
|
+
for (const key of keys2) {
|
|
975
975
|
const value2 = id(change[key]);
|
|
976
976
|
if (value2 === void 0) continue;
|
|
977
977
|
if (!privileged && !own.includes(value2)) throw fsError("EPERM", `${key} ${value2}`);
|
|
@@ -3617,12 +3617,12 @@ function next(p) {
|
|
|
3617
3617
|
var ANY = [[">=", [0, 0, 0, []]]];
|
|
3618
3618
|
var NOTHING = [["<", [0, 0, 0, ["0"]]]];
|
|
3619
3619
|
function plain(op, p) {
|
|
3620
|
-
const
|
|
3620
|
+
const exact2 = !p.parts.includes(void 0);
|
|
3621
3621
|
const low = floor(p);
|
|
3622
3622
|
const high = next(p);
|
|
3623
3623
|
if (op === ">=") return [[">=", low]];
|
|
3624
|
-
if (op === "<") return [["<",
|
|
3625
|
-
if (
|
|
3624
|
+
if (op === "<") return [["<", exact2 ? low : [low[0], low[1], low[2], ["0"]]]];
|
|
3625
|
+
if (exact2) return [[op === "" ? "=" : op, low]];
|
|
3626
3626
|
if (op === ">") return high ? [[">=", high]] : NOTHING;
|
|
3627
3627
|
if (op === "<=") return high ? [["<", high]] : ANY;
|
|
3628
3628
|
return high ? [
|
|
@@ -3709,7 +3709,7 @@ function satisfies(version, range2) {
|
|
|
3709
3709
|
}
|
|
3710
3710
|
|
|
3711
3711
|
// src/version.ts
|
|
3712
|
-
var KERNEL_VERSION = true ? "1.
|
|
3712
|
+
var KERNEL_VERSION = true ? "1.178.1" : "0.0.0-dev";
|
|
3713
3713
|
var OS_RELEASE = "6.0.0-slicc";
|
|
3714
3714
|
function procVersion() {
|
|
3715
3715
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -4299,7 +4299,7 @@ function intlZone(name2) {
|
|
|
4299
4299
|
minute: "numeric",
|
|
4300
4300
|
second: "numeric"
|
|
4301
4301
|
});
|
|
4302
|
-
const
|
|
4302
|
+
const exact2 = (at) => {
|
|
4303
4303
|
const parts2 = {};
|
|
4304
4304
|
for (const part of format.formatToParts(at)) parts2[part.type] = Number(part.value);
|
|
4305
4305
|
const wall = Date.UTC(
|
|
@@ -4319,12 +4319,12 @@ function intlZone(name2) {
|
|
|
4319
4319
|
const bucket = Math.floor(at / BUCKET_MS);
|
|
4320
4320
|
let known = buckets.get(bucket);
|
|
4321
4321
|
if (known === void 0) {
|
|
4322
|
-
const start2 =
|
|
4323
|
-
known = start2 ===
|
|
4322
|
+
const start2 = exact2(bucket * BUCKET_MS);
|
|
4323
|
+
known = start2 === exact2(bucket * BUCKET_MS + BUCKET_MS - MINUTE_MS) ? start2 : null;
|
|
4324
4324
|
if (buckets.size >= CACHE_MAX) buckets.clear();
|
|
4325
4325
|
buckets.set(bucket, known);
|
|
4326
4326
|
}
|
|
4327
|
-
return known ??
|
|
4327
|
+
return known ?? exact2(at);
|
|
4328
4328
|
}
|
|
4329
4329
|
};
|
|
4330
4330
|
}
|
|
@@ -5567,11 +5567,11 @@ var CronService = class {
|
|
|
5567
5567
|
const now = this.clock.now();
|
|
5568
5568
|
const system = this.host.timeZone;
|
|
5569
5569
|
const due2 = [];
|
|
5570
|
-
const
|
|
5570
|
+
const keys2 = /* @__PURE__ */ new Set();
|
|
5571
5571
|
let next3 = now + WAKE_MS;
|
|
5572
5572
|
for (const entry of loaded) {
|
|
5573
5573
|
const key = keyOf2(entry);
|
|
5574
|
-
|
|
5574
|
+
keys2.add(key);
|
|
5575
5575
|
const { when } = entry.job;
|
|
5576
5576
|
if (when.reboot) {
|
|
5577
5577
|
if (this.rebooted || stamp.reboot[key] === this.host.bootId) continue;
|
|
@@ -5597,7 +5597,7 @@ var CronService = class {
|
|
|
5597
5597
|
}
|
|
5598
5598
|
this.rebooted = true;
|
|
5599
5599
|
for (const map of [stamp.jobs, stamp.reboot, stamp.when])
|
|
5600
|
-
for (const key of Object.keys(map)) if (!
|
|
5600
|
+
for (const key of Object.keys(map)) if (!keys2.has(key)) delete map[key];
|
|
5601
5601
|
await this.writeStamp(stamp);
|
|
5602
5602
|
for (const each of due2) void this.spawn(each);
|
|
5603
5603
|
return next3;
|
|
@@ -6883,16 +6883,16 @@ function missing2(err) {
|
|
|
6883
6883
|
return err?.code === "ENOENT";
|
|
6884
6884
|
}
|
|
6885
6885
|
function synthetic(directory, path) {
|
|
6886
|
-
const
|
|
6886
|
+
const epoch2 = /* @__PURE__ */ new Date(0);
|
|
6887
6887
|
return {
|
|
6888
6888
|
isFile: !directory,
|
|
6889
6889
|
isDirectory: directory,
|
|
6890
6890
|
isSymbolicLink: false,
|
|
6891
6891
|
size: 0,
|
|
6892
6892
|
mode: (directory ? S_IFDIR : S_IFREG) | 493,
|
|
6893
|
-
mtime:
|
|
6894
|
-
atime:
|
|
6895
|
-
ctime:
|
|
6893
|
+
mtime: epoch2,
|
|
6894
|
+
atime: epoch2,
|
|
6895
|
+
ctime: epoch2,
|
|
6896
6896
|
ino: inodeOf(path)
|
|
6897
6897
|
};
|
|
6898
6898
|
}
|
|
@@ -7111,16 +7111,16 @@ function missing3(err) {
|
|
|
7111
7111
|
return err?.code === "ENOENT";
|
|
7112
7112
|
}
|
|
7113
7113
|
function statOf(path, bytes) {
|
|
7114
|
-
const
|
|
7114
|
+
const epoch2 = /* @__PURE__ */ new Date(0);
|
|
7115
7115
|
return {
|
|
7116
7116
|
isFile: true,
|
|
7117
7117
|
isDirectory: false,
|
|
7118
7118
|
isSymbolicLink: false,
|
|
7119
7119
|
size: bytes.length,
|
|
7120
7120
|
mode: S_IFREG2 | 420,
|
|
7121
|
-
mtime:
|
|
7122
|
-
atime:
|
|
7123
|
-
ctime:
|
|
7121
|
+
mtime: epoch2,
|
|
7122
|
+
atime: epoch2,
|
|
7123
|
+
ctime: epoch2,
|
|
7124
7124
|
ino: inodeOf(path),
|
|
7125
7125
|
nlink: 1,
|
|
7126
7126
|
uid: 0,
|
|
@@ -9476,10 +9476,10 @@ async function copyByBytes(fs, from, to, options) {
|
|
|
9476
9476
|
|
|
9477
9477
|
// src/fs/view.ts
|
|
9478
9478
|
function view(target, overrides) {
|
|
9479
|
-
const
|
|
9479
|
+
const keys2 = new Set(Object.keys(overrides));
|
|
9480
9480
|
return new Proxy(target, {
|
|
9481
9481
|
get(t, prop, receiver) {
|
|
9482
|
-
if (
|
|
9482
|
+
if (keys2.has(prop)) return overrides[prop];
|
|
9483
9483
|
return Reflect.get(t, prop, receiver);
|
|
9484
9484
|
},
|
|
9485
9485
|
set(t, prop, value2) {
|
|
@@ -9735,11 +9735,11 @@ var HeldPaths = class extends Map {
|
|
|
9735
9735
|
handlesAt(path) {
|
|
9736
9736
|
return [...this.handles].filter(([, key]) => key === path).map(([id2]) => id2);
|
|
9737
9737
|
}
|
|
9738
|
-
doubt(over,
|
|
9738
|
+
doubt(over, exact2 = false) {
|
|
9739
9739
|
if (this.bound.size === 0) return;
|
|
9740
9740
|
for (const [handle, key] of this.handles) {
|
|
9741
9741
|
if (!this.bound.has(handle) || this.keptHandles.has(handle)) continue;
|
|
9742
|
-
if (
|
|
9742
|
+
if (exact2 ? key === over : within5(key, over)) this.doubted.set(handle, ++this.doubts);
|
|
9743
9743
|
}
|
|
9744
9744
|
}
|
|
9745
9745
|
boundIno(handle) {
|
|
@@ -13594,8 +13594,8 @@ function refused2(engine, session, err, opening2) {
|
|
|
13594
13594
|
const code = verifyCode(flags2);
|
|
13595
13595
|
if (code && flags2 !== -1)
|
|
13596
13596
|
return new KernelError("EPROTO", `${code}: ${engine.verifyInfo(flags2).trim()}`);
|
|
13597
|
-
const
|
|
13598
|
-
if (
|
|
13597
|
+
const record3 = recordRefusal(opening2);
|
|
13598
|
+
if (record3) return record3;
|
|
13599
13599
|
const text = err instanceof TlsError ? err.message : String(err);
|
|
13600
13600
|
return new KernelError("EPROTO", `ERR_SSL_HANDSHAKE_FAILURE: ${text}${alertOf(session)}`);
|
|
13601
13601
|
}
|
|
@@ -14710,6 +14710,120 @@ var SessionScrubs = class {
|
|
|
14710
14710
|
}
|
|
14711
14711
|
};
|
|
14712
14712
|
|
|
14713
|
+
// src/kernel/sealed-oauth.ts
|
|
14714
|
+
var SEALED_OAUTH_TIMEOUT_MS = 45e3;
|
|
14715
|
+
var SEALED_OAUTH_OPS = /* @__PURE__ */ new Set([
|
|
14716
|
+
"oauthGrant",
|
|
14717
|
+
"oauthStatus",
|
|
14718
|
+
"oauthRefresh",
|
|
14719
|
+
"oauthRemove"
|
|
14720
|
+
]);
|
|
14721
|
+
var MESSAGES = {
|
|
14722
|
+
EINVAL: "Invalid OAuth request",
|
|
14723
|
+
ENOENT: "OAuth credential not found",
|
|
14724
|
+
EEXIST: "OAuth credential identity conflicts",
|
|
14725
|
+
EACCES: "OAuth operation refused",
|
|
14726
|
+
EAGAIN: "OAuth credential changed; try again",
|
|
14727
|
+
ENOTSUP: "OAuth operation is not supported",
|
|
14728
|
+
ENOTCONN: "Vault is unavailable",
|
|
14729
|
+
ETIMEDOUT: "Vault request timed out",
|
|
14730
|
+
EIO: "Invalid vault reply"
|
|
14731
|
+
};
|
|
14732
|
+
var CAUSES = {
|
|
14733
|
+
"vault-locked": "Vault is locked",
|
|
14734
|
+
"oauth-reauth-required": "OAuth sign-in is required",
|
|
14735
|
+
"oauth-refresh-failed": "OAuth refresh failed"
|
|
14736
|
+
};
|
|
14737
|
+
var COMMON = ["v", "op", "caller"];
|
|
14738
|
+
var IDENTITY = [...COMMON, "name", "provider", "account"];
|
|
14739
|
+
var GRANT = [...IDENTITY, "accessToken", "refreshToken", "tokenType", "expiresAt", "oauth"];
|
|
14740
|
+
var CONFIG = ["tokenUrl", "clientId", "authMethod", "scope", "resource"];
|
|
14741
|
+
var UNSUPPORTED = ["auth", "clientSecret", "fields"];
|
|
14742
|
+
var STATES2 = /* @__PURE__ */ new Set([
|
|
14743
|
+
"ready",
|
|
14744
|
+
"refreshing",
|
|
14745
|
+
"expired",
|
|
14746
|
+
"reauth-required",
|
|
14747
|
+
"removed",
|
|
14748
|
+
"locked"
|
|
14749
|
+
]);
|
|
14750
|
+
var NAME2 = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
14751
|
+
var PROVIDER = /^[a-z0-9][a-z0-9-]{0,127}$/;
|
|
14752
|
+
var PLACEHOLDER2 = /^fnox_proxy_[0-9a-f]{24}$/;
|
|
14753
|
+
var CONTROL = /[\u0000-\u001f\u007f-\u009f]/;
|
|
14754
|
+
function record(value2) {
|
|
14755
|
+
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
14756
|
+
}
|
|
14757
|
+
function keys(value2, allowed) {
|
|
14758
|
+
return Object.keys(value2).every((key) => allowed.includes(key));
|
|
14759
|
+
}
|
|
14760
|
+
function exact(value2, fields) {
|
|
14761
|
+
return Object.keys(value2).length === fields.length && keys(value2, fields);
|
|
14762
|
+
}
|
|
14763
|
+
function nonempty(value2) {
|
|
14764
|
+
return typeof value2 === "string" && value2.length > 0;
|
|
14765
|
+
}
|
|
14766
|
+
function epoch(value2) {
|
|
14767
|
+
return typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0;
|
|
14768
|
+
}
|
|
14769
|
+
function sealedOAuthOperation(op) {
|
|
14770
|
+
return op === "capabilities" || SEALED_OAUTH_OPS.has(op);
|
|
14771
|
+
}
|
|
14772
|
+
function validateSealedOAuth(req) {
|
|
14773
|
+
const config = req.oauth;
|
|
14774
|
+
if (UNSUPPORTED.some((key) => Object.hasOwn(req, key)) || record(config) && UNSUPPORTED.some((key) => Object.hasOwn(config, key)))
|
|
14775
|
+
throw new KernelError("ENOTSUP");
|
|
14776
|
+
const allowed = req.op === "capabilities" ? COMMON : req.op === "oauthGrant" ? GRANT : IDENTITY;
|
|
14777
|
+
if (!keys(req, allowed)) throw new KernelError("EINVAL");
|
|
14778
|
+
if (req.op === "capabilities") return;
|
|
14779
|
+
if (typeof req.name !== "string" || !NAME2.test(req.name) || typeof req.provider !== "string" || !PROVIDER.test(req.provider) || !nonempty(req.account) || req.account.length > 256 || CONTROL.test(req.account))
|
|
14780
|
+
throw new KernelError("EINVAL");
|
|
14781
|
+
if (req.op !== "oauthGrant") return;
|
|
14782
|
+
if (!nonempty(req.accessToken) || req.refreshToken !== void 0 && !nonempty(req.refreshToken) || req.tokenType !== void 0 && typeof req.tokenType !== "string" || req.expiresAt !== void 0 && !epoch(req.expiresAt) || !record(config) || !keys(config, CONFIG) || !nonempty(config.tokenUrl) || !nonempty(config.clientId) || typeof config.authMethod !== "string" || config.scope !== void 0 && typeof config.scope !== "string" || config.resource !== void 0 && !nonempty(config.resource))
|
|
14783
|
+
throw new KernelError("EINVAL");
|
|
14784
|
+
if (config.authMethod !== "none" || typeof req.tokenType === "string" && req.tokenType.toLowerCase() !== "bearer")
|
|
14785
|
+
throw new KernelError("ENOTSUP");
|
|
14786
|
+
}
|
|
14787
|
+
function sealedOAuthError(code = "EIO", cause) {
|
|
14788
|
+
return {
|
|
14789
|
+
v: 1,
|
|
14790
|
+
error: { code, ...cause ? { cause } : {}, message: cause ? CAUSES[cause] : MESSAGES[code] }
|
|
14791
|
+
};
|
|
14792
|
+
}
|
|
14793
|
+
function sealedOAuthReply(op, value2) {
|
|
14794
|
+
try {
|
|
14795
|
+
return checkedReply(op, value2) ?? sealedOAuthError();
|
|
14796
|
+
} catch {
|
|
14797
|
+
return sealedOAuthError();
|
|
14798
|
+
}
|
|
14799
|
+
}
|
|
14800
|
+
function checkedError(value2) {
|
|
14801
|
+
const error = value2.error;
|
|
14802
|
+
if (!exact(value2, ["v", "error"]) || !record(error) || !Object.hasOwn(error, "code") || !Object.hasOwn(error, "message"))
|
|
14803
|
+
return void 0;
|
|
14804
|
+
const { code, cause, message: message3 } = error;
|
|
14805
|
+
if (!keys(error, ["code", "cause", "message"]) || typeof code !== "string" || !Object.hasOwn(MESSAGES, code) || typeof message3 !== "string" || cause !== void 0 && (typeof cause !== "string" || !Object.hasOwn(CAUSES, cause)))
|
|
14806
|
+
return void 0;
|
|
14807
|
+
return sealedOAuthError(code, cause);
|
|
14808
|
+
}
|
|
14809
|
+
function checkedReply(op, value2) {
|
|
14810
|
+
if (!record(value2) || !Object.hasOwn(value2, "v") || value2.v !== 1) return void 0;
|
|
14811
|
+
if (Object.hasOwn(value2, "error")) return checkedError(value2);
|
|
14812
|
+
if (op === "capabilities") {
|
|
14813
|
+
const caps = value2.capabilities;
|
|
14814
|
+
if (!exact(value2, ["v", "capabilities"]) || !record(caps) || !keys(caps, ["kernelOAuth"]))
|
|
14815
|
+
return void 0;
|
|
14816
|
+
if (Object.hasOwn(caps, "kernelOAuth") && caps.kernelOAuth !== 1) return void 0;
|
|
14817
|
+
return { v: 1, capabilities: Object.hasOwn(caps, "kernelOAuth") ? { kernelOAuth: 1 } : {} };
|
|
14818
|
+
}
|
|
14819
|
+
if (op === "oauthRemove")
|
|
14820
|
+
return exact(value2, ["v", "removed"]) && value2.removed === true ? { v: 1, removed: true } : void 0;
|
|
14821
|
+
const { placeholder, state, expiresAt, refreshable } = value2;
|
|
14822
|
+
if (!exact(value2, ["v", "placeholder", "state", "expiresAt", "refreshable"]) || typeof placeholder !== "string" || !PLACEHOLDER2.test(placeholder) || typeof state !== "string" || !STATES2.has(state) || expiresAt !== null && !epoch(expiresAt) || typeof refreshable !== "boolean")
|
|
14823
|
+
return void 0;
|
|
14824
|
+
return { v: 1, placeholder, state, expiresAt, refreshable };
|
|
14825
|
+
}
|
|
14826
|
+
|
|
14713
14827
|
// src/kernel/sealed-session.ts
|
|
14714
14828
|
var SESSION_WINDOW_MS = 6e4;
|
|
14715
14829
|
var SESSION_PLACEHOLDERS = 16;
|
|
@@ -14820,8 +14934,8 @@ function sessionValues(reply, asked) {
|
|
|
14820
14934
|
const { error, values } = reply;
|
|
14821
14935
|
if (error !== void 0 || typeof values !== "object" || values === null) return void 0;
|
|
14822
14936
|
const out = {};
|
|
14823
|
-
const
|
|
14824
|
-
const wanted = asked ?? (
|
|
14937
|
+
const keys2 = Object.keys(values);
|
|
14938
|
+
const wanted = asked ?? (keys2.length === 1 && SESSION_PLACEHOLDER.test(keys2[0]) ? keys2 : []);
|
|
14825
14939
|
for (const ph of wanted) {
|
|
14826
14940
|
const value2 = values[ph];
|
|
14827
14941
|
if (typeof value2 === "string" && value2.length > 0 && !UNSAFE.test(value2)) out[ph] = value2;
|
|
@@ -14845,7 +14959,9 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
|
|
|
14845
14959
|
"request",
|
|
14846
14960
|
"status",
|
|
14847
14961
|
"cancel",
|
|
14848
|
-
"materialize"
|
|
14962
|
+
"materialize",
|
|
14963
|
+
"capabilities",
|
|
14964
|
+
...SEALED_OAUTH_OPS
|
|
14849
14965
|
]);
|
|
14850
14966
|
function tagFields(tags) {
|
|
14851
14967
|
const [tag] = tags ?? [];
|
|
@@ -14880,6 +14996,7 @@ function parseSealed(bytes) {
|
|
|
14880
14996
|
throw new KernelError("EINVAL");
|
|
14881
14997
|
if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
|
|
14882
14998
|
if (req.op === "ask" && !askable(req)) throw new KernelError("EINVAL");
|
|
14999
|
+
if (sealedOAuthOperation(req.op)) validateSealedOAuth(req);
|
|
14883
15000
|
return req;
|
|
14884
15001
|
} finally {
|
|
14885
15002
|
bytes.fill(0);
|
|
@@ -14958,7 +15075,7 @@ var SealedChannel = class {
|
|
|
14958
15075
|
const ask = fields.op === "ask";
|
|
14959
15076
|
const timer = setTimeout(
|
|
14960
15077
|
() => control.abort(ask ? "expired" : "timeout"),
|
|
14961
|
-
ask ? this.broker.askTimeoutMs : this.broker.timeoutMs
|
|
15078
|
+
ask ? this.broker.askTimeoutMs : SEALED_OAUTH_OPS.has(fields.op) ? this.broker.oauthTimeoutMs : this.broker.timeoutMs
|
|
14962
15079
|
);
|
|
14963
15080
|
const req = { ...fields, v: 1, op: fields.op, caller };
|
|
14964
15081
|
const asked = ask ? this.asking(fields, caller, requester, control.signal) : void 0;
|
|
@@ -14981,24 +15098,26 @@ var SealedChannel = class {
|
|
|
14981
15098
|
if (!facts) return errorReply("EIO", "the kernel has no record of this process");
|
|
14982
15099
|
return this.answer(askRequest(fields, caller, facts), signal);
|
|
14983
15100
|
}
|
|
14984
|
-
stopped(signal) {
|
|
15101
|
+
stopped(signal, oauth = false) {
|
|
14985
15102
|
if (signal.reason === "timeout")
|
|
14986
|
-
return errorReply("ETIMEDOUT", "the vault did not answer within 10 s");
|
|
15103
|
+
return oauth ? sealedOAuthError("ETIMEDOUT") : errorReply("ETIMEDOUT", "the vault did not answer within 10 s");
|
|
14987
15104
|
return signal.reason === "expired" ? { v: 1, state: "expired" } : void 0;
|
|
14988
15105
|
}
|
|
14989
15106
|
async answer(req, signal) {
|
|
14990
15107
|
const hook = this.broker.hook;
|
|
14991
|
-
|
|
15108
|
+
const oauth = sealedOAuthOperation(req.op);
|
|
15109
|
+
if (!hook)
|
|
15110
|
+
return oauth ? sealedOAuthError("ENOTCONN") : errorReply("ENOTCONN", "no vault is connected");
|
|
14992
15111
|
const stopped = new Promise((resolve2) => {
|
|
14993
15112
|
signal.addEventListener("abort", () => resolve2(void 0), { once: true });
|
|
14994
15113
|
});
|
|
14995
15114
|
if (signal.aborted) return void 0;
|
|
14996
15115
|
const asked = Promise.resolve().then(() => hook(req, { signal })).then(
|
|
14997
|
-
(reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
|
|
14998
|
-
(err) => errorReply("EIO", lineOf3(err))
|
|
15116
|
+
(reply2) => oauth ? sealedOAuthReply(req.op, reply2) : isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
|
|
15117
|
+
(err) => oauth ? sealedOAuthError() : errorReply("EIO", lineOf3(err))
|
|
14999
15118
|
);
|
|
15000
15119
|
const reply = await Promise.race([asked, stopped]);
|
|
15001
|
-
return signal.aborted ? this.stopped(signal) : reply;
|
|
15120
|
+
return signal.aborted ? this.stopped(signal, oauth) : reply;
|
|
15002
15121
|
}
|
|
15003
15122
|
drop() {
|
|
15004
15123
|
this.reply?.fill(0);
|
|
@@ -15061,6 +15180,7 @@ var SealedBroker = class {
|
|
|
15061
15180
|
tickets = new TicketBook();
|
|
15062
15181
|
sessions = new SessionScrubs();
|
|
15063
15182
|
askTimeoutMs = SEALED_ASK_TIMEOUT_MS;
|
|
15183
|
+
oauthTimeoutMs = SEALED_OAUTH_TIMEOUT_MS;
|
|
15064
15184
|
userOf = async () => void 0;
|
|
15065
15185
|
factsOf = async () => void 0;
|
|
15066
15186
|
constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
|
|
@@ -15181,7 +15301,7 @@ var Timestamps = class {
|
|
|
15181
15301
|
// src/sudoers.ts
|
|
15182
15302
|
var SUDOERS = "/etc/sudoers";
|
|
15183
15303
|
var SUDOERS_D = "/etc/sudoers.d";
|
|
15184
|
-
var
|
|
15304
|
+
var NAME3 = /^[a-z_][a-z0-9_.-]*$/;
|
|
15185
15305
|
var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
|
|
15186
15306
|
var KERNEL_FORM = new RegExp(
|
|
15187
15307
|
String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()]*)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(\S+)\s+)?Cmnd(?:\s+(.*))?$`
|
|
@@ -15191,7 +15311,7 @@ var RUNAS = /^\(([^()]*)\)\s*/;
|
|
|
15191
15311
|
var TAG = /^([A-Z_]+)\s*:\s*/;
|
|
15192
15312
|
var OPTION = /^([A-Z_]+)=/;
|
|
15193
15313
|
var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
15194
|
-
var
|
|
15314
|
+
var CONTROL2 = /[\p{Cc}\u2028\u2029]/u;
|
|
15195
15315
|
var CONTROLS = /[\p{Cc}\u2028\u2029]/gu;
|
|
15196
15316
|
var SHOWN_MAX = 60;
|
|
15197
15317
|
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] Cmnd <command\u2026|*>";
|
|
@@ -15200,7 +15320,7 @@ var EXPECTED = `expected '${KERNEL_SYNTAX}' or '${SUDOERS_SYNTAX}'`;
|
|
|
15200
15320
|
var TIMESTAMP_TIMEOUT = 5;
|
|
15201
15321
|
var TIMESTAMP_TYPE = "tty";
|
|
15202
15322
|
function hasControl(text) {
|
|
15203
|
-
return
|
|
15323
|
+
return CONTROL2.test(text);
|
|
15204
15324
|
}
|
|
15205
15325
|
function withoutControl(text) {
|
|
15206
15326
|
return text.replace(CONTROLS, (ch) => {
|
|
@@ -15338,7 +15458,7 @@ function commandText(cmnd) {
|
|
|
15338
15458
|
}
|
|
15339
15459
|
function validWho(who) {
|
|
15340
15460
|
if (who === "ALL") return true;
|
|
15341
|
-
return
|
|
15461
|
+
return NAME3.test(who.startsWith("%") ? who.slice(1) : who);
|
|
15342
15462
|
}
|
|
15343
15463
|
function whoError(who) {
|
|
15344
15464
|
if (validWho(who)) return void 0;
|
|
@@ -15371,7 +15491,7 @@ function kernelRules(m) {
|
|
|
15371
15491
|
const [, nopasswd, who = "", target = "root", dir, stdin, rest2 = ""] = m;
|
|
15372
15492
|
const bad = whoError(who);
|
|
15373
15493
|
if (bad !== void 0) return bad;
|
|
15374
|
-
if (target !== "ALL" && !
|
|
15494
|
+
if (target !== "ALL" && !NAME3.test(target)) return targetError(target);
|
|
15375
15495
|
const cwd = dir === void 0 ? void 0 : cwdOf(dir);
|
|
15376
15496
|
if (cwd === "") return `Cwd expects one absolute directory, not ${shown(dir)}`;
|
|
15377
15497
|
if (stdin !== void 0 && stdin !== "none") return `Stdin takes none, not ${shown(stdin)}`;
|
|
@@ -15395,11 +15515,11 @@ function runasOf(list) {
|
|
|
15395
15515
|
return colon < 0 ? "() names no user to run as: name one, or leave the (\u2026) out for root" : `(${list}) runs as the caller with another group, which the kernel's sudo can't do`;
|
|
15396
15516
|
}
|
|
15397
15517
|
for (const name2 of names2) {
|
|
15398
|
-
if (name2 === "ALL" ||
|
|
15518
|
+
if (name2 === "ALL" || NAME3.test(name2)) continue;
|
|
15399
15519
|
return `expected user names or ALL to run as, not ${shown(name2)} (no %group, #uid or !user)`;
|
|
15400
15520
|
}
|
|
15401
15521
|
for (const group of groups) {
|
|
15402
|
-
if (group === "" || group === "ALL" ||
|
|
15522
|
+
if (group === "" || group === "ALL" || NAME3.test(group)) continue;
|
|
15403
15523
|
return `expected group names or ALL after ':' in (\u2026), not ${shown(group)}`;
|
|
15404
15524
|
}
|
|
15405
15525
|
return names2.includes("ALL") ? ["ALL"] : [...new Set(names2)];
|
|
@@ -15488,7 +15608,7 @@ function directiveError(text) {
|
|
|
15488
15608
|
}
|
|
15489
15609
|
function recordedBare(line, file, rule) {
|
|
15490
15610
|
if (rule.cmnd === "*" || rule.cmnd.args !== void 0 || !rule.nopasswd) return false;
|
|
15491
|
-
if (file !== `${SUDOERS_D}/${rule.who}` || !
|
|
15611
|
+
if (file !== `${SUDOERS_D}/${rule.who}` || !NAME3.test(rule.who) || !NAME3.test(rule.target)) {
|
|
15492
15612
|
return false;
|
|
15493
15613
|
}
|
|
15494
15614
|
const head = `NOPASSWD ${rule.who} (${rule.target}) `;
|
|
@@ -15618,7 +15738,7 @@ var PASSWD = "/etc/passwd";
|
|
|
15618
15738
|
var GROUP = "/etc/group";
|
|
15619
15739
|
var DEFAULT_PASSWD = "root:x:0:0:root:/root:/bin/bash\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n";
|
|
15620
15740
|
var DEFAULT_GROUP = "root:x:0:\ndialout:x:20:\nsudo:x:27:\nusers:x:100:\nnogroup:x:65534:\n";
|
|
15621
|
-
var
|
|
15741
|
+
var NAME4 = /^[a-z_][a-z0-9_.-]*$/;
|
|
15622
15742
|
var MAX_NAME = 255;
|
|
15623
15743
|
var FIRST_UID = 1e3;
|
|
15624
15744
|
var LAST_UID = 6e4;
|
|
@@ -15866,7 +15986,7 @@ var UserDb = class {
|
|
|
15866
15986
|
add(options) {
|
|
15867
15987
|
return this.serial(async () => {
|
|
15868
15988
|
const { name: name2 } = options;
|
|
15869
|
-
if (typeof name2 !== "string" || name2.length > MAX_NAME || !
|
|
15989
|
+
if (typeof name2 !== "string" || name2.length > MAX_NAME || !NAME4.test(name2)) {
|
|
15870
15990
|
throw fsError("EINVAL", `not a user name: ${String(name2)}`);
|
|
15871
15991
|
}
|
|
15872
15992
|
const remember = () => {
|
|
@@ -15920,7 +16040,7 @@ var UserDb = class {
|
|
|
15920
16040
|
}
|
|
15921
16041
|
addGroup(name2, gid) {
|
|
15922
16042
|
return this.serial(async () => {
|
|
15923
|
-
if (typeof name2 !== "string" || name2.length > MAX_NAME || !
|
|
16043
|
+
if (typeof name2 !== "string" || name2.length > MAX_NAME || !NAME4.test(name2)) {
|
|
15924
16044
|
throw fsError("EINVAL", `not a group name: ${String(name2)}`);
|
|
15925
16045
|
}
|
|
15926
16046
|
if (typeof gid !== "number" || !Number.isInteger(gid) || gid < 0 || gid > MAX_GID) {
|
|
@@ -16565,7 +16685,7 @@ ${lines2}
|
|
|
16565
16685
|
async remember(self, target, cwd, argv) {
|
|
16566
16686
|
const { fs } = this.o;
|
|
16567
16687
|
const path = `${SUDOERS_D}/${self.name}`;
|
|
16568
|
-
if (!
|
|
16688
|
+
if (!NAME3.test(self.name)) return false;
|
|
16569
16689
|
try {
|
|
16570
16690
|
await this.provision();
|
|
16571
16691
|
if (!await this.trusted(SUDOERS_D)) return false;
|
|
@@ -17934,8 +18054,8 @@ function sameCert(a, b) {
|
|
|
17934
18054
|
if (!a || !b) return a === b;
|
|
17935
18055
|
return a.cert.length === b.cert.length && a.cert.every((byte, i) => byte === b.cert[i]);
|
|
17936
18056
|
}
|
|
17937
|
-
function winner(current, expected,
|
|
17938
|
-
return sameCert(current, expected) ?
|
|
18057
|
+
function winner(current, expected, record3) {
|
|
18058
|
+
return sameCert(current, expected) ? record3 : current;
|
|
17939
18059
|
}
|
|
17940
18060
|
var DAY = 24 * 60 * 60 * 1e3;
|
|
17941
18061
|
var CA_LIFETIME = 3650 * DAY;
|
|
@@ -17952,14 +18072,14 @@ function validLeafName(host) {
|
|
|
17952
18072
|
}
|
|
17953
18073
|
var RealmCa = class _RealmCa {
|
|
17954
18074
|
record;
|
|
17955
|
-
constructor(
|
|
17956
|
-
this.record =
|
|
18075
|
+
constructor(record3) {
|
|
18076
|
+
this.record = record3;
|
|
17957
18077
|
}
|
|
17958
18078
|
static async open(owner, store, now = Date.now()) {
|
|
17959
18079
|
const stored = await store.get(owner).catch(() => void 0);
|
|
17960
18080
|
if (stored && stored.notAfter - now > CA_RENEW_BEFORE) return new _RealmCa(stored);
|
|
17961
|
-
const
|
|
17962
|
-
return new _RealmCa(await store.swap(owner, stored,
|
|
18081
|
+
const record3 = await _RealmCa.create(owner, now);
|
|
18082
|
+
return new _RealmCa(await store.swap(owner, stored, record3));
|
|
17963
18083
|
}
|
|
17964
18084
|
static async create(owner, now) {
|
|
17965
18085
|
const pair = await crypto.subtle.generateKey(ECDSA, false, ["sign", "verify"]);
|
|
@@ -18185,8 +18305,8 @@ function memoryCaStore() {
|
|
|
18185
18305
|
const records = /* @__PURE__ */ new Map();
|
|
18186
18306
|
return {
|
|
18187
18307
|
get: async (owner) => records.get(owner),
|
|
18188
|
-
swap: async (owner, expected,
|
|
18189
|
-
const kept = winner(records.get(owner), expected,
|
|
18308
|
+
swap: async (owner, expected, record3) => {
|
|
18309
|
+
const kept = winner(records.get(owner), expected, record3);
|
|
18190
18310
|
records.set(owner, kept);
|
|
18191
18311
|
return kept;
|
|
18192
18312
|
}
|
|
@@ -18287,6 +18407,9 @@ function enableNetwork(net, options) {
|
|
|
18287
18407
|
return () => proxy;
|
|
18288
18408
|
}
|
|
18289
18409
|
|
|
18410
|
+
// src/kernel/net/transport.ts
|
|
18411
|
+
var FORBIDDEN_HEADER = "forbidden-header";
|
|
18412
|
+
|
|
18290
18413
|
// src/kernel/net/ws-syscalls.ts
|
|
18291
18414
|
var WS_OPS = [
|
|
18292
18415
|
"net-ws-open",
|
|
@@ -18586,6 +18709,9 @@ var HttpHandles = class {
|
|
|
18586
18709
|
if (e instanceof PlaceholderRefused) {
|
|
18587
18710
|
return { ok: false, errno: e.code, message: text, detail: "placeholder" };
|
|
18588
18711
|
}
|
|
18712
|
+
if (e.detail === FORBIDDEN_HEADER) {
|
|
18713
|
+
return { ok: false, errno: "ENOTSUP", message: text, detail: FORBIDDEN_HEADER };
|
|
18714
|
+
}
|
|
18589
18715
|
const status = e.status;
|
|
18590
18716
|
if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
|
|
18591
18717
|
const code = codeOf2(e);
|
|
@@ -26755,7 +26881,7 @@ function header2(id2, flags2, counts) {
|
|
|
26755
26881
|
function opt() {
|
|
26756
26882
|
return [0, 0, TYPE_OPT, UDP_EDNS >> 8, UDP_EDNS & 255, 0, 0, 0, 0, 0, 0];
|
|
26757
26883
|
}
|
|
26758
|
-
function
|
|
26884
|
+
function record2(type, rdata) {
|
|
26759
26885
|
const ttl = DNS_STUB_TTL;
|
|
26760
26886
|
return [
|
|
26761
26887
|
192,
|
|
@@ -26863,14 +26989,14 @@ var DnsStub = class {
|
|
|
26863
26989
|
const address = reverseAddress(q.name);
|
|
26864
26990
|
const name2 = address !== void 0 ? await this.reverse(address) : void 0;
|
|
26865
26991
|
if (name2 !== void 0)
|
|
26866
|
-
return answerReply(query, q, NOERROR, [
|
|
26992
|
+
return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
|
|
26867
26993
|
return this.forward(query, q);
|
|
26868
26994
|
}
|
|
26869
26995
|
if (q.type === TYPE_A || q.type === TYPE_AAAA) {
|
|
26870
26996
|
const addresses = q.name === "" ? [] : await this.lookup(q.name);
|
|
26871
26997
|
const answers2 = addresses.flatMap((a) => {
|
|
26872
26998
|
const data = addressData(a, q.type);
|
|
26873
|
-
return data ? [
|
|
26999
|
+
return data ? [record2(q.type, data)] : [];
|
|
26874
27000
|
});
|
|
26875
27001
|
if (answers2.length > 0 || local(q.name) || await this.known(q.name)) {
|
|
26876
27002
|
const rcode = addresses.length > 0 ? NOERROR : NXDOMAIN;
|
|
@@ -27059,7 +27185,7 @@ var TransportError = class extends Error {
|
|
|
27059
27185
|
super(message3);
|
|
27060
27186
|
this.status = status;
|
|
27061
27187
|
this.code = code;
|
|
27062
|
-
this.detail = detail === "transport" ? detail : void 0;
|
|
27188
|
+
this.detail = detail === "transport" || detail === FORBIDDEN_HEADER ? detail : void 0;
|
|
27063
27189
|
}
|
|
27064
27190
|
};
|
|
27065
27191
|
function unfollowed2(redirect) {
|
|
@@ -30001,8 +30127,8 @@ var OpfsFs = class {
|
|
|
30001
30127
|
renamingDirs = /* @__PURE__ */ new Set();
|
|
30002
30128
|
intents = 0;
|
|
30003
30129
|
async intend(rename, at = `${RENAMES2}/${crypto.randomUUID()}`) {
|
|
30004
|
-
const
|
|
30005
|
-
await this.meta.update(at, () => ({ path: at, rename:
|
|
30130
|
+
const record3 = { ...rename, at: Date.now() * 1e3 + this.intents++ % 1e3 };
|
|
30131
|
+
await this.meta.update(at, () => ({ path: at, rename: record3 }));
|
|
30006
30132
|
return at;
|
|
30007
30133
|
}
|
|
30008
30134
|
async rollBack(from, to, intents) {
|
|
@@ -33413,8 +33539,8 @@ var Launcher = class {
|
|
|
33413
33539
|
const caller = this.jobs.shown(ppid);
|
|
33414
33540
|
const named = typeof req.argv0 === "string" && !planned.script && !planned.target.fixed;
|
|
33415
33541
|
const argv = named ? [req.argv0, ...req.argv.slice(1)] : req.argv;
|
|
33416
|
-
const
|
|
33417
|
-
if (!
|
|
33542
|
+
const exact2 = booked?.pid === caller && req.file === booked.path && await physicalPathAsync(this.base, req.cwd) === booked.cwd && await argvDigest(argv) === booked.argv;
|
|
33543
|
+
if (!exact2) throw new SpawnError("EACCES");
|
|
33418
33544
|
const { requested: path, requestedCwd: cwd, path: resolved2 } = booked;
|
|
33419
33545
|
const pid = this.nextPid++;
|
|
33420
33546
|
const ids = { pid: req.exec ? caller : pid, ppid: caller };
|
|
@@ -34777,9 +34903,9 @@ var Launcher = class {
|
|
|
34777
34903
|
}
|
|
34778
34904
|
pnpmReady() {
|
|
34779
34905
|
if (this.pnpmMade !== void 0) return this.pnpmMade;
|
|
34780
|
-
const
|
|
34906
|
+
const epoch2 = this.pnpmEpoch;
|
|
34781
34907
|
const made2 = this.base.mkdir(this.pnpmHome, { recursive: true }).then(() => this.owners ? this.stampLayout() : void 0).catch(() => {
|
|
34782
|
-
if (this.pnpmEpoch ===
|
|
34908
|
+
if (this.pnpmEpoch === epoch2) this.pnpmMade = void 0;
|
|
34783
34909
|
});
|
|
34784
34910
|
this.pnpmMade = made2;
|
|
34785
34911
|
return made2;
|
|
@@ -35480,6 +35606,44 @@ function sentHeaders(headers, inPage) {
|
|
|
35480
35606
|
if (inPage) out.delete("user-agent");
|
|
35481
35607
|
return out;
|
|
35482
35608
|
}
|
|
35609
|
+
var TRANSPORT_OWN = /* @__PURE__ */ new Set([
|
|
35610
|
+
"accept-encoding",
|
|
35611
|
+
"connection",
|
|
35612
|
+
"content-length",
|
|
35613
|
+
"expect",
|
|
35614
|
+
"host",
|
|
35615
|
+
"keep-alive",
|
|
35616
|
+
"te",
|
|
35617
|
+
"trailer",
|
|
35618
|
+
"transfer-encoding",
|
|
35619
|
+
"upgrade"
|
|
35620
|
+
]);
|
|
35621
|
+
var PROBE = "http://forbidden-headers.invalid/";
|
|
35622
|
+
var filters;
|
|
35623
|
+
function platformFilters() {
|
|
35624
|
+
filters ??= !new Request(PROBE, { headers: [["cookie", "probe=1"]] }).headers.has("cookie");
|
|
35625
|
+
return filters;
|
|
35626
|
+
}
|
|
35627
|
+
function unsendableHeaders(url, names2, hint) {
|
|
35628
|
+
const which = names2.length > 1 ? "headers" : "header";
|
|
35629
|
+
return Object.assign(
|
|
35630
|
+
new Error(
|
|
35631
|
+
`fetch ${url} refused: the page's fetch can't send the request ${which} ${names2.join(", ")}, so it was not sent${hint ? `: ${hint}` : ""}`
|
|
35632
|
+
),
|
|
35633
|
+
{ status: 502, code: "ENOTSUP", detail: FORBIDDEN_HEADER, headers: [...names2] }
|
|
35634
|
+
);
|
|
35635
|
+
}
|
|
35636
|
+
function refuseUnsendable(url, out, hint) {
|
|
35637
|
+
if (!platformFilters()) return;
|
|
35638
|
+
const kept = new Request(PROBE, { headers: out }).headers;
|
|
35639
|
+
const refused4 = [];
|
|
35640
|
+
for (const name2 of new Set(out.keys())) {
|
|
35641
|
+
if (kept.has(name2)) continue;
|
|
35642
|
+
if (TRANSPORT_OWN.has(name2)) out.delete(name2);
|
|
35643
|
+
else refused4.push(name2);
|
|
35644
|
+
}
|
|
35645
|
+
if (refused4.length > 0) throw unsendableHeaders(url, refused4, hint);
|
|
35646
|
+
}
|
|
35483
35647
|
function streamOf(body) {
|
|
35484
35648
|
const it = body[Symbol.asyncIterator]();
|
|
35485
35649
|
return new ReadableStream(
|
|
@@ -35508,6 +35672,7 @@ function fetchTransport(options = {}) {
|
|
|
35508
35672
|
const headers = options.webSocketHeaders === true;
|
|
35509
35673
|
const instance = options.redirect ?? "follow";
|
|
35510
35674
|
const inPage = !options.fetch && "location" in globalThis;
|
|
35675
|
+
const refuse2 = (options.forbiddenHeaders ?? (options.fetch ? "send" : "refuse")) === "refuse";
|
|
35511
35676
|
return {
|
|
35512
35677
|
traits: {
|
|
35513
35678
|
manualRedirects: instance === "manual",
|
|
@@ -35520,11 +35685,13 @@ function fetchTransport(options = {}) {
|
|
|
35520
35685
|
},
|
|
35521
35686
|
...Socket ? { websocket: (request) => openWebSocket(Socket, request, headers) } : {},
|
|
35522
35687
|
async fetch(request) {
|
|
35688
|
+
const sent = sentHeaders(request.headers, inPage);
|
|
35689
|
+
if (refuse2) refuseUnsendable(request.url, sent, options.hint);
|
|
35523
35690
|
let response;
|
|
35524
35691
|
try {
|
|
35525
35692
|
response = await send2(request.url, {
|
|
35526
35693
|
method: request.method,
|
|
35527
|
-
headers:
|
|
35694
|
+
headers: sent,
|
|
35528
35695
|
...requestBody(request),
|
|
35529
35696
|
signal: request.signal,
|
|
35530
35697
|
redirect: request.redirect ?? instance,
|
|
@@ -35625,7 +35792,7 @@ function serveTransport(peer, transport, {
|
|
|
35625
35792
|
message: e instanceof Error ? e.message : String(e),
|
|
35626
35793
|
...typeof status === "number" ? { status } : {},
|
|
35627
35794
|
...typeof code === "string" ? { code } : {},
|
|
35628
|
-
...detail === "transport" ? { detail } : {}
|
|
35795
|
+
...detail === "transport" || detail === FORBIDDEN_HEADER ? { detail } : {}
|
|
35629
35796
|
});
|
|
35630
35797
|
};
|
|
35631
35798
|
const uploads = uploadsFor(peer);
|