@tokenoftrust/cli 3.0.6 → 3.0.7

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/validate.mjs +35 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/cli",
3
- "version": "3.0.6",
3
+ "version": "3.0.7",
4
4
  "description": "Token of Trust developer CLI — clone a tenant store, run it locally with save→reload, and submit it for preview. Installs the `tot` command.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Token of Trust",
package/src/validate.mjs CHANGED
@@ -459,6 +459,39 @@ export function isSafeRehostedAssetPathForTenant(value, tenantId) {
459
459
  return value.startsWith("/shared/") || value.startsWith(`/tenants/${tenantId}/`);
460
460
  }
461
461
 
462
+ export function isPlatformPoolImagePath(value) {
463
+ return typeof value === "string" && /^\/_a\/[0-9a-f]{64}(?:\.[a-z0-9]{1,8})?$/.test(value);
464
+ }
465
+
466
+ const IMAGE_SOURCE_DNS_NAME = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i;
467
+ const IMAGE_SOURCE_BLOCKED_NAME = /(?:^|\.)(?:localhost|localdomain|local|internal|intranet|lan|home|arpa)$/i;
468
+ const IMAGE_SOURCE_IPV4 = /^\d{1,3}(?:\.\d{1,3}){3}$/;
469
+
470
+ export function imageSourceUrlRefusal(value) {
471
+ if (typeof value !== "string" || value.length === 0 || value.length > 2048) return "unsupported";
472
+ let url;
473
+ try { url = new URL(value); } catch { return "unsupported"; }
474
+ if (url.protocol !== "https:") return "unsafe-scheme";
475
+ if (url.username || url.password) return "userinfo";
476
+ if (url.port) return "nonstandard-port";
477
+ const hostname = url.hostname;
478
+ if (!hostname || hostname.includes("%")) return "blocked-address";
479
+ if (IMAGE_SOURCE_IPV4.test(hostname)) return "ip-literal";
480
+ const name = hostname.endsWith(".") ? hostname.slice(0, -1) : hostname;
481
+ if (IMAGE_SOURCE_BLOCKED_NAME.test(name) || !IMAGE_SOURCE_DNS_NAME.test(hostname)) return "blocked-address";
482
+ return null;
483
+ }
484
+
485
+ export function isPageBlockImageReference(value, tenantId) {
486
+ if (typeof value !== "string") return false;
487
+ if (tenantId === undefined || tenantId === null
488
+ ? isSafeRehostedAssetPath(value)
489
+ : isSafeRehostedAssetPathForTenant(value, tenantId)) return true;
490
+ if (isPlatformPoolImagePath(value)) return true;
491
+ if (!/^https:\/\//i.test(value) || /[\u0000-\u0020\u007f"'<>\\`]/.test(value)) return false;
492
+ return imageSourceUrlRefusal(value) === null;
493
+ }
494
+
462
495
  export function isSafeStorefrontHref(value) {
463
496
  if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//")) return false;
464
497
  if (value.includes("\\") || value.includes("?") || value.includes("#") || /[\u0000-\u001f\u007f]/.test(value)) return false;
@@ -509,11 +542,8 @@ function validateProductSource(value, file, at) {
509
542
  }
510
543
 
511
544
  function validateVisualAsset(value, file, at, out, tenantId) {
512
- const safe = tenantId
513
- ? isSafeRehostedAssetPathForTenant(value, tenantId)
514
- : isSafeRehostedAssetPath(value);
515
- if (!safe) {
516
- out.push(mk(ERROR, "asset-path", file, `${at} must be a /shared/... asset or belong to the active tenant`));
545
+ if (!isPageBlockImageReference(value, tenantId || undefined)) {
546
+ out.push(mk(ERROR, "asset-path", file, `${at} must be a /shared/... or active-tenant asset, a /_a/<sha256> pool reference, or an https source URL on a public host (captured into the pool at preview and publish)`));
517
547
  }
518
548
  }
519
549