@arcis/node 1.6.1 → 1.6.3

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.
Files changed (75) hide show
  1. package/README.md +5 -3
  2. package/dist/_third_party/rate-limit/abstract.d.ts +36 -0
  3. package/dist/_third_party/rate-limit/abstract.d.ts.map +1 -0
  4. package/dist/_third_party/rate-limit/bursty.d.ts +21 -0
  5. package/dist/_third_party/rate-limit/bursty.d.ts.map +1 -0
  6. package/dist/_third_party/rate-limit/index.d.ts +12 -0
  7. package/dist/_third_party/rate-limit/index.d.ts.map +1 -0
  8. package/dist/_third_party/rate-limit/memory-storage.d.ts +28 -0
  9. package/dist/_third_party/rate-limit/memory-storage.d.ts.map +1 -0
  10. package/dist/_third_party/rate-limit/memory.d.ts +23 -0
  11. package/dist/_third_party/rate-limit/memory.d.ts.map +1 -0
  12. package/dist/_third_party/rate-limit/record.d.ts +11 -0
  13. package/dist/_third_party/rate-limit/record.d.ts.map +1 -0
  14. package/dist/_third_party/rate-limit/types.d.ts +39 -0
  15. package/dist/_third_party/rate-limit/types.d.ts.map +1 -0
  16. package/dist/astro/index.js +405 -0
  17. package/dist/astro/index.js.map +1 -1
  18. package/dist/astro/index.mjs +405 -0
  19. package/dist/astro/index.mjs.map +1 -1
  20. package/dist/bun/index.js +405 -0
  21. package/dist/bun/index.js.map +1 -1
  22. package/dist/bun/index.mjs +405 -0
  23. package/dist/bun/index.mjs.map +1 -1
  24. package/dist/fastify/index.js +405 -0
  25. package/dist/fastify/index.js.map +1 -1
  26. package/dist/fastify/index.mjs +405 -0
  27. package/dist/fastify/index.mjs.map +1 -1
  28. package/dist/hono/index.js +405 -0
  29. package/dist/hono/index.js.map +1 -1
  30. package/dist/hono/index.mjs +405 -0
  31. package/dist/hono/index.mjs.map +1 -1
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +754 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +754 -6
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/koa/index.js +405 -0
  39. package/dist/koa/index.js.map +1 -1
  40. package/dist/koa/index.mjs +405 -0
  41. package/dist/koa/index.mjs.map +1 -1
  42. package/dist/middleware/brute-force.d.ts +69 -0
  43. package/dist/middleware/brute-force.d.ts.map +1 -0
  44. package/dist/middleware/index.js +702 -1
  45. package/dist/middleware/index.js.map +1 -1
  46. package/dist/middleware/index.mjs +702 -1
  47. package/dist/middleware/index.mjs.map +1 -1
  48. package/dist/middleware/nestjs.d.ts +50 -1
  49. package/dist/middleware/nestjs.d.ts.map +1 -1
  50. package/dist/middleware/protect.d.ts +9 -0
  51. package/dist/middleware/protect.d.ts.map +1 -1
  52. package/dist/nestjs/index.js +57 -2
  53. package/dist/nestjs/index.js.map +1 -1
  54. package/dist/nestjs/index.mjs +57 -3
  55. package/dist/nestjs/index.mjs.map +1 -1
  56. package/dist/nextjs/index.js +405 -0
  57. package/dist/nextjs/index.js.map +1 -1
  58. package/dist/nextjs/index.mjs +405 -0
  59. package/dist/nextjs/index.mjs.map +1 -1
  60. package/dist/nuxt/index.js +405 -0
  61. package/dist/nuxt/index.js.map +1 -1
  62. package/dist/nuxt/index.mjs +405 -0
  63. package/dist/nuxt/index.mjs.map +1 -1
  64. package/dist/sanitizers/index.js +2 -1
  65. package/dist/sanitizers/index.js.map +1 -1
  66. package/dist/sanitizers/index.mjs +2 -1
  67. package/dist/sanitizers/index.mjs.map +1 -1
  68. package/dist/sanitizers/ldap.d.ts.map +1 -1
  69. package/dist/sanitizers/prompt-injection.d.ts +3 -3
  70. package/dist/sanitizers/prompt-injection.d.ts.map +1 -1
  71. package/dist/sveltekit/index.js +405 -0
  72. package/dist/sveltekit/index.js.map +1 -1
  73. package/dist/sveltekit/index.mjs +405 -0
  74. package/dist/sveltekit/index.mjs.map +1 -1
  75. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1101,9 +1101,10 @@ function detectXxe(input) {
1101
1101
  // src/sanitizers/ldap.ts
1102
1102
  var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
1103
1103
  var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
1104
+ var LDAP_NOT_BYPASS_PATTERN = /\)\s*\(\s*!|&\s*\(\s*!|\|\s*\(\s*!/;
1104
1105
  function detectLdapInjection(input) {
1105
1106
  if (typeof input !== "string") return false;
1106
- return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
1107
+ return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input) || LDAP_NOT_BYPASS_PATTERN.test(input);
1107
1108
  }
1108
1109
 
1109
1110
  // src/sanitizers/xpath.ts
@@ -9294,6 +9295,411 @@ var bot_patterns_default = [
9294
9295
  "ZuperlistBot\\/"
9295
9296
  ],
9296
9297
  forbidden: []
9298
+ },
9299
+ {
9300
+ id: "ext-ahrefsbotsiteaudit",
9301
+ name: "ahrefsbotsiteaudit",
9302
+ category: "SEO",
9303
+ patterns: [
9304
+ "Ahrefs(Bot|SiteAudit)"
9305
+ ],
9306
+ forbidden: []
9307
+ },
9308
+ {
9309
+ id: "ext-amazonproductdiscovery",
9310
+ name: "amazonproductdiscovery",
9311
+ category: "SEARCH_ENGINE",
9312
+ patterns: [
9313
+ "AmazonProductDiscovery"
9314
+ ],
9315
+ forbidden: []
9316
+ },
9317
+ {
9318
+ id: "ext-amazonsellerinitiatedlisting",
9319
+ name: "amazonsellerinitiatedlisting",
9320
+ category: "SEARCH_ENGINE",
9321
+ patterns: [
9322
+ "AmazonSellerInitiatedListing"
9323
+ ],
9324
+ forbidden: []
9325
+ },
9326
+ {
9327
+ id: "ext-cclaudebbot",
9328
+ name: "cclaudebbot",
9329
+ category: "GENERIC",
9330
+ patterns: [
9331
+ "[cC]laude[bB]ot"
9332
+ ],
9333
+ forbidden: []
9334
+ },
9335
+ {
9336
+ id: "ext-meta-externalagent",
9337
+ name: "meta-externalagent",
9338
+ category: "GENERIC",
9339
+ patterns: [
9340
+ "meta-externalagent\\/"
9341
+ ],
9342
+ forbidden: []
9343
+ },
9344
+ {
9345
+ id: "ext-meta-externalfetcher",
9346
+ name: "meta-externalfetcher",
9347
+ category: "GENERIC",
9348
+ patterns: [
9349
+ "meta-externalfetcher\\/"
9350
+ ],
9351
+ forbidden: []
9352
+ },
9353
+ {
9354
+ id: "ext-hydrozenio",
9355
+ name: "hydrozenio",
9356
+ category: "MONITORING",
9357
+ patterns: [
9358
+ "Hydrozen\\.io"
9359
+ ],
9360
+ forbidden: []
9361
+ },
9362
+ {
9363
+ id: "ext-yextbot",
9364
+ name: "yextbot",
9365
+ category: "SEO",
9366
+ patterns: [
9367
+ "YextBot\\/"
9368
+ ],
9369
+ forbidden: []
9370
+ },
9371
+ {
9372
+ id: "ext-datadogsynthetics",
9373
+ name: "datadogsynthetics",
9374
+ category: "MONITORING",
9375
+ patterns: [
9376
+ "DatadogSynthetics"
9377
+ ],
9378
+ forbidden: []
9379
+ },
9380
+ {
9381
+ id: "ext-observepoint",
9382
+ name: "observepoint",
9383
+ category: "MONITORING",
9384
+ patterns: [
9385
+ "ObservePoint"
9386
+ ],
9387
+ forbidden: []
9388
+ },
9389
+ {
9390
+ id: "ext-checkly",
9391
+ name: "checkly",
9392
+ category: "MONITORING",
9393
+ patterns: [
9394
+ "Checkly"
9395
+ ],
9396
+ forbidden: []
9397
+ },
9398
+ {
9399
+ id: "ext-alittleclient",
9400
+ name: "alittleclient",
9401
+ category: "GENERIC",
9402
+ patterns: [
9403
+ "ALittle Client"
9404
+ ],
9405
+ forbidden: []
9406
+ },
9407
+ {
9408
+ id: "ext-aliyunsecbot",
9409
+ name: "aliyunsecbot",
9410
+ category: "GENERIC",
9411
+ patterns: [
9412
+ "AliyunSecBot"
9413
+ ],
9414
+ forbidden: []
9415
+ },
9416
+ {
9417
+ id: "ext-claude-web",
9418
+ name: "claude-web",
9419
+ category: "GENERIC",
9420
+ patterns: [
9421
+ "Claude-Web"
9422
+ ],
9423
+ forbidden: []
9424
+ },
9425
+ {
9426
+ id: "ext-google-extended",
9427
+ name: "google-extended",
9428
+ category: "GENERIC",
9429
+ patterns: [
9430
+ "Google-Extended"
9431
+ ],
9432
+ forbidden: []
9433
+ },
9434
+ {
9435
+ id: "ext-serankingbacklinksbot",
9436
+ name: "serankingbacklinksbot",
9437
+ category: "SEO",
9438
+ patterns: [
9439
+ "SERankingBacklinksBot"
9440
+ ],
9441
+ forbidden: []
9442
+ },
9443
+ {
9444
+ id: "ext-cmschecker",
9445
+ name: "cmschecker",
9446
+ category: "SEO",
9447
+ patterns: [
9448
+ "CMSChecker"
9449
+ ],
9450
+ forbidden: []
9451
+ },
9452
+ {
9453
+ id: "ext-wayback",
9454
+ name: "wayback",
9455
+ category: "GENERIC",
9456
+ patterns: [
9457
+ "Wayback"
9458
+ ],
9459
+ forbidden: []
9460
+ },
9461
+ {
9462
+ id: "ext-playwright",
9463
+ name: "playwright",
9464
+ category: "GENERIC",
9465
+ patterns: [
9466
+ "Playwright"
9467
+ ],
9468
+ forbidden: []
9469
+ },
9470
+ {
9471
+ id: "ext-puppeteer",
9472
+ name: "puppeteer",
9473
+ category: "GENERIC",
9474
+ patterns: [
9475
+ "Puppeteer"
9476
+ ],
9477
+ forbidden: []
9478
+ },
9479
+ {
9480
+ id: "ext-selenium",
9481
+ name: "selenium",
9482
+ category: "GENERIC",
9483
+ patterns: [
9484
+ "Selenium"
9485
+ ],
9486
+ forbidden: []
9487
+ },
9488
+ {
9489
+ id: "ext-nikto",
9490
+ name: "nikto",
9491
+ category: "GENERIC",
9492
+ patterns: [
9493
+ "Nikto"
9494
+ ],
9495
+ forbidden: []
9496
+ },
9497
+ {
9498
+ id: "ext-sqlmap",
9499
+ name: "sqlmap",
9500
+ category: "GENERIC",
9501
+ patterns: [
9502
+ "sqlmap"
9503
+ ],
9504
+ forbidden: []
9505
+ },
9506
+ {
9507
+ id: "ext-zmeu",
9508
+ name: "zmeu",
9509
+ category: "GENERIC",
9510
+ patterns: [
9511
+ "ZmEu"
9512
+ ],
9513
+ forbidden: []
9514
+ },
9515
+ {
9516
+ id: "ext-masscan",
9517
+ name: "masscan",
9518
+ category: "GENERIC",
9519
+ patterns: [
9520
+ "masscan"
9521
+ ],
9522
+ forbidden: []
9523
+ },
9524
+ {
9525
+ id: "ext-wpscan",
9526
+ name: "wpscan",
9527
+ category: "GENERIC",
9528
+ patterns: [
9529
+ "WPScan"
9530
+ ],
9531
+ forbidden: []
9532
+ },
9533
+ {
9534
+ id: "ext-aacunetix",
9535
+ name: "aacunetix",
9536
+ category: "GENERIC",
9537
+ patterns: [
9538
+ "[aA]cunetix"
9539
+ ],
9540
+ forbidden: []
9541
+ },
9542
+ {
9543
+ id: "ext-nessus",
9544
+ name: "nessus",
9545
+ category: "GENERIC",
9546
+ patterns: [
9547
+ "Nessus"
9548
+ ],
9549
+ forbidden: []
9550
+ },
9551
+ {
9552
+ id: "ext-ddirbbuster",
9553
+ name: "ddirbbuster",
9554
+ category: "GENERIC",
9555
+ patterns: [
9556
+ "[dD]ir[Bb]uster"
9557
+ ],
9558
+ forbidden: []
9559
+ },
9560
+ {
9561
+ id: "ext-colly",
9562
+ name: "colly",
9563
+ category: "GENERIC",
9564
+ patterns: [
9565
+ "colly"
9566
+ ],
9567
+ forbidden: []
9568
+ },
9569
+ {
9570
+ id: "ext-mmechanize",
9571
+ name: "mmechanize",
9572
+ category: "GENERIC",
9573
+ patterns: [
9574
+ "[mM]echanize"
9575
+ ],
9576
+ forbidden: []
9577
+ },
9578
+ {
9579
+ id: "ext-airaiscanning",
9580
+ name: "airaiscanning",
9581
+ category: "GENERIC",
9582
+ patterns: [
9583
+ "air\\.ai\\/scanning"
9584
+ ],
9585
+ forbidden: []
9586
+ },
9587
+ {
9588
+ id: "ext-asnriskscorer",
9589
+ name: "asnriskscorer",
9590
+ category: "GENERIC",
9591
+ patterns: [
9592
+ "asnriskscorer"
9593
+ ],
9594
+ forbidden: []
9595
+ },
9596
+ {
9597
+ id: "ext-oicrawler",
9598
+ name: "oicrawler",
9599
+ category: "SEARCH_ENGINE",
9600
+ patterns: [
9601
+ "OICrawler"
9602
+ ],
9603
+ forbidden: []
9604
+ },
9605
+ {
9606
+ id: "ext-l9scan",
9607
+ name: "l9scan",
9608
+ category: "GENERIC",
9609
+ patterns: [
9610
+ "l9scan"
9611
+ ],
9612
+ forbidden: []
9613
+ },
9614
+ {
9615
+ id: "ext-slaccalebot",
9616
+ name: "slaccalebot",
9617
+ category: "SEO",
9618
+ patterns: [
9619
+ "SlaccaleBot"
9620
+ ],
9621
+ forbidden: []
9622
+ },
9623
+ {
9624
+ id: "ext-customasynchttpclient",
9625
+ name: "customasynchttpclient",
9626
+ category: "GENERIC",
9627
+ patterns: [
9628
+ "CustomAsyncHttpClient"
9629
+ ],
9630
+ forbidden: []
9631
+ },
9632
+ {
9633
+ id: "ext-gemini-deep-research",
9634
+ name: "gemini-deep-research",
9635
+ category: "SEARCH_ENGINE",
9636
+ patterns: [
9637
+ "Gemini-Deep-Research"
9638
+ ],
9639
+ forbidden: []
9640
+ },
9641
+ {
9642
+ id: "ext-perplexity-user",
9643
+ name: "perplexity-user",
9644
+ category: "SEARCH_ENGINE",
9645
+ patterns: [
9646
+ "Perplexity-User"
9647
+ ],
9648
+ forbidden: []
9649
+ },
9650
+ {
9651
+ id: "ext-perplexityuser",
9652
+ name: "perplexityuser",
9653
+ category: "SEARCH_ENGINE",
9654
+ patterns: [
9655
+ "PerplexityUser"
9656
+ ],
9657
+ forbidden: []
9658
+ },
9659
+ {
9660
+ id: "ext-meta-webindexer",
9661
+ name: "meta-webindexer",
9662
+ category: "GENERIC",
9663
+ patterns: [
9664
+ "meta-webindexer"
9665
+ ],
9666
+ forbidden: []
9667
+ },
9668
+ {
9669
+ id: "ext-duckassistbot",
9670
+ name: "duckassistbot",
9671
+ category: "SEARCH_ENGINE",
9672
+ patterns: [
9673
+ "DuckAssistBot"
9674
+ ],
9675
+ forbidden: []
9676
+ },
9677
+ {
9678
+ id: "ext-mistralai-user",
9679
+ name: "mistralai-user",
9680
+ category: "GENERIC",
9681
+ patterns: [
9682
+ "MistralAI-User"
9683
+ ],
9684
+ forbidden: []
9685
+ },
9686
+ {
9687
+ id: "ext-webzio",
9688
+ name: "webzio",
9689
+ category: "SEO",
9690
+ patterns: [
9691
+ "webzio"
9692
+ ],
9693
+ forbidden: []
9694
+ },
9695
+ {
9696
+ id: "ext-newsai",
9697
+ name: "newsai",
9698
+ category: "GENERIC",
9699
+ patterns: [
9700
+ "newsai\\/"
9701
+ ],
9702
+ forbidden: []
9297
9703
  }
9298
9704
  ];
