@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.mjs CHANGED
@@ -1255,17 +1255,17 @@ var PolyDateFormatter = class {
1255
1255
  constructor(dt, intl, opts) {
1256
1256
  this.opts = opts;
1257
1257
  this.originalZone = void 0;
1258
- let z54 = void 0;
1258
+ let z56 = void 0;
1259
1259
  if (this.opts.timeZone) {
1260
1260
  this.dt = dt;
1261
1261
  } else if (dt.zone.type === "fixed") {
1262
1262
  const gmtOffset = -1 * (dt.offset / 60);
1263
1263
  const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;
1264
1264
  if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) {
1265
- z54 = offsetZ;
1265
+ z56 = offsetZ;
1266
1266
  this.dt = dt;
1267
1267
  } else {
1268
- z54 = "UTC";
1268
+ z56 = "UTC";
1269
1269
  this.dt = dt.offset === 0 ? dt : dt.setZone("UTC").plus({ minutes: dt.offset });
1270
1270
  this.originalZone = dt.zone;
1271
1271
  }
@@ -1273,14 +1273,14 @@ var PolyDateFormatter = class {
1273
1273
  this.dt = dt;
1274
1274
  } else if (dt.zone.type === "iana") {
1275
1275
  this.dt = dt;
1276
- z54 = dt.zone.name;
1276
+ z56 = dt.zone.name;
1277
1277
  } else {
1278
- z54 = "UTC";
1278
+ z56 = "UTC";
1279
1279
  this.dt = dt.setZone("UTC").plus({ minutes: dt.offset });
1280
1280
  this.originalZone = dt.zone;
1281
1281
  }
1282
1282
  const intlOpts = __spreadValues({}, this.opts);
1283
- intlOpts.timeZone = intlOpts.timeZone || z54;
1283
+ intlOpts.timeZone = intlOpts.timeZone || z56;
1284
1284
  this.dtf = getCachedDTF(intl, intlOpts);
1285
1285
  }
1286
1286
  format() {
@@ -11430,6 +11430,50 @@ z.object({
11430
11430
  endDate: z.date()
11431
11431
  })
11432
11432
  });
11433
+ var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
11434
+ var utcDateSchema = (endOfDay) => z.preprocess((value) => {
11435
+ if (typeof value !== "string") {
11436
+ return value;
11437
+ }
11438
+ const parsed = DateTime.fromISO(value, { zone: "utc" });
11439
+ if (!parsed.isValid) {
11440
+ return new Date(Number.NaN);
11441
+ }
11442
+ return endOfDay && DATE_ONLY_PATTERN.test(value) ? parsed.endOf("day").toJSDate() : parsed.toJSDate();
11443
+ }, z.coerce.date());
11444
+ var funnelCountsSchema = z.object({
11445
+ blocked: z.number().int().nonnegative().describe(
11446
+ "Distinct assets whose earliest report for this org falls in the range and whose current status or pending status is BLOCKED"
11447
+ )
11448
+ });
11449
+ z.object({
11450
+ slug: z.string().min(1).describe("Organization slug"),
11451
+ startDate: utcDateSchema(false).describe(
11452
+ "Only include assets whose earliest report was created on or after this date. A date-only value (YYYY-MM-DD) starts at midnight UTC"
11453
+ ),
11454
+ endDate: utcDateSchema(true).describe(
11455
+ "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"
11456
+ ),
11457
+ brandIds: z.array(z.number().int().positive()).optional().describe("Only include assets belonging to these brands")
11458
+ }).refine((input) => input.startDate.getTime() <= input.endDate.getTime(), {
11459
+ message: "startDate must be on or before endDate",
11460
+ path: ["startDate"]
11461
+ });
11462
+ z.object({
11463
+ range: z.object({
11464
+ startDate: z.date(),
11465
+ endDate: z.date()
11466
+ }),
11467
+ customer: funnelCountsSchema.describe(
11468
+ "Currently blocked assets first reported by the customer (reportedByCustomer, external reporter, or customer-role user)"
11469
+ ),
11470
+ chainpatrol: funnelCountsSchema.describe(
11471
+ "Currently blocked assets first reported by ChainPatrol staff or automation. Staff and automation are collapsed together."
11472
+ ),
11473
+ totals: funnelCountsSchema.describe(
11474
+ "customer + chainpatrol. This is a first-report blocked cohort and does not equal metrics/found, which counts assets by blockedAt."
11475
+ )
11476
+ });
11433
11477
  z.object({
11434
11478
  slug: z.string().min(1),
11435
11479
  startDate: z.date().optional(),
@@ -11461,8 +11505,8 @@ z.object({
11461
11505
  })
11462
11506
  )
11463
11507
  });
