@trops/dash-core 0.1.450 → 0.1.451

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.
@@ -74480,6 +74480,101 @@ const ZIP_EXCLUDE_DIRS = new Set([
74480
74480
  "coverage",
74481
74481
  ]);
74482
74482
 
74483
+ // ─── Personal-path scanner ───────────────────────────────────────────────────
74484
+
74485
+ // Text files we scan for personal paths. Binary / huge files are skipped.
74486
+ const SCANNABLE_EXTS = new Set([
74487
+ ".js",
74488
+ ".jsx",
74489
+ ".ts",
74490
+ ".tsx",
74491
+ ".mjs",
74492
+ ".cjs",
74493
+ ".json",
74494
+ ".md",
74495
+ ".html",
74496
+ ".css",
74497
+ ]);
74498
+ const MAX_FINDINGS = 50;
74499
+ const MAX_FILE_SIZE = 2 * 1024 * 1024;
74500
+
74501
+ // Patterns that strongly suggest a personal filesystem path got baked into
74502
+ // source. Conservative by design — we'd rather ask the user than leak
74503
+ // something. Tildes (`~/…`) are NOT flagged because they're ubiquitous in
74504
+ // widget defaults and don't reveal identity.
74505
+ const PERSONAL_PATH_PATTERNS = [
74506
+ // /Users/<username>/... — macOS. Username after /Users is the leak.
74507
+ /\/Users\/[A-Za-z][\w.-]{1,32}\/[\w./ -]+/g,
74508
+ // /home/<username>/... — Linux.
74509
+ /\/home\/[a-z][\w.-]{1,32}\/[\w./ -]+/g,
74510
+ // C:\Users\<username>\... — Windows. Allow both \\ and / separators so
74511
+ // JSON-escaped paths match too.
74512
+ /[Cc]:[\\/]+Users[\\/]+[A-Za-z][\w.-]{1,32}[\\/]+[\w\\/. -]+/g,
74513
+ ];
74514
+
74515
+ /**
74516
+ * Walk a widget package directory and collect any strings that look like
74517
+ * a user's personal filesystem path. Returns an array of
74518
+ * `{ file, line, match, context }` findings, capped at MAX_FINDINGS.
74519
+ *
74520
+ * Applies the same exclude rules as the ZIP builder — we don't want to
74521
+ * warn about paths in files that won't ship anyway.
74522
+ */
74523
+ function scanForPersonalPaths(packagePath) {
74524
+ const findings = [];
74525
+ const walk = (absDir, relDir = "") => {
74526
+ if (findings.length >= MAX_FINDINGS) return;
74527
+ let entries;
74528
+ try {
74529
+ entries = fs.readdirSync(absDir, { withFileTypes: true });
74530
+ } catch {
74531
+ return;
74532
+ }
74533
+ for (const entry of entries) {
74534
+ if (findings.length >= MAX_FINDINGS) break;
74535
+ if (ZIP_EXCLUDE_DIRS.has(entry.name)) continue;
74536
+ if (entry.name.startsWith(".")) continue;
74537
+ const abs = path.join(absDir, entry.name);
74538
+ const rel = relDir ? path.join(relDir, entry.name) : entry.name;
74539
+ if (entry.isDirectory()) {
74540
+ walk(abs, rel);
74541
+ continue;
74542
+ }
74543
+ if (!entry.isFile()) continue;
74544
+ const ext = path.extname(entry.name).toLowerCase();
74545
+ if (!SCANNABLE_EXTS.has(ext)) continue;
74546
+ let content;
74547
+ try {
74548
+ const stat = fs.statSync(abs);
74549
+ if (stat.size > MAX_FILE_SIZE) continue;
74550
+ content = fs.readFileSync(abs, "utf8");
74551
+ } catch {
74552
+ continue;
74553
+ }
74554
+ const lines = content.split(/\r?\n/);
74555
+ for (let i = 0; i < lines.length; i++) {
74556
+ if (findings.length >= MAX_FINDINGS) break;
74557
+ const line = lines[i];
74558
+ for (const pattern of PERSONAL_PATH_PATTERNS) {
74559
+ pattern.lastIndex = 0;
74560
+ const m = pattern.exec(line);
74561
+ if (m) {
74562
+ findings.push({
74563
+ file: rel,
74564
+ line: i + 1,
74565
+ match: m[0],
74566
+ context: line.trim().slice(0, 200),
74567
+ });
74568
+ break; // one finding per line keeps the list digestible
74569
+ }
74570
+ }
74571
+ }
74572
+ }
74573
+ };
74574
+ walk(packagePath);
74575
+ return findings;
74576
+ }
74577
+
74483
74578
  /**
74484
74579
  * Recursively add a directory to a ZIP, skipping excluded dirs + dotfiles.
74485
74580
  */
@@ -74581,6 +74676,24 @@ async function prepareWidgetForPublish$1(appId, packageId, options = {}) {
74581
74676
  const parsedName = parsePackageName(pkgJson.name || "");
74582
74677
  const resolvedScope = options.scope || callerScope;
74583
74678
 
74679
+ // 3.5 Pre-zip privacy scan. Flag any personal filesystem paths baked
74680
+ // into shipped source (e.g. someone edited a `.dash.js`'s
74681
+ // `defaultValue` from `~/Library/...` to `/Users/me/...` to skip
74682
+ // re-entering it every install). We run BEFORE any state mutation
74683
+ // so that a "cancel" on the confirmation dialog leaves the package
74684
+ // exactly as it was — no version bump, no file rewrites.
74685
+ if (!options.confirmPersonalPaths) {
74686
+ const personalPathFindings = scanForPersonalPaths(widget.path);
74687
+ if (personalPathFindings.length > 0) {
74688
+ return {
74689
+ success: false,
74690
+ needsConfirmation: true,
74691
+ reason: "personal-paths",
74692
+ personalPathFindings,
74693
+ };
74694
+ }
74695
+ }
74696
+
74584
74697
  // 4. Compute + persist new version
74585
74698
  const previousVersion = pkgJson.version || "1.0.0";
74586
74699
  const newVersion = resolveNextVersion(previousVersion, options);