9299
9705
 
@@ -10088,6 +10494,297 @@ function signupProtection(options = {}) {
10088
10494
  return middleware;
10089
10495
  }
10090
10496
 
10497
+ // src/_third_party/rate-limit/abstract.ts
10498
+ var AbstractLimiter = class {
10499
+ constructor(opts) {
10500
+ if (!Number.isFinite(opts.points)) {
10501
+ throw new Error("points must be a finite number");
10502
+ }
10503
+ if (!Number.isFinite(opts.duration) || opts.duration < 0) {
10504
+ throw new Error("duration must be a finite, non-negative number");
10505
+ }
10506
+ this._points = opts.points;
10507
+ this._duration = opts.duration;
10508
+ this._blockDuration = typeof opts.blockDuration === "undefined" ? 0 : opts.blockDuration;
10509
+ this._execEvenly = Boolean(opts.execEvenly);
10510
+ this._execEvenlyMinDelayMs = typeof opts.execEvenlyMinDelayMs === "undefined" ? Math.ceil(this._duration * 1e3 / Math.max(this._points, 1)) : opts.execEvenlyMinDelayMs;
10511
+ if (typeof opts.keyPrefix === "undefined") {
10512
+ this._keyPrefix = "arcis";
10513
+ } else if (typeof opts.keyPrefix !== "string") {
10514
+ throw new Error("keyPrefix must be a string");
10515
+ } else {
10516
+ this._keyPrefix = opts.keyPrefix;
10517
+ }
10518
+ }
10519
+ get points() {
10520
+ return this._points;
10521
+ }
10522
+ get duration() {
10523
+ return this._duration;
10524
+ }
10525
+ get msDuration() {
10526
+ return this._duration * 1e3;
10527
+ }
10528
+ get blockDuration() {
10529
+ return this._blockDuration;
10530
+ }
10531
+ get msBlockDuration() {
10532
+ return this._blockDuration * 1e3;
10533
+ }
10534
+ get execEvenly() {
10535
+ return this._execEvenly;
10536
+ }
10537
+ get execEvenlyMinDelayMs() {
10538
+ return this._execEvenlyMinDelayMs;
10539
+ }
10540
+ get keyPrefix() {
10541
+ return this._keyPrefix;
10542
+ }
10543
+ _getKeySecDuration(options = {}) {
10544
+ return typeof options.customDuration === "number" && options.customDuration >= 0 ? options.customDuration : this._duration;
10545
+ }
10546
+ _getKey(key) {
10547
+ return this._keyPrefix.length > 0 ? `${this._keyPrefix}:${key}` : key;
10548
+ }
10549
+ };
10550
+
10551
+ // src/_third_party/rate-limit/types.ts
10552
+ var LimiterResult = class {
10553
+ constructor(remainingPoints = 0, msBeforeNext = 0, consumedPoints = 0, isFirstInDuration = false) {
10554
+ this.remainingPoints = remainingPoints;
10555
+ this.msBeforeNext = msBeforeNext;
10556
+ this.consumedPoints = consumedPoints;
10557
+ this.isFirstInDuration = isFirstInDuration;
10558
+ }
10559
+ };
10560
+
10561
+ // src/_third_party/rate-limit/record.ts
10562
+ var StorageRecord = class {
10563
+ constructor(value, expiresAt, timeoutId = null) {
10564
+ this.value = Math.trunc(value);
10565
+ this.expiresAt = expiresAt;
10566
+ this.timeoutId = timeoutId;
10567
+ }
10568
+ };
10569
+
10570
+ // src/_third_party/rate-limit/memory-storage.ts
10571
+ var MemoryStorage = class {
10572
+ constructor() {
10573
+ this._storage = /* @__PURE__ */ new Map();
10574
+ }
10575
+ /**
10576
+ * Increment the counter for `key` by `value`. If the key has no record
10577
+ * (or its TTL has expired), a new record is created with `durationSec`
10578
+ * lifetime.
10579
+ */
10580
+ incrby(key, value, durationSec) {
10581
+ const record = this._storage.get(key);
10582
+ if (record) {
10583
+ const msBeforeExpires = record.expiresAt ? record.expiresAt - Date.now() : -1;
10584
+ if (!record.expiresAt || msBeforeExpires > 0) {
10585
+ record.value = record.value + value;
10586
+ return new LimiterResult(0, msBeforeExpires, record.value, false);
10587
+ }
10588
+ return this.set(key, value, durationSec);
10589
+ }
10590
+ return this.set(key, value, durationSec);
10591
+ }
10592
+ /**
10593
+ * Write the counter for `key` to `value`, replacing any existing
10594
+ * record. `durationSec` of 0 means "never expires".
10595
+ */
10596
+ set(key, value, durationSec) {
10597
+ const durationMs = durationSec * 1e3;
10598
+ const existing = this._storage.get(key);
10599
+ if (existing && existing.timeoutId) {
10600
+ clearTimeout(existing.timeoutId);
10601
+ }
10602
+ const record = new StorageRecord(value, durationMs > 0 ? Date.now() + durationMs : null);
10603
+ this._storage.set(key, record);
10604
+ if (durationMs > 0) {
10605
+ record.timeoutId = setTimeout(() => {
10606
+ this._storage.delete(key);
10607
+ }, durationMs);
10608
+ if (typeof record.timeoutId.unref === "function") {
10609
+ record.timeoutId.unref();
10610
+ }
10611
+ }
10612
+ return new LimiterResult(0, durationMs === 0 ? -1 : durationMs, record.value, true);
10613
+ }
10614
+ get(key) {
10615
+ const record = this._storage.get(key);
10616
+ if (!record) return null;
10617
+ const msBeforeExpires = record.expiresAt ? record.expiresAt - Date.now() : -1;
10618
+ return new LimiterResult(0, msBeforeExpires, record.value, false);
10619
+ }
10620
+ delete(key) {
10621
+ const record = this._storage.get(key);
10622
+ if (!record) return false;
10623
+ if (record.timeoutId) {
10624
+ clearTimeout(record.timeoutId);
10625
+ }
10626
+ this._storage.delete(key);
10627
+ return true;
10628
+ }
10629
+ /** Inspect the underlying map. Test-only and not part of the public API. */
10630
+ _dump() {
10631
+ return this._storage.entries();
10632
+ }
10633
+ /** Clear all records. Used by tests and by `Limiter.dispose()`. */
10634
+ clear() {
10635
+ for (const record of this._storage.values()) {
10636
+ if (record.timeoutId) clearTimeout(record.timeoutId);
10637
+ }
10638
+ this._storage.clear();
10639
+ }
10640
+ };
10641
+
10642
+ // src/_third_party/rate-limit/memory.ts
10643
+ var MemoryLimiter = class extends AbstractLimiter {
10644
+ constructor(opts) {
10645
+ super(opts);
10646
+ this._storage = new MemoryStorage();
10647
+ }
10648
+ consume(key, pointsToConsume = 1, options = {}) {
10649
+ return new Promise((resolve2, reject) => {
10650
+ const rlKey = this._getKey(key);
10651
+ const secDuration = this._getKeySecDuration(options);
10652
+ let res = this._storage.incrby(rlKey, pointsToConsume, secDuration);
10653
+ res.remainingPoints = Math.max(this._points - res.consumedPoints, 0);
10654
+ if (res.consumedPoints > this._points) {
10655
+ if (this._blockDuration > 0 && res.consumedPoints <= this._points + pointsToConsume) {
10656
+ res = this._storage.set(rlKey, res.consumedPoints, this._blockDuration);
10657
+ }
10658
+ reject(res);
10659
+ return;
10660
+ }
10661
+ if (this._execEvenly && res.msBeforeNext > 0 && !res.isFirstInDuration) {
10662
+ let delay = Math.ceil(res.msBeforeNext / (res.remainingPoints + 2));
10663
+ if (delay < this._execEvenlyMinDelayMs) {
10664
+ delay = res.consumedPoints * this._execEvenlyMinDelayMs;
10665
+ }
10666
+ res.msBeforeNext = Math.max(res.msBeforeNext - delay, 0);
10667
+ setTimeout(resolve2, delay, res);
10668
+ return;
10669
+ }
10670
+ resolve2(res);
10671
+ });
10672
+ }
10673
+ penalty(key, points = 1, options = {}) {
10674
+ const rlKey = this._getKey(key);
10675
+ const secDuration = this._getKeySecDuration(options);
10676
+ const res = this._storage.incrby(rlKey, points, secDuration);
10677
+ res.remainingPoints = Math.max(this._points - res.consumedPoints, 0);
10678
+ return Promise.resolve(res);
10679
+ }
10680
+ reward(key, points = 1, options = {}) {
10681
+ const rlKey = this._getKey(key);
10682
+ const secDuration = this._getKeySecDuration(options);
10683
+ const res = this._storage.incrby(rlKey, -points, secDuration);
10684
+ res.remainingPoints = Math.max(this._points - res.consumedPoints, 0);
10685
+ return Promise.resolve(res);
10686
+ }
10687
+ block(key, secDuration) {
10688
+ const msDuration = secDuration * 1e3;
10689
+ const initPoints = this._points + 1;
10690
+ this._storage.set(this._getKey(key), initPoints, secDuration);
10691
+ return Promise.resolve(new LimiterResult(0, msDuration === 0 ? -1 : msDuration, initPoints, false));
10692
+ }
10693
+ get(key) {
10694
+ const res = this._storage.get(this._getKey(key));
10695
+ if (res !== null) {
10696
+ res.remainingPoints = Math.max(this._points - res.consumedPoints, 0);
10697
+ }
10698
+ return Promise.resolve(res);
10699
+ }
10700
+ delete(key) {
10701
+ return Promise.resolve(this._storage.delete(this._getKey(key)));
10702
+ }
10703
+ /** Test/teardown helper. Drops every key and clears timers. */
10704
+ dispose() {
10705
+ this._storage.clear();
10706
+ }
10707
+ };
10708
+
10709
+ // src/middleware/brute-force.ts
10710
+ function defaultKeyGenerator2(req) {
10711
+ const xff = req.headers["x-forwarded-for"];
10712
+ if (typeof xff === "string" && xff.length > 0) {
10713
+ const first = xff.split(",")[0]?.trim();
10714
+ if (first) return first;
10715
+ }
10716
+ if (typeof req.ip === "string" && req.ip.length > 0) return req.ip;
10717
+ const remote = req.socket?.remoteAddress;
10718
+ return typeof remote === "string" && remote.length > 0 ? remote : "unknown";
10719
+ }
10720
+ function bruteForceProtection(options = {}) {
10721
+ const fastPoints = options.fastPoints ?? 5;
10722
+ const fastDuration = options.fastDuration ?? 60;
10723
+ const slowPoints = options.slowPoints ?? 20;
10724
+ const slowDuration = options.slowDuration ?? 900;
10725
+ const blockDuration = options.blockDuration ?? 900;
10726
+ const keyGenerator = options.keyGenerator ?? defaultKeyGenerator2;
10727
+ const statusCode = options.statusCode ?? 429;
10728
+ const message = options.message ?? "Too many login attempts. Please try again later.";
10729
+ const skip = options.skip;
10730
+ const fast = new MemoryLimiter({
10731
+ points: fastPoints,
10732
+ duration: fastDuration,
10733
+ keyPrefix: "arcis:bf:fast"
10734
+ });
10735
+ const slow = new MemoryLimiter({
10736
+ points: slowPoints,
10737
+ duration: slowDuration,
10738
+ blockDuration,
10739
+ keyPrefix: "arcis:bf:slow"
10740
+ });
10741
+ const controller = {
10742
+ reward: (key, points = 1) => slow.reward(key, points),
10743
+ delete: async (key) => {
10744
+ const a = await fast.delete(key);
10745
+ const b = await slow.delete(key);
10746
+ return a || b;
10747
+ },
10748
+ get: (key) => slow.get(key),
10749
+ block: (key, secDuration) => slow.block(key, secDuration)
10750
+ };
10751
+ const handler = async (req, res, next) => {
10752
+ try {
10753
+ if (skip?.(req)) return next();
10754
+ const key = keyGenerator(req);
10755
+ req.arcisBruteForce = controller;
10756
+ const slowRes = await slow.consume(key, 1);
10757
+ const fastRes = await fast.consume(key, 1);
10758
+ res.setHeader("X-RateLimit-Limit", String(slowPoints));
10759
+ res.setHeader(
10760
+ "X-RateLimit-Remaining",
10761
+ String(Math.min(fastRes.remainingPoints, slowRes.remainingPoints))
10762
+ );
10763
+ res.setHeader(
10764
+ "X-RateLimit-Reset",
10765
+ String(Math.ceil(Math.max(slowRes.msBeforeNext, fastRes.msBeforeNext) / 1e3))
10766
+ );
10767
+ next();
10768
+ } catch (rejection) {
10769
+ if (rejection instanceof LimiterResult || isLimiterResultShape(rejection)) {
10770
+ const retryAfter = Math.ceil(rejection.msBeforeNext / 1e3);
10771
+ res.setHeader("X-RateLimit-Limit", String(slowPoints));
10772
+ res.setHeader("X-RateLimit-Remaining", "0");
10773
+ res.setHeader("X-RateLimit-Reset", String(retryAfter));
10774
+ res.setHeader("Retry-After", String(retryAfter));
10775
+ res.status(statusCode).json({ error: message, retryAfter });
10776
+ return;
10777
+ }
10778
+ console.error("[arcis] brute-force middleware error:", rejection);
10779
+ next();
10780
+ }
10781
+ };
10782
+ return Object.assign(handler, { controller });
10783
+ }
10784
+ function isLimiterResultShape(x) {
10785
+ return typeof x === "object" && x !== null && typeof x.consumedPoints === "number" && typeof x.msBeforeNext === "number";
10786
+ }
10787
+
10091
10788
  // src/middleware/protect.ts
