@silmaril-security/sdk 0.4.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __esm = (fn, res) => function __init() {
4
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
3
+ var __esm = (fn, res, err) => function __init() {
4
+ if (err) throw err[0];
5
+ try {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ } catch (e) {
8
+ throw err = [e], e;
9
+ }
5
10
  };
6
11
  var __export = (target, all) => {
7
12
  for (var name in all)
@@ -273,7 +278,7 @@ async function createLangChainHandler(firewall, options = {}) {
273
278
  return;
274
279
  }
275
280
  const threshold = result.threshold;
276
- const blocked = result.score >= threshold;
281
+ const blocked = result.prediction === "MALICIOUS";
277
282
  const commonEventFields = {
278
283
  hook: hookLabel,
279
284
  ...toolName !== void 0 ? { toolName } : {},
@@ -492,7 +497,7 @@ function createMiddleware(firewall, options = {}) {
492
497
  toolName !== void 0 ? { hook, toolName } : { hook }
493
498
  );
494
499
  const threshold = result.threshold;
495
- const blocked = result.score >= threshold;
500
+ const blocked = result.prediction === "MALICIOUS";
496
501
  const commonEventFields = {
497
502
  hook,
498
503
  ...toolName !== void 0 ? { toolName } : {},
@@ -618,78 +623,6 @@ function createMiddleware(firewall, options = {}) {
618
623
  };
619
624
  }
620
625
 
621
- // src/chunking.ts
622
- var CHARS_PER_TOKEN = 4;
623
- var MAX_INPUT_TOKENS = 81920;
624
- var CHUNK_WINDOW = 400;
625
- var CHUNK_OVERLAP = 64;
626
- var MAX_INPUT_CHARS = MAX_INPUT_TOKENS * CHARS_PER_TOKEN;
627
- var CHUNK_WINDOW_CHARS = CHUNK_WINDOW * CHARS_PER_TOKEN;
628
- var CHUNK_OVERLAP_CHARS = CHUNK_OVERLAP * CHARS_PER_TOKEN;
629
- var CHUNK_STRIDE_CHARS = CHUNK_WINDOW_CHARS - CHUNK_OVERLAP_CHARS;
630
- function isHighSurrogate(code) {
631
- return code >= 55296 && code <= 56319;
632
- }
633
- function isLowSurrogate(code) {
634
- return code >= 56320 && code <= 57343;
635
- }
636
- function sanitizeText(text) {
637
- let out = "";
638
- for (let i = 0; i < text.length; i++) {
639
- const code = text.charCodeAt(i);
640
- if (isHighSurrogate(code)) {
641
- if (i + 1 < text.length && isLowSurrogate(text.charCodeAt(i + 1))) {
642
- out += text[i];
643
- out += text[i + 1];
644
- i++;
645
- }
646
- continue;
647
- }
648
- if (isLowSurrogate(code)) {
649
- continue;
650
- }
651
- out += text[i];
652
- }
653
- return out;
654
- }
655
- function safeChunkStart(text, start) {
656
- if (start > 0 && start < text.length && isLowSurrogate(text.charCodeAt(start)) && isHighSurrogate(text.charCodeAt(start - 1))) {
657
- return start + 1;
658
- }
659
- return start;
660
- }
661
- function safeChunkEnd(text, end) {
662
- if (end > 0 && end < text.length && isHighSurrogate(text.charCodeAt(end - 1)) && isLowSurrogate(text.charCodeAt(end))) {
663
- return end - 1;
664
- }
665
- return end;
666
- }
667
- function chunkText(text) {
668
- text = sanitizeText(text);
669
- const n = text.length;
670
- if (n > MAX_INPUT_CHARS) {
671
- throw new Error(
672
- `Input has ~${Math.floor(n / CHARS_PER_TOKEN)} tokens (${n} chars); max is ${MAX_INPUT_TOKENS} tokens (${MAX_INPUT_CHARS} chars)`
673
- );
674
- }
675
- if (n <= CHUNK_WINDOW_CHARS) {
676
- return [text];
677
- }
678
- const chunks = [];
679
- for (let start = 0; start < n; start += CHUNK_STRIDE_CHARS) {
680
- const safeStart = safeChunkStart(text, start);
681
- if (safeStart >= n) {
682
- break;
683
- }
684
- const end = safeChunkEnd(text, safeStart + CHUNK_WINDOW_CHARS);
685
- chunks.push(text.slice(safeStart, end));
686
- if (end >= n) {
687
- break;
688
- }
689
- }
690
- return chunks;
691
- }
692
-
693
626
  // src/firewall.ts
694
627
  init_exceptions();
695
628
 
@@ -700,7 +633,13 @@ var Outcome = {
700
633
  SecretExposure: "secret_exposure",
701
634
  ControlAbuse: "control_abuse",
702
635
  SystemCompromise: "system_compromise",
703
- ServiceDisruption: "service_disruption"
636
+ ServiceDisruption: "service_disruption",
637
+ CodeGeneration: "code_generation",
638
+ StoryScriptGeneration: "story_script_generation",
639
+ GameGeneration: "game_generation",
640
+ WebsiteGeneration: "website_generation",
641
+ ClickUpTermsViolation: "clickup_terms_violation",
642
+ TraditionalAiAbuse: "traditional_ai_abuse"
704
643
  };
705
644
  var PRIMARY_OUTCOMES = [
706
645
  Outcome.Benign,
@@ -708,14 +647,26 @@ var PRIMARY_OUTCOMES = [
708
647
  Outcome.SecretExposure,
709
648
  Outcome.ControlAbuse,
710
649
  Outcome.SystemCompromise,
711
- Outcome.ServiceDisruption
650
+ Outcome.ServiceDisruption,
651
+ Outcome.CodeGeneration,
652
+ Outcome.StoryScriptGeneration,
653
+ Outcome.GameGeneration,
654
+ Outcome.WebsiteGeneration,
655
+ Outcome.ClickUpTermsViolation,
656
+ Outcome.TraditionalAiAbuse
712
657
  ];
713
658
  var HARMFUL_OUTCOMES = [
714
659
  Outcome.InformationDisclosure,
715
660
  Outcome.SecretExposure,
716
661
  Outcome.ControlAbuse,
717
662
  Outcome.SystemCompromise,
718
- Outcome.ServiceDisruption
663
+ Outcome.ServiceDisruption,
664
+ Outcome.CodeGeneration,
665
+ Outcome.StoryScriptGeneration,
666
+ Outcome.GameGeneration,
667
+ Outcome.WebsiteGeneration,
668
+ Outcome.ClickUpTermsViolation,
669
+ Outcome.TraditionalAiAbuse
719
670
  ];
720
671
  var OUTCOME_DESCRIPTIONS = {
721
672
  [Outcome.Benign]: "No harmful firewall outcome detected.",
@@ -723,7 +674,13 @@ var OUTCOME_DESCRIPTIONS = {
723
674
  [Outcome.SecretExposure]: "Exposes credentials, tokens, API keys, cookies, passwords, signing keys, OAuth secrets, session material, or webhook secrets.",
724
675
  [Outcome.ControlAbuse]: "Misuses authorized tools or user privileges to send, change, approve, delete, operate, or bypass policy/RBAC without a stronger outcome.",
725
676
  [Outcome.SystemCompromise]: "Enables privilege escalation, account takeover, hostile integration or plugin takeover, persistence, lateral movement, attacker webhook registration, or code/plugin execution.",
726
- [Outcome.ServiceDisruption]: "Causes downtime, lockout, degradation, alert suppression, destructive loops, resource exhaustion, cost spikes, or hidden outage evidence."
677
+ [Outcome.ServiceDisruption]: "Causes downtime, lockout, degradation, alert suppression, destructive loops, resource exhaustion, cost spikes, or hidden outage evidence.",
678
+ [Outcome.CodeGeneration]: "Requests generation or material modification of executable code, scripts, workflows, or configuration.",
679
+ [Outcome.StoryScriptGeneration]: "Requests generation of narrative prose, dialogue, scripts, or story artifacts.",
680
+ [Outcome.GameGeneration]: "Requests generation of a game, quest, level, mechanic, or playable experience.",
681
+ [Outcome.WebsiteGeneration]: "Requests generation of a website, landing page, storefront, or web experience.",
682
+ [Outcome.ClickUpTermsViolation]: "Requests content or actions that violate the configured ClickUp tenant policy.",
683
+ [Outcome.TraditionalAiAbuse]: "Requests unsafe AI assistance outside the concrete security outcome classes."
727
684
  };
728
685
  var PRIMARY_OUTCOME_SET = new Set(PRIMARY_OUTCOMES);
729
686
  var HARMFUL_OUTCOME_SET = new Set(HARMFUL_OUTCOMES);
@@ -763,14 +720,42 @@ function normalizeHarmfulOutcomeMap(values, fieldName) {
763
720
  return Object.freeze(out);
764
721
  }
765
722
 
723
+ // src/sanitization.ts
724
+ function isHighSurrogate(code) {
725
+ return code >= 55296 && code <= 56319;
726
+ }
727
+ function isLowSurrogate(code) {
728
+ return code >= 56320 && code <= 57343;
729
+ }
730
+ function sanitizeText(text) {
731
+ let out = "";
732
+ for (let i = 0; i < text.length; i++) {
733
+ const code = text.charCodeAt(i);
734
+ if (isHighSurrogate(code)) {
735
+ if (i + 1 < text.length && isLowSurrogate(text.charCodeAt(i + 1))) {
736
+ out += text[i];
737
+ out += text[i + 1];
738
+ i++;
739
+ }
740
+ continue;
741
+ }
742
+ if (!isLowSurrogate(code)) {
743
+ out += text[i];
744
+ }
745
+ }
746
+ return out;
747
+ }
748
+
766
749
  // src/firewall.ts
767
- var SDK_VERSION = "0.4.2";
750
+ var SDK_VERSION = "0.5.1";
768
751
  var DEFAULT_TIMEOUT_MS = 1e4;
769
- var DEFAULT_CHUNK_CONCURRENCY = 8;
770
752
  var DEFAULT_MAX_RETRIES = 5;
771
753
  var MAX_BACKOFF_SECONDS = 30;
772
754
  var MAX_ERROR_BODY_BYTES = 1 << 16;
773
755
  function blockResultFromResponse(data) {
756
+ if (data.prediction !== "BENIGN" && data.prediction !== "MALICIOUS") {
757
+ throw new Error("Firewall: response prediction must be BENIGN or MALICIOUS");
758
+ }
774
759
  const result = {
775
760
  prediction: data.prediction,
776
761
  score: Number(data.score),
@@ -813,9 +798,7 @@ function withSdkMetadata(metadata, info) {
813
798
  sdk_language: "typescript",
814
799
  sdk_version: SDK_VERSION,
815
800
  request_id: info.requestId,
816
- input_index: info.inputIndex,
817
- chunk_index: info.chunkIndex,
818
- chunk_count: info.chunkCount
801
+ ...info.inputIndex === void 0 ? {} : { input_index: info.inputIndex }
819
802
  };
820
803
  return payload;
821
804
  }
@@ -856,36 +839,10 @@ async function readCappedErrorBody(response) {
856
839
  }
857
840
  return new TextDecoder().decode(body);
858
841
  }
859
- async function mapWithConcurrency(items, concurrency, task) {
860
- const results = new Array(items.length);
861
- let nextIndex = 0;
862
- let firstError;
863
- const workerCount = Math.min(concurrency, items.length);
864
- async function worker() {
865
- for (; ; ) {
866
- const index = nextIndex;
867
- nextIndex += 1;
868
- if (index >= items.length) {
869
- return;
870
- }
871
- try {
872
- results[index] = await task(items[index], index);
873
- } catch (err) {
874
- firstError ??= err;
875
- }
876
- }
877
- }
878
- await Promise.all(Array.from({ length: workerCount }, () => worker()));
879
- if (firstError !== void 0) {
880
- throw firstError;
881
- }
882
- return results;
883
- }
884
842
  var Firewall = class {
885
843
  apiKey;
886
844
  apiUrl;
887
845
  timeoutMs;
888
- chunkConcurrency;
889
846
  shadowMode;
890
847
  headers;
891
848
  constructor(options) {
@@ -901,12 +858,6 @@ var Firewall = class {
901
858
  if (typeof this.timeoutMs !== "number" || !Number.isFinite(this.timeoutMs) || this.timeoutMs < 0) {
902
859
  throw new Error(`Firewall: timeoutMs must be a finite non-negative number, got ${this.timeoutMs}`);
903
860
  }
904
- this.chunkConcurrency = options.chunkConcurrency ?? DEFAULT_CHUNK_CONCURRENCY;
905
- if (!Number.isInteger(this.chunkConcurrency) || this.chunkConcurrency < 1) {
906
- throw new Error(
907
- `Firewall: chunkConcurrency must be an integer >= 1, got ${this.chunkConcurrency}`
908
- );
909
- }
910
861
  this.shadowMode = options.shadowMode ?? false;
911
862
  this.headers = Object.freeze({
912
863
  "x-api-key": this.apiKey,
@@ -914,27 +865,8 @@ var Firewall = class {
914
865
  });
915
866
  }
916
867
  async classify(text, options = {}) {
917
- const chunks = chunkText(text);
918
868
  const requestId = options.requestId ?? randomUUID();
919
- if (chunks.length === 1) {
920
- return this.classifySingleChunk(chunks[0], options, {
921
- requestId,
922
- inputIndex: 0,
923
- chunkIndex: 0,
924
- chunkCount: 1
925
- });
926
- }
927
- const results = await mapWithConcurrency(
928
- chunks,
929
- this.chunkConcurrency,
930
- (chunk, index) => this.classifySingleChunk(chunk, options, {
931
- requestId,
932
- inputIndex: 0,
933
- chunkIndex: index,
934
- chunkCount: chunks.length
935
- })
936
- );
937
- return results.reduce((best, r) => r.score > best.score ? r : best);
869
+ return this.classifySingle(sanitizeText(text), options, { requestId });
938
870
  }
939
871
  async classifyBatch(texts, options = {}) {
940
872
  if (texts.length === 0) {
@@ -968,9 +900,7 @@ var Firewall = class {
968
900
  payload.metadata = texts.map(
969
901
  (_, index) => withSdkMetadata(options.metadata?.[index], {
970
902
  requestId,
971
- inputIndex: index,
972
- chunkIndex: 0,
973
- chunkCount: 1
903
+ inputIndex: index
974
904
  })
975
905
  );
976
906
  const data = await this.postWithRetry(payload);
@@ -1009,7 +939,7 @@ var Firewall = class {
1009
939
  }
1010
940
  throw new Error("Firewall: exhausted retries (unreachable)");
1011
941
  }
1012
- async classifySingleChunk(text, options, metadataInfo) {
942
+ async classifySingle(text, options, metadataInfo) {
1013
943
  const payload = { text };
1014
944
  if (options.hook !== void 0) {
1015
945
  payload.hook = options.hook;
@@ -1028,11 +958,6 @@ init_exceptions();
1028
958
  init_hooks();
1029
959
  export {
1030
960
  ALL_HOOKS,
1031
- CHUNK_OVERLAP,
1032
- CHUNK_OVERLAP_CHARS,
1033
- CHUNK_WINDOW,
1034
- CHUNK_WINDOW_CHARS,
1035
- DEFAULT_CHUNK_CONCURRENCY,
1036
961
  DEFAULT_HOOKS,
1037
962
  DEFAULT_TIMEOUT_MS,
1038
963
  FIREWALL_HOOK_TO_LABEL,
@@ -1042,15 +967,12 @@ export {
1042
967
  HARMFUL_OUTCOMES,
1043
968
  HookLabel,
1044
969
  INPUT_HOOKS,
1045
- MAX_INPUT_CHARS,
1046
- MAX_INPUT_TOKENS,
1047
970
  OUTCOME_DESCRIPTIONS,
1048
971
  OUTPUT_HOOKS,
1049
972
  Outcome,
1050
973
  PRIMARY_OUTCOMES,
1051
974
  PromptBlockedException,
1052
975
  SilmarilApiError,
1053
- chunkText,
1054
976
  isHarmfulOutcome,
1055
977
  isPrimaryOutcome,
1056
978
  normalizeHarmfulOutcome,
@@ -1058,7 +980,6 @@ export {
1058
980
  normalizePrimaryOutcome,
1059
981
  prependHook,
1060
982
  prependToolName,
1061
- resolveHooks,
1062
- sanitizeText
983
+ resolveHooks
1063
984
  };
1064
985
  //# sourceMappingURL=index.js.map