← back to Dw Cowtan Recrawl

probe4.js

32 lines

#!/usr/bin/env node
// Sniff the cowtan SPA shell + JS bundles for the new API endpoints (feed-first discovery).
const https = require('https');
function get(u, m = 5) {
  return new Promise((res, rej) => { https.get(u, { timeout: 25000, headers: { 'User-Agent': 'Mozilla/5.0', 'Accept': '*/*' } }, r => {
    if ((r.statusCode == 301 || r.statusCode == 302) && r.headers.location && m > 0) { r.resume(); const loc = r.headers.location.startsWith('http') ? r.headers.location : 'https://designs.cowtan.com' + r.headers.location; return get(loc, m - 1).then(res).catch(rej); }
    let d = ''; r.on('data', c => d += c); r.on('end', () => res({ status: r.statusCode, body: d })); }).on('error', rej).on('timeout', function () { this.destroy(); rej(new Error('timeout')); }); });
}
(async () => {
  const shell = await get('https://designs.cowtan.com/');
  console.log('shell', shell.status, 'len', shell.body.length);
  // script srcs
  const scripts = [...new Set([...shell.body.matchAll(/<script[^>]+src="([^"]+)"/g)].map(m => m[1]))];
  console.log('scripts:', scripts.length); scripts.forEach(s => console.log('  ', s));
  // inline api hints in shell
  const apiHints = [...new Set([...shell.body.matchAll(/["'`](\/(?:api|Search|Design|Product|Catalog)[^"'`\s]*)["'`]/gi)].map(m => m[1]))];
  console.log('shell api-path hints:', apiHints.slice(0, 30).join('  '));
  // fetch each same-origin/relative JS and grep for fetch/axios/api paths
  for (const s of scripts) {
    const url = s.startsWith('http') ? s : (s.startsWith('//') ? 'https:' + s : 'https://designs.cowtan.com' + s);
    if (!/cowtan/.test(url)) { console.log('skip 3rd-party', url); continue; }
    try {
      const js = await get(url);
      const paths = [...new Set([...js.body.matchAll(/["'`](\/(?:api|Search|Design|Product|Catalog|Get|List|Data)[A-Za-z0-9_\/\-?=&.]*)["'`]/g)].map(m => m[1]))];
      const fetches = [...new Set([...js.body.matchAll(/(?:fetch|axios\.\w+|\.get|\.post)\(\s*["'`]([^"'`]+)["'`]/g)].map(m => m[1]))];
      console.log('\nJS', url, 'len', js.body.length);
      console.log('  api-ish paths:', paths.slice(0, 40).join('  ') || '(none)');
      console.log('  fetch/axios urls:', fetches.slice(0, 40).join('  ') || '(none)');
    } catch (e) { console.log('JS', url, 'ERR', e.message); }
  }
})().catch(e => console.error('FATAL', e.message));