[object Object]

← back to Dw Marketing Reels

chore: harden /api/youtube (escapeAttr href, strict video-id regex), v0.9.1 (session close)

69537a313ef297247dde2c02bec9b5abeeb69fce · 2026-08-25 10:55:58 -0700 · steve

Files touched

Diff

commit 69537a313ef297247dde2c02bec9b5abeeb69fce
Author: steve <steve@designerwallcoverings.com>
Date:   Tue Aug 25 10:55:58 2026 -0700

    chore: harden /api/youtube (escapeAttr href, strict video-id regex), v0.9.1 (session close)
---
 package-lock.json | 4 ++--
 package.json      | 2 +-
 public/index.html | 2 +-
 server.js         | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 6c141e5..4aefc16 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "dw-marketing-reels",
-  "version": "0.9.0",
+  "version": "0.9.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "dw-marketing-reels",
-      "version": "0.9.0"
+      "version": "0.9.1"
     }
   }
 }
diff --git a/package.json b/package.json
index 2489f36..6aa016b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "dw-marketing-reels",
-  "version": "0.9.0",
+  "version": "0.9.1",
   "private": true,
   "type": "module",
   "description": "DW Marketing — auto-generated New Arrivals reels (HyperFrames) + auth-gated console",
diff --git a/public/index.html b/public/index.html
index b5e1027..6b9d986 100644
--- a/public/index.html
+++ b/public/index.html
@@ -417,7 +417,7 @@ $('#videos').addEventListener('click', async e => {
       body: JSON.stringify({ path: b.dataset.path }) })).json();
     if (j.ok) {
       b.textContent = '✓ On YouTube';
-      if (out) out.innerHTML = `✓ Uploaded <b>unlisted</b> · <a href="${j.watch}" target="_blank" rel="noopener">${escapeHtml(j.watch)}</a> — flip to Public in YouTube Studio.`;
+      if (out) out.innerHTML = `✓ Uploaded <b>unlisted</b> · <a href="${escapeAttr(j.watch)}" target="_blank" rel="noopener">${escapeHtml(j.watch)}</a> — flip to Public in YouTube Studio.`;
     } else {
       b.disabled = false; b.textContent = label;
       if (out) out.textContent = '✗ ' + (j.error || 'upload failed');
diff --git a/server.js b/server.js
index d1abec0..5573b08 100644
--- a/server.js
+++ b/server.js
@@ -322,7 +322,7 @@ const server = http.createServer(async (req, res) => {
           ch.on('close', code => resolve({ code, out }));
         });
         building = false; lastLog += `\n[youtube exit ${result.code}]`;
-        const m = /UPLOAD_OK\s+(\S+)/.exec(result.out);
+        const m = /UPLOAD_OK\s+([A-Za-z0-9_-]{6,32})/.exec(result.out);
         if (result.code === 0 && m) return res.writeHead(200, { 'content-type': 'application/json' })
           .end(JSON.stringify({ ok: true, id: m[1], watch: 'https://youtu.be/' + m[1], privacy: 'unlisted' }));
         return res.writeHead(502, { 'content-type': 'application/json' })

← b778dff publish-ig.mjs: resolve per-account ig_user_id (--account /  ·  back to Dw Marketing Reels  ·  5x: verify accounts console clean twice (35 ready/5 pending) 1e97884 →