@chainpatrol/sdk 1.7.0 → 1.9.0

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/index.js CHANGED
@@ -1313,17 +1313,17 @@ var PolyDateFormatter = class {
1313
1313
  constructor(dt, intl, opts) {
1314
1314
  this.opts = opts;
1315
1315
  this.originalZone = void 0;
1316
- let z54 = void 0;
1316
+ let z56 = void 0;
1317
1317
  if (this.opts.timeZone) {
1318
1318
  this.dt = dt;
1319
1319
  } else if (dt.zone.type === "fixed") {
1320
1320
  const gmtOffset = -1 * (dt.offset / 60);
1321
1321
  const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;
1322
1322
  if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) {
1323
- z54 = offsetZ;
1323
+ z56 = offsetZ;
1324
1324
  this.dt = dt;
1325
1325
  } else {
1326
- z54 = "UTC";
1326
+ z56 = "UTC";
1327
1327
  this.dt = dt.offset === 0 ? dt : dt.setZone("UTC").plus({ minutes: dt.offset });
1328
1328
  this.originalZone = dt.zone;
1329
1329
  }
@@ -1331,14 +1331,14 @@ var PolyDateFormatter = class {
1331
1331
  this.dt = dt;
1332
1332
  } else if (dt.zone.type === "iana") {
1333
1333
  this.dt = dt;
1334
- z54 = dt.zone.name;
1334
+ z56 = dt.zone.name;
1335
1335
  } else {
1336
- z54 = "UTC";
1336
+ z56 = "UTC";
1337
1337
  this.dt = dt.setZone("UTC").plus({ minutes: dt.offset });
1338
1338
  this.originalZone = dt.zone;
1339
1339
  }
1340
1340
  const intlOpts = __spreadValues({}, this.opts);
1341
- intlOpts.timeZone = intlOpts.timeZone || z54;
1341
+ intlOpts.timeZone = intlOpts.timeZone || z56;
1342
1342
  this.dtf = getCachedDTF(intl, intlOpts);
1343
1343
  }
1344
1344
  format() {
@@ -11488,6 +11488,50 @@ zod.z.object({
11488
11488
  endDate: zod.z.date()
11489
11489
  })
11490
11490
  });
11491
+ var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
11492
+ var utcDateSchema = (endOfDay) => zod.z.preprocess((value) => {
11493
+ if (typeof value !== "string") {
11494
+ return value;
11495
+ }
11496
+ const parsed = DateTime.fromISO(value, { zone: "utc" });
11497
+ if (!parsed.isValid) {
11498
+ return new Date(Number.NaN);
11499
+ }
11500
+ return endOfDay && DATE_ONLY_PATTERN.test(value) ? parsed.endOf("day").toJSDate() : parsed.toJSDate();
11501
+ }, zod.z.coerce.date());
11502
+ var funnelCountsSchema = zod.z.object({
11503
+ blocked: zod.z.number().int().nonnegative().describe(
11504
+ "Distinct assets whose earliest report for this org falls in the range and whose current status or pending status is BLOCKED"
11505
+ )
11506
+ });
11507
+ zod.z.object({
11508
+ slug: zod.z.string().min(1).describe("Organization slug"),
11509
+ startDate: utcDateSchema(false).describe(
11510
+ "Only include assets whose earliest report was created on or after this date. A date-only value (YYYY-MM-DD) starts at midnight UTC"
11511
+ ),
11512
+ endDate: utcDateSchema(true).describe(
11513
+ "Only include assets whose earliest report was created on or before this date. A date-only value (YYYY-MM-DD) covers the whole day in UTC; pass a full timestamp for a precise cut-off"
11514
+ ),
11515
+ brandIds: zod.z.array(zod.z.number().int().positive()).optional().describe("Only include assets belonging to these brands")
11516
+ }).refine((input) => input.startDate.getTime() <= input.endDate.getTime(), {
11517
+ message: "startDate must be on or before endDate",
11518
+ path: ["startDate"]
11519
+ });
11520
+ zod.z.object({
11521
+ range: zod.z.object({
11522
+ startDate: zod.z.date(),
11523
+ endDate: zod.z.date()
11524
+ }),
11525
+ customer: funnelCountsSchema.describe(
11526
+ "Currently blocked assets first reported by the customer (reportedByCustomer, external reporter, or customer-role user)"
11527
+ ),
11528
+ chainpatrol: funnelCountsSchema.describe(
11529
+ "Currently blocked assets first reported by ChainPatrol staff or automation. Staff and automation are collapsed together."
11530
+ ),
11531
+ totals: funnelCountsSchema.describe(
11532
+ "customer + chainpatrol. This is a first-report blocked cohort and does not equal metrics/found, which counts assets by blockedAt."
11533
+ )
11534
+ });
11491
11535
  zod.z.object({
11492
11536
  slug: zod.z.string().min(1),
11493
11537
  startDate: zod.z.date().optional(),
@@ -11519,8 +11563,8 @@ zod.z.object({
11519
11563
  })
11520
11564
  )
11521
11565
  });