11464
- var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
11465
- var utcDateSchema = (endOfDay) => z.preprocess((value) => {
11508
+ var DATE_ONLY_PATTERN2 = /^\d{4}-\d{2}-\d{2}$/;
11509
+ var utcDateSchema2 = (endOfDay) => z.preprocess((value) => {
11466
11510
  if (typeof value !== "string") {
11467
11511
  return value;
11468
11512
  }
@@ -11470,14 +11514,14 @@ var utcDateSchema = (endOfDay) => z.preprocess((value) => {
11470
11514
  if (!parsed.isValid) {
11471
11515
  return new Date(Number.NaN);
11472
11516
  }
11473
- return endOfDay && DATE_ONLY_PATTERN.test(value) ? parsed.endOf("day").toJSDate() : parsed.toJSDate();
11517
+ return endOfDay && DATE_ONLY_PATTERN2.test(value) ? parsed.endOf("day").toJSDate() : parsed.toJSDate();
11474
11518
  }, z.coerce.date());
11475
11519
  z.object({
11476
11520
  slug: z.string().min(1).describe("Organization slug"),
11477
- startDate: utcDateSchema(false).optional().describe(
11521
+ startDate: utcDateSchema2(false).optional().describe(
11478
11522
  "Only include takedowns completed on or after this date. A date-only value (YYYY-MM-DD) starts at midnight UTC"
11479
11523
  ),
11480
- endDate: utcDateSchema(true).optional().describe(
11524
+ endDate: utcDateSchema2(true).optional().describe(
11481
11525
  "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"
11482
11526
  ),
11483
11527
  blockLabel: z.string().optional().describe(
@@ -11500,6 +11544,55 @@ z.object({
11500
11544
  "Median time from takedown start to completion, one entry per asset type, ascending by median"
11501
11545
  )
11502
11546
  });
11547
+ z.object({
11548
+ slug: z.string().min(1).describe("Organization slug"),
11549
+ months: z.number().int().min(2).max(36).default(12).describe("Size of the trailing window in whole months, including the current month"),
11550
+ assetTypes: z.array(z.nativeEnum(AssetType)).max(5).optional().describe(
11551
+ "Break the trend down by these asset types, one series each. Omit for a single series pooled across every asset type"
11552
+ ),
11553
+ blockLabel: z.string().optional().describe(
11554
+ 'Only include assets blocked with this review label, e.g. "Brand Impersonation", "Employee Impersonation" or "Targeting Org Users"'
11555
+ ),
11556
+ brandType: z.nativeEnum(BrandType).optional().describe("Only include assets belonging to brands of this type"),
11557
+ countryCodes: z.array(z.string().length(2)).optional().describe("Only include assets scanned from these ISO 3166-1 alpha-2 countries"),
11558
+ brandIds: z.array(z.number().int().positive()).optional().describe("Only include assets belonging to these brands")
11559
+ });
11560
+ var MetricsTakedownTimeTrendPointSchema = z.object({
11561
+ period: z.string().describe("Month the takedowns completed in, as a yyyy-MM key"),
11562
+ medianTakedownMinutes: z.number().nullable().describe("Null when no takedowns completed in this month"),
11563
+ medianTakedownHours: z.number().nullable(),
11564
+ medianTakedownDays: z.number().nullable(),
11565
+ count: z.number().describe("Number of completed takedowns behind the median")
11566
+ });
11567
+ var MetricsTakedownTimeTrendChangeSchema = z.object({
11568
+ latest: MetricsTakedownTimeTrendPointSchema.nullable().describe(
11569
+ "Most recent month in the window that had completed takedowns"
11570
+ ),
11571
+ previous: MetricsTakedownTimeTrendPointSchema.nullable().describe(
11572
+ "Month with completed takedowns immediately before `latest`, skipping empty months"
11573
+ ),
11574
+ percentChange: z.number().nullable().describe(
11575
+ "Change in the median from `previous` to `latest`, as a percentage. Negative means takedowns got faster. Null when there is no comparable earlier month"
11576
+ ),
11577
+ direction: z.enum(["improved", "worsened", "unchanged"]).nullable().describe("Sign of `percentChange`, read as lower-is-better. Null when it is null")
11578
+ });
11579
+ z.object({
11580
+ months: z.number().describe("Size of the trailing window that was used"),
11581
+ periods: z.array(z.string()).describe("Every yyyy-MM key in the window, oldest first"),
11582
+ series: z.array(
11583
+ z.object({
11584
+ assetType: z.nativeEnum(AssetType).nullable().describe("Null for the series pooled across every asset type"),
11585
+ points: z.array(MetricsTakedownTimeTrendPointSchema).describe(
11586
+ "One point per month in the window, oldest first. Months with no completed takedowns carry a null median"
11587
+ ),
11588
+ change: MetricsTakedownTimeTrendChangeSchema.describe(
11589
+ "Month-over-month comparison for this series"
11590
+ )
11591
+ })
11592
+ ).describe(
11593
+ "One series per requested asset type, in the order requested, or a single pooled series when no asset types were given"
11594
+ )
11595
+ });
11503
11596
  z.object({
11504
11597
  slug: z.string().optional().describe(
11505
11598
  "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."
@@ -11886,7 +11979,9 @@ var organizationMetricsIncludeFieldSchema = z.enum([
11886
11979
  "newThreats",
11887
11980
  "threatsWatchlisted",
11888
11981
  "takedownsFiled",
11982
+ "takedownsInProgress",
11889
11983
  "takedownsCompleted",
11984
+ "takedownsCancelled",
11890
11985
  "domainThreats",
11891
11986
  "twitterThreats",
11892
11987
  "telegramThreats",
@@ -11928,7 +12023,7 @@ z.object({
11928
12023
  startDate: z.coerce.date().optional(),
11929
12024
  endDate: z.coerce.date().optional(),
11930
12025
  include: z.array(organizationMetricsIncludeFieldSchema).optional().describe(
11931
- "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`."
12026
+ "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."
11932
12027
  )
11933
12028
  }).refine(
11934
12029
  (value) => {
@@ -11956,7 +12051,9 @@ var metricsObjectSchema = z.object({
11956
12051
  newThreats: metricValue,
11957
12052
  threatsWatchlisted: metricValue,
11958
12053
  takedownsFiled: metricValue,
12054
+ takedownsInProgress: metricValue,
11959
12055
  takedownsCompleted: metricValue,
12056
+ takedownsCancelled: metricValue,
11960
12057
  domainThreats: metricValue,
11961
12058
  twitterThreats: metricValue,
11962
12059
  telegramThreats: metricValue,
@@ -13339,20 +13436,20 @@ var convertA98ToXyz65_default = convertA98ToXyz65;
13339
13436
 
13340
13437
  // ../../node_modules/culori/src/a98/convertXyz65ToA98.js
13341
13438
  var gamma = (v) => Math.pow(Math.abs(v), 256 / 563) * Math.sign(v);
13342
- var convertXyz65ToA98 = ({ x, y, z: z54, alpha }) => {
13439
+ var convertXyz65ToA98 = ({ x, y, z: z56, alpha }) => {
13343
13440
  if (x === void 0) x = 0;
13344
13441
  if (y === void 0) y = 0;
13345
- if (z54 === void 0) z54 = 0;
13442
+ if (z56 === void 0) z56 = 0;
13346
13443
  let res = {
13347
13444
  mode: "a98",
13348
13445
  r: gamma(
13349
- x * 2.0415879038107465 - y * 0.5650069742788597 - 0.3447313507783297 * z54
13446
+ x * 2.0415879038107465 - y * 0.5650069742788597 - 0.3447313507783297 * z56
13350
13447
  ),
13351
13448
  g: gamma(
13352
- x * -0.9692436362808798 + y * 1.8759675015077206 + 0.0415550574071756 * z54
13449
+ x * -0.9692436362808798 + y * 1.8759675015077206 + 0.0415550574071756 * z56
13353
13450
  ),
13354
13451
  b: gamma(
13355
- x * 0.0134442806320312 - y * 0.1183623922310184 + 1.0151749943912058 * z54
13452
+ x * 0.0134442806320312 - y * 0.1183623922310184 + 1.0151749943912058 * z56
13356
13453
  )
13357
13454
  };
13358
13455
  if (alpha !== void 0) {
@@ -13419,14 +13516,14 @@ var convertLrgbToRgb = ({ r, g, b, alpha }, mode = "rgb") => {
13419
13516
  var convertLrgbToRgb_default = convertLrgbToRgb;
13420
13517
 
13421
13518
  // ../../node_modules/culori/src/xyz65/convertXyz65ToRgb.js
13422
- var convertXyz65ToRgb = ({ x, y, z: z54, alpha }) => {
13519
+ var convertXyz65ToRgb = ({ x, y, z: z56, alpha }) => {
13423
13520
  if (x === void 0) x = 0;
13424
13521
  if (y === void 0) y = 0;
13425
- if (z54 === void 0) z54 = 0;
13522
+ if (z56 === void 0) z56 = 0;
13426
13523
  let res = convertLrgbToRgb_default({
13427
- r: x * 3.2409699419045226 - y * 1.537383177570094 - 0.4986107602930034 * z54,
13428
- g: x * -0.9692436362808796 + y * 1.8759675015077204 + 0.0415550574071756 * z54,
13429
- b: x * 0.0556300796969936 - y * 0.2039769588889765 + 1.0569715142428784 * z54
13524
+ r: x * 3.2409699419045226 - y * 1.537383177570094 - 0.4986107602930034 * z56,
13525
+ g: x * -0.9692436362808796 + y * 1.8759675015077204 + 0.0415550574071756 * z56,
13526
+ b: x * 0.0556300796969936 - y * 0.2039769588889765 + 1.0569715142428784 * z56
13430
13527
  });
13431
13528
  if (alpha !== void 0) {
13432
13529
  res.alpha = alpha;
@@ -13676,13 +13773,13 @@ var convertLab65ToRgb_default = convertLab65ToRgb;
13676
13773
 
13677
13774
  // ../../node_modules/culori/src/lab65/convertXyz65ToLab65.js
13678
13775
  var f = (value) => value > e ? Math.cbrt(value) : (k * value + 16) / 116;
13679
- var convertXyz65ToLab65 = ({ x, y, z: z54, alpha }) => {
13776
+ var convertXyz65ToLab65 = ({ x, y, z: z56, alpha }) => {
13680
13777
  if (x === void 0) x = 0;
13681
13778
  if (y === void 0) y = 0;
13682
- if (z54 === void 0) z54 = 0;
13779
+ if (z56 === void 0) z56 = 0;
13683
13780
  let f0 = f(x / D65.X);
13684
13781
  let f1 = f(y / D65.Y);
13685
- let f22 = f(z54 / D65.Z);
13782
+ let f22 = f(z56 / D65.Z);
13686
13783
  let res = {
13687
13784
  mode: "lab65",
13688
13785
  l: 116 * f1 - 16,
@@ -14339,10 +14436,10 @@ var convertItpToXyz65_default = convertItpToXyz65;
14339
14436
 
14340
14437
  // ../../node_modules/culori/src/itp/convertXyz65ToItp.js
14341
14438
  var toAbs = (c2 = 0) => Math.max(c2 * YW, 0);
14342
- var convertXyz65ToItp = ({ x, y, z: z54, alpha }) => {
14439
+ var convertXyz65ToItp = ({ x, y, z: z56, alpha }) => {
14343
14440
  const absX = toAbs(x);
14344
14441
  const absY = toAbs(y);
14345
- const absZ = toAbs(z54);
14442
+ const absZ = toAbs(z56);
14346
14443
  const l2 = transferPqEncode(
14347
14444
  0.3592832590121217 * absX + 0.6976051147779502 * absY - 0.0358915932320289 * absZ
14348
14445
  );
@@ -14400,15 +14497,15 @@ var jabPqEncode = (v) => {
14400
14497
  return Math.pow((C1 + C2 * vn3) / (1 + C3 * vn3), p);
14401
14498
  };
14402
14499
  var abs = (v = 0) => Math.max(v * 203, 0);
14403
- var convertXyz65ToJab = ({ x, y, z: z54, alpha }) => {
14500
+ var convertXyz65ToJab = ({ x, y, z: z56, alpha }) => {
14404
14501
  x = abs(x);
14405
14502
  y = abs(y);
14406
- z54 = abs(z54);
14407
- let xp = 1.15 * x - 0.15 * z54;
14503
+ z56 = abs(z56);
14504
+ let xp = 1.15 * x - 0.15 * z56;
14408
14505
  let yp = 0.66 * y + 0.34 * x;
14409
- let l2 = jabPqEncode(0.41478972 * xp + 0.579999 * yp + 0.014648 * z54);
14410
- let m = jabPqEncode(-0.20151 * xp + 1.120649 * yp + 0.0531008 * z54);
14411
- let s2 = jabPqEncode(-0.0166008 * xp + 0.2648 * yp + 0.6684799 * z54);
14506
+ let l2 = jabPqEncode(0.41478972 * xp + 0.579999 * yp + 0.014648 * z56);
14507
+ let m = jabPqEncode(-0.20151 * xp + 1.120649 * yp + 0.0531008 * z56);
14508
+ let s2 = jabPqEncode(-0.0166008 * xp + 0.2648 * yp + 0.6684799 * z56);
14412
14509
  let i = (l2 + m) / 2;
14413
14510
  let res = {
14414
14511
  mode: "jab",
@@ -14594,14 +14691,14 @@ var convertLabToXyz50 = ({ l: l2, a, b, alpha }) => {
14594
14691
  var convertLabToXyz50_default = convertLabToXyz50;
14595
14692
 
14596
14693
  // ../../node_modules/culori/src/xyz50/convertXyz50ToRgb.js
14597
- var convertXyz50ToRgb = ({ x, y, z: z54, alpha }) => {
14694
+ var convertXyz50ToRgb = ({ x, y, z: z56, alpha }) => {
14598
14695
  if (x === void 0) x = 0;
14599
14696
  if (y === void 0) y = 0;
14600
- if (z54 === void 0) z54 = 0;
14697
+ if (z56 === void 0) z56 = 0;
14601
14698
  let res = convertLrgbToRgb_default({
14602
- r: x * 3.1341359569958707 - y * 1.6173863321612538 - 0.4906619460083532 * z54,
14603
- g: x * -0.978795502912089 + y * 1.916254567259524 + 0.03344273116131949 * z54,
14604
- b: x * 0.07195537988411677 - y * 0.2289768264158322 + 1.405386058324125 * z54
14699
+ r: x * 3.1341359569958707 - y * 1.6173863321612538 - 0.4906619460083532 * z56,
14700
+ g: x * -0.978795502912089 + y * 1.916254567259524 + 0.03344273116131949 * z56,
14701
+ b: x * 0.07195537988411677 - y * 0.2289768264158322 + 1.405386058324125 * z56
14605
14702
  });
14606
14703
  if (alpha !== void 0) {
14607
14704
  res.alpha = alpha;
@@ -14632,13 +14729,13 @@ var convertRgbToXyz50_default = convertRgbToXyz50;
14632
14729
 
14633
14730
  // ../../node_modules/culori/src/lab/convertXyz50ToLab.js
14634
14731
  var f2 = (value) => value > e3 ? Math.cbrt(value) : (k3 * value + 16) / 116;
14635
- var convertXyz50ToLab = ({ x, y, z: z54, alpha }) => {
14732
+ var convertXyz50ToLab = ({ x, y, z: z56, alpha }) => {
14636
14733
  if (x === void 0) x = 0;
14637
14734
  if (y === void 0) y = 0;
14638
- if (z54 === void 0) z54 = 0;
14735
+ if (z56 === void 0) z56 = 0;
14639
14736
  let f0 = f2(x / D50.X);
14640
14737
  let f1 = f2(y / D50.Y);
14641
- let f22 = f2(z54 / D50.Z);
14738
+ let f22 = f2(z56 / D50.Z);
14642
14739
  let res = {
14643
14740
  mode: "lab",
14644
14741
  l: 116 * f1 - 16,
@@ -14873,18 +14970,18 @@ var convertLchuvToLuv = ({ l: l2, c: c2, h, alpha }) => {
14873
14970
  var convertLchuvToLuv_default = convertLchuvToLuv;
14874
14971
 
14875
14972
  // ../../node_modules/culori/src/luv/convertXyz50ToLuv.js
14876
- var u_fn = (x, y, z54) => 4 * x / (x + 15 * y + 3 * z54);
14877
- var v_fn = (x, y, z54) => 9 * y / (x + 15 * y + 3 * z54);
14973
+ var u_fn = (x, y, z56) => 4 * x / (x + 15 * y + 3 * z56);
14974
+ var v_fn = (x, y, z56) => 9 * y / (x + 15 * y + 3 * z56);
14878
14975
  var un = u_fn(D50.X, D50.Y, D50.Z);
14879
14976
  var vn = v_fn(D50.X, D50.Y, D50.Z);
14880
14977
  var l_fn = (value) => value <= e3 ? k3 * value : 116 * Math.cbrt(value) - 16;
14881
- var convertXyz50ToLuv = ({ x, y, z: z54, alpha }) => {
14978
+ var convertXyz50ToLuv = ({ x, y, z: z56, alpha }) => {
14882
14979
  if (x === void 0) x = 0;
14883
14980
  if (y === void 0) y = 0;
14884
- if (z54 === void 0) z54 = 0;
14981
+ if (z56 === void 0) z56 = 0;
14885
14982
  let l2 = l_fn(y / D50.Y);
14886
- let u = u_fn(x, y, z54);
14887
- let v = v_fn(x, y, z54);
14983
+ let u = u_fn(x, y, z56);
14984
+ let v = v_fn(x, y, z56);
14888
14985
  if (!isFinite(u) || !isFinite(v)) {
14889
14986
  l2 = u = v = 0;
14890
14987
  } else {
@@ -14905,8 +15002,8 @@ var convertXyz50ToLuv = ({ x, y, z: z54, alpha }) => {
14905
15002
  var convertXyz50ToLuv_default = convertXyz50ToLuv;
14906
15003
 
14907
15004
  // ../../node_modules/culori/src/luv/convertLuvToXyz50.js
14908
- var u_fn2 = (x, y, z54) => 4 * x / (x + 15 * y + 3 * z54);
14909
- var v_fn2 = (x, y, z54) => 9 * y / (x + 15 * y + 3 * z54);
15005
+ var u_fn2 = (x, y, z56) => 4 * x / (x + 15 * y + 3 * z56);
15006
+ var v_fn2 = (x, y, z56) => 9 * y / (x + 15 * y + 3 * z56);
14910
15007
  var un2 = u_fn2(D50.X, D50.Y, D50.Z);
14911
15008
  var vn2 = v_fn2(D50.X, D50.Y, D50.Z);
14912
15009
  var convertLuvToXyz50 = ({ l: l2, u, v, alpha }) => {
@@ -14920,8 +15017,8 @@ var convertLuvToXyz50 = ({ l: l2, u, v, alpha }) => {
14920
15017
  let vp = v / (13 * l2) + vn2;
14921
15018
  let y = D50.Y * (l2 <= 8 ? l2 / k3 : Math.pow((l2 + 16) / 116, 3));
14922
15019
  let x = y * (9 * up) / (4 * vp);
14923
- let z54 = y * (12 - 3 * up - 20 * vp) / (4 * vp);
14924
- let res = { mode: "xyz50", x, y, z: z54 };
15020
+ let z56 = y * (12 - 3 * up - 20 * vp) / (4 * vp);
15021
+ let res = { mode: "xyz50", x, y, z: z56 };
14925
15022
  if (alpha !== void 0) {
14926
15023
  res.alpha = alpha;
14927
15024
  }
@@ -15538,15 +15635,15 @@ var convertP3ToXyz65 = (rgb2) => {
15538
15635
  var convertP3ToXyz65_default = convertP3ToXyz65;
15539
15636
 
15540
15637
  // ../../node_modules/culori/src/p3/convertXyz65ToP3.js
15541
- var convertXyz65ToP3 = ({ x, y, z: z54, alpha }) => {
15638
+ var convertXyz65ToP3 = ({ x, y, z: z56, alpha }) => {
15542
15639
  if (x === void 0) x = 0;
15543
15640
  if (y === void 0) y = 0;
15544
- if (z54 === void 0) z54 = 0;
15641
+ if (z56 === void 0) z56 = 0;
15545
15642
  let res = convertLrgbToRgb_default(
15546
15643
  {
15547
- r: x * 2.4934969119414263 - y * 0.9313836179191242 - 0.402710784450717 * z54,
15548
- g: x * -0.8294889695615749 + y * 1.7626640603183465 + 0.0236246858419436 * z54,
15549
- b: x * 0.0358458302437845 - y * 0.0761723892680418 + 0.9568845240076871 * z54
15644
+ r: x * 2.4934969119414263 - y * 0.9313836179191242 - 0.402710784450717 * z56,
15645
+ g: x * -0.8294889695615749 + y * 1.7626640603183465 + 0.0236246858419436 * z56,
15646
+ b: x * 0.0358458302437845 - y * 0.0761723892680418 + 0.9568845240076871 * z56
15550
15647
  },
15551
15648
  "p3"
15552
15649
  );
@@ -15581,19 +15678,19 @@ var gamma2 = (v) => {
15581
15678
  }
15582
15679
  return 16 * v;
15583
15680
  };
15584
- var convertXyz50ToProphoto = ({ x, y, z: z54, alpha }) => {
15681
+ var convertXyz50ToProphoto = ({ x, y, z: z56, alpha }) => {
15585
15682
  if (x === void 0) x = 0;
15586
15683
  if (y === void 0) y = 0;
15587
- if (z54 === void 0) z54 = 0;
15684
+ if (z56 === void 0) z56 = 0;
15588
15685
  let res = {
15589
15686
  mode: "prophoto",
15590
15687
  r: gamma2(
15591
- x * 1.3457868816471585 - y * 0.2555720873797946 - 0.0511018649755453 * z54
15688
+ x * 1.3457868816471585 - y * 0.2555720873797946 - 0.0511018649755453 * z56
15592
15689
  ),
15593
15690
  g: gamma2(
15594
- x * -0.5446307051249019 + y * 1.5082477428451466 + 0.0205274474364214 * z54
15691
+ x * -0.5446307051249019 + y * 1.5082477428451466 + 0.0205274474364214 * z56
15595
15692
  ),
15596
- b: gamma2(x * 0 + y * 0 + 1.2119675456389452 * z54)
15693
+ b: gamma2(x * 0 + y * 0 + 1.2119675456389452 * z56)
15597
15694
  };
15598
15695
  if (alpha !== void 0) {
15599
15696
  res.alpha = alpha;
@@ -15653,20 +15750,20 @@ var gamma3 = (v) => {
15653
15750
  }
15654
15751
  return 4.5 * v;
15655
15752
  };
15656
- var convertXyz65ToRec2020 = ({ x, y, z: z54, alpha }) => {
15753
+ var convertXyz65ToRec2020 = ({ x, y, z: z56, alpha }) => {
15657
15754
  if (x === void 0) x = 0;
15658
15755
  if (y === void 0) y = 0;
15659
- if (z54 === void 0) z54 = 0;
15756
+ if (z56 === void 0) z56 = 0;
15660
15757
  let res = {
15661
15758
  mode: "rec2020",
15662
15759
  r: gamma3(
15663
- x * 1.7166511879712683 - y * 0.3556707837763925 - 0.2533662813736599 * z54
15760
+ x * 1.7166511879712683 - y * 0.3556707837763925 - 0.2533662813736599 * z56
15664
15761
  ),
15665
15762
  g: gamma3(
15666
- x * -0.6666843518324893 + y * 1.6164812366349395 + 0.0157685458139111 * z54
15763
+ x * -0.6666843518324893 + y * 1.6164812366349395 + 0.0157685458139111 * z56
15667
15764
  ),
15668
15765
  b: gamma3(
15669
- x * 0.0176398574453108 - y * 0.0427706132578085 + 0.9421031212354739 * z54
15766
+ x * 0.0176398574453108 - y * 0.0427706132578085 + 0.9421031212354739 * z56
15670
15767
  )
15671
15768
  };
15672
15769
  if (alpha !== void 0) {
@@ -15819,15 +15916,15 @@ var definition_default26 = definition26;
15819
15916
 
15820
15917
  // ../../node_modules/culori/src/xyz65/convertXyz65ToXyz50.js
15821
15918
  var convertXyz65ToXyz50 = (xyz652) => {
15822
- let { x, y, z: z54, alpha } = xyz652;
15919
+ let { x, y, z: z56, alpha } = xyz652;
15823
15920
  if (x === void 0) x = 0;
15824
15921
  if (y === void 0) y = 0;
15825
- if (z54 === void 0) z54 = 0;
15922
+ if (z56 === void 0) z56 = 0;
15826
15923
  let res = {
15827
15924
  mode: "xyz50",
15828
- x: 1.0479298208405488 * x + 0.0229467933410191 * y - 0.0501922295431356 * z54,
15829
- y: 0.0296278156881593 * x + 0.990434484573249 * y - 0.0170738250293851 * z54,
15830
- z: -0.0092430581525912 * x + 0.0150551448965779 * y + 0.7518742899580008 * z54
15925
+ x: 1.0479298208405488 * x + 0.0229467933410191 * y - 0.0501922295431356 * z56,
15926
+ y: 0.0296278156881593 * x + 0.990434484573249 * y - 0.0170738250293851 * z56,
15927
+ z: -0.0092430581525912 * x + 0.0150551448965779 * y + 0.7518742899580008 * z56
15831
15928
  };
15832
15929
  if (alpha !== void 0) {
15833
15930
  res.alpha = alpha;
@@ -15838,15 +15935,15 @@ var convertXyz65ToXyz50_default = convertXyz65ToXyz50;
15838
15935
 
15839
15936
  // ../../node_modules/culori/src/xyz65/convertXyz50ToXyz65.js
15840
15937
  var convertXyz50ToXyz65 = (xyz502) => {
15841
- let { x, y, z: z54, alpha } = xyz502;
15938
+ let { x, y, z: z56, alpha } = xyz502;
15842
15939
  if (x === void 0) x = 0;
15843
15940
  if (y === void 0) y = 0;
15844
- if (z54 === void 0) z54 = 0;
15941
+ if (z56 === void 0) z56 = 0;
15845
15942
  let res = {
15846
15943
  mode: "xyz65",
15847
- x: 0.9554734527042182 * x - 0.0230985368742614 * y + 0.0632593086610217 * z54,
15848
- y: -0.0283697069632081 * x + 1.0099954580058226 * y + 0.021041398966943 * z54,
15849
- z: 0.0123140016883199 * x - 0.0205076964334779 * y + 1.3303659366080753 * z54
15944
+ x: 0.9554734527042182 * x - 0.0230985368742614 * y + 0.0632593086610217 * z56,
15945
+ y: -0.0283697069632081 * x + 1.0099954580058226 * y + 0.021041398966943 * z56,
15946
+ z: 0.0123140016883199 * x - 0.0205076964334779 * y + 1.3303659366080753 * z56
15850
15947
  };
15851
15948
  if (alpha !== void 0) {
15852
15949
  res.alpha = alpha;
@@ -16116,6 +16213,571 @@ z.string().optional().nullable().transform((value) => {
16116
16213
  }
16117
16214
  );
16118
16215
 
16216
+ // ../../internal/utils/src/industry.ts
16217
+ var Industry = {
16218
+ // Finance
16219
+ BANKING: "banking",
16220
+ FINANCIAL_SERVICES: "financial_services",
16221
+ FINTECH: "fintech",
16222
+ PAYMENTS: "payments"};
16223
+
16224
+ // ../../internal/validation/src/dark-web/templates.ts
16225
+ var BRAND = { kind: "brand" };
16226
+ var lit = (value) => ({ kind: "literal", value });
16227
+ var phrase = (...tokens) => ({ type: "phrase", tokens });
16228
+ var bn = (slop, ...words) => ({
16229
+ type: "phrase",
16230
+ tokens: [BRAND, ...words.map(lit)],
16231
+ slop
16232
+ });
16233
+ var nb = (slop, ...words) => ({
16234
+ type: "phrase",
16235
+ tokens: [...words.map(lit), BRAND],
16236
+ slop
16237
+ });
16238
+ var word = (value) => ({ type: "term", value, match: "word" });
16239
+ var prefix = (value) => ({
16240
+ type: "term",
16241
+ value,
16242
+ match: "prefix"
16243
+ });
16244
+ var or = (...children) => ({
16245
+ type: "group",
16246
+ op: "OR",
16247
+ children
16248
+ });
16249
+ var and = (...children) => ({
16250
+ type: "group",
16251
+ op: "AND",
16252
+ children
16253
+ });
16254
+ var notOneOf = (...children) => ({
16255
+ type: "group",
16256
+ op: "OR",
16257
+ children,
16258
+ negated: true
16259
+ });
16260
+ var brandWith = (...values) => and(phrase(BRAND), or(...values.map(word)));
16261
+ var SCAN_TEMPLATES = [
16262
+ {
16263
+ id: "p2p-instant-transfer",
16264
+ category: "payment-fraud",
16265
+ title: "P2P and instant transfer abuse",
16266
+ 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.",
16267
+ // Source rows: 1, 55. The original ("[brand]"~3) was proximity on one
16268
+ // position, a no-op; dropped rather than ported.
16269
+ query: and(
16270
+ or(
16271
+ phrase(BRAND, lit("to"), BRAND),
16272
+ bn(2, "instant"),
16273
+ bn(2, "anyone"),
16274
+ bn(1, "pay"),
16275
+ bn(3, "pay anyone"),
16276
+ bn(2, "p2p"),
16277
+ bn(3, "instant transfer"),
16278
+ bn(3, "receive from anyone"),
16279
+ bn(4, "phone number"),
16280
+ bn(2, "member to member"),
16281
+ nb(3, "send")
16282
+ ),
16283
+ notOneOf(word("rate"), word("price"))
16284
+ )
16285
+ },
16286
+ {
16287
+ id: "transfers-ach-direct-debit",
16288
+ category: "payment-fraud",
16289
+ title: "Transfers, ACH and direct debit",
16290
+ description: "Discussion of pushing or pulling funds via ACH, direct debit or bill pay, including chatter about pending and settlement timing.",
16291
+ // Source rows: 6, 8, 18, 41. Row 18 was missing an OR before "Direct Debit".
16292
+ query: or(
16293
+ bn(1, "transfer"),
16294
+ nb(1, "transfer"),
16295
+ bn(5, "ach"),
16296
+ nb(4, "pull"),
16297
+ nb(4, "push"),
16298
+ bn(4, "pending"),
16299
+ bn(4, "settle"),
16300
+ nb(4, "link"),
16301
+ brandWith("billpay", "acctverify"),
16302
+ and(phrase(BRAND), or(phrase(lit("direct debit")), phrase(lit("direct deposit"))))
16303
+ )
16304
+ },
16305
+ {
16306
+ id: "wallet-linking",
16307
+ category: "payment-fraud",
16308
+ title: "Wallet linking and instant funding",
16309
+ description: "Linking the brand to mobile wallets for instant funding, and 'linkable' account trading where a name match is being sold.",
16310
+ // Source rows: 19, 44, 51
16311
+ query: and(
16312
+ or(
16313
+ bn(5, "apple pay"),
16314
+ bn(5, "apple"),
16315
+ bn(3, "apple linkable"),
16316
+ bn(2, "linkable"),
16317
+ bn(2, "linkables"),
16318
+ nb(2, "linkable"),
16319
+ nb(2, "link"),
16320
+ and(phrase(BRAND), phrase(lit("name match")))
16321
+ ),
16322
+ notOneOf(word("espn"), word("airtag"))
16323
+ )
16324
+ },
16325
+ {
16326
+ id: "card-data-exposure",
16327
+ category: "payment-fraud",
16328
+ title: "Card data and virtual cards",
16329
+ description: "Brand-issued card details being traded: expiry, CVV, virtual card numbers and card activation.",
16330
+ // Source rows: 43
16331
+ query: and(
16332
+ brandWith("exp", "cvv", "vcc", "debit"),
16333
+ notOneOf(word("checking"), word("limits"))
16334
+ )
16335
+ },
16336
+ {
16337
+ id: "card-loading",
16338
+ category: "payment-fraud",
16339
+ title: "Card and account loading",
16340
+ description: "'Loading' services that push stolen funds onto brand accounts or cards for a cut.",
16341
+ // Source rows: 42
16342
+ query: or(
16343
+ nb(1, "loading"),
16344
+ nb(1, "load"),
16345
+ bn(1, "loads"),
16346
+ bn(1, "loading"),
16347
+ bn(3, "drop"),
16348
+ bn(2, "cash"),
16349
+ and(
16350
+ phrase(BRAND),
16351
+ or(
16352
+ phrase(lit("loading method")),
16353
+ phrase(lit("loading guide")),
16354
+ phrase(lit("loading tut")),
16355
+ phrase(lit("loading tutorial"))
16356
+ )
16357
+ )
16358
+ )
16359
+ },
16360
+ {
16361
+ id: "cross-platform-cashout",
16362
+ category: "payment-fraud",
16363
+ title: "Cross-platform cash-out",
16364
+ description: "Moving funds from the brand into other consumer payment apps to break the trail.",
16365
+ // Source rows: 56
16366
+ query: and(
16367
+ phrase(BRAND),
16368
+ or(
16369
+ phrase(lit("cash app")),
16370
+ word("venmo"),
16371
+ phrase(lit("transfer to paypal")),
16372
+ phrase(lit("visa money transfer"))
16373
+ )
16374
+ )
16375
+ },
16376
+ {
16377
+ id: "crypto-offramp",
16378
+ category: "payment-fraud",
16379
+ title: "Crypto off-ramp",
16380
+ description: "Converting funds held at the brand into cryptocurrency through exchanges and P2P markets.",
16381
+ // Source rows: 63
16382
+ query: and(
16383
+ phrase(BRAND),
16384
+ or(
16385
+ word("crypto"),
16386
+ word("moonpay"),
16387
+ word("switchere"),
16388
+ word("kraken"),
16389
+ word("paxful"),
16390
+ word("noones"),
16391
+ word("coinomi"),
16392
+ word("coinzoom"),
16393
+ word("bitcoin"),
16394
+ word("btc")
16395
+ )
16396
+ )
16397
+ },
16398
+ {
16399
+ id: "mobile-check-deposit",
16400
+ category: "deposit-fraud",
16401
+ title: "Mobile check deposit fraud",
16402
+ description: "Check fraud against mobile deposit, traded under slang including 'glass', 'MCD', 'slips' and 'persy'.",
16403
+ // Source rows: 2, 13, 35, 46
16404
+ industries: [
16405
+ Industry.BANKING,
16406
+ Industry.FINANCIAL_SERVICES,
16407
+ Industry.FINTECH,
16408
+ Industry.PAYMENTS
16409
+ ],
16410
+ query: or(
16411
+ bn(5, "checks"),
16412
+ bn(5, "check"),
16413
+ bn(5, "mobile deposit"),
16414
+ nb(5, "mobile deposit"),
16415
+ bn(5, "slip"),
16416
+ bn(5, "slips"),
16417
+ bn(5, "glass"),
16418
+ bn(5, "md"),
16419
+ bn(5, "mcd"),
16420
+ bn(5, "treasury"),
16421
+ nb(5, "treasury"),
16422
+ bn(5, "persy"),
16423
+ bn(5, "deposit limit"),
16424
+ and(phrase(BRAND), or(prefix("stimm"), word("mcd"), phrase(lit("pop on"))))
16425
+ )
16426
+ },
16427
+ {
16428
+ id: "atm-abuse",
16429
+ category: "deposit-fraud",
16430
+ title: "ATM abuse and 'box work'",
16431
+ description: "ATM deposit and withdrawal manipulation, including dispute-driven 'box work' against cash machines.",
16432
+ // Source rows: 9, 47, 57, 70
16433
+ industries: [
16434
+ Industry.BANKING,
16435
+ Industry.FINANCIAL_SERVICES,
16436
+ Industry.FINTECH,
16437
+ Industry.PAYMENTS
16438
+ ],
16439
+ query: or(
16440
+ bn(2, "atm"),
16441
+ bn(2, "withdrawal"),
16442
+ and(phrase(BRAND), phrase(lit("box work"))),
16443
+ and(
16444
+ phrase(BRAND),
16445
+ or(
16446
+ phrase(lit("atm deposit")),
16447
+ phrase(lit("atm withdrawal")),
16448
+ phrase(lit("atm withdraw")),
16449
+ phrase(lit("atm limit")),
16450
+ phrase(lit("atm card")),
16451
+ phrase(lit("cash withdrawal fee"))
16452
+ )
16453
+ )
16454
+ )
16455
+ },
16456
+ {
16457
+ id: "retail-cashout-network",
16458
+ category: "deposit-fraud",
16459
+ title: "Retail cash-out network",
16460
+ description: "Cash-out through retail and ATM partner networks, where funds are pulled at a storefront rather than a branch.",
16461
+ // Source rows: 30
16462
+ industries: [
16463
+ Industry.BANKING,
16464
+ Industry.FINANCIAL_SERVICES,
16465
+ Industry.FINTECH,
16466
+ Industry.PAYMENTS
16467
+ ],
16468
+ query: or(
16469
+ bn(5, "allpoint"),
16470
+ bn(5, "walgreens"),
16471
+ bn(5, "cvs"),
16472
+ bn(5, "7-eleven"),
16473
+ bn(5, "7eleven"),
16474
+ bn(5, "711"),
16475
+ bn(5, "circle k"),
16476
+ bn(5, "target"),
16477
+ bn(5, "visa plus alliance"),
16478
+ bn(3, "moneypass"),
16479
+ bn(3, "vanilladirect"),
16480
+ bn(3, "vanilla"),
16481
+ bn(3, "dollar general"),
16482
+ bn(3, "dollar tree"),
16483
+ bn(3, "safeway"),
16484
+ bn(3, "walmart"),
16485
+ bn(3, "speedway")
16486
+ )
16487
+ },
16488
+ {
16489
+ id: "otp-bots",
16490
+ category: "account-takeover",
16491
+ title: "OTP bots and 2FA bypass",
16492
+ description: "Automated one-time-passcode interception services targeting the brand's login and step-up flows.",
16493
+ // Source rows: 34
16494
+ query: or(
16495
+ bn(2, "otp"),
16496
+ nb(1, "otp"),
16497
+ phrase(BRAND, lit("otp bot")),
16498
+ bn(2, "2fa"),
16499
+ bn(2, "code"),
16500
+ bn(2, "bypass"),
16501
+ bn(2, "bot"),
16502
+ bn(2, "bots")
16503
+ )
16504
+ },
16505
+ {
16506
+ id: "remote-access-tooling",
16507
+ category: "account-takeover",
16508
+ title: "Remote-access tooling",
16509
+ description: "Legitimate remote-management tools named alongside the brand, the standard toolkit for support-impersonation account takeover.",
16510
+ // Source rows: 29, 64. Truncated by the export; recoverable portion only.
16511
+ query: and(
16512
+ phrase(BRAND),
16513
+ or(
16514
+ word("action1"),
16515
+ phrase(lit("ammy admin")),
16516
+ word("ammyadmin"),
16517
+ word("anydesk"),
16518
+ phrase(lit("any desk")),
16519
+ word("atera"),
16520
+ word("beanywhere"),
16521
+ phrase(lit("chrome remote desktop")),
16522
+ word("connectwise"),
16523
+ word("screenconnect"),
16524
+ word("dameware"),
16525
+ word("domotz"),
16526
+ word("dwservice"),
16527
+ word("fleetdeck"),
16528
+ word("intelliadmin"),
16529
+ phrase(lit("itarian endpoint manager")),
16530
+ phrase(lit("level.io")),
16531
+ word("meshcentral"),
16532
+ word("mremoteng"),
16533
+ phrase(lit("n-able")),
16534
+ word("ngrok"),
16535
+ phrase(lit("pdq deploy")),
16536
+ word("pulseway"),
16537
+ word("realvnc"),
16538
+ phrase(lit("remote utilities")),
16539
+ word("rport")
16540
+ )
16541
+ )
16542
+ },
16543
+ {
16544
+ id: "antidetect-kyc-evasion",
16545
+ category: "account-takeover",
16546
+ title: "Anti-detect and KYC evasion tooling",
16547
+ description: "Fingerprint spoofing, residential proxies and emulators used to defeat the brand's device and identity checks.",
16548
+ // Source row: 40. The original filtered to Telegram OR Dread; DarkOwl's
16549
+ // `source` takes one value, so precision comes from the tooling terms.
16550
+ query: and(
16551
+ phrase(BRAND),
16552
+ or(
16553
+ word("tlo"),
16554
+ word("scamalytics"),
16555
+ word("burner"),
16556
+ prefix("prox"),
16557
+ word("vpn"),
16558
+ word("vps"),
16559
+ word("antidetect"),
16560
+ phrase(lit("anti-detect")),
16561
+ prefix("emulat")
16562
+ )
16563
+ )
16564
+ },
16565
+ {
16566
+ id: "brand-domains-sso",
16567
+ category: "account-takeover",
16568
+ title: "Brand domains and SSO endpoints",
16569
+ description: "The brand's own domains and identity-provider endpoints appearing in credential and access chatter.",
16570
+ // Source row: 31, as proximity not literal hostnames — the index tokenizes
16571
+ // `chime.okta.com`, so a glued literal would never match. Row 23's
16572
+ // "[brand] pass*"~1 is omitted: no wildcards inside a phrase.
16573
+ query: and(
16574
+ or(bn(2, "okta"), bn(2, "login"), bn(2, "portal"), bn(1, "bank")),
16575
+ notOneOf(word("zendesk"))
16576
+ )
16577
+ },
16578
+ {
16579
+ id: "account-opening-abuse",
16580
+ category: "onboarding-abuse",
16581
+ title: "Account opening and self-registration",
16582
+ description: "Bulk or fraudulent account creation against the brand, including 'self-reg' and 'drop' accounts opened on stolen identities.",
16583
+ // Source rows: 5, 17, 54
16584
+ query: or(
16585
+ bn(5, "enroll"),
16586
+ nb(5, "enroll"),
16587
+ bn(5, "open"),
16588
+ nb(5, "open"),
16589
+ bn(5, "drop"),
16590
+ nb(5, "drop"),
16591
+ nb(5, "make"),
16592
+ nb(5, "create"),
16593
+ nb(5, "fresh"),
16594
+ nb(5, "register"),
16595
+ bn(5, "self-reg"),
16596
+ bn(5, "selfreg"),
16597
+ bn(5, "self-registered"),
16598
+ bn(5, "self register"),
16599
+ bn(1, "fullz")
16600
+ )
16601
+ },
16602
+ {
16603
+ id: "identity-documents",
16604
+ category: "onboarding-abuse",
16605
+ title: "Identity documents and fullz",
16606
+ 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.",
16607
+ // Source row: 15. Lexicon-only, so the renderer AND-s the anchor on.
16608
+ query: or(
16609
+ word("fullz"),
16610
+ phrase(lit("get pros")),
16611
+ phrase(lit("driver's license")),
16612
+ phrase(lit("tlo bot")),
16613
+ phrase(lit("tlo plug")),
16614
+ phrase(lit("need dl")),
16615
+ phrase(lit("get id"))
16616
+ )
16617
+ },
16618
+ {
16619
+ id: "referral-bonus-abuse",
16620
+ category: "onboarding-abuse",
16621
+ title: "Referral and bonus abuse",
16622
+ description: "Methods for farming the brand's referral bonuses and promotional offers, often paired with fake direct deposits.",
16623
+ // Source rows: 48
16624
+ query: and(
16625
+ or(bn(3, "referral"), bn(3, "bonus"), bn(3, "promotion"), bn(3, "offer")),
16626
+ or(
16627
+ word("bypass"),
16628
+ word("dd"),
16629
+ phrase(lit("direct deposit")),
16630
+ phrase(lit("work around")),
16631
+ word("trick"),
16632
+ word("method"),
16633
+ word("glitch"),
16634
+ word("tutorial"),
16635
+ word("tut"),
16636
+ word("wave"),
16637
+ word("sauce")
16638
+ )
16639
+ )
16640
+ },
16641
+ {
16642
+ id: "insider-recruitment",
16643
+ category: "insider-threat",
16644
+ title: "Insider recruitment",
16645
+ description: "Actors seeking or advertising an employee on the inside ('inny'), the highest-severity signal in this catalog.",
16646
+ // Source rows: 7, 38
16647
+ query: and(
16648
+ or(
16649
+ bn(4, "inny"),
16650
+ bn(4, "insider"),
16651
+ and(
16652
+ phrase(BRAND),
16653
+ or(
16654
+ word("insider"),
16655
+ word("inny"),
16656
+ phrase(lit("in office")),
16657
+ word("employee"),
16658
+ phrase(lit("i work at")),
16659
+ phrase(lit("i am an employee")),
16660
+ phrase(lit("i can get access")),
16661
+ phrase(lit("i have access to")),
16662
+ phrase(lit("employed at")),
16663
+ word("occupation")
16664
+ )
16665
+ )
16666
+ ),
16667
+ notOneOf(phrase(lit("welcome to insider chat")))
16668
+ )
16669
+ },
16670
+ {
16671
+ id: "mule-crew-recruitment",
16672
+ category: "insider-threat",
16673
+ title: "Mule and cash-out crew recruitment",
16674
+ description: "Recruitment of account holders to receive and forward funds, priced as a revenue split ('50/50', '60/40').",
16675
+ // Source rows: 39
16676
+ query: or(
16677
+ bn(5, "cl"),
16678
+ bn(5, "mule"),
16679
+ bn(5, "tap in"),
16680
+ bn(5, "50/50"),
16681
+ bn(5, "60/40"),
16682
+ bn(5, "striker")
16683
+ )
16684
+ },
16685
+ {
16686
+ id: "methods-and-tutorials",
16687
+ category: "tradecraft",
16688
+ title: "Methods, 'sauce' and tutorials",
16689
+ description: "Fraud methods being taught or sold against the brand, under the usual slang: sauce, glitch, tut, wave, motion, tap in.",
16690
+ // Source rows: 4, 21, 59
16691
+ query: or(
16692
+ bn(3, "sauce"),
16693
+ bn(3, "method"),
16694
+ bn(3, "guide"),
16695
+ bn(3, "tut"),
16696
+ bn(3, "tutorial"),
16697
+ bn(4, "hack"),
16698
+ bn(4, "glitch"),
16699
+ bn(4, "wave"),
16700
+ bn(4, "motion"),
16701
+ bn(4, "walkthrough"),
16702
+ bn(4, "walk through"),
16703
+ bn(2, "tap in"),
16704
+ bn(2, "tapin"),
16705
+ nb(2, "tapn")
16706
+ )
16707
+ },
16708
+ {
16709
+ id: "hack-leak-chatter",
16710
+ category: "tradecraft",
16711
+ title: "Hack and leak chatter",
16712
+ description: "Claims of having hacked, breached or leaked the brand, whether credible or not.",
16713
+ // Source rows: 28
16714
+ query: or(
16715
+ bn(4, "hack"),
16716
+ bn(4, "glitch"),
16717
+ nb(4, "hacking"),
16718
+ bn(4, "leak"),
16719
+ bn(4, "leaked")
16720
+ )
16721
+ },
16722
+ {
16723
+ id: "support-impersonation",
16724
+ category: "brand-abuse",
16725
+ title: "Support impersonation",
16726
+ description: "Actors impersonating the brand's support or service staff to social-engineer customers.",
16727
+ // Source rows: 36
16728
+ query: or(
16729
+ bn(1, "agent"),
16730
+ bn(1, "support"),
16731
+ bn(1, "service"),
16732
+ bn(1, "customer"),
16733
+ nb(1, "impersonate"),
16734
+ bn(1, "impersonation"),
16735
+ bn(3, "scam"),
16736
+ nb(1, "scam")
16737
+ )
16738
+ },
16739
+ {
16740
+ id: "social-impersonation",
16741
+ category: "brand-abuse",
16742
+ title: "Social account impersonation",
16743
+ description: "The brand discussed alongside social platforms, where fake support and giveaway accounts are staged.",
16744
+ // Source rows: 25
16745
+ query: or(
16746
+ bn(5, "instagram"),
16747
+ bn(5, "insta"),
16748
+ bn(5, "facebook"),
16749
+ bn(5, "twitter"),
16750
+ bn(5, "tiktok")
16751
+ )
16752
+ },
16753
+ {
16754
+ id: "vendor-supply-chain",
16755
+ category: "data-exposure",
16756
+ title: "Vendor and BPO supply chain",
16757
+ description: "Outsourced support and processing vendors named alongside the brand, where a third-party breach exposes brand customer data.",
16758
+ // Source row: 60, truncated by the export. Generic BPO/CX providers; a
16759
+ // per-org vendor list would be materially better.
16760
+ query: and(
16761
+ phrase(BRAND),
16762
+ or(
16763
+ word("zendesk"),
16764
+ word("teleperformance"),
16765
+ word("concentrix"),
16766
+ word("taskus"),
16767
+ word("genpact"),
16768
+ word("ubiquity"),
16769
+ word("hioperator"),
16770
+ word("galileo"),
16771
+ word("fiserv"),
16772
+ phrase(lit("etech global"))
16773
+ )
16774
+ )
16775
+ }
16776
+ ];
16777
+ new Map(
16778
+ SCAN_TEMPLATES.map((template) => [template.id, template])
16779
+ );
16780
+
16119
16781
  // src/client.ts
16120
16782
  function trimTrailingSlashes(url) {
16121
16783
  return url.replace(/\/+$/, "");