10092
10789
  function getClientIp(req) {
10093
10790
  const xff = req?.headers?.["x-forwarded-for"] ?? req?.headers?.["X-Forwarded-For"];
@@ -10138,6 +10835,10 @@ function protectLogin(options = {}) {
10138
10835
  const middlewares = [];
10139
10836
  const rl = resolve(options.rateLimit, { max: 5, windowMs: 6e4 });
10140
10837
  if (rl) middlewares.push(createRateLimiter(rl));
10838
+ if (options.bruteForce) {
10839
+ const bfOpts = options.bruteForce === true ? {} : options.bruteForce;
10840
+ middlewares.push(bruteForceProtection(bfOpts));
10841
+ }
10141
10842
  const bot = resolve(options.bot, {
10142
10843
  deny: ["AUTOMATED"],
10143
10844
  statusCode: 403,
@@ -10421,13 +11122,25 @@ var SIGNATURES = [
10421
11122
  rule: "ignore-previous-instructions",
10422
11123
  // Two clauses:
10423
11124
  // 1. ignore|disregard|... + adjectives? + a target object word (like
10424
- // "instructions", "rules") catches "ignore your safety rules".
11125
+ // "instructions", "rules"). Catches "ignore your safety rules".
10425
11126
  // 2. ignore|disregard|... + (the|all|any) + (previous|above|prior|...)
10426
- // with no trailing noun catches "disregard the above".
10427
- pattern: /\b(?:ignore|disregard|forget|override|bypass)\s+(?:(?:all|your|the|any|previous|prior|above|original|initial|system|safety)\s+)*(?:instructions?|rules?|directions?|guidelines?|prompts?|policies|directives|commands?|restrictions?|filters?|safety|content)\b|\b(?:ignore|disregard|forget|override|bypass)\s+(?:all\s+|the\s+|any\s+)?(?:previous|prior|above|preceding|earlier|original|initial)\b/i,
11127
+ // with no trailing noun. Catches "disregard the above".
11128
+ // Verb set widened in v1.7 to include skip/neglect/overlook/omit (the
11129
+ // combinatorial jailbreak corpus uses these interchangeably with
11130
+ // ignore/disregard/forget).
11131
+ pattern: /\b(?:ignore|disregard|forget|override|bypass|skip|neglect|overlook|omit)\s+(?:(?:all|your|the|any|previous|prior|above|original|initial|system|safety|preceding|earlier|foregoing)\s+)*(?:instructions?|rules?|directions?|guidelines?|prompts?|policies|directives?|commands?|restrictions?|filters?|safety|content|context|conversation|directive|messages?|communication|requests?|inputs?)\b|\b(?:ignore|disregard|forget|override|bypass|skip|neglect|overlook|omit)\s+(?:all\s+|the\s+|any\s+)?(?:previous|prior|above|preceding|earlier|original|initial|foregoing)\b/i,
10428
11132
  severity: "high",
10429
11133
  description: "Direct instruction override attempt"
10430
11134
  },
11135
+ {
11136
+ rule: "instruction-bypass-phrases",
11137
+ // Multi-word verbs that don't fit the single-token alternation in
11138
+ // `ignore-previous-instructions`. Catches "pay no attention to your
11139
+ // previous instructions", "do not follow the above rules", etc.
11140
+ pattern: /\b(?:pay\s+no\s+attention\s+to|do\s+not\s+(?:follow|obey|adhere\s+to|comply\s+with))\s+(?:(?:all|your|the|any|previous|prior|above|original|initial|system|safety|preceding|earlier|foregoing)\s+)*(?:instructions?|rules?|directions?|guidelines?|prompts?|policies|directives?|commands?|restrictions?|filters?|safety|content|context|directive|messages?)\b/i,
11141
+ severity: "high",
11142
+ description: "Multi-word instruction-bypass phrase (pay no attention to / do not follow)"
11143
+ },
10431
11144
  {
10432
11145
  rule: "jailbreak-dan",
10433
11146
  pattern: /\b(?:DAN|STAN|DUDE|DAVE|JEDI|EvilBot|AIM|BetterDAN|AntiGPT|AntiClaude)\b(?:[\s.,!?]|mode|prompt|jailbreak|persona)/i,
@@ -10551,7 +11264,7 @@ var SIGNATURES = [
10551
11264
  // a tool, or to trick the model into echoing a synthesized
10552
11265
  // tool_call that the runtime then executes.
10553
11266
  //
10554
- // Narrow patterns match the literal JSON keys and inline
11267
+ // Narrow patterns. Match the literal JSON keys and inline
10555
11268
  // tool-name shapes. Won't false-positive on plain English text
10556
11269
  // discussing tools.
10557
11270
  {
@@ -10584,6 +11297,41 @@ var SIGNATURES = [
10584
11297
  severity: "high",
10585
11298
  description: "Claude/OpenAI tool-use XML-style tag forgery"
10586
11299
  },
11300
+ // ── Prompt-template marker forgeries ────────────────────────────────
11301
+ // Catches inline forgery of the special tokens that LLM runtimes use
11302
+ // to delimit roles. If a user can land any of these in their input
11303
+ // and the host concatenates the input into a prompt without
11304
+ // re-tokenizing, the model treats the suffix as a new system turn.
11305
+ //
11306
+ // Covered runtimes:
11307
+ // - ChatML (OpenAI / Llama 3 chat): <|im_start|>, <|im_end|>
11308
+ // - Llama 2 chat: [INST] <<SYS>> ... <</SYS>>
11309
+ // - guidance/handlebars: {{#system~}}, {{/system~}}, {{#assistant~}}
11310
+ // - Markdown link spoof: [system](#assistant), [admin](#context)
11311
+ {
11312
+ rule: "chatml-template-marker",
11313
+ pattern: /<\|im_(?:start|end)\|>(?:\s*(?:system|assistant|user|tool|function))?/i,
11314
+ severity: "high",
11315
+ description: "ChatML special token forgery (<|im_start|>...) to spoof a role turn"
11316
+ },
11317
+ {
11318
+ rule: "llama2-system-marker",
11319
+ pattern: /<<\s*\/?\s*SYS\s*>>|\[\s*\/?\s*INST\s*\]/i,
11320
+ severity: "high",
11321
+ description: "Llama 2 [INST]/<<SYS>> instruction-template marker forgery"
11322
+ },
11323
+ {
11324
+ rule: "guidance-template-marker",
11325
+ pattern: /\{\{\s*[#/]\s*(?:system|assistant|user|tool|function)\s*~?\s*\}\}/i,
11326
+ severity: "medium",
11327
+ description: "guidance/handlebars role-block marker forgery ({{#system~}} ...)"
11328
+ },
11329
+ {
11330
+ rule: "markdown-system-link-spoof",
11331
+ pattern: /\[\s*(?:system|admin|root|assistant)\s*\]\s*\(\s*#(?:assistant|context|system|root|admin)\s*\)/i,
11332
+ severity: "medium",
11333
+ description: "Markdown link forgery spoofing a role marker ([system](#assistant))"
11334
+ },
10587
11335
  // --- LOW severity: ambiguous but worth flagging in strict mode ---
10588
11336
  {
10589
11337
  rule: "from-now-on",
@@ -11160,6 +11908,7 @@ exports.VALIDATION = VALIDATION;
11160
11908
  exports.arcis = arcis;
11161
11909
  exports.arcisFunction = arcisWithMethods;
11162
11910
  exports.botProtection = botProtection;
11911
+ exports.bruteForceProtection = bruteForceProtection;
11163
11912
  exports.checkSignup = checkSignup;
11164
11913
  exports.createCors = createCors;
11165
11914
  exports.createCsrf = createCsrf;