11522
- var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
11523
- var utcDateSchema = (endOfDay) => zod.z.preprocess((value) => {
11566
+ var DATE_ONLY_PATTERN2 = /^\d{4}-\d{2}-\d{2}$/;
11567
+ var utcDateSchema2 = (endOfDay) => zod.z.preprocess((value) => {
11524
11568
  if (typeof value !== "string") {
11525
11569
  return value;
11526
11570
  }
@@ -11528,14 +11572,14 @@ var utcDateSchema = (endOfDay) => zod.z.preprocess((value) => {
11528
11572
  if (!parsed.isValid) {
11529
11573
  return new Date(Number.NaN);
11530
11574
  }
11531
- return endOfDay && DATE_ONLY_PATTERN.test(value) ? parsed.endOf("day").toJSDate() : parsed.toJSDate();
11575
+ return endOfDay && DATE_ONLY_PATTERN2.test(value) ? parsed.endOf("day").toJSDate() : parsed.toJSDate();
11532
11576
  }, zod.z.coerce.date());
11533
11577
  zod.z.object({
11534
11578
  slug: zod.z.string().min(1).describe("Organization slug"),
11535
- startDate: utcDateSchema(false).optional().describe(
11579
+ startDate: utcDateSchema2(false).optional().describe(
11536
11580
  "Only include takedowns completed on or after this date. A date-only value (YYYY-MM-DD) starts at midnight UTC"
11537
11581
  ),
11538
- endDate: utcDateSchema(true).optional().describe(
11582
+ endDate: utcDateSchema2(true).optional().describe(
11539
11583
  "Only include takedowns completed on or before this date. A date-only value (YYYY-MM-DD) covers the whole day in UTC; pass a full timestamp for a precise cut-off"
11540
11584
  ),
11541
11585
  blockLabel: zod.z.string().optional().describe(
@@ -11558,6 +11602,55 @@ zod.z.object({
11558
11602
  "Median time from takedown start to completion, one entry per asset type, ascending by median"
11559
11603
  )
11560
11604
  });
11605
+ zod.z.object({
11606
+ slug: zod.z.string().min(1).describe("Organization slug"),
11607
+ months: zod.z.number().int().min(2).max(36).default(12).describe("Size of the trailing window in whole months, including the current month"),
11608
+ assetTypes: zod.z.array(zod.z.nativeEnum(AssetType)).max(5).optional().describe(
11609
+ "Break the trend down by these asset types, one series each. Omit for a single series pooled across every asset type"
11610
+ ),
11611
+ blockLabel: zod.z.string().optional().describe(
11612
+ 'Only include assets blocked with this review label, e.g. "Brand Impersonation", "Employee Impersonation" or "Targeting Org Users"'
11613
+ ),
11614
+ brandType: zod.z.nativeEnum(BrandType).optional().describe("Only include assets belonging to brands of this type"),
11615
+ countryCodes: zod.z.array(zod.z.string().length(2)).optional().describe("Only include assets scanned from these ISO 3166-1 alpha-2 countries"),
11616
+ brandIds: zod.z.array(zod.z.number().int().positive()).optional().describe("Only include assets belonging to these brands")
11617
+ });
11618
+ var MetricsTakedownTimeTrendPointSchema = zod.z.object({
11619
+ period: zod.z.string().describe("Month the takedowns completed in, as a yyyy-MM key"),
11620
+ medianTakedownMinutes: zod.z.number().nullable().describe("Null when no takedowns completed in this month"),
11621
+ medianTakedownHours: zod.z.number().nullable(),
11622
+ medianTakedownDays: zod.z.number().nullable(),
11623
+ count: zod.z.number().describe("Number of completed takedowns behind the median")
11624
+ });
11625
+ var MetricsTakedownTimeTrendChangeSchema = zod.z.object({
11626
+ latest: MetricsTakedownTimeTrendPointSchema.nullable().describe(
11627
+ "Most recent month in the window that had completed takedowns"
11628
+ ),
11629
+ previous: MetricsTakedownTimeTrendPointSchema.nullable().describe(
11630
+ "Month with completed takedowns immediately before `latest`, skipping empty months"
11631
+ ),
11632
+ percentChange: zod.z.number().nullable().describe(
11633
+ "Change in the median from `previous` to `latest`, as a percentage. Negative means takedowns got faster. Null when there is no comparable earlier month"
11634
+ ),
11635
+ direction: zod.z.enum(["improved", "worsened", "unchanged"]).nullable().describe("Sign of `percentChange`, read as lower-is-better. Null when it is null")
11636
+ });
11637
+ zod.z.object({
11638
+ months: zod.z.number().describe("Size of the trailing window that was used"),
11639
+ periods: zod.z.array(zod.z.string()).describe("Every yyyy-MM key in the window, oldest first"),
11640
+ series: zod.z.array(
11641
+ zod.z.object({
11642
+ assetType: zod.z.nativeEnum(AssetType).nullable().describe("Null for the series pooled across every asset type"),
11643
+ points: zod.z.array(MetricsTakedownTimeTrendPointSchema).describe(
11644
+ "One point per month in the window, oldest first. Months with no completed takedowns carry a null median"
11645
+ ),
11646
+ change: MetricsTakedownTimeTrendChangeSchema.describe(
11647
+ "Month-over-month comparison for this series"
11648
+ )
11649
+ })
11650
+ ).describe(
11651
+ "One series per requested asset type, in the order requested, or a single pooled series when no asset types were given"
11652
+ )
11653
+ });
11561
11654
  zod.z.object({
11562
11655
  slug: zod.z.string().optional().describe(
11563
11656
  "Organization slug. Optional for organization-scoped API keys, which resolve the organization from the key itself. Required when your credentials can reach more than one organization."
@@ -11944,7 +12037,9 @@ var organizationMetricsIncludeFieldSchema = zod.z.enum([
11944
12037
  "newThreats",
11945
12038
  "threatsWatchlisted",
11946
12039
  "takedownsFiled",
12040
+ "takedownsInProgress",
11947
12041
  "takedownsCompleted",
12042
+ "takedownsCancelled",
11948
12043
  "domainThreats",
11949
12044
  "twitterThreats",
11950
12045
  "telegramThreats",
@@ -11986,7 +12081,7 @@ zod.z.object({
11986
12081
  startDate: zod.z.coerce.date().optional(),
11987
12082
  endDate: zod.z.coerce.date().optional(),
11988
12083
  include: zod.z.array(organizationMetricsIncludeFieldSchema).optional().describe(
11989
- "Subset of metrics to compute. When omitted or empty, every metric is computed (legacy behavior). Each value in `include` costs roughly one Prisma aggregate, so narrow the list when an agent only needs a couple of numbers \u2014 this is the main lever for keeping big-org calls inside the 30s function cap. Fields not in `include` come back as `null`."
12084
+ "Subset of metrics to compute. When omitted or empty, every metric is computed (legacy behavior). Each value in `include` costs roughly one aggregate query, so narrow the list when an agent only needs a couple of numbers \u2014 this is the main lever for keeping big-org calls inside the 30s function cap. Fields not in `include` come back as `null`. `takedownsInProgress` and `takedownsCancelled` count takedowns currently in that status whose `updatedAt` falls in the window (same windowing as `takedownsFiled`), so they reconcile with the takedowns list filtered by status over the same date range."
11990
12085
  )
11991
12086
  }).refine(
11992
12087
  (value) => {
@@ -12014,7 +12109,9 @@ var metricsObjectSchema = zod.z.object({
12014
12109
  newThreats: metricValue,
12015
12110
  threatsWatchlisted: metricValue,
12016
12111
  takedownsFiled: metricValue,
12112
+ takedownsInProgress: metricValue,
12017
12113
  takedownsCompleted: metricValue,
12114
+ takedownsCancelled: metricValue,
12018
12115
  domainThreats: metricValue,
12019
12116
  twitterThreats: metricValue,
12020
12117
  telegramThreats: metricValue,
@@ -13397,20 +13494,20 @@ var convertA98ToXyz65_default = convertA98ToXyz65;
13397
13494
 
13398
13495
  // ../../node_modules/culori/src/a98/convertXyz65ToA98.js
13399
13496
  var gamma = (v) => Math.pow(Math.abs(v), 256 / 563) * Math.sign(v);
13400
- var convertXyz65ToA98 = ({ x, y, z: z54, alpha }) => {
13497
+ var convertXyz65ToA98 = ({ x, y, z: z56, alpha }) => {
13401
13498
  if (x === void 0) x = 0;
13402
13499
  if (y === void 0) y = 0;
13403
- if (z54 === void 0) z54 = 0;
13500
+ if (z56 === void 0) z56 = 0;
13404
13501
  let res = {
13405
13502
  mode: "a98",
13406
13503
  r: gamma(
13407
- x * 2.0415879038107465 - y * 0.5650069742788597 - 0.3447313507783297 * z54
13504
+ x * 2.0415879038107465 - y * 0.5650069742788597 - 0.3447313507783297 * z56
13408
13505
  ),
13409
13506
  g: gamma(
13410
- x * -0.9692436362808798 + y * 1.8759675015077206 + 0.0415550574071756 * z54
13507
+ x * -0.9692436362808798 + y * 1.8759675015077206 + 0.0415550574071756 * z56
13411
13508
  ),
13412
13509
  b: gamma(
13413
- x * 0.0134442806320312 - y * 0.1183623922310184 + 1.0151749943912058 * z54
13510
+ x * 0.0134442806320312 - y * 0.1183623922310184 + 1.0151749943912058 * z56
13414
13511
  )
13415
13512
  };
13416
13513
  if (alpha !== void 0) {
@@ -13477,14 +13574,14 @@ var convertLrgbToRgb = ({ r, g, b, alpha }, mode = "rgb") => {
13477
13574
  var convertLrgbToRgb_default = convertLrgbToRgb;
13478
13575
 
13479
13576
  // ../../node_modules/culori/src/xyz65/convertXyz65ToRgb.js
13480
- var convertXyz65ToRgb = ({ x, y, z: z54, alpha }) => {
13577
+ var convertXyz65ToRgb = ({ x, y, z: z56, alpha }) => {
13481
13578
  if (x === void 0) x = 0;
13482
13579
  if (y === void 0) y = 0;
13483
- if (z54 === void 0) z54 = 0;
13580
+ if (z56 === void 0) z56 = 0;
13484
13581
  let res = convertLrgbToRgb_default({
13485
- r: x * 3.2409699419045226 - y * 1.537383177570094 - 0.4986107602930034 * z54,
13486
- g: x * -0.9692436362808796 + y * 1.8759675015077204 + 0.0415550574071756 * z54,
13487
- b: x * 0.0556300796969936 - y * 0.2039769588889765 + 1.0569715142428784 * z54
13582
+ r: x * 3.2409699419045226 - y * 1.537383177570094 - 0.4986107602930034 * z56,
13583
+ g: x * -0.9692436362808796 + y * 1.8759675015077204 + 0.0415550574071756 * z56,
13584
+ b: x * 0.0556300796969936 - y * 0.2039769588889765 + 1.0569715142428784 * z56
13488
13585
  });
13489
13586
  if (alpha !== void 0) {
13490
13587
  res.alpha = alpha;
@@ -13734,13 +13831,13 @@ var convertLab65ToRgb_default = convertLab65ToRgb;
13734
13831
 
13735
13832
  // ../../node_modules/culori/src/lab65/convertXyz65ToLab65.js
13736
13833
  var f = (value) => value > e ? Math.cbrt(value) : (k * value + 16) / 116;
13737
- var convertXyz65ToLab65 = ({ x, y, z: z54, alpha }) => {
13834
+ var convertXyz65ToLab65 = ({ x, y, z: z56, alpha }) => {
13738
13835
  if (x === void 0) x = 0;
13739
13836
  if (y === void 0) y = 0;
13740
- if (z54 === void 0) z54 = 0;
13837
+ if (z56 === void 0) z56 = 0;
13741
13838
  let f0 = f(x / D65.X);
13742
13839
  let f1 = f(y / D65.Y);
13743
- let f22 = f(z54 / D65.Z);
13840
+ let f22 = f(z56 / D65.Z);
13744
13841
  let res = {
13745
13842
  mode: "lab65",
13746
13843
  l: 116 * f1 - 16,
@@ -14397,10 +14494,10 @@ var convertItpToXyz65_default = convertItpToXyz65;
14397
14494
 
14398
14495
  // ../../node_modules/culori/src/itp/convertXyz65ToItp.js
14399
14496
  var toAbs = (c2 = 0) => Math.max(c2 * YW, 0);
14400
- var convertXyz65ToItp = ({ x, y, z: z54, alpha }) => {
14497
+ var convertXyz65ToItp = ({ x, y, z: z56, alpha }) => {
14401
14498
  const absX = toAbs(x);
14402
14499
  const absY = toAbs(y);
14403
- const absZ = toAbs(z54);
14500
+ const absZ = toAbs(z56);
14404
14501
  const l2 = transferPqEncode(
14405
14502
  0.3592832590121217 * absX + 0.6976051147779502 * absY - 0.0358915932320289 * absZ
14406
14503
  );
@@ -14458,15 +14555,15 @@ var jabPqEncode = (v) => {
14458
14555
  return Math.pow((C1 + C2 * vn3) / (1 + C3 * vn3), p);
14459
14556
  };
14460
14557
  var abs = (v = 0) => Math.max(v * 203, 0);
14461
- var convertXyz65ToJab = ({ x, y, z: z54, alpha }) => {
14558
+ var convertXyz65ToJab = ({ x, y, z: z56, alpha }) => {
14462
14559
  x = abs(x);
14463
14560
  y = abs(y);
14464
- z54 = abs(z54);
14465
- let xp = 1.15 * x - 0.15 * z54;
14561
+ z56 = abs(z56);
14562
+ let xp = 1.15 * x - 0.15 * z56;
14466
14563
  let yp = 0.66 * y + 0.34 * x;
14467
- let l2 = jabPqEncode(0.41478972 * xp + 0.579999 * yp + 0.014648 * z54);
14468
- let m = jabPqEncode(-0.20151 * xp + 1.120649 * yp + 0.0531008 * z54);
14469
- let s2 = jabPqEncode(-0.0166008 * xp + 0.2648 * yp + 0.6684799 * z54);
14564
+ let l2 = jabPqEncode(0.41478972 * xp + 0.579999 * yp + 0.014648 * z56);
14565
+ let m = jabPqEncode(-0.20151 * xp + 1.120649 * yp + 0.0531008 * z56);
14566
+ let s2 = jabPqEncode(-0.0166008 * xp + 0.2648 * yp + 0.6684799 * z56);
14470
14567
  let i = (l2 + m) / 2;
14471
14568
  let res = {
14472
14569
  mode: "jab",
@@ -14652,14 +14749,14 @@ var convertLabToXyz50 = ({ l: l2, a, b, alpha }) => {
14652
14749
  var convertLabToXyz50_default = convertLabToXyz50;
14653
14750
 
14654
14751
  // ../../node_modules/culori/src/xyz50/convertXyz50ToRgb.js
14655
- var convertXyz50ToRgb = ({ x, y, z: z54, alpha }) => {
14752
+ var convertXyz50ToRgb = ({ x, y, z: z56, alpha }) => {
14656
14753
  if (x === void 0) x = 0;
14657
14754
  if (y === void 0) y = 0;
14658
- if (z54 === void 0) z54 = 0;
14755
+ if (z56 === void 0) z56 = 0;
14659
14756
  let res = convertLrgbToRgb_default({
14660
- r: x * 3.1341359569958707 - y * 1.6173863321612538 - 0.4906619460083532 * z54,
14661
- g: x * -0.978795502912089 + y * 1.916254567259524 + 0.03344273116131949 * z54,
14662
- b: x * 0.07195537988411677 - y * 0.2289768264158322 + 1.405386058324125 * z54
14757
+ r: x * 3.1341359569958707 - y * 1.6173863321612538 - 0.4906619460083532 * z56,
14758
+ g: x * -0.978795502912089 + y * 1.916254567259524 + 0.03344273116131949 * z56,
14759
+ b: x * 0.07195537988411677 - y * 0.2289768264158322 + 1.405386058324125 * z56
14663
14760
  });
14664
14761
  if (alpha !== void 0) {
14665
14762
  res.alpha = alpha;
@@ -14690,13 +14787,13 @@ var convertRgbToXyz50_default = convertRgbToXyz50;
14690
14787
 
14691
14788
  // ../../node_modules/culori/src/lab/convertXyz50ToLab.js
14692
14789
  var f2 = (value) => value > e3 ? Math.cbrt(value) : (k3 * value + 16) / 116;
14693
- var convertXyz50ToLab = ({ x, y, z: z54, alpha }) => {
14790
+ var convertXyz50ToLab = ({ x, y, z: z56, alpha }) => {
14694
14791
  if (x === void 0) x = 0;
14695
14792
  if (y === void 0) y = 0;
14696
- if (z54 === void 0) z54 = 0;
14793
+ if (z56 === void 0) z56 = 0;
14697
14794
  let f0 = f2(x / D50.X);
14698
14795
  let f1 = f2(y / D50.Y);
14699
- let f22 = f2(z54 / D50.Z);
14796
+ let f22 = f2(z56 / D50.Z);
14700
14797
  let res = {
14701
14798
  mode: "lab",
14702
14799
  l: 116 * f1 - 16,
@@ -14931,18 +15028,18 @@ var convertLchuvToLuv = ({ l: l2, c: c2, h, alpha }) => {
14931
15028
  var convertLchuvToLuv_default = convertLchuvToLuv;
14932
15029
 
14933
15030
  // ../../node_modules/culori/src/luv/convertXyz50ToLuv.js
14934
- var u_fn = (x, y, z54) => 4 * x / (x + 15 * y + 3 * z54);
14935
- var v_fn = (x, y, z54) => 9 * y / (x + 15 * y + 3 * z54);
15031
+ var u_fn = (x, y, z56) => 4 * x / (x + 15 * y + 3 * z56);
15032
+ var v_fn = (x, y, z56) => 9 * y / (x + 15 * y + 3 * z56);
14936
15033
  var un = u_fn(D50.X, D50.Y, D50.Z);
14937
15034
  var vn = v_fn(D50.X, D50.Y, D50.Z);
14938
15035
  var l_fn = (value) => value <= e3 ? k3 * value : 116 * Math.cbrt(value) - 16;
14939
- var convertXyz50ToLuv = ({ x, y, z: z54, alpha }) => {
15036
+ var convertXyz50ToLuv = ({ x, y, z: z56, alpha }) => {
14940
15037
  if (x === void 0) x = 0;
14941
15038
  if (y === void 0) y = 0;
14942
- if (z54 === void 0) z54 = 0;
15039
+ if (z56 === void 0) z56 = 0;
14943
15040
  let l2 = l_fn(y / D50.Y);
14944
- let u = u_fn(x, y, z54);
14945
- let v = v_fn(x, y, z54);
15041
+ let u = u_fn(x, y, z56);
15042
+ let v = v_fn(x, y, z56);
14946
15043
  if (!isFinite(u) || !isFinite(v)) {
14947
15044
  l2 = u = v = 0;
14948
15045
  } else {
@@ -14963,8 +15060,8 @@ var convertXyz50ToLuv = ({ x, y, z: z54, alpha }) => {
14963
15060
  var convertXyz50ToLuv_default = convertXyz50ToLuv;
14964
15061
 
14965
15062
  // ../../node_modules/culori/src/luv/convertLuvToXyz50.js
14966
- var u_fn2 = (x, y, z54) => 4 * x / (x + 15 * y + 3 * z54);
14967
- var v_fn2 = (x, y, z54) => 9 * y / (x + 15 * y + 3 * z54);
15063
+ var u_fn2 = (x, y, z56) => 4 * x / (x + 15 * y + 3 * z56);
15064
+ var v_fn2 = (x, y, z56) => 9 * y / (x + 15 * y + 3 * z56);
14968
15065
  var un2 = u_fn2(D50.X, D50.Y, D50.Z);
14969
15066
  var vn2 = v_fn2(D50.X, D50.Y, D50.Z);
14970
15067
  var convertLuvToXyz50 = ({ l: l2, u, v, alpha }) => {
@@ -14978,8 +15075,8 @@ var convertLuvToXyz50 = ({ l: l2, u, v, alpha }) => {
14978
15075
  let vp = v / (13 * l2) + vn2;
14979
15076
  let y = D50.Y * (l2 <= 8 ? l2 / k3 : Math.pow((l2 + 16) / 116, 3));
14980
15077
  let x = y * (9 * up) / (4 * vp);
14981
- let z54 = y * (12 - 3 * up - 20 * vp) / (4 * vp);
14982
- let res = { mode: "xyz50", x, y, z: z54 };
15078
+ let z56 = y * (12 - 3 * up - 20 * vp) / (4 * vp);
15079
+ let res = { mode: "xyz50", x, y, z: z56 };
14983
15080
  if (alpha !== void 0) {
14984
15081
  res.alpha = alpha;
14985
15082
  }
@@ -15596,15 +15693,15 @@ var convertP3ToXyz65 = (rgb2) => {
15596
15693
  var convertP3ToXyz65_default = convertP3ToXyz65;
15597
15694
 
15598
15695
  // ../../node_modules/culori/src/p3/convertXyz65ToP3.js
15599
- var convertXyz65ToP3 = ({ x, y, z: z54, alpha }) => {
15696
+ var convertXyz65ToP3 = ({ x, y, z: z56, alpha }) => {
15600
15697
  if (x === void 0) x = 0;
15601
15698
  if (y === void 0) y = 0;
15602
- if (z54 === void 0) z54 = 0;
15699
+ if (z56 === void 0) z56 = 0;
15603
15700
  let res = convertLrgbToRgb_default(
15604
15701
  {
15605
- r: x * 2.4934969119414263 - y * 0.9313836179191242 - 0.402710784450717 * z54,
15606
- g: x * -0.8294889695615749 + y * 1.7626640603183465 + 0.0236246858419436 * z54,
15607
- b: x * 0.0358458302437845 - y * 0.0761723892680418 + 0.9568845240076871 * z54
15702
+ r: x * 2.4934969119414263 - y * 0.9313836179191242 - 0.402710784450717 * z56,
15703
+ g: x * -0.8294889695615749 + y * 1.7626640603183465 + 0.0236246858419436 * z56,
15704
+ b: x * 0.0358458302437845 - y * 0.0761723892680418 + 0.9568845240076871 * z56
15608
15705
  },
15609
15706
  "p3"
15610
15707
  );
@@ -15639,19 +15736,19 @@ var gamma2 = (v) => {
15639
15736
  }
15640
15737
  return 16 * v;
15641
15738
  };
15642
- var convertXyz50ToProphoto = ({ x, y, z: z54, alpha }) => {
15739
+ var convertXyz50ToProphoto = ({ x, y, z: z56, alpha }) => {
15643
15740
  if (x === void 0) x = 0;
15644
15741
  if (y === void 0) y = 0;
15645
- if (z54 === void 0) z54 = 0;
15742
+ if (z56 === void 0) z56 = 0;
15646
15743
  let res = {
15647
15744
  mode: "prophoto",
15648
15745
  r: gamma2(
15649
- x * 1.3457868816471585 - y * 0.2555720873797946 - 0.0511018649755453 * z54
15746
+ x * 1.3457868816471585 - y * 0.2555720873797946 - 0.0511018649755453 * z56
15650
15747
  ),
15651
15748
  g: gamma2(
15652
- x * -0.5446307051249019 + y * 1.5082477428451466 + 0.0205274474364214 * z54
15749
+ x * -0.5446307051249019 + y * 1.5082477428451466 + 0.0205274474364214 * z56
15653
15750
  ),
15654
- b: gamma2(x * 0 + y * 0 + 1.2119675456389452 * z54)
15751
+ b: gamma2(x * 0 + y * 0 + 1.2119675456389452 * z56)
15655
15752
  };
15656
15753
  if (alpha !== void 0) {
15657
15754
  res.alpha = alpha;
@@ -15711,20 +15808,20 @@ var gamma3 = (v) => {
15711
15808
  }
15712
15809
  return 4.5 * v;
15713
15810
  };
15714
- var convertXyz65ToRec2020 = ({ x, y, z: z54, alpha }) => {
15811
+ var convertXyz65ToRec2020 = ({ x, y, z: z56, alpha }) => {
15715
15812
  if (x === void 0) x = 0;
15716
15813
  if (y === void 0) y = 0;
15717
- if (z54 === void 0) z54 = 0;
15814
+ if (z56 === void 0) z56 = 0;
15718
15815
  let res = {
15719
15816
  mode: "rec2020",
15720
15817
  r: gamma3(
15721
- x * 1.7166511879712683 - y * 0.3556707837763925 - 0.2533662813736599 * z54
15818
+ x * 1.7166511879712683 - y * 0.3556707837763925 - 0.2533662813736599 * z56
15722
15819
  ),
15723
15820
  g: gamma3(
15724
- x * -0.6666843518324893 + y * 1.6164812366349395 + 0.0157685458139111 * z54
15821
+ x * -0.6666843518324893 + y * 1.6164812366349395 + 0.0157685458139111 * z56
15725
15822
  ),
15726
15823
  b: gamma3(
15727
- x * 0.0176398574453108 - y * 0.0427706132578085 + 0.9421031212354739 * z54
15824
+ x * 0.0176398574453108 - y * 0.0427706132578085 + 0.9421031212354739 * z56
15728
15825
  )
15729
15826
  };
15730
15827
  if (alpha !== void 0) {
@@ -15877,15 +15974,15 @@ var definition_default26 = definition26;
15877
15974
 
15878
15975
  // ../../node_modules/culori/src/xyz65/convertXyz65ToXyz50.js
15879
15976
  var convertXyz65ToXyz50 = (xyz652) => {
15880
- let { x, y, z: z54, alpha } = xyz652;
15977
+ let { x, y, z: z56, alpha } = xyz652;
15881
15978
  if (x === void 0) x = 0;
15882
15979
  if (y === void 0) y = 0;
15883
- if (z54 === void 0) z54 = 0;
15980
+ if (z56 === void 0) z56 = 0;
15884
15981
  let res = {
15885
15982
  mode: "xyz50",
15886
- x: 1.0479298208405488 * x + 0.0229467933410191 * y - 0.0501922295431356 * z54,
15887
- y: 0.0296278156881593 * x + 0.990434484573249 * y - 0.0170738250293851 * z54,
15888
- z: -0.0092430581525912 * x + 0.0150551448965779 * y + 0.7518742899580008 * z54
15983
+ x: 1.0479298208405488 * x + 0.0229467933410191 * y - 0.0501922295431356 * z56,
15984
+ y: 0.0296278156881593 * x + 0.990434484573249 * y - 0.0170738250293851 * z56,
15985
+ z: -0.0092430581525912 * x + 0.0150551448965779 * y + 0.7518742899580008 * z56
15889
15986
  };
15890
15987
  if (alpha !== void 0) {
15891
15988
  res.alpha = alpha;
@@ -15896,15 +15993,15 @@ var convertXyz65ToXyz50_default = convertXyz65ToXyz50;
15896
15993
 
15897
15994
  // ../../node_modules/culori/src/xyz65/convertXyz50ToXyz65.js
15898
15995
  var convertXyz50ToXyz65 = (xyz502) => {
15899
- let { x, y, z: z54, alpha } = xyz502;
15996
+ let { x, y, z: z56, alpha } = xyz502;
15900
15997
  if (x === void 0) x = 0;
15901
15998
  if (y === void 0) y = 0;
15902
- if (z54 === void 0) z54 = 0;
15999
+ if (z56 === void 0) z56 = 0;
15903
16000
  let res = {
15904
16001
  mode: "xyz65",
15905
- x: 0.9554734527042182 * x - 0.0230985368742614 * y + 0.0632593086610217 * z54,
15906
- y: -0.0283697069632081 * x + 1.0099954580058226 * y + 0.021041398966943 * z54,
15907
- z: 0.0123140016883199 * x - 0.0205076964334779 * y + 1.3303659366080753 * z54
16002
+ x: 0.9554734527042182 * x - 0.0230985368742614 * y + 0.0632593086610217 * z56,
16003
+ y: -0.0283697069632081 * x + 1.0099954580058226 * y + 0.021041398966943 * z56,
16004
+ z: 0.0123140016883199 * x - 0.0205076964334779 * y + 1.3303659366080753 * z56
15908
16005
  };
15909
16006
  if (alpha !== void 0) {
15910
16007
  res.alpha = alpha;
@@ -16174,6 +16271,571 @@ zod.z.string().optional().nullable().transform((value) => {
16174
16271
  }
16175
16272
  );
16176
16273
 
16274
+ // ../../internal/utils/src/industry.ts
16275
+ var Industry = {
16276
+ // Finance
16277
+ BANKING: "banking",
16278
+ FINANCIAL_SERVICES: "financial_services",
16279
+ FINTECH: "fintech",
16280
+ PAYMENTS: "payments"};
16281
+
16282
+ // ../../internal/validation/src/dark-web/templates.ts
16283
+ var BRAND = { kind: "brand" };
16284
+ var lit = (value) => ({ kind: "literal", value });
16285
+ var phrase = (...tokens) => ({ type: "phrase", tokens });
16286
+ var bn = (slop, ...words) => ({
16287
+ type: "phrase",
16288
+ tokens: [BRAND, ...words.map(lit)],
16289
+ slop
16290
+ });
16291
+ var nb = (slop, ...words) => ({
16292
+ type: "phrase",
16293
+ tokens: [...words.map(lit), BRAND],
16294
+ slop
16295
+ });
16296
+ var word = (value) => ({ type: "term", value, match: "word" });
16297
+ var prefix = (value) => ({
16298
+ type: "term",
16299
+ value,
16300
+ match: "prefix"
16301
+ });
16302
+ var or = (...children) => ({
16303
+ type: "group",
16304
+ op: "OR",
16305
+ children
16306
+ });
16307
+ var and = (...children) => ({
16308
+ type: "group",
16309
+ op: "AND",
16310
+ children
16311
+ });
16312
+ var notOneOf = (...children) => ({
16313
+ type: "group",
16314
+ op: "OR",
16315
+ children,
16316
+ negated: true
16317
+ });
16318
+ var brandWith = (...values) => and(phrase(BRAND), or(...values.map(word)));
16319
+ var SCAN_TEMPLATES = [
16320
+ {
16321
+ id: "p2p-instant-transfer",
16322
+ category: "payment-fraud",
16323
+ title: "P2P and instant transfer abuse",
16324
+ description: "Actors advertising instant peer-to-peer transfers into or out of the brand, a common way to move stolen funds before a hold clears.",
16325
+ // Source rows: 1, 55. The original ("[brand]"~3) was proximity on one
16326
+ // position, a no-op; dropped rather than ported.
16327
+ query: and(
16328
+ or(
16329
+ phrase(BRAND, lit("to"), BRAND),
16330
+ bn(2, "instant"),
16331
+ bn(2, "anyone"),
16332
+ bn(1, "pay"),
16333
+ bn(3, "pay anyone"),
16334
+ bn(2, "p2p"),
16335
+ bn(3, "instant transfer"),
16336
+ bn(3, "receive from anyone"),
16337
+ bn(4, "phone number"),
16338
+ bn(2, "member to member"),
16339
+ nb(3, "send")
16340
+ ),
16341
+ notOneOf(word("rate"), word("price"))
16342
+ )
16343
+ },
16344
+ {
16345
+ id: "transfers-ach-direct-debit",
16346
+ category: "payment-fraud",
16347
+ title: "Transfers, ACH and direct debit",
16348
+ description: "Discussion of pushing or pulling funds via ACH, direct debit or bill pay, including chatter about pending and settlement timing.",
16349
+ // Source rows: 6, 8, 18, 41. Row 18 was missing an OR before "Direct Debit".
16350
+ query: or(
16351
+ bn(1, "transfer"),
16352
+ nb(1, "transfer"),
16353
+ bn(5, "ach"),
16354
+ nb(4, "pull"),
16355
+ nb(4, "push"),
16356
+ bn(4, "pending"),
16357
+ bn(4, "settle"),
16358
+ nb(4, "link"),
16359
+ brandWith("billpay", "acctverify"),
16360
+ and(phrase(BRAND), or(phrase(lit("direct debit")), phrase(lit("direct deposit"))))
16361
+ )
16362
+ },
16363
+ {
16364
+ id: "wallet-linking",
16365
+ category: "payment-fraud",
16366
+ title: "Wallet linking and instant funding",
16367
+ description: "Linking the brand to mobile wallets for instant funding, and 'linkable' account trading where a name match is being sold.",
16368
+ // Source rows: 19, 44, 51
16369
+ query: and(
16370
+ or(
16371
+ bn(5, "apple pay"),
16372
+ bn(5, "apple"),
16373
+ bn(3, "apple linkable"),
16374
+ bn(2, "linkable"),
16375
+ bn(2, "linkables"),
16376
+ nb(2, "linkable"),
16377
+ nb(2, "link"),
16378
+ and(phrase(BRAND), phrase(lit("name match")))
16379
+ ),
16380
+ notOneOf(word("espn"), word("airtag"))
16381
+ )
16382
+ },
16383
+ {
16384
+ id: "card-data-exposure",
16385
+ category: "payment-fraud",
16386
+ title: "Card data and virtual cards",
16387
+ description: "Brand-issued card details being traded: expiry, CVV, virtual card numbers and card activation.",
16388
+ // Source rows: 43
16389
+ query: and(
16390
+ brandWith("exp", "cvv", "vcc", "debit"),
16391
+ notOneOf(word("checking"), word("limits"))
16392
+ )
16393
+ },
16394
+ {
16395
+ id: "card-loading",
16396
+ category: "payment-fraud",
16397
+ title: "Card and account loading",
16398
+ description: "'Loading' services that push stolen funds onto brand accounts or cards for a cut.",
16399
+ // Source rows: 42
16400
+ query: or(
16401
+ nb(1, "loading"),
16402
+ nb(1, "load"),
16403
+ bn(1, "loads"),
16404
+ bn(1, "loading"),
16405
+ bn(3, "drop"),
16406
+ bn(2, "cash"),
16407
+ and(
16408
+ phrase(BRAND),
16409
+ or(
16410
+ phrase(lit("loading method")),
16411
+ phrase(lit("loading guide")),
16412
+ phrase(lit("loading tut")),
16413
+ phrase(lit("loading tutorial"))
16414
+ )
16415
+ )
16416
+ )
16417
+ },
16418
+ {
16419
+ id: "cross-platform-cashout",
16420
+ category: "payment-fraud",
16421
+ title: "Cross-platform cash-out",
16422
+ description: "Moving funds from the brand into other consumer payment apps to break the trail.",
16423
+ // Source rows: 56
16424
+ query: and(
16425
+ phrase(BRAND),
16426
+ or(
16427
+ phrase(lit("cash app")),
16428
+ word("venmo"),
16429
+ phrase(lit("transfer to paypal")),
16430
+ phrase(lit("visa money transfer"))
16431
+ )
16432
+ )
16433
+ },
16434
+ {
16435
+ id: "crypto-offramp",
16436
+ category: "payment-fraud",
16437
+ title: "Crypto off-ramp",
16438
+ description: "Converting funds held at the brand into cryptocurrency through exchanges and P2P markets.",
16439
+ // Source rows: 63
16440
+ query: and(
16441
+ phrase(BRAND),
16442
+ or(
16443
+ word("crypto"),
16444
+ word("moonpay"),
16445
+ word("switchere"),
16446
+ word("kraken"),
16447
+ word("paxful"),
16448
+ word("noones"),
16449
+ word("coinomi"),
16450
+ word("coinzoom"),
16451
+ word("bitcoin"),
16452
+ word("btc")
16453
+ )
16454
+ )
16455
+ },
16456
+ {
16457
+ id: "mobile-check-deposit",
16458
+ category: "deposit-fraud",
16459
+ title: "Mobile check deposit fraud",
16460
+ description: "Check fraud against mobile deposit, traded under slang including 'glass', 'MCD', 'slips' and 'persy'.",
16461
+ // Source rows: 2, 13, 35, 46
16462
+ industries: [
16463
+ Industry.BANKING,
16464
+ Industry.FINANCIAL_SERVICES,
16465
+ Industry.FINTECH,
16466
+ Industry.PAYMENTS
16467
+ ],
16468
+ query: or(
16469
+ bn(5, "checks"),
16470
+ bn(5, "check"),
16471
+ bn(5, "mobile deposit"),
16472
+ nb(5, "mobile deposit"),
16473
+ bn(5, "slip"),
16474
+ bn(5, "slips"),
16475
+ bn(5, "glass"),
16476
+ bn(5, "md"),
16477
+ bn(5, "mcd"),
16478
+ bn(5, "treasury"),
16479
+ nb(5, "treasury"),
16480
+ bn(5, "persy"),
16481
+ bn(5, "deposit limit"),
16482
+ and(phrase(BRAND), or(prefix("stimm"), word("mcd"), phrase(lit("pop on"))))
16483
+ )
16484
+ },
16485
+ {
16486
+ id: "atm-abuse",
16487
+ category: "deposit-fraud",
16488
+ title: "ATM abuse and 'box work'",
16489
+ description: "ATM deposit and withdrawal manipulation, including dispute-driven 'box work' against cash machines.",
16490
+ // Source rows: 9, 47, 57, 70
16491
+ industries: [
16492
+ Industry.BANKING,
16493
+ Industry.FINANCIAL_SERVICES,
16494
+ Industry.FINTECH,
16495
+ Industry.PAYMENTS
16496
+ ],
16497
+ query: or(
16498
+ bn(2, "atm"),
16499
+ bn(2, "withdrawal"),
16500
+ and(phrase(BRAND), phrase(lit("box work"))),
16501
+ and(
16502
+ phrase(BRAND),
16503
+ or(
16504
+ phrase(lit("atm deposit")),
16505
+ phrase(lit("atm withdrawal")),
16506
+ phrase(lit("atm withdraw")),
16507
+ phrase(lit("atm limit")),
16508
+ phrase(lit("atm card")),
16509
+ phrase(lit("cash withdrawal fee"))
16510
+ )
16511
+ )
16512
+ )
16513
+ },
16514
+ {
16515
+ id: "retail-cashout-network",
16516
+ category: "deposit-fraud",
16517
+ title: "Retail cash-out network",
16518
+ description: "Cash-out through retail and ATM partner networks, where funds are pulled at a storefront rather than a branch.",
16519
+ // Source rows: 30
16520
+ industries: [
16521
+ Industry.BANKING,
16522
+ Industry.FINANCIAL_SERVICES,
16523
+ Industry.FINTECH,
16524
+ Industry.PAYMENTS
16525
+ ],
16526
+ query: or(
16527
+ bn(5, "allpoint"),
16528
+ bn(5, "walgreens"),
16529
+ bn(5, "cvs"),
16530
+ bn(5, "7-eleven"),
16531
+ bn(5, "7eleven"),
16532
+ bn(5, "711"),
16533
+ bn(5, "circle k"),
16534
+ bn(5, "target"),
16535
+ bn(5, "visa plus alliance"),
16536
+ bn(3, "moneypass"),
16537
+ bn(3, "vanilladirect"),
16538
+ bn(3, "vanilla"),
16539
+ bn(3, "dollar general"),
16540
+ bn(3, "dollar tree"),
16541
+ bn(3, "safeway"),
16542
+ bn(3, "walmart"),
16543
+ bn(3, "speedway")
16544
+ )
16545
+ },
16546
+ {
16547
+ id: "otp-bots",
16548
+ category: "account-takeover",
16549
+ title: "OTP bots and 2FA bypass",
16550
+ description: "Automated one-time-passcode interception services targeting the brand's login and step-up flows.",
16551
+ // Source rows: 34
16552
+ query: or(
16553
+ bn(2, "otp"),
16554
+ nb(1, "otp"),
16555
+ phrase(BRAND, lit("otp bot")),
16556
+ bn(2, "2fa"),
16557
+ bn(2, "code"),
16558
+ bn(2, "bypass"),
16559
+ bn(2, "bot"),
16560
+ bn(2, "bots")
16561
+ )
16562
+ },
16563
+ {
16564
+ id: "remote-access-tooling",
16565
+ category: "account-takeover",
16566
+ title: "Remote-access tooling",
16567
+ description: "Legitimate remote-management tools named alongside the brand, the standard toolkit for support-impersonation account takeover.",
16568
+ // Source rows: 29, 64. Truncated by the export; recoverable portion only.
16569
+ query: and(
16570
+ phrase(BRAND),
16571
+ or(
16572
+ word("action1"),
16573
+ phrase(lit("ammy admin")),
16574
+ word("ammyadmin"),
16575
+ word("anydesk"),
16576
+ phrase(lit("any desk")),
16577
+ word("atera"),
16578
+ word("beanywhere"),
16579
+ phrase(lit("chrome remote desktop")),
16580
+ word("connectwise"),
16581
+ word("screenconnect"),
16582
+ word("dameware"),
16583
+ word("domotz"),
16584
+ word("dwservice"),
16585
+ word("fleetdeck"),
16586
+ word("intelliadmin"),
16587
+ phrase(lit("itarian endpoint manager")),
16588
+ phrase(lit("level.io")),
16589
+ word("meshcentral"),
16590
+ word("mremoteng"),
16591
+ phrase(lit("n-able")),
16592
+ word("ngrok"),
16593
+ phrase(lit("pdq deploy")),
16594
+ word("pulseway"),
16595
+ word("realvnc"),
16596
+ phrase(lit("remote utilities")),
16597
+ word("rport")
16598
+ )
16599
+ )
16600
+ },
16601
+ {
16602
+ id: "antidetect-kyc-evasion",
16603
+ category: "account-takeover",
16604
+ title: "Anti-detect and KYC evasion tooling",
16605
+ description: "Fingerprint spoofing, residential proxies and emulators used to defeat the brand's device and identity checks.",
16606
+ // Source row: 40. The original filtered to Telegram OR Dread; DarkOwl's
16607
+ // `source` takes one value, so precision comes from the tooling terms.
16608
+ query: and(
16609
+ phrase(BRAND),
16610
+ or(
16611
+ word("tlo"),
16612
+ word("scamalytics"),
16613
+ word("burner"),
16614
+ prefix("prox"),
16615
+ word("vpn"),
16616
+ word("vps"),
16617
+ word("antidetect"),
16618
+ phrase(lit("anti-detect")),
16619
+ prefix("emulat")
16620
+ )
16621
+ )
16622
+ },
16623
+ {
16624
+ id: "brand-domains-sso",
16625
+ category: "account-takeover",
16626
+ title: "Brand domains and SSO endpoints",
16627
+ description: "The brand's own domains and identity-provider endpoints appearing in credential and access chatter.",
16628
+ // Source row: 31, as proximity not literal hostnames — the index tokenizes
16629
+ // `chime.okta.com`, so a glued literal would never match. Row 23's
16630
+ // "[brand] pass*"~1 is omitted: no wildcards inside a phrase.
16631
+ query: and(
16632
+ or(bn(2, "okta"), bn(2, "login"), bn(2, "portal"), bn(1, "bank")),
16633
+ notOneOf(word("zendesk"))
16634
+ )
16635
+ },
16636
+ {
16637
+ id: "account-opening-abuse",
16638
+ category: "onboarding-abuse",
16639
+ title: "Account opening and self-registration",
16640
+ description: "Bulk or fraudulent account creation against the brand, including 'self-reg' and 'drop' accounts opened on stolen identities.",
16641
+ // Source rows: 5, 17, 54
16642
+ query: or(
16643
+ bn(5, "enroll"),
16644
+ nb(5, "enroll"),
16645
+ bn(5, "open"),
16646
+ nb(5, "open"),
16647
+ bn(5, "drop"),
16648
+ nb(5, "drop"),
16649
+ nb(5, "make"),
16650
+ nb(5, "create"),
16651
+ nb(5, "fresh"),
16652
+ nb(5, "register"),
16653
+ bn(5, "self-reg"),
16654
+ bn(5, "selfreg"),
16655
+ bn(5, "self-registered"),
16656
+ bn(5, "self register"),
16657
+ bn(1, "fullz")
16658
+ )
16659
+ },
16660
+ {
16661
+ id: "identity-documents",
16662
+ category: "onboarding-abuse",
16663
+ title: "Identity documents and fullz",
16664
+ description: "Trade in identity packages and lookup services used to pass onboarding checks. Anchored to the brand at the root, since the source query was brand-independent.",
16665
+ // Source row: 15. Lexicon-only, so the renderer AND-s the anchor on.
16666
+ query: or(
16667
+ word("fullz"),
16668
+ phrase(lit("get pros")),
16669
+ phrase(lit("driver's license")),
16670
+ phrase(lit("tlo bot")),
16671
+ phrase(lit("tlo plug")),
16672
+ phrase(lit("need dl")),
16673
+ phrase(lit("get id"))
16674
+ )
16675
+ },
16676
+ {
16677
+ id: "referral-bonus-abuse",
16678
+ category: "onboarding-abuse",
16679
+ title: "Referral and bonus abuse",
16680
+ description: "Methods for farming the brand's referral bonuses and promotional offers, often paired with fake direct deposits.",
16681
+ // Source rows: 48
16682
+ query: and(
16683
+ or(bn(3, "referral"), bn(3, "bonus"), bn(3, "promotion"), bn(3, "offer")),
16684
+ or(
16685
+ word("bypass"),
16686
+ word("dd"),
16687
+ phrase(lit("direct deposit")),
16688
+ phrase(lit("work around")),
16689
+ word("trick"),
16690
+ word("method"),
16691
+ word("glitch"),
16692
+ word("tutorial"),
16693
+ word("tut"),
16694
+ word("wave"),
16695
+ word("sauce")
16696
+ )
16697
+ )
16698
+ },
16699
+ {
16700
+ id: "insider-recruitment",
16701
+ category: "insider-threat",
16702
+ title: "Insider recruitment",
16703
+ description: "Actors seeking or advertising an employee on the inside ('inny'), the highest-severity signal in this catalog.",
16704
+ // Source rows: 7, 38
16705
+ query: and(
16706
+ or(
16707
+ bn(4, "inny"),
16708
+ bn(4, "insider"),
16709
+ and(
16710
+ phrase(BRAND),
16711
+ or(
16712
+ word("insider"),
16713
+ word("inny"),
16714
+ phrase(lit("in office")),
16715
+ word("employee"),
16716
+ phrase(lit("i work at")),
16717
+ phrase(lit("i am an employee")),
16718
+ phrase(lit("i can get access")),
16719
+ phrase(lit("i have access to")),
16720
+ phrase(lit("employed at")),
16721
+ word("occupation")
16722
+ )
16723
+ )
16724
+ ),
16725
+ notOneOf(phrase(lit("welcome to insider chat")))
16726
+ )
16727
+ },
16728
+ {
16729
+ id: "mule-crew-recruitment",
16730
+ category: "insider-threat",
16731
+ title: "Mule and cash-out crew recruitment",
16732
+ description: "Recruitment of account holders to receive and forward funds, priced as a revenue split ('50/50', '60/40').",
16733
+ // Source rows: 39
16734
+ query: or(
16735
+ bn(5, "cl"),
16736
+ bn(5, "mule"),
16737
+ bn(5, "tap in"),
16738
+ bn(5, "50/50"),
16739
+ bn(5, "60/40"),
16740
+ bn(5, "striker")
16741
+ )
16742
+ },
16743
+ {
16744
+ id: "methods-and-tutorials",
16745
+ category: "tradecraft",
16746
+ title: "Methods, 'sauce' and tutorials",
16747
+ description: "Fraud methods being taught or sold against the brand, under the usual slang: sauce, glitch, tut, wave, motion, tap in.",
16748
+ // Source rows: 4, 21, 59
16749
+ query: or(
16750
+ bn(3, "sauce"),
16751
+ bn(3, "method"),
16752
+ bn(3, "guide"),
16753
+ bn(3, "tut"),
16754
+ bn(3, "tutorial"),
16755
+ bn(4, "hack"),
16756
+ bn(4, "glitch"),
16757
+ bn(4, "wave"),
16758
+ bn(4, "motion"),
16759
+ bn(4, "walkthrough"),
16760
+ bn(4, "walk through"),
16761
+ bn(2, "tap in"),
16762
+ bn(2, "tapin"),
16763
+ nb(2, "tapn")
16764
+ )
16765
+ },
16766
+ {
16767
+ id: "hack-leak-chatter",
16768
+ category: "tradecraft",
16769
+ title: "Hack and leak chatter",
16770
+ description: "Claims of having hacked, breached or leaked the brand, whether credible or not.",
16771
+ // Source rows: 28
16772
+ query: or(
16773
+ bn(4, "hack"),
16774
+ bn(4, "glitch"),
16775
+ nb(4, "hacking"),
16776
+ bn(4, "leak"),
16777
+ bn(4, "leaked")
16778
+ )
16779
+ },
16780
+ {
16781
+ id: "support-impersonation",
16782
+ category: "brand-abuse",
16783
+ title: "Support impersonation",
16784
+ description: "Actors impersonating the brand's support or service staff to social-engineer customers.",
16785
+ // Source rows: 36
16786
+ query: or(
16787
+ bn(1, "agent"),
16788
+ bn(1, "support"),
16789
+ bn(1, "service"),
16790
+ bn(1, "customer"),
16791
+ nb(1, "impersonate"),
16792
+ bn(1, "impersonation"),
16793
+ bn(3, "scam"),
16794
+ nb(1, "scam")
16795
+ )
16796
+ },
16797
+ {
16798
+ id: "social-impersonation",
16799
+ category: "brand-abuse",
16800
+ title: "Social account impersonation",
16801
+ description: "The brand discussed alongside social platforms, where fake support and giveaway accounts are staged.",
16802
+ // Source rows: 25
16803
+ query: or(
16804
+ bn(5, "instagram"),
16805
+ bn(5, "insta"),
16806
+ bn(5, "facebook"),
16807
+ bn(5, "twitter"),
16808
+ bn(5, "tiktok")
16809
+ )
16810
+ },
16811
+ {
16812
+ id: "vendor-supply-chain",
16813
+ category: "data-exposure",
16814
+ title: "Vendor and BPO supply chain",
16815
+ description: "Outsourced support and processing vendors named alongside the brand, where a third-party breach exposes brand customer data.",
16816
+ // Source row: 60, truncated by the export. Generic BPO/CX providers; a
16817
+ // per-org vendor list would be materially better.
16818
+ query: and(
16819
+ phrase(BRAND),
16820
+ or(
16821
+ word("zendesk"),
16822
+ word("teleperformance"),
16823
+ word("concentrix"),
16824
+ word("taskus"),
16825
+ word("genpact"),
16826
+ word("ubiquity"),
16827
+ word("hioperator"),
16828
+ word("galileo"),
16829
+ word("fiserv"),
16830
+ phrase(lit("etech global"))
16831
+ )
16832
+ )
16833
+ }
16834
+ ];
16835
+ new Map(
16836
+ SCAN_TEMPLATES.map((template) => [template.id, template])
16837
+ );
16838
+
16177
16839
  // src/client.ts
16178
16840
  function trimTrailingSlashes(url) {
16179
16841
  return url.replace(/\/+$/, "");