@xnetjs/core 0.0.3 → 0.1.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/dist/index.d.ts +58 -1
- package/dist/index.js +105 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -807,6 +807,63 @@ interface PolicyEvaluator {
|
|
|
807
807
|
invalidateSubject(did: DID$1): void;
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
+
/**
|
|
811
|
+
* Small, dependency-free numeric helpers.
|
|
812
|
+
*
|
|
813
|
+
* These were independently re-implemented across many packages; this is the
|
|
814
|
+
* single canonical home for the behaviour-identical variants. Specialized
|
|
815
|
+
* clampers (`clampRatio`, `clampLimit`, `clampInteger`, …) intentionally stay
|
|
816
|
+
* local to their callers — they encode caller-specific fallbacks and bounds.
|
|
817
|
+
*/
|
|
818
|
+
/** Clamp `value` into the inclusive `[min, max]` range. */
|
|
819
|
+
declare function clamp(value: number, min: number, max: number): number;
|
|
820
|
+
/** Clamp `value` into the inclusive `[0, 1]` range. */
|
|
821
|
+
declare function clamp01(value: number): number;
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Small, dependency-free formatting helpers shared across packages.
|
|
825
|
+
*/
|
|
826
|
+
/**
|
|
827
|
+
* Format a byte count into a human-readable string using binary (1024) units.
|
|
828
|
+
*
|
|
829
|
+
* Canonical replacement for the several `formatBytes`/`formatFileSize` copies
|
|
830
|
+
* that diverged across packages — notably ones that silently capped at MB and
|
|
831
|
+
* mis-reported gigabyte-scale sizes. Scales all the way to PB and keeps one
|
|
832
|
+
* decimal place above the byte unit (e.g. `1536` → `"1.5 KB"`, `0` → `"0 B"`).
|
|
833
|
+
*/
|
|
834
|
+
declare function formatBytes(bytes: number): string;
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* SSRF guard for server-side / outbound requests.
|
|
838
|
+
*
|
|
839
|
+
* A *literal-host* guard (scheme + hostname/IP inspection), not a
|
|
840
|
+
* post-DNS-resolution guard: a hostname that resolves to a private IP at
|
|
841
|
+
* request time is not caught here — that deeper check belongs in the fetch
|
|
842
|
+
* implementation. This closes the common, cheap holes by construction:
|
|
843
|
+
* non-http(s) schemes, `localhost`, `.local`/`.internal` suffixes, the cloud
|
|
844
|
+
* metadata host, and private/loopback/link-local IP literals (v4 and v6).
|
|
845
|
+
*
|
|
846
|
+
* This is the canonical implementation; `@xnetjs/hub` and `@xnetjs/plugins`
|
|
847
|
+
* both delegate here so the IP-range logic lives in exactly one place.
|
|
848
|
+
*/
|
|
849
|
+
declare class SsrfError extends Error {
|
|
850
|
+
readonly url: string;
|
|
851
|
+
constructor(message: string, url: string);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Throw {@link SsrfError} unless `rawUrl` is a plausibly-public HTTP(S)
|
|
855
|
+
* endpoint.
|
|
856
|
+
*/
|
|
857
|
+
declare function assertPublicUrl(rawUrl: string): void;
|
|
858
|
+
/**
|
|
859
|
+
* Non-throwing variant of {@link assertPublicUrl}: returns `{ valid: true }`
|
|
860
|
+
* for a public HTTP(S) URL, or `{ valid: false, error }` otherwise.
|
|
861
|
+
*/
|
|
862
|
+
declare function validateExternalUrl(rawUrl: string): {
|
|
863
|
+
valid: boolean;
|
|
864
|
+
error?: string;
|
|
865
|
+
};
|
|
866
|
+
|
|
810
867
|
/**
|
|
811
868
|
* @xnetjs/core - Core types, schemas, and content addressing
|
|
812
869
|
*/
|
|
@@ -814,4 +871,4 @@ interface PolicyEvaluator {
|
|
|
814
871
|
type DID = `did:key:${string}`;
|
|
815
872
|
type DocumentPath = `xnet://${DID}/workspace/${string}/doc/${string}`;
|
|
816
873
|
|
|
817
|
-
export { ALL_CAPABILITIES, AUTH_ACTIONS, type ActionKey, type AllowExpr, type AndExpr, type AuthAction, type AuthCheckInput, type AuthDecision, type AuthDenyReason, type AuthExpression, type AuthTrace, type AuthTraceStep, type AuthenticatedExpr, type AuthorizationDefinition, BOOTSTRAP_PEERS, type Capability, type ChainStatus, type Condition, type ContentChunk, type ContentId, type ContentResolver, type ContentTree, type CreatorRoleResolver, DEFAULT_SNAPSHOT_TRIGGERS, DEFAULT_STREAMING_OPTIONS, DHT_CONFIG, type DID, type DIDResolution, type DIDResolver, type DataSource, type DenyExpr, type DocumentLoad, type DocumentPath, type Fork, type Group, type IPCondition, type MembershipRoleResolver, type MerkleNode, type NotExpr, type OrExpr, type PeerLocation, type PermissionEvaluator, type PermissionGrant, type PolicyEvaluator, type PropertyRoleResolver, type PublicExpr, type Query, type QueryPlan, type QueryRequest, type QueryResponse, type QueryRouter, RESOLUTION_CACHE_CONFIG, type RelationRoleResolver, type ResolutionStrategy, type ResourceScope, type Role, type RoleKey, type RoleRefExpr, type RoleResolver, STANDARD_ROLES, type SchemaAction, type SerializedAuthExpression, type SerializedAuthorization, type SerializedRoleResolver, type SignedUpdate, type Snapshot, type SnapshotTriggers, type StreamingQueryOptions, type SubQuery, type TimeCondition, type UpdateVerifier, type VectorClock, buildMerkleTree, compareVectorClocks, createChunk, createContentId, deduplicatedUnion, detectFork, estimateQueryCost, evaluateCondition, getMostPermissiveCapability, hashContent, incrementVectorClock, isLocationFresh, isValidDID, isValidProgression, mergeStateVectors, mergeVectorClocks, parseContentId, parseDID, roleHasCapability, shouldCreateSnapshot, unionAggregate, verifyContent, verifyUpdateChain };
|
|
874
|
+
export { ALL_CAPABILITIES, AUTH_ACTIONS, type ActionKey, type AllowExpr, type AndExpr, type AuthAction, type AuthCheckInput, type AuthDecision, type AuthDenyReason, type AuthExpression, type AuthTrace, type AuthTraceStep, type AuthenticatedExpr, type AuthorizationDefinition, BOOTSTRAP_PEERS, type Capability, type ChainStatus, type Condition, type ContentChunk, type ContentId, type ContentResolver, type ContentTree, type CreatorRoleResolver, DEFAULT_SNAPSHOT_TRIGGERS, DEFAULT_STREAMING_OPTIONS, DHT_CONFIG, type DID, type DIDResolution, type DIDResolver, type DataSource, type DenyExpr, type DocumentLoad, type DocumentPath, type Fork, type Group, type IPCondition, type MembershipRoleResolver, type MerkleNode, type NotExpr, type OrExpr, type PeerLocation, type PermissionEvaluator, type PermissionGrant, type PolicyEvaluator, type PropertyRoleResolver, type PublicExpr, type Query, type QueryPlan, type QueryRequest, type QueryResponse, type QueryRouter, RESOLUTION_CACHE_CONFIG, type RelationRoleResolver, type ResolutionStrategy, type ResourceScope, type Role, type RoleKey, type RoleRefExpr, type RoleResolver, STANDARD_ROLES, type SchemaAction, type SerializedAuthExpression, type SerializedAuthorization, type SerializedRoleResolver, type SignedUpdate, type Snapshot, type SnapshotTriggers, SsrfError, type StreamingQueryOptions, type SubQuery, type TimeCondition, type UpdateVerifier, type VectorClock, assertPublicUrl, buildMerkleTree, clamp, clamp01, compareVectorClocks, createChunk, createContentId, deduplicatedUnion, detectFork, estimateQueryCost, evaluateCondition, formatBytes, getMostPermissiveCapability, hashContent, incrementVectorClock, isLocationFresh, isValidDID, isValidProgression, mergeStateVectors, mergeVectorClocks, parseContentId, parseDID, roleHasCapability, shouldCreateSnapshot, unionAggregate, validateExternalUrl, verifyContent, verifyUpdateChain };
|
package/dist/index.js
CHANGED
|
@@ -308,6 +308,105 @@ function getMostPermissiveCapability(capabilities) {
|
|
|
308
308
|
|
|
309
309
|
// src/auth-types.ts
|
|
310
310
|
var AUTH_ACTIONS = ["read", "write", "delete", "share", "admin"];
|
|
311
|
+
|
|
312
|
+
// src/utils/math.ts
|
|
313
|
+
function clamp(value, min, max) {
|
|
314
|
+
if (value < min) return min;
|
|
315
|
+
if (value > max) return max;
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
function clamp01(value) {
|
|
319
|
+
return clamp(value, 0, 1);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/utils/format.ts
|
|
323
|
+
var BYTE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
324
|
+
function formatBytes(bytes) {
|
|
325
|
+
if (!Number.isFinite(bytes)) return "\u2014";
|
|
326
|
+
const negative = bytes < 0;
|
|
327
|
+
let value = Math.abs(bytes);
|
|
328
|
+
if (value < 1024) return `${negative ? "-" : ""}${value} B`;
|
|
329
|
+
let unitIndex = 0;
|
|
330
|
+
while (value >= 1024 && unitIndex < BYTE_UNITS.length - 1) {
|
|
331
|
+
value /= 1024;
|
|
332
|
+
unitIndex += 1;
|
|
333
|
+
}
|
|
334
|
+
return `${negative ? "-" : ""}${value.toFixed(1)} ${BYTE_UNITS[unitIndex]}`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/utils/ssrf.ts
|
|
338
|
+
var SsrfError = class extends Error {
|
|
339
|
+
constructor(message, url) {
|
|
340
|
+
super(message);
|
|
341
|
+
this.url = url;
|
|
342
|
+
this.name = "SsrfError";
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
function parseIpv4(host) {
|
|
346
|
+
const parts = host.split(".");
|
|
347
|
+
if (parts.length !== 4) return null;
|
|
348
|
+
let value = 0;
|
|
349
|
+
for (const part of parts) {
|
|
350
|
+
if (!/^\d{1,3}$/.test(part)) return null;
|
|
351
|
+
const octet = Number(part);
|
|
352
|
+
if (octet > 255) return null;
|
|
353
|
+
value = value * 256 + octet;
|
|
354
|
+
}
|
|
355
|
+
return value >>> 0;
|
|
356
|
+
}
|
|
357
|
+
function isPrivateIpv4(host) {
|
|
358
|
+
const ip = parseIpv4(host);
|
|
359
|
+
if (ip === null) return false;
|
|
360
|
+
const inRange = (base, bits) => {
|
|
361
|
+
const baseIp = parseIpv4(base);
|
|
362
|
+
const mask = bits === 0 ? 0 : 4294967295 << 32 - bits >>> 0;
|
|
363
|
+
return (ip & mask) === (baseIp & mask);
|
|
364
|
+
};
|
|
365
|
+
return inRange("0.0.0.0", 8) || // "this" network / 0.0.0.0
|
|
366
|
+
inRange("10.0.0.0", 8) || // private
|
|
367
|
+
inRange("127.0.0.0", 8) || // loopback
|
|
368
|
+
inRange("169.254.0.0", 16) || // link-local + cloud metadata (169.254.169.254)
|
|
369
|
+
inRange("172.16.0.0", 12) || // private
|
|
370
|
+
inRange("192.168.0.0", 16) || // private
|
|
371
|
+
inRange("100.64.0.0", 10);
|
|
372
|
+
}
|
|
373
|
+
function isBlockedIpv6(host) {
|
|
374
|
+
const h = host.replace(/^\[|\]$/g, "").toLowerCase();
|
|
375
|
+
if (h === "::1" || h === "::") return true;
|
|
376
|
+
if (h.startsWith("fc") || h.startsWith("fd")) return true;
|
|
377
|
+
if (h.startsWith("::ffff:") || h.startsWith("64:ff9b::")) return true;
|
|
378
|
+
const firstHextet = parseInt(h.split(":")[0] || "0", 16);
|
|
379
|
+
if (Number.isFinite(firstHextet) && (firstHextet & 65472) === 65152) return true;
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "metadata.google.internal"]);
|
|
383
|
+
function assertPublicUrl(rawUrl) {
|
|
384
|
+
let url;
|
|
385
|
+
try {
|
|
386
|
+
url = new URL(rawUrl);
|
|
387
|
+
} catch {
|
|
388
|
+
throw new SsrfError(`URL is not valid: ${rawUrl}`, rawUrl);
|
|
389
|
+
}
|
|
390
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
391
|
+
throw new SsrfError(`URL must be http(s): ${rawUrl}`, rawUrl);
|
|
392
|
+
}
|
|
393
|
+
const host = url.hostname.toLowerCase().replace(/\.$/, "");
|
|
394
|
+
if (host.length === 0) {
|
|
395
|
+
throw new SsrfError(`URL has no hostname: ${rawUrl}`, rawUrl);
|
|
396
|
+
}
|
|
397
|
+
const blocked = BLOCKED_HOSTNAMES.has(host) || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal") || (host.startsWith("[") ? isBlockedIpv6(host) : isPrivateIpv4(host));
|
|
398
|
+
if (blocked) {
|
|
399
|
+
throw new SsrfError(`URL targets a non-public host: ${host}`, rawUrl);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function validateExternalUrl(rawUrl) {
|
|
403
|
+
try {
|
|
404
|
+
assertPublicUrl(rawUrl);
|
|
405
|
+
return { valid: true };
|
|
406
|
+
} catch (error) {
|
|
407
|
+
return { valid: false, error: error instanceof Error ? error.message : "Invalid URL" };
|
|
408
|
+
}
|
|
409
|
+
}
|
|
311
410
|
export {
|
|
312
411
|
ALL_CAPABILITIES,
|
|
313
412
|
AUTH_ACTIONS,
|
|
@@ -317,7 +416,11 @@ export {
|
|
|
317
416
|
DHT_CONFIG,
|
|
318
417
|
RESOLUTION_CACHE_CONFIG,
|
|
319
418
|
STANDARD_ROLES,
|
|
419
|
+
SsrfError,
|
|
420
|
+
assertPublicUrl,
|
|
320
421
|
buildMerkleTree,
|
|
422
|
+
clamp,
|
|
423
|
+
clamp01,
|
|
321
424
|
compareVectorClocks,
|
|
322
425
|
createChunk,
|
|
323
426
|
createContentId,
|
|
@@ -325,6 +428,7 @@ export {
|
|
|
325
428
|
detectFork,
|
|
326
429
|
estimateQueryCost,
|
|
327
430
|
evaluateCondition,
|
|
431
|
+
formatBytes,
|
|
328
432
|
getMostPermissiveCapability,
|
|
329
433
|
hashContent,
|
|
330
434
|
incrementVectorClock,
|
|
@@ -338,6 +442,7 @@ export {
|
|
|
338
442
|
roleHasCapability,
|
|
339
443
|
shouldCreateSnapshot,
|
|
340
444
|
unionAggregate,
|
|
445
|
+
validateExternalUrl,
|
|
341
446
|
verifyContent,
|
|
342
447
|
verifyUpdateChain
|
|
343
448
|
};
|