Lucene search
+L

Third-Party Script Inventory

🗓️ 31 Jul 2026 07:47:17Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 4 Views

KittySploit provides a Third-Party Script Inventory script to audit browser script tags.

Refs
Code
from kittysploit import *
import json


class Module(BrowserAuxiliary):

	__info__ = {
		"name": "Third-Party Script Inventory",
		"description": "Inventory script tags, classify first vs third-party, and flag known CDN / tracker hosts",
		"author": "KittySploit Team",
		"browser": Browser.ALL,
		"platform": Platform.ALL,
		"session_type": SessionType.BROWSER,
	}

	include_inline = OptBool(True, "Include inline scripts (size + hash preview)", False)
	include_modules = OptBool(True, "Include modulepreload / importmap hints", False)

	def run(self):
		include_inline = self._to_bool(self.include_inline)
		include_modules = self._to_bool(self.include_modules)

		code_js = f"""
		(function() {{
			const INCLUDE_INLINE = {str(include_inline).lower()};
			const INCLUDE_MODULES = {str(include_modules).lower()};
			const pageOrigin = location.origin;
			const scripts = [];
			const links = [];

			function hostOf(url) {{
				try {{ return new URL(url, location.href).host; }} catch (e) {{ return null; }}
			}}
			function isThird(url) {{
				try {{
					const u = new URL(url, location.href);
					return u.origin !== pageOrigin;
				}} catch (e) {{
					return false;
				}}
			}}

			Array.from(document.scripts || []).forEach(function(s, idx) {{
				const src = s.src || null;
				const item = {{
					index: idx,
					src: src,
					host: src ? hostOf(src) : null,
					third_party: src ? isThird(src) : false,
					async: !!s.async,
					defer: !!s.defer,
					type: s.type || 'text/javascript',
					crossorigin: s.crossOrigin || null,
					integrity: s.integrity || null,
					nonce: s.nonce || null,
					inline: !src
				}};
				if (!src && INCLUDE_INLINE) {{
					const text = s.textContent || '';
					item.inline_length = text.length;
					item.inline_preview = text.slice(0, 120).replace(/\\s+/g, ' ');
				}}
				if (src || INCLUDE_INLINE) {{
					scripts.push(item);
				}}
			}});

			if (INCLUDE_MODULES) {{
				document.querySelectorAll('link[rel=\"modulepreload\"], link[rel=\"preload\"][as=\"script\"], link[rel=\"preconnect\"], link[rel=\"dns-prefetch\"]').forEach(function(l, idx) {{
					const href = l.href || l.getAttribute('href');
					links.push({{
						index: idx,
						rel: l.rel,
						as: l.getAttribute('as'),
						href: href,
						host: href ? hostOf(href) : null,
						third_party: href ? isThird(href) : false
					}});
				}});
				const importMap = document.querySelector('script[type=\"importmap\"]');
				if (importMap) {{
					links.push({{
						index: -1,
						rel: 'importmap',
						href: null,
						inline_length: (importMap.textContent || '').length,
						preview: (importMap.textContent || '').slice(0, 200)
					}});
				}}
			}}

			return JSON.stringify({{
				ok: true,
				origin: pageOrigin,
				page_url: location.href,
				script_count: scripts.length,
				external_count: scripts.filter(function(s) {{ return !!s.src; }}).length,
				third_party_count: scripts.filter(function(s) {{ return s.third_party; }}).length,
				inline_count: scripts.filter(function(s) {{ return s.inline; }}).length,
				scripts: scripts,
				resource_hints: links
			}});
		}})();
		"""

		result = self.send_js_and_wait_for_response(code_js, timeout=15.0)
		if not result:
			print_error("Failed to inventory scripts")
			return False
		if isinstance(result, str) and result.startswith("Error:"):
			print_error(result)
			return False

		try:
			data = json.loads(result)
		except json.JSONDecodeError as exc:
			print_error(f"Failed to parse response: {exc}")
			return False

		known = {
			"googletagmanager.com": "Google Tag Manager",
			"google-analytics.com": "Google Analytics",
			"googleapis.com": "Google APIs / CDN",
			"gstatic.com": "Google static",
			"facebook.net": "Meta / Facebook",
			"connect.facebook.net": "Meta / Facebook",
			"doubleclick.net": "Google Ads",
			"cdn.jsdelivr.net": "jsDelivr",
			"cdnjs.cloudflare.com": "cdnjs",
			"unpkg.com": "unpkg",
			"ajax.cloudflare.com": "Cloudflare",
			"cloudflareinsights.com": "Cloudflare Insights",
			"hotjar.com": "Hotjar",
			"segment.com": "Segment",
			"segment.io": "Segment",
			"sentry.io": "Sentry",
			"newrelic.com": "New Relic",
			"clarity.ms": "Microsoft Clarity",
			"linkedin.com": "LinkedIn",
			"twitter.com": "X / Twitter",
			"x.com": "X / Twitter",
			"shopify.com": "Shopify",
			"stripe.com": "Stripe",
			"paypal.com": "PayPal",
			"intercom.io": "Intercom",
			"crisp.chat": "Crisp",
		}

		def classify_host(host: str):
			if not host:
				return None
			h = host.lower()
			for needle, label in known.items():
				if h == needle or h.endswith("." + needle):
					return label
			return None

		print_info("=" * 60)
		print_info("Third-Party Script Inventory")
		print_info(f"  Page: {data.get('page_url')}")
		print_info(
			f"  Scripts: {data.get('script_count')} "
			f"(external={data.get('external_count')}, "
			f"third-party={data.get('third_party_count')}, "
			f"inline={data.get('inline_count')})"
		)

		third = [s for s in (data.get("scripts") or []) if s.get("third_party")]
		first = [s for s in (data.get("scripts") or []) if s.get("src") and not s.get("third_party")]

		if third:
			print_info("-" * 60)
			print_warning(f"Third-party scripts ({len(third)}):")
			for s in third:
				label = classify_host(s.get("host") or "")
				tag = f" [{label}]" if label else ""
				attrs = []
				if s.get("async"):
					attrs.append("async")
				if s.get("defer"):
					attrs.append("defer")
				if s.get("integrity"):
					attrs.append("sri")
				attr_s = f" ({', '.join(attrs)})" if attrs else ""
				print_info(f"  {s.get('host')}{tag}{attr_s}")
				print_info(f"    {s.get('src')}")

		if first:
			print_info("-" * 60)
			print_status(f"First-party external scripts ({len(first)}):")
			for s in first[:20]:
				print_info(f"  {s.get('src')}")

		hints = data.get("resource_hints") or []
		if hints:
			print_info("-" * 60)
			print_status(f"Resource hints / importmap ({len(hints)}):")
			for h in hints[:20]:
				print_info(f"  [{h.get('rel')}] {h.get('href') or h.get('preview') or ''}")

		# Supply-chain risk hints
		risks = []
		for s in third:
			if not s.get("integrity"):
				host = s.get("host") or ""
				label = classify_host(host)
				if label and any(x in (label or "").lower() for x in ("cdn", "jsdelivr", "unpkg", "cdnjs")):
					risks.append(f"{host} loaded without SRI")
		if risks:
			print_warning("Supply-chain notes:")
			for r in risks[:10]:
				print_info(f"  {r}")

		return True

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

31 Jul 2026 07:47Current
5.8Medium risk
Vulners AI Score5.8
4