@q32/signal-scanner 0.1.0 → 0.1.1
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.
- package/dist/dynamic.d.ts +43 -0
- package/dist/dynamic.d.ts.map +1 -0
- package/{src/dynamic.ts → dist/dynamic.js} +133 -156
- package/dist/dynamic.js.map +1 -0
- package/dist/feeds.d.ts +66 -0
- package/dist/feeds.d.ts.map +1 -0
- package/dist/feeds.js +259 -0
- package/dist/feeds.js.map +1 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1251 -0
- package/dist/index.js.map +1 -0
- package/dist/intel.d.ts +72 -0
- package/dist/intel.d.ts.map +1 -0
- package/dist/intel.js +480 -0
- package/dist/intel.js.map +1 -0
- package/dist/node-tls.d.ts +8 -0
- package/dist/node-tls.d.ts.map +1 -0
- package/dist/node-tls.js +48 -0
- package/dist/node-tls.js.map +1 -0
- package/dist/render.d.ts +26 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +248 -0
- package/dist/render.js.map +1 -0
- package/dist/rules/packs/binary.d.ts +4 -0
- package/dist/rules/packs/binary.d.ts.map +1 -0
- package/dist/rules/packs/binary.js +101 -0
- package/dist/rules/packs/binary.js.map +1 -0
- package/dist/rules/packs/css.d.ts +3 -0
- package/dist/rules/packs/css.d.ts.map +1 -0
- package/dist/rules/packs/css.js +43 -0
- package/dist/rules/packs/css.js.map +1 -0
- package/dist/rules/packs/decoders.d.ts +3 -0
- package/dist/rules/packs/decoders.d.ts.map +1 -0
- package/dist/rules/packs/decoders.js +46 -0
- package/dist/rules/packs/decoders.js.map +1 -0
- package/dist/rules/packs/html.d.ts +4 -0
- package/dist/rules/packs/html.d.ts.map +1 -0
- package/dist/rules/packs/html.js +227 -0
- package/dist/rules/packs/html.js.map +1 -0
- package/dist/rules/packs/index.d.ts +24 -0
- package/dist/rules/packs/index.d.ts.map +1 -0
- package/dist/rules/packs/index.js +75 -0
- package/dist/rules/packs/index.js.map +1 -0
- package/dist/rules/packs/script-risk.d.ts +4 -0
- package/dist/rules/packs/script-risk.d.ts.map +1 -0
- package/dist/rules/packs/script-risk.js +231 -0
- package/dist/rules/packs/script-risk.js.map +1 -0
- package/dist/rules/packs/source-code.d.ts +3 -0
- package/dist/rules/packs/source-code.d.ts.map +1 -0
- package/dist/rules/packs/source-code.js +179 -0
- package/dist/rules/packs/source-code.js.map +1 -0
- package/dist/rules/packs/urls.d.ts +3 -0
- package/dist/rules/packs/urls.d.ts.map +1 -0
- package/dist/rules/packs/urls.js +123 -0
- package/dist/rules/packs/urls.js.map +1 -0
- package/dist/rules/types.d.ts +34 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -0
- package/package.json +18 -14
- package/src/feeds.ts +0 -334
- package/src/index.ts +0 -1366
- package/src/intel.ts +0 -561
- package/src/node-tls.ts +0 -55
- package/src/render.ts +0 -233
- package/src/rules/packs/binary.ts +0 -103
- package/src/rules/packs/css.ts +0 -44
- package/src/rules/packs/decoders.ts +0 -47
- package/src/rules/packs/html.ts +0 -255
- package/src/rules/packs/index.ts +0 -76
- package/src/rules/packs/script-risk.ts +0 -236
- package/src/rules/packs/source-code.ts +0 -180
- package/src/rules/packs/urls.ts +0 -138
- package/src/rules/types.ts +0 -56
package/dist/intel.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
// URL threat-intelligence correlation for the signal scanner.
|
|
2
|
+
//
|
|
3
|
+
// Runtime-agnostic: depends only on the WHATWG `fetch`, `URL`, and standard
|
|
4
|
+
// timers, so it runs unchanged in Node and in Cloudflare Workers. Everything
|
|
5
|
+
// environment-specific (the fetch implementation, API keys, bounds) is injected
|
|
6
|
+
// through `UrlIntelConfig` — there are no `process.env` or node imports here.
|
|
7
|
+
//
|
|
8
|
+
// It takes the URLs/hosts a scan discovered, correlates them against open and
|
|
9
|
+
// keyed reputation sources, and returns both normalized scanner `Finding`s and a
|
|
10
|
+
// per-source result so callers can show which feeds ran, matched, or failed.
|
|
11
|
+
import { matchCachedFeeds } from "./feeds.js";
|
|
12
|
+
// Live API hits are treated as strong evidence; cached feeds carry their own band.
|
|
13
|
+
const LIVE_INTEL_SCORE = 95;
|
|
14
|
+
const DEFAULT_MAX_HOSTS = 100;
|
|
15
|
+
const DEFAULT_MAX_URLS = 100;
|
|
16
|
+
const DEFAULT_TIMEOUT_MS = 4000;
|
|
17
|
+
const DEFAULT_USER_AGENT = "q32-signal-scanner/0.1";
|
|
18
|
+
/**
|
|
19
|
+
* Correlate discovered URLs/hosts against threat-intelligence sources.
|
|
20
|
+
* Never throws for individual source failures — a failed source is reported
|
|
21
|
+
* with `status: "error"` so callers can surface it instead of treating a feed
|
|
22
|
+
* outage as a clean result.
|
|
23
|
+
*/
|
|
24
|
+
export async function checkUrlIntel(input, config = {}) {
|
|
25
|
+
const maxHosts = config.maxHosts ?? DEFAULT_MAX_HOSTS;
|
|
26
|
+
const maxUrls = config.maxUrls ?? DEFAULT_MAX_URLS;
|
|
27
|
+
const urls = dedupe(input.urls ?? []).slice(0, maxUrls);
|
|
28
|
+
const hosts = dedupe([...(input.hosts ?? []), ...hostsFromUrls(urls)]).slice(0, maxHosts);
|
|
29
|
+
if (config.disabled) {
|
|
30
|
+
return { sources: [], matches: [], findings: [] };
|
|
31
|
+
}
|
|
32
|
+
const ctx = {
|
|
33
|
+
// Must be bound to globalThis: calling it as ctx.fetch(...) otherwise makes
|
|
34
|
+
// `this` the context object, which Cloudflare rejects (Illegal Invocation).
|
|
35
|
+
fetch: config.fetchImpl ?? globalThis.fetch.bind(globalThis),
|
|
36
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
37
|
+
userAgent: config.userAgent ?? DEFAULT_USER_AGENT,
|
|
38
|
+
googleSafeBrowsingKey: config.googleSafeBrowsingKey,
|
|
39
|
+
abuseChAuthKey: config.abuseChAuthKey,
|
|
40
|
+
storage: config.storage
|
|
41
|
+
};
|
|
42
|
+
// The cached-feed source only joins when storage is wired — we don't list a
|
|
43
|
+
// source we can't query.
|
|
44
|
+
const activeSources = ctx.storage ? [...INTEL_SOURCES, CACHED_FEEDS_SOURCE] : INTEL_SOURCES;
|
|
45
|
+
const sources = await Promise.all(activeSources.map((source) => source.run({ urls, hosts }, ctx)));
|
|
46
|
+
const matches = sources.flatMap((source) => source.matches);
|
|
47
|
+
const findings = matches.map((match, index) => findingForMatch(match, index));
|
|
48
|
+
return { sources, matches, findings };
|
|
49
|
+
}
|
|
50
|
+
/** Derive intel targets (urls + registrable hosts) from a scanner report's URL inventory. */
|
|
51
|
+
export function intelTargetsFromUrls(urls) {
|
|
52
|
+
const normalized = urls.map((url) => url.normalized).filter(Boolean);
|
|
53
|
+
return { urls: dedupe(normalized), hosts: dedupe(hostsFromUrls(normalized)) };
|
|
54
|
+
}
|
|
55
|
+
const INTEL_SOURCES = [
|
|
56
|
+
{
|
|
57
|
+
source: "urlhaus",
|
|
58
|
+
provider: "URLhaus",
|
|
59
|
+
async run(input, ctx) {
|
|
60
|
+
const base = { source: "urlhaus", provider: "URLhaus" };
|
|
61
|
+
const matches = [];
|
|
62
|
+
let lastError;
|
|
63
|
+
for (const host of input.hosts) {
|
|
64
|
+
const r = await guarded(() => queryUrlhausHost(host, ctx));
|
|
65
|
+
if (r.error)
|
|
66
|
+
lastError = r.error;
|
|
67
|
+
else if (r.value)
|
|
68
|
+
matches.push(r.value);
|
|
69
|
+
}
|
|
70
|
+
for (const url of input.urls) {
|
|
71
|
+
const r = await guarded(() => queryUrlhausUrl(url, ctx));
|
|
72
|
+
if (r.error)
|
|
73
|
+
lastError = r.error;
|
|
74
|
+
else if (r.value)
|
|
75
|
+
matches.push(r.value);
|
|
76
|
+
}
|
|
77
|
+
return settle(base, input.urls.length, input.hosts.length, matches, lastError);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
source: "threatfox",
|
|
82
|
+
provider: "ThreatFox",
|
|
83
|
+
async run(input, ctx) {
|
|
84
|
+
const base = { source: "threatfox", provider: "ThreatFox" };
|
|
85
|
+
const matches = [];
|
|
86
|
+
let lastError;
|
|
87
|
+
for (const host of input.hosts) {
|
|
88
|
+
const r = await guarded(() => queryThreatFoxHost(host, ctx));
|
|
89
|
+
if (r.error)
|
|
90
|
+
lastError = r.error;
|
|
91
|
+
else if (r.value)
|
|
92
|
+
matches.push(r.value);
|
|
93
|
+
}
|
|
94
|
+
return settle(base, 0, input.hosts.length, matches, lastError);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
source: "google-safebrowsing",
|
|
99
|
+
provider: "Google Safe Browsing",
|
|
100
|
+
async run(input, ctx) {
|
|
101
|
+
const base = { source: "google-safebrowsing", provider: "Google Safe Browsing" };
|
|
102
|
+
if (!ctx.googleSafeBrowsingKey) {
|
|
103
|
+
return { ...base, status: "error", reason: "Google Safe Browsing key not configured", urlsChecked: input.urls.length, hostsChecked: 0, matches: [] };
|
|
104
|
+
}
|
|
105
|
+
const r = await guarded(() => queryGoogleSafeBrowsing(input.urls, ctx));
|
|
106
|
+
if (r.error) {
|
|
107
|
+
return { ...base, status: "error", reason: r.error, urlsChecked: input.urls.length, hostsChecked: 0, matches: [] };
|
|
108
|
+
}
|
|
109
|
+
const matches = r.value ?? [];
|
|
110
|
+
return settle(base, input.urls.length, 0, matches, undefined);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
];
|
|
114
|
+
// Matches crawled hosts against the cached blocklist feed index in storage.
|
|
115
|
+
// Only included in the run when config.storage is provided.
|
|
116
|
+
const CACHED_FEEDS_SOURCE = {
|
|
117
|
+
source: "cached-feeds",
|
|
118
|
+
provider: "Blocklist feeds",
|
|
119
|
+
async run(input, ctx) {
|
|
120
|
+
const base = { source: "cached-feeds", provider: "Blocklist feeds" };
|
|
121
|
+
if (!ctx.storage)
|
|
122
|
+
return { ...base, status: "clean", urlsChecked: 0, hostsChecked: 0, matches: [] };
|
|
123
|
+
const r = await guarded(() => matchCachedFeeds(ctx.storage, input.hosts));
|
|
124
|
+
if (r.error) {
|
|
125
|
+
return { ...base, status: "error", reason: r.error, urlsChecked: 0, hostsChecked: input.hosts.length, matches: [] };
|
|
126
|
+
}
|
|
127
|
+
const matches = (r.value ?? []).map((m) => ({
|
|
128
|
+
source: "cached-feeds",
|
|
129
|
+
provider: m.source ? `Blocklist: ${m.source}` : "Blocklist feeds",
|
|
130
|
+
host: m.host,
|
|
131
|
+
score: m.score,
|
|
132
|
+
detail: { feed: m.feedId, feed_source: m.source }
|
|
133
|
+
}));
|
|
134
|
+
return settle(base, 0, input.hosts.length, matches, undefined);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
function settle(base, urlsChecked, hostsChecked, matches, errorReason) {
|
|
138
|
+
if (matches.length)
|
|
139
|
+
return { ...base, status: "match", urlsChecked, hostsChecked, matches };
|
|
140
|
+
if (errorReason)
|
|
141
|
+
return { ...base, status: "error", reason: errorReason, urlsChecked, hostsChecked, matches: [] };
|
|
142
|
+
return { ...base, status: "clean", urlsChecked, hostsChecked, matches: [] };
|
|
143
|
+
}
|
|
144
|
+
async function guarded(query) {
|
|
145
|
+
try {
|
|
146
|
+
return { value: await query() };
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return { error: error instanceof Error ? error.message : "lookup failed" };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// ---- Sources -------------------------------------------------------------
|
|
153
|
+
async function queryUrlhausHost(host, ctx) {
|
|
154
|
+
if (isPrivateOrLocalHost(host))
|
|
155
|
+
return null;
|
|
156
|
+
// Multi-tenant hosts (cloud buckets, CDNs, app-hosting platforms) collect
|
|
157
|
+
// feed entries for OTHER tenants' malicious objects. A site that merely loads
|
|
158
|
+
// an asset from such a host must not inherit that reputation — only an
|
|
159
|
+
// exact-URL match (an object the site actually loads) convicts. See
|
|
160
|
+
// queryUrlhausUrl, which still runs against the crawled URL inventory.
|
|
161
|
+
if (isMultiTenantHost(host))
|
|
162
|
+
return null;
|
|
163
|
+
const response = await postForm("https://urlhaus-api.abuse.ch/v1/host/", { host }, ctx);
|
|
164
|
+
if (!response || response.query_status !== "ok")
|
|
165
|
+
return null;
|
|
166
|
+
const urls = Array.isArray(response.urls) ? response.urls : [];
|
|
167
|
+
// A host appearing in URLhaus is not automatically malware infrastructure:
|
|
168
|
+
// popular hosts (open redirectors, file hosts) collect entries when a single
|
|
169
|
+
// URL is abused. Score by how live and how recent the evidence is — a dead,
|
|
170
|
+
// years-old entry on an otherwise-legitimate host is weak signal, while a
|
|
171
|
+
// currently-online recent listing is a strong conviction.
|
|
172
|
+
const { score, basis, onlineCount } = urlhausHostStrength(urls);
|
|
173
|
+
return {
|
|
174
|
+
source: "urlhaus",
|
|
175
|
+
provider: "URLhaus",
|
|
176
|
+
host,
|
|
177
|
+
score,
|
|
178
|
+
detail: {
|
|
179
|
+
query_status: response.query_status,
|
|
180
|
+
url_count: urls.length,
|
|
181
|
+
online_count: onlineCount,
|
|
182
|
+
score_basis: basis,
|
|
183
|
+
sample: urls.slice(0, 5)
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const RECENT_INTEL_DAYS = 90;
|
|
188
|
+
/** Grade a URLhaus host listing by liveness and recency of its URLs. */
|
|
189
|
+
function urlhausHostStrength(urls) {
|
|
190
|
+
let anyOnline = false;
|
|
191
|
+
let recentOnline = false;
|
|
192
|
+
let recentOffline = false;
|
|
193
|
+
let onlineCount = 0;
|
|
194
|
+
for (const u of urls) {
|
|
195
|
+
const online = String(u?.url_status ?? "").toLowerCase() === "online";
|
|
196
|
+
const age = daysSince(u?.date_added);
|
|
197
|
+
const recent = age !== null && age <= RECENT_INTEL_DAYS;
|
|
198
|
+
if (online) {
|
|
199
|
+
anyOnline = true;
|
|
200
|
+
onlineCount += 1;
|
|
201
|
+
if (recent)
|
|
202
|
+
recentOnline = true;
|
|
203
|
+
}
|
|
204
|
+
else if (recent) {
|
|
205
|
+
recentOffline = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (recentOnline)
|
|
209
|
+
return { score: 95, basis: "online_recent", onlineCount };
|
|
210
|
+
if (anyOnline)
|
|
211
|
+
return { score: 75, basis: "online_aged", onlineCount };
|
|
212
|
+
if (recentOffline)
|
|
213
|
+
return { score: 45, basis: "offline_recent", onlineCount };
|
|
214
|
+
return { score: 20, basis: "offline_aged", onlineCount };
|
|
215
|
+
}
|
|
216
|
+
async function queryUrlhausUrl(url, ctx) {
|
|
217
|
+
const host = hostOf(url);
|
|
218
|
+
if (!host || isPrivateOrLocalHost(host))
|
|
219
|
+
return null;
|
|
220
|
+
const response = await postForm("https://urlhaus-api.abuse.ch/v1/url/", { url }, ctx);
|
|
221
|
+
if (!response || response.query_status !== "ok")
|
|
222
|
+
return null;
|
|
223
|
+
return {
|
|
224
|
+
source: "urlhaus",
|
|
225
|
+
provider: "URLhaus",
|
|
226
|
+
url,
|
|
227
|
+
host,
|
|
228
|
+
score: LIVE_INTEL_SCORE,
|
|
229
|
+
detail: {
|
|
230
|
+
query_status: response.query_status,
|
|
231
|
+
threat: response.threat,
|
|
232
|
+
url_status: response.url_status,
|
|
233
|
+
tags: Array.isArray(response.tags) ? response.tags : []
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
async function queryThreatFoxHost(host, ctx) {
|
|
238
|
+
if (isPrivateOrLocalHost(host))
|
|
239
|
+
return null;
|
|
240
|
+
const response = await postJson("https://threatfox-api.abuse.ch/api/v1/", { query: "search_ioc", search_term: host }, ctx);
|
|
241
|
+
if (!response || response.query_status !== "ok")
|
|
242
|
+
return null;
|
|
243
|
+
const data = Array.isArray(response.data) ? response.data : [];
|
|
244
|
+
const target = host.toLowerCase();
|
|
245
|
+
// ThreatFox `search_ioc` is a SUBSTRING search: querying "google.com" returns
|
|
246
|
+
// IOCs that merely contain that string — "guard-google.com",
|
|
247
|
+
// "google.com-x18-...sslip.io", a malware file on "drive.google.com", etc.
|
|
248
|
+
// None of those make google.com itself malicious. Only domain/IP IOCs whose
|
|
249
|
+
// host EXACTLY equals the queried host actually convict that host. URL IOCs
|
|
250
|
+
// flag one path on a (possibly shared) host and are not a host-level verdict.
|
|
251
|
+
const exact = data.filter((ioc) => {
|
|
252
|
+
const type = String(ioc?.ioc_type ?? "");
|
|
253
|
+
if (type !== "domain" && type !== "ip:port" && type !== "ip")
|
|
254
|
+
return false;
|
|
255
|
+
return iocHost(ioc?.ioc) === target;
|
|
256
|
+
});
|
|
257
|
+
if (!exact.length)
|
|
258
|
+
return null;
|
|
259
|
+
return {
|
|
260
|
+
source: "threatfox",
|
|
261
|
+
provider: "ThreatFox",
|
|
262
|
+
host,
|
|
263
|
+
score: LIVE_INTEL_SCORE,
|
|
264
|
+
detail: {
|
|
265
|
+
query_status: response.query_status,
|
|
266
|
+
ioc_count: exact.length,
|
|
267
|
+
sample: exact.slice(0, 5)
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/** Extract a bare host from a ThreatFox IOC string (url, domain, or host:port). */
|
|
272
|
+
function iocHost(ioc) {
|
|
273
|
+
if (!ioc)
|
|
274
|
+
return null;
|
|
275
|
+
const s = String(ioc).trim();
|
|
276
|
+
if (s.includes("://"))
|
|
277
|
+
return hostOf(s);
|
|
278
|
+
const host = s.split("/")[0].split(":")[0];
|
|
279
|
+
return host ? host.toLowerCase() : null;
|
|
280
|
+
}
|
|
281
|
+
/** Age in days of an abuse.ch timestamp ("2024-11-12 06:08:05 UTC"), or null. */
|
|
282
|
+
function daysSince(value) {
|
|
283
|
+
if (!value)
|
|
284
|
+
return null;
|
|
285
|
+
const t = Date.parse(String(value).replace(" UTC", "Z").replace(" ", "T"));
|
|
286
|
+
if (Number.isNaN(t))
|
|
287
|
+
return null;
|
|
288
|
+
return (Date.now() - t) / 86_400_000;
|
|
289
|
+
}
|
|
290
|
+
// One batched request covers every discovered URL.
|
|
291
|
+
async function queryGoogleSafeBrowsing(urls, ctx) {
|
|
292
|
+
const entries = urls.filter((url) => {
|
|
293
|
+
const host = hostOf(url);
|
|
294
|
+
return host && !isPrivateOrLocalHost(host);
|
|
295
|
+
});
|
|
296
|
+
if (!entries.length)
|
|
297
|
+
return [];
|
|
298
|
+
const response = await postJson(`https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${encodeURIComponent(ctx.googleSafeBrowsingKey ?? "")}`, {
|
|
299
|
+
client: { clientId: "q32-signal-scanner", clientVersion: "0.1" },
|
|
300
|
+
threatInfo: {
|
|
301
|
+
threatTypes: ["MALWARE", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE", "POTENTIALLY_HARMFUL_APPLICATION"],
|
|
302
|
+
platformTypes: ["ANY_PLATFORM"],
|
|
303
|
+
threatEntryTypes: ["URL"],
|
|
304
|
+
threatEntries: entries.slice(0, 500).map((url) => ({ url }))
|
|
305
|
+
}
|
|
306
|
+
}, ctx);
|
|
307
|
+
if (!response || !Array.isArray(response.matches))
|
|
308
|
+
return [];
|
|
309
|
+
return response.matches.map((match) => ({
|
|
310
|
+
source: "google-safebrowsing",
|
|
311
|
+
provider: "Google Safe Browsing",
|
|
312
|
+
url: typeof match?.threat?.url === "string" ? match.threat.url : undefined,
|
|
313
|
+
host: hostOf(String(match?.threat?.url ?? "")) ?? undefined,
|
|
314
|
+
score: LIVE_INTEL_SCORE,
|
|
315
|
+
detail: {
|
|
316
|
+
threat_type: match?.threatType,
|
|
317
|
+
platform_type: match?.platformType,
|
|
318
|
+
cache_duration: match?.cacheDuration
|
|
319
|
+
}
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
// ---- Findings ------------------------------------------------------------
|
|
323
|
+
/** Map an evidence score (0-100) onto the lib's severity/confidence buckets. */
|
|
324
|
+
export function severityForScore(score) {
|
|
325
|
+
if (score >= 85)
|
|
326
|
+
return "high";
|
|
327
|
+
if (score >= 60)
|
|
328
|
+
return "medium";
|
|
329
|
+
if (score >= 40)
|
|
330
|
+
return "low";
|
|
331
|
+
return "info";
|
|
332
|
+
}
|
|
333
|
+
function findingForMatch(match, index) {
|
|
334
|
+
const locationValue = match.url ?? match.host ?? "unknown";
|
|
335
|
+
const ruleId = `intel.${match.source}`;
|
|
336
|
+
const scoreModel = { base: match.score, tags: ["hosting", "url"] };
|
|
337
|
+
return {
|
|
338
|
+
id: `${ruleId}:${index}`,
|
|
339
|
+
ruleId,
|
|
340
|
+
severity: severityForScore(match.score),
|
|
341
|
+
confidence: match.score >= 80 ? "high" : "medium",
|
|
342
|
+
score: match.score,
|
|
343
|
+
scoreModel,
|
|
344
|
+
title: `Known-bad ${match.url ? "URL" : "host"} flagged by ${match.provider}`,
|
|
345
|
+
description: `${match.provider} threat intelligence matched a crawled ${match.url ? "URL" : "host"} (score ${match.score}).`,
|
|
346
|
+
locationType: "url",
|
|
347
|
+
locationValue,
|
|
348
|
+
metadata: { intel_source: match.source, provider: match.provider, host: match.host, url: match.url, score: match.score, ...match.detail }
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
// ---- HTTP + host helpers -------------------------------------------------
|
|
352
|
+
// abuse.ch (URLhaus + ThreatFox) require an Auth-Key header. Send it to those
|
|
353
|
+
// hosts only; never leak it to other endpoints (e.g. Safe Browsing).
|
|
354
|
+
function abuseChHeaders(url, ctx) {
|
|
355
|
+
if (!ctx.abuseChAuthKey || !/\.abuse\.ch$/i.test(safeHost(url)))
|
|
356
|
+
return {};
|
|
357
|
+
return { "Auth-Key": ctx.abuseChAuthKey };
|
|
358
|
+
}
|
|
359
|
+
function safeHost(url) {
|
|
360
|
+
try {
|
|
361
|
+
return new URL(url).hostname.toLowerCase();
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return "";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async function postForm(url, body, ctx) {
|
|
368
|
+
const response = await ctx.fetch(url, {
|
|
369
|
+
method: "POST",
|
|
370
|
+
headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": ctx.userAgent, ...abuseChHeaders(url, ctx) },
|
|
371
|
+
body: new URLSearchParams(body),
|
|
372
|
+
signal: AbortSignal.timeout(ctx.timeoutMs)
|
|
373
|
+
});
|
|
374
|
+
if (!response.ok)
|
|
375
|
+
throw new Error(`${url} responded ${response.status}`);
|
|
376
|
+
const parsed = await response.json();
|
|
377
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
378
|
+
}
|
|
379
|
+
async function postJson(url, body, ctx) {
|
|
380
|
+
const response = await ctx.fetch(url, {
|
|
381
|
+
method: "POST",
|
|
382
|
+
headers: { "content-type": "application/json", "user-agent": ctx.userAgent, ...abuseChHeaders(url, ctx) },
|
|
383
|
+
body: JSON.stringify(body),
|
|
384
|
+
signal: AbortSignal.timeout(ctx.timeoutMs)
|
|
385
|
+
});
|
|
386
|
+
if (!response.ok)
|
|
387
|
+
throw new Error(`${url} responded ${response.status}`);
|
|
388
|
+
const parsed = await response.json();
|
|
389
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
390
|
+
}
|
|
391
|
+
function hostOf(url) {
|
|
392
|
+
try {
|
|
393
|
+
return new URL(url).hostname.toLowerCase();
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function hostsFromUrls(urls) {
|
|
400
|
+
const hosts = [];
|
|
401
|
+
for (const url of urls) {
|
|
402
|
+
const host = hostOf(url);
|
|
403
|
+
if (host)
|
|
404
|
+
hosts.push(host);
|
|
405
|
+
}
|
|
406
|
+
return hosts;
|
|
407
|
+
}
|
|
408
|
+
function dedupe(values) {
|
|
409
|
+
return [...new Set(values.filter(Boolean))];
|
|
410
|
+
}
|
|
411
|
+
// Hosts that serve content for many independent tenants under one name: cloud
|
|
412
|
+
// object storage, file sharing, CDNs that publish arbitrary packages/content,
|
|
413
|
+
// and app/site-hosting platforms (one tenant per subdomain). One tenant's
|
|
414
|
+
// malicious object is not evidence about a different tenant, so these are never
|
|
415
|
+
// convicted at the host level — only an exact-URL match counts.
|
|
416
|
+
const MULTI_TENANT_HOST_EXACT = new Set([
|
|
417
|
+
"storage.googleapis.com",
|
|
418
|
+
"firebasestorage.googleapis.com",
|
|
419
|
+
"drive.google.com",
|
|
420
|
+
"docs.google.com",
|
|
421
|
+
"s3.amazonaws.com",
|
|
422
|
+
"raw.githubusercontent.com",
|
|
423
|
+
"gist.githubusercontent.com",
|
|
424
|
+
"objects.githubusercontent.com",
|
|
425
|
+
"cdn.jsdelivr.net",
|
|
426
|
+
"unpkg.com",
|
|
427
|
+
"files.catbox.moe",
|
|
428
|
+
"cdn.discordapp.com",
|
|
429
|
+
"media.discordapp.net"
|
|
430
|
+
]);
|
|
431
|
+
const MULTI_TENANT_HOST_SUFFIXES = [
|
|
432
|
+
// Cloud object storage
|
|
433
|
+
".amazonaws.com",
|
|
434
|
+
".blob.core.windows.net",
|
|
435
|
+
".r2.dev",
|
|
436
|
+
".r2.cloudflarestorage.com",
|
|
437
|
+
".digitaloceanspaces.com",
|
|
438
|
+
".googleusercontent.com",
|
|
439
|
+
".dropboxusercontent.com",
|
|
440
|
+
// CDNs serving arbitrary tenant/package content
|
|
441
|
+
".cloudfront.net",
|
|
442
|
+
".akamaihd.net",
|
|
443
|
+
".b-cdn.net",
|
|
444
|
+
".fastly.net",
|
|
445
|
+
// App / site hosting platforms (one tenant per subdomain)
|
|
446
|
+
".web.app",
|
|
447
|
+
".firebaseapp.com",
|
|
448
|
+
".netlify.app",
|
|
449
|
+
".vercel.app",
|
|
450
|
+
".github.io",
|
|
451
|
+
".herokuapp.com",
|
|
452
|
+
".pages.dev",
|
|
453
|
+
".workers.dev",
|
|
454
|
+
".azurewebsites.net",
|
|
455
|
+
".appspot.com",
|
|
456
|
+
".glitch.me",
|
|
457
|
+
".repl.co",
|
|
458
|
+
".surge.sh"
|
|
459
|
+
];
|
|
460
|
+
export function isMultiTenantHost(host) {
|
|
461
|
+
const lower = host.toLowerCase();
|
|
462
|
+
if (MULTI_TENANT_HOST_EXACT.has(lower))
|
|
463
|
+
return true;
|
|
464
|
+
return MULTI_TENANT_HOST_SUFFIXES.some((suffix) => lower.endsWith(suffix));
|
|
465
|
+
}
|
|
466
|
+
export function isPrivateOrLocalHost(host) {
|
|
467
|
+
const lower = host.toLowerCase();
|
|
468
|
+
if (lower === "localhost" || lower.endsWith(".localhost") || lower.endsWith(".local"))
|
|
469
|
+
return true;
|
|
470
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(lower)) {
|
|
471
|
+
const parts = lower.split(".").map(Number);
|
|
472
|
+
return (parts[0] === 10 ||
|
|
473
|
+
parts[0] === 127 ||
|
|
474
|
+
(parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
|
|
475
|
+
(parts[0] === 192 && parts[1] === 168) ||
|
|
476
|
+
(parts[0] === 169 && parts[1] === 254));
|
|
477
|
+
}
|
|
478
|
+
return lower === "::1" || lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80");
|
|
479
|
+
}
|
|
480
|
+
//# sourceMappingURL=intel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intel.js","sourceRoot":"","sources":["../src/intel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TlsMetadata } from "./index.js";
|
|
2
|
+
export interface CollectTlsMetadataOptions {
|
|
3
|
+
port?: number;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
rejectUnauthorized?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function collectTlsMetadata(target: string | URL, options?: CollectTlsMetadataOptions): Promise<TlsMetadata | undefined>;
|
|
8
|
+
//# sourceMappingURL=node-tls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-tls.d.ts","sourceRoot":"","sources":["../src/node-tls.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,yBAAyB;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,GAAE,yBAA8B,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAoCxI"}
|
package/dist/node-tls.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { connect } from "node:tls";
|
|
2
|
+
export async function collectTlsMetadata(target, options = {}) {
|
|
3
|
+
const url = typeof target === "string" ? new URL(target.includes("://") ? target : `https://${target}`) : target;
|
|
4
|
+
if (url.protocol !== "https:")
|
|
5
|
+
return undefined;
|
|
6
|
+
const port = options.port ?? (url.port ? Number(url.port) : 443);
|
|
7
|
+
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
8
|
+
return await new Promise((resolve) => {
|
|
9
|
+
const socket = connect({
|
|
10
|
+
host: url.hostname,
|
|
11
|
+
port,
|
|
12
|
+
servername: url.hostname,
|
|
13
|
+
rejectUnauthorized: options.rejectUnauthorized ?? false,
|
|
14
|
+
timeout: timeoutMs
|
|
15
|
+
});
|
|
16
|
+
const done = (metadata) => {
|
|
17
|
+
socket.removeAllListeners();
|
|
18
|
+
socket.destroy();
|
|
19
|
+
resolve(metadata);
|
|
20
|
+
};
|
|
21
|
+
socket.once("secureConnect", () => {
|
|
22
|
+
const certificate = socket.getPeerCertificate();
|
|
23
|
+
if (!certificate || Object.keys(certificate).length === 0)
|
|
24
|
+
return done(undefined);
|
|
25
|
+
done({
|
|
26
|
+
authorized: socket.authorized,
|
|
27
|
+
authorizationError: socket.authorizationError ? String(socket.authorizationError) : null,
|
|
28
|
+
issuer: distinguishedName(certificate.issuer),
|
|
29
|
+
subject: distinguishedName(certificate.subject),
|
|
30
|
+
validFrom: certificate.valid_from,
|
|
31
|
+
validTo: certificate.valid_to,
|
|
32
|
+
fingerprint256: certificate.fingerprint256,
|
|
33
|
+
serialNumber: certificate.serialNumber
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
socket.once("timeout", () => done(undefined));
|
|
37
|
+
socket.once("error", () => done(undefined));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function distinguishedName(value) {
|
|
41
|
+
if (!value)
|
|
42
|
+
return undefined;
|
|
43
|
+
const entries = Object.entries(value)
|
|
44
|
+
.filter(([, item]) => typeof item === "string" && item)
|
|
45
|
+
.map(([key, item]) => `${key}=${item}`);
|
|
46
|
+
return entries.length ? entries.join(", ") : undefined;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=node-tls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-tls.js","sourceRoot":"","sources":["../src/node-tls.ts"],"names":[],"mappings":"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"}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type BehaviorReport } from "./dynamic.js";
|
|
2
|
+
/** Self-contained input for the in-isolate core: HTML + already-fetched external script bodies. */
|
|
3
|
+
export interface RenderInput {
|
|
4
|
+
html: string;
|
|
5
|
+
url?: string;
|
|
6
|
+
externalScripts?: string[];
|
|
7
|
+
}
|
|
8
|
+
/** Runs renderDom — in-process by default, or inside an isolate (isolated-vm / Dynamic Worker). */
|
|
9
|
+
export type RenderInvoke = (input: RenderInput) => RenderResult | Promise<RenderResult>;
|
|
10
|
+
export interface RenderOptions {
|
|
11
|
+
url?: string;
|
|
12
|
+
/** Fetch an external script body (caller provides IO + egress). Omit to skip externals. */
|
|
13
|
+
fetchScript?: (absoluteUrl: string) => Promise<string | null>;
|
|
14
|
+
/** Where renderDom runs (caller's isolate). Default: in-process (trusted/synthetic use only). */
|
|
15
|
+
invoke?: RenderInvoke;
|
|
16
|
+
maxExternalScripts?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface RenderResult {
|
|
19
|
+
/** Serialized DOM after scripts ran — feed this to the static scanner. */
|
|
20
|
+
html: string;
|
|
21
|
+
/** Behaviors recorded while scripts ran. */
|
|
22
|
+
report: BehaviorReport;
|
|
23
|
+
}
|
|
24
|
+
export declare function renderAndScan(html: string, options?: RenderOptions): Promise<RenderResult>;
|
|
25
|
+
export declare function renderDom(input: RenderInput): RenderResult;
|
|
26
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAiBA,OAAO,EAA8C,KAAK,cAAc,EAAuB,MAAM,cAAc,CAAC;AAKpH,mGAAmG;AACnG,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,mGAAmG;AACnG,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAExF,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9D,iGAAiG;IACjG,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,cAAc,CAAC;CACxB;AASD,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA0BpG;AAMD,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CA0B1D"}
|