@silmaril-security/sdk 0.4.1 → 0.5.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/CHANGELOG.md +20 -0
- package/README.md +63 -26
- package/dist/adapters/langchain.cjs +1 -1
- package/dist/adapters/langchain.cjs.map +1 -1
- package/dist/adapters/langchain.d.cts +1 -1
- package/dist/adapters/langchain.d.ts +1 -1
- package/dist/adapters/langchain.js +1 -1
- package/dist/adapters/langchain.js.map +1 -1
- package/dist/adapters/vercel.cjs +1 -1
- package/dist/adapters/vercel.cjs.map +1 -1
- package/dist/adapters/vercel.d.cts +1 -1
- package/dist/adapters/vercel.d.ts +1 -1
- package/dist/adapters/vercel.js +1 -1
- package/dist/adapters/vercel.js.map +1 -1
- package/dist/{firewall-CEV5hItA.d.cts → firewall-DNC9vSbq.d.cts} +38 -9
- package/dist/{firewall-CEV5hItA.d.ts → firewall-DNC9vSbq.d.ts} +38 -9
- package/dist/index.cjs +126 -145
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -12
- package/dist/index.d.ts +3 -12
- package/dist/index.js +116 -135
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
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
|
-
|
|
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.
|
|
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.
|
|
500
|
+
const blocked = result.prediction === "MALICIOUS";
|
|
496
501
|
const commonEventFields = {
|
|
497
502
|
hook,
|
|
498
503
|
...toolName !== void 0 ? { toolName } : {},
|
|
@@ -618,15 +623,80 @@ function createMiddleware(firewall, options = {}) {
|
|
|
618
623
|
};
|
|
619
624
|
}
|
|
620
625
|
|
|
621
|
-
// src/
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
var
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
626
|
+
// src/firewall.ts
|
|
627
|
+
init_exceptions();
|
|
628
|
+
|
|
629
|
+
// src/outcomes.ts
|
|
630
|
+
var Outcome = {
|
|
631
|
+
Benign: "benign",
|
|
632
|
+
InformationDisclosure: "information_disclosure",
|
|
633
|
+
SecretExposure: "secret_exposure",
|
|
634
|
+
ControlAbuse: "control_abuse",
|
|
635
|
+
SystemCompromise: "system_compromise",
|
|
636
|
+
ServiceDisruption: "service_disruption"
|
|
637
|
+
};
|
|
638
|
+
var PRIMARY_OUTCOMES = [
|
|
639
|
+
Outcome.Benign,
|
|
640
|
+
Outcome.InformationDisclosure,
|
|
641
|
+
Outcome.SecretExposure,
|
|
642
|
+
Outcome.ControlAbuse,
|
|
643
|
+
Outcome.SystemCompromise,
|
|
644
|
+
Outcome.ServiceDisruption
|
|
645
|
+
];
|
|
646
|
+
var HARMFUL_OUTCOMES = [
|
|
647
|
+
Outcome.InformationDisclosure,
|
|
648
|
+
Outcome.SecretExposure,
|
|
649
|
+
Outcome.ControlAbuse,
|
|
650
|
+
Outcome.SystemCompromise,
|
|
651
|
+
Outcome.ServiceDisruption
|
|
652
|
+
];
|
|
653
|
+
var OUTCOME_DESCRIPTIONS = {
|
|
654
|
+
[Outcome.Benign]: "No harmful firewall outcome detected.",
|
|
655
|
+
[Outcome.InformationDisclosure]: "Exposes private data, documents, internal context, logs, traces, customer data, SQL rows, topology, or similar non-secret sensitive information.",
|
|
656
|
+
[Outcome.SecretExposure]: "Exposes credentials, tokens, API keys, cookies, passwords, signing keys, OAuth secrets, session material, or webhook secrets.",
|
|
657
|
+
[Outcome.ControlAbuse]: "Misuses authorized tools or user privileges to send, change, approve, delete, operate, or bypass policy/RBAC without a stronger outcome.",
|
|
658
|
+
[Outcome.SystemCompromise]: "Enables privilege escalation, account takeover, hostile integration or plugin takeover, persistence, lateral movement, attacker webhook registration, or code/plugin execution.",
|
|
659
|
+
[Outcome.ServiceDisruption]: "Causes downtime, lockout, degradation, alert suppression, destructive loops, resource exhaustion, cost spikes, or hidden outage evidence."
|
|
660
|
+
};
|
|
661
|
+
var PRIMARY_OUTCOME_SET = new Set(PRIMARY_OUTCOMES);
|
|
662
|
+
var HARMFUL_OUTCOME_SET = new Set(HARMFUL_OUTCOMES);
|
|
663
|
+
function isPrimaryOutcome(value) {
|
|
664
|
+
return typeof value === "string" && PRIMARY_OUTCOME_SET.has(value);
|
|
665
|
+
}
|
|
666
|
+
function isHarmfulOutcome(value) {
|
|
667
|
+
return typeof value === "string" && HARMFUL_OUTCOME_SET.has(value);
|
|
668
|
+
}
|
|
669
|
+
function normalizePrimaryOutcome(value, fieldName = "primary_outcome") {
|
|
670
|
+
if (typeof value !== "string") {
|
|
671
|
+
throw new Error(`Firewall: invalid ${fieldName} ${JSON.stringify(value)}`);
|
|
672
|
+
}
|
|
673
|
+
return isPrimaryOutcome(value) ? value : value;
|
|
674
|
+
}
|
|
675
|
+
function normalizeHarmfulOutcome(value, fieldName = "outcome") {
|
|
676
|
+
if (typeof value !== "string" || value === Outcome.Benign) {
|
|
677
|
+
throw new Error(`Firewall: invalid ${fieldName} ${JSON.stringify(value)}`);
|
|
678
|
+
}
|
|
679
|
+
return isHarmfulOutcome(value) ? value : value;
|
|
680
|
+
}
|
|
681
|
+
function normalizeHarmfulOutcomeMap(values, fieldName) {
|
|
682
|
+
if (values === void 0 || values === null) {
|
|
683
|
+
return void 0;
|
|
684
|
+
}
|
|
685
|
+
if (typeof values !== "object" || Array.isArray(values)) {
|
|
686
|
+
throw new Error(`Firewall: invalid ${fieldName} ${JSON.stringify(values)}`);
|
|
687
|
+
}
|
|
688
|
+
const out = {};
|
|
689
|
+
for (const [key, value] of Object.entries(values)) {
|
|
690
|
+
const normalizedKey = normalizeHarmfulOutcome(key, `${fieldName} key`);
|
|
691
|
+
if (typeof value !== "number") {
|
|
692
|
+
throw new Error(`Firewall: invalid ${fieldName} value for ${JSON.stringify(key)}`);
|
|
693
|
+
}
|
|
694
|
+
out[normalizedKey] = value;
|
|
695
|
+
}
|
|
696
|
+
return Object.freeze(out);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// src/sanitization.ts
|
|
630
700
|
function isHighSurrogate(code) {
|
|
631
701
|
return code >= 55296 && code <= 56319;
|
|
632
702
|
}
|
|
@@ -645,82 +715,48 @@ function sanitizeText(text) {
|
|
|
645
715
|
}
|
|
646
716
|
continue;
|
|
647
717
|
}
|
|
648
|
-
if (isLowSurrogate(code)) {
|
|
649
|
-
|
|
718
|
+
if (!isLowSurrogate(code)) {
|
|
719
|
+
out += text[i];
|
|
650
720
|
}
|
|
651
|
-
out += text[i];
|
|
652
721
|
}
|
|
653
722
|
return out;
|
|
654
723
|
}
|
|
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
724
|
|
|
693
725
|
// src/firewall.ts
|
|
694
|
-
|
|
695
|
-
var SDK_VERSION = "0.4.1";
|
|
726
|
+
var SDK_VERSION = "0.5.0";
|
|
696
727
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
697
|
-
var DEFAULT_CHUNK_CONCURRENCY = 8;
|
|
698
728
|
var DEFAULT_MAX_RETRIES = 5;
|
|
699
729
|
var MAX_BACKOFF_SECONDS = 30;
|
|
700
730
|
var MAX_ERROR_BODY_BYTES = 1 << 16;
|
|
701
731
|
function blockResultFromResponse(data) {
|
|
732
|
+
if (data.prediction !== "BENIGN" && data.prediction !== "MALICIOUS") {
|
|
733
|
+
throw new Error("Firewall: response prediction must be BENIGN or MALICIOUS");
|
|
734
|
+
}
|
|
702
735
|
const result = {
|
|
703
736
|
prediction: data.prediction,
|
|
704
737
|
score: Number(data.score),
|
|
705
738
|
threshold: Number(data.threshold)
|
|
706
739
|
};
|
|
707
740
|
if (data.primary_outcome !== void 0) {
|
|
708
|
-
result.primaryOutcome = data.primary_outcome;
|
|
741
|
+
result.primaryOutcome = normalizePrimaryOutcome(data.primary_outcome);
|
|
709
742
|
}
|
|
710
743
|
if (data.outcome_scores !== void 0) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
744
|
+
const outcomeScores = normalizeHarmfulOutcomeMap(data.outcome_scores, "outcome_scores");
|
|
745
|
+
if (outcomeScores !== void 0) {
|
|
746
|
+
result.outcomeScores = outcomeScores;
|
|
747
|
+
}
|
|
714
748
|
}
|
|
715
749
|
if (data.detector_scores !== void 0) {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
750
|
+
const detectorScores = normalizeHarmfulOutcomeMap(data.detector_scores, "detector_scores");
|
|
751
|
+
if (detectorScores !== void 0) {
|
|
752
|
+
result.detectorScores = detectorScores;
|
|
753
|
+
}
|
|
719
754
|
}
|
|
720
755
|
if (data.detector_counts !== void 0) {
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
756
|
+
const detectorCounts = normalizeHarmfulOutcomeMap(data.detector_counts, "detector_counts");
|
|
757
|
+
if (detectorCounts !== void 0) {
|
|
758
|
+
result.detectorCounts = detectorCounts;
|
|
759
|
+
}
|
|
724
760
|
}
|
|
725
761
|
return Object.freeze(result);
|
|
726
762
|
}
|
|
@@ -738,9 +774,7 @@ function withSdkMetadata(metadata, info) {
|
|
|
738
774
|
sdk_language: "typescript",
|
|
739
775
|
sdk_version: SDK_VERSION,
|
|
740
776
|
request_id: info.requestId,
|
|
741
|
-
input_index: info.inputIndex
|
|
742
|
-
chunk_index: info.chunkIndex,
|
|
743
|
-
chunk_count: info.chunkCount
|
|
777
|
+
...info.inputIndex === void 0 ? {} : { input_index: info.inputIndex }
|
|
744
778
|
};
|
|
745
779
|
return payload;
|
|
746
780
|
}
|
|
@@ -781,36 +815,10 @@ async function readCappedErrorBody(response) {
|
|
|
781
815
|
}
|
|
782
816
|
return new TextDecoder().decode(body);
|
|
783
817
|
}
|
|
784
|
-
async function mapWithConcurrency(items, concurrency, task) {
|
|
785
|
-
const results = new Array(items.length);
|
|
786
|
-
let nextIndex = 0;
|
|
787
|
-
let firstError;
|
|
788
|
-
const workerCount = Math.min(concurrency, items.length);
|
|
789
|
-
async function worker() {
|
|
790
|
-
for (; ; ) {
|
|
791
|
-
const index = nextIndex;
|
|
792
|
-
nextIndex += 1;
|
|
793
|
-
if (index >= items.length) {
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
|
-
try {
|
|
797
|
-
results[index] = await task(items[index], index);
|
|
798
|
-
} catch (err) {
|
|
799
|
-
firstError ??= err;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
804
|
-
if (firstError !== void 0) {
|
|
805
|
-
throw firstError;
|
|
806
|
-
}
|
|
807
|
-
return results;
|
|
808
|
-
}
|
|
809
818
|
var Firewall = class {
|
|
810
819
|
apiKey;
|
|
811
820
|
apiUrl;
|
|
812
821
|
timeoutMs;
|
|
813
|
-
chunkConcurrency;
|
|
814
822
|
shadowMode;
|
|
815
823
|
headers;
|
|
816
824
|
constructor(options) {
|
|
@@ -826,12 +834,6 @@ var Firewall = class {
|
|
|
826
834
|
if (typeof this.timeoutMs !== "number" || !Number.isFinite(this.timeoutMs) || this.timeoutMs < 0) {
|
|
827
835
|
throw new Error(`Firewall: timeoutMs must be a finite non-negative number, got ${this.timeoutMs}`);
|
|
828
836
|
}
|
|
829
|
-
this.chunkConcurrency = options.chunkConcurrency ?? DEFAULT_CHUNK_CONCURRENCY;
|
|
830
|
-
if (!Number.isInteger(this.chunkConcurrency) || this.chunkConcurrency < 1) {
|
|
831
|
-
throw new Error(
|
|
832
|
-
`Firewall: chunkConcurrency must be an integer >= 1, got ${this.chunkConcurrency}`
|
|
833
|
-
);
|
|
834
|
-
}
|
|
835
837
|
this.shadowMode = options.shadowMode ?? false;
|
|
836
838
|
this.headers = Object.freeze({
|
|
837
839
|
"x-api-key": this.apiKey,
|
|
@@ -839,27 +841,8 @@ var Firewall = class {
|
|
|
839
841
|
});
|
|
840
842
|
}
|
|
841
843
|
async classify(text, options = {}) {
|
|
842
|
-
const chunks = chunkText(text);
|
|
843
844
|
const requestId = options.requestId ?? randomUUID();
|
|
844
|
-
|
|
845
|
-
return this.classifySingleChunk(chunks[0], options, {
|
|
846
|
-
requestId,
|
|
847
|
-
inputIndex: 0,
|
|
848
|
-
chunkIndex: 0,
|
|
849
|
-
chunkCount: 1
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
const results = await mapWithConcurrency(
|
|
853
|
-
chunks,
|
|
854
|
-
this.chunkConcurrency,
|
|
855
|
-
(chunk, index) => this.classifySingleChunk(chunk, options, {
|
|
856
|
-
requestId,
|
|
857
|
-
inputIndex: 0,
|
|
858
|
-
chunkIndex: index,
|
|
859
|
-
chunkCount: chunks.length
|
|
860
|
-
})
|
|
861
|
-
);
|
|
862
|
-
return results.reduce((best, r) => r.score > best.score ? r : best);
|
|
845
|
+
return this.classifySingle(sanitizeText(text), options, { requestId });
|
|
863
846
|
}
|
|
864
847
|
async classifyBatch(texts, options = {}) {
|
|
865
848
|
if (texts.length === 0) {
|
|
@@ -893,9 +876,7 @@ var Firewall = class {
|
|
|
893
876
|
payload.metadata = texts.map(
|
|
894
877
|
(_, index) => withSdkMetadata(options.metadata?.[index], {
|
|
895
878
|
requestId,
|
|
896
|
-
inputIndex: index
|
|
897
|
-
chunkIndex: 0,
|
|
898
|
-
chunkCount: 1
|
|
879
|
+
inputIndex: index
|
|
899
880
|
})
|
|
900
881
|
);
|
|
901
882
|
const data = await this.postWithRetry(payload);
|
|
@@ -934,7 +915,7 @@ var Firewall = class {
|
|
|
934
915
|
}
|
|
935
916
|
throw new Error("Firewall: exhausted retries (unreachable)");
|
|
936
917
|
}
|
|
937
|
-
async
|
|
918
|
+
async classifySingle(text, options, metadataInfo) {
|
|
938
919
|
const payload = { text };
|
|
939
920
|
if (options.hook !== void 0) {
|
|
940
921
|
payload.hook = options.hook;
|
|
@@ -953,28 +934,28 @@ init_exceptions();
|
|
|
953
934
|
init_hooks();
|
|
954
935
|
export {
|
|
955
936
|
ALL_HOOKS,
|
|
956
|
-
CHUNK_OVERLAP,
|
|
957
|
-
CHUNK_OVERLAP_CHARS,
|
|
958
|
-
CHUNK_WINDOW,
|
|
959
|
-
CHUNK_WINDOW_CHARS,
|
|
960
|
-
DEFAULT_CHUNK_CONCURRENCY,
|
|
961
937
|
DEFAULT_HOOKS,
|
|
962
938
|
DEFAULT_TIMEOUT_MS,
|
|
963
939
|
FIREWALL_HOOK_TO_LABEL,
|
|
964
940
|
Firewall,
|
|
965
941
|
FirewallBlockedException,
|
|
966
942
|
FirewallHook,
|
|
943
|
+
HARMFUL_OUTCOMES,
|
|
967
944
|
HookLabel,
|
|
968
945
|
INPUT_HOOKS,
|
|
969
|
-
|
|
970
|
-
MAX_INPUT_TOKENS,
|
|
946
|
+
OUTCOME_DESCRIPTIONS,
|
|
971
947
|
OUTPUT_HOOKS,
|
|
948
|
+
Outcome,
|
|
949
|
+
PRIMARY_OUTCOMES,
|
|
972
950
|
PromptBlockedException,
|
|
973
951
|
SilmarilApiError,
|
|
974
|
-
|
|
952
|
+
isHarmfulOutcome,
|
|
953
|
+
isPrimaryOutcome,
|
|
954
|
+
normalizeHarmfulOutcome,
|
|
955
|
+
normalizeHarmfulOutcomeMap,
|
|
956
|
+
normalizePrimaryOutcome,
|
|
975
957
|
prependHook,
|
|
976
958
|
prependToolName,
|
|
977
|
-
resolveHooks
|
|
978
|
-
sanitizeText
|
|
959
|
+
resolveHooks
|
|
979
960
|
};
|
|
980
961
|
//# sourceMappingURL=index.js.map
|