← back to Tk10630 Sku Suffix Canary
leak-assess-accurate.mjs
41 lines
// Accurate private-label leak assessment — validates matches as WHOLE TOKENS
// (handles/titles use hyphen/space separators), eliminating substring false
// positives like versailles/versace for 'versa'. READ-ONLY.
import { readFileSync } from 'node:fs';
import { gql } from './shopify.mjs';
const denylist = JSON.parse(readFileSync(`${process.env.HOME}/.claude/skills/dw-leak-scanner/denylist.json`, 'utf8'));
const NOISY = new Set(['york', 'yorkwall']);
const terms = [...new Set(denylist.terms.map(t => t.term.toLowerCase().split(/\s+/)[0]).filter(t => t.length >= 4 && !NOISY.has(t)))];
const mapsTo = Object.fromEntries(denylist.terms.map(t => [t.term.toLowerCase().split(/\s+/)[0], t.maps_to]));
// whole-token: term bounded by start/end or a non-alphanumeric (hyphen/space)
const tok = term => new RegExp(`(^|[^a-z0-9])${term}([^a-z0-9]|$)`, 'i');
const rows = [];
for (const term of terms) {
const re = tok(term);
// fetch all substring-matching active products (bounded), then validate whole-token
const seen = new Map();
for (const field of ['handle', 'title', 'tag']) {
let cursor = null, guard = 0;
while (guard++ < 20) {
const d = await gql(`query($c:String){ products(first:100, query:"${field}:*${term}* status:ACTIVE", after:$c){ pageInfo{ hasNextPage endCursor } nodes{ id handle title tags images(first:2){ nodes{ altText } } } } }`);
for (const p of d.data.products.nodes) if (!seen.has(p.id)) seen.set(p.id, p);
if (!d.data.products.pageInfo.hasNextPage) break;
cursor = d.data.products.pageInfo.endCursor;
}
}
let h = 0, t = 0, g = 0, alt = 0;
for (const p of seen.values()) {
if (re.test(p.handle || '')) h++;
if (re.test(p.title || '')) t++;
if ((p.tags || []).some(x => re.test(x))) g++;
if (p.images.nodes.some(i => re.test(i.altText || ''))) alt++;
}
if (h || t || g || alt) rows.push({ term, maps_to: mapsTo[term], handle: h, title: t, tag: g, alt, distinct_products: seen.size });
}
rows.sort((a, b) => (b.handle + b.title + b.tag + b.alt) - (a.handle + a.title + a.tag + a.alt));
console.log(JSON.stringify({ terms_checked: terms.length, real_leaking_terms: rows.length, rows }, null, 2));
console.log(`\nREAL active handle leaks (whole-token): ${rows.reduce((a, r) => a + r.handle, 0)}`);