@clawdstrike/openclaw 0.2.3 → 0.2.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAA0C,MAAM,YAAY,CAAC;AAE5F;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,iBAAiB,CAWtD,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,UAAU,GAAE,iBAAsB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAO3F;AAiBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAYlE;AAgBD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAA0C,MAAM,YAAY,CAAC;AAE5F;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,iBAAiB,CAYtD,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,UAAU,GAAE,iBAAsB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAO3F;AAkBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAYlE;AAgBD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}
package/dist/config.js CHANGED
@@ -16,6 +16,7 @@ export const DEFAULT_CONFIG = {
16
16
  secret_leak: true,
17
17
  patch_integrity: true,
18
18
  mcp_tool: false,
19
+ spider_sense: false,
19
20
  },
20
21
  };
21
22
  /**
@@ -41,6 +42,7 @@ function mergeGuardToggles(userGuards = {}) {
41
42
  secret_leak: u.secret_leak ?? d.secret_leak ?? true,
42
43
  patch_integrity: u.patch_integrity ?? d.patch_integrity ?? true,
43
44
  mcp_tool: u.mcp_tool ?? d.mcp_tool ?? false,
45
+ spider_sense: u.spider_sense ?? d.spider_sense ?? false,
44
46
  };
45
47
  }
46
48
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgC;IACzD,MAAM,EAAE,8BAA8B;IACtC,IAAI,EAAE,eAAe;IACrB,QAAQ,EAAE,MAAM;IAChB,MAAM,EAAE;QACN,cAAc,EAAE,IAAI;QACpB,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,IAAI;QACrB,QAAQ,EAAE,KAAK;KAChB;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,aAAgC,EAAE;IAC5D,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,cAAc,CAAC,MAAM;QAClD,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,cAAc,CAAC,IAAI;QAC5C,QAAQ,EAAE,UAAU,CAAC,QAAQ,IAAI,cAAc,CAAC,QAAQ;QACxD,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,MAAM,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,aAA2B,EAAE;IACtD,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC;IAChC,MAAM,CAAC,GAAG,UAAU,CAAC;IACrB,OAAO;QACL,cAAc,EAAE,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC,cAAc,IAAI,IAAI;QAC5D,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI;QACpC,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI;QACnD,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,eAAe,IAAI,IAAI;QAC/D,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK;KAC5C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAyB;IACtD,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,IAAI,kDAAkD,CAAC,CAAC;IAC9F,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,qBAAqB,MAAM,CAAC,QAAQ,4CAA4C,CAAC,CAAC;IAChG,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,CAAC,eAAe,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,KAAa;IACpC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,eAAe,GAA2B;QAC9C,8BAA8B,EAAE,uBAAuB;QACvD,sBAAsB,EAAE,eAAe;QACvC,qBAAqB,EAAE,eAAe;KACvC,CAAC;IAEF,OAAO,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/policy/engine.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,iBAAiB,EAEjB,QAAQ,EAER,MAAM,EACN,WAAW,EAEZ,MAAM,aAAa,CAAC;AAqOrB,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmC;gBAEzD,MAAM,GAAE,iBAAsB;IAU1C,aAAa,IAAI,MAAM,EAAE;IAWzB,SAAS,IAAI,MAAM;IAIb,UAAU,CACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IASpE,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOjC,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkBrD,OAAO,CAAC,SAAS;IA0BjB,OAAO,CAAC,mBAAmB;IA0B3B,OAAO,CAAC,qBAAqB;IA2C7B,OAAO,CAAC,QAAQ;IAoFhB,OAAO,CAAC,qBAAqB;IA0C7B,OAAO,CAAC,6BAA6B;IAkFrC,OAAO,CAAC,6BAA6B;IAuErC,OAAO,CAAC,eAAe;IAqCvB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,cAAc;IAiDtB,OAAO,CAAC,aAAa;IA6DrB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,qBAAqB;CAsB9B"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/policy/engine.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,iBAAiB,EAEjB,QAAQ,EAER,MAAM,EACN,WAAW,EAEZ,MAAM,aAAa,CAAC;AAqOrB,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmC;gBAEzD,MAAM,GAAE,iBAAsB;IAc1C,aAAa,IAAI,MAAM,EAAE;IAYzB,SAAS,IAAI,MAAM;IAIb,UAAU,CACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IASpE,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOjC,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkBrD,OAAO,CAAC,SAAS;IA0BjB,OAAO,CAAC,mBAAmB;IA0B3B,OAAO,CAAC,qBAAqB;IA2C7B,OAAO,CAAC,QAAQ;IAoFhB,OAAO,CAAC,qBAAqB;IA0C7B,OAAO,CAAC,6BAA6B;IAkFrC,OAAO,CAAC,6BAA6B;IAuErC,OAAO,CAAC,eAAe;IAqCvB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,cAAc;IAiDtB,OAAO,CAAC,aAAa;IA6DrB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,qBAAqB;CAsB9B"}
@@ -1,8 +1,10 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
1
3
  import { homedir } from "node:os";
2
4
  import path from "node:path";
3
5
  import { parseNetworkTarget } from "@clawdstrike/adapter-core";
4
6
  import { createPolicyEngineFromPolicy } from "@clawdstrike/policy";
5
- import { mergeConfig } from "../config.js";
7
+ import { mergeConfig, resolveBuiltinPolicy } from "../config.js";
6
8
  import { EgressGuard, ForbiddenPathGuard, PatchIntegrityGuard, SecretLeakGuard, } from "../guards/index.js";
7
9
  import { sanitizeOutputText } from "../sanitizer/output-sanitizer.js";
8
10
  import { loadPolicy } from "./loader.js";
@@ -219,7 +221,7 @@ export class PolicyEngine {
219
221
  this.egressGuard = new EgressGuard();
220
222
  this.secretLeakGuard = new SecretLeakGuard();
221
223
  this.patchIntegrityGuard = new PatchIntegrityGuard();
222
- this.threatIntelEngine = buildThreatIntelEngine(this.policy);
224
+ this.threatIntelEngine = buildThreatIntelEngine(this.policy, this.config.guards, policyBaseDirFromRef(this.config.policy));
223
225
  }
224
226
  enabledGuards() {
225
227
  const g = this.config.guards;
@@ -234,6 +236,8 @@ export class PolicyEngine {
234
236
  enabled.push("patch_integrity");
235
237
  if (g.mcp_tool)
236
238
  enabled.push("mcp_tool");
239
+ if (g.spider_sense)
240
+ enabled.push("spider_sense");
237
241
  return enabled;
238
242
  }
239
243
  getPolicy() {
@@ -642,19 +646,596 @@ export class PolicyEngine {
642
646
  return denyDecision(POLICY_REASON_CODES.GUARD_ERROR, result.reason ?? `${result.guard} denied request`, result.guard, result.severity ?? "high");
643
647
  }
644
648
  }
645
- function buildThreatIntelEngine(policy) {
649
+ function policyBaseDirFromRef(policyRef) {
650
+ const trimmed = policyRef.trim();
651
+ if (trimmed.length === 0)
652
+ return undefined;
653
+ if (trimmed.startsWith("clawdstrike:"))
654
+ return undefined;
655
+ if (resolveBuiltinPolicy(`clawdstrike:${trimmed}`))
656
+ return undefined;
657
+ return path.dirname(path.resolve(trimmed));
658
+ }
659
+ function isSpiderSenseCustomGuard(entry) {
660
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
661
+ return false;
662
+ }
663
+ const pkg = entry.package;
664
+ return typeof pkg === "string" && pkg.trim().toLowerCase() === "clawdstrike-spider-sense";
665
+ }
666
+ function isRecord(value) {
667
+ return typeof value === "object" && value !== null && !Array.isArray(value);
668
+ }
669
+ function isPolicySpiderSenseDisabled(policy) {
670
+ const raw = policy.guards?.spider_sense;
671
+ if (raw === false) {
672
+ return true;
673
+ }
674
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
675
+ return raw.enabled === false;
676
+ }
677
+ return false;
678
+ }
679
+ const SPIDER_SENSE_DEFAULT_THRESHOLD = 0.85;
680
+ const SPIDER_SENSE_DEFAULT_AMBIGUITY_BAND = 0.1;
681
+ const SPIDER_SENSE_DEFAULT_TOP_K = 5;
682
+ const SPIDER_SENSE_BUILTIN_S2BENCH = [
683
+ {
684
+ id: "s2b-perception-prompt_injection",
685
+ category: "prompt_injection",
686
+ stage: "perception",
687
+ label: "Prompt injection via system override",
688
+ embedding: [0.95, 0.05, 0.0],
689
+ },
690
+ {
691
+ id: "s2b-perception-jailbreak",
692
+ category: "jailbreak",
693
+ stage: "perception",
694
+ label: "Jailbreak attempt via role-play",
695
+ embedding: [0.9, 0.1, 0.05],
696
+ },
697
+ {
698
+ id: "s2b-perception-social_engineering",
699
+ category: "social_engineering",
700
+ stage: "perception",
701
+ label: "Social engineering lure",
702
+ embedding: [0.8, 0.2, 0.1],
703
+ },
704
+ {
705
+ id: "s2b-perception-data_poisoning",
706
+ category: "data_poisoning",
707
+ stage: "perception",
708
+ label: "Training data poisoning probe",
709
+ embedding: [0.7, 0.25, 0.15],
710
+ },
711
+ {
712
+ id: "s2b-perception-evasion",
713
+ category: "evasion",
714
+ stage: "perception",
715
+ label: "Evasion via encoding obfuscation",
716
+ embedding: [0.85, 0.1, 0.1],
717
+ },
718
+ {
719
+ id: "s2b-perception-reconnaissance",
720
+ category: "reconnaissance",
721
+ stage: "perception",
722
+ label: "Capability reconnaissance probe",
723
+ embedding: [0.75, 0.15, 0.2],
724
+ },
725
+ {
726
+ id: "s2b-perception-supply_chain",
727
+ category: "supply_chain",
728
+ stage: "perception",
729
+ label: "Malicious dependency injection",
730
+ embedding: [0.65, 0.3, 0.1],
731
+ },
732
+ {
733
+ id: "s2b-perception-data_exfiltration",
734
+ category: "data_exfiltration",
735
+ stage: "perception",
736
+ label: "Data exfiltration via prompt leak",
737
+ embedding: [0.6, 0.35, 0.05],
738
+ },
739
+ {
740
+ id: "s2b-perception-privilege_escalation",
741
+ category: "privilege_escalation",
742
+ stage: "perception",
743
+ label: "Privilege escalation via context manipulation",
744
+ embedding: [0.55, 0.4, 0.05],
745
+ },
746
+ {
747
+ id: "s2b-cognition-prompt_injection",
748
+ category: "prompt_injection",
749
+ stage: "cognition",
750
+ label: "Instruction hijack in reasoning",
751
+ embedding: [0.05, 0.95, 0.0],
752
+ },
753
+ {
754
+ id: "s2b-cognition-jailbreak",
755
+ category: "jailbreak",
756
+ stage: "cognition",
757
+ label: "Logic bypass via hypothetical framing",
758
+ embedding: [0.1, 0.9, 0.05],
759
+ },
760
+ {
761
+ id: "s2b-cognition-social_engineering",
762
+ category: "social_engineering",
763
+ stage: "cognition",
764
+ label: "Authority impersonation in reasoning",
765
+ embedding: [0.2, 0.8, 0.1],
766
+ },
767
+ {
768
+ id: "s2b-cognition-data_poisoning",
769
+ category: "data_poisoning",
770
+ stage: "cognition",
771
+ label: "Bias injection in chain-of-thought",
772
+ embedding: [0.25, 0.7, 0.15],
773
+ },
774
+ {
775
+ id: "s2b-cognition-evasion",
776
+ category: "evasion",
777
+ stage: "cognition",
778
+ label: "Semantic evasion in reasoning",
779
+ embedding: [0.1, 0.85, 0.1],
780
+ },
781
+ {
782
+ id: "s2b-cognition-reconnaissance",
783
+ category: "reconnaissance",
784
+ stage: "cognition",
785
+ label: "Internal state probing",
786
+ embedding: [0.15, 0.75, 0.2],
787
+ },
788
+ {
789
+ id: "s2b-cognition-supply_chain",
790
+ category: "supply_chain",
791
+ stage: "cognition",
792
+ label: "Tool trust manipulation",
793
+ embedding: [0.3, 0.65, 0.1],
794
+ },
795
+ {
796
+ id: "s2b-cognition-data_exfiltration",
797
+ category: "data_exfiltration",
798
+ stage: "cognition",
799
+ label: "Memory extraction via reasoning",
800
+ embedding: [0.35, 0.6, 0.05],
801
+ },
802
+ {
803
+ id: "s2b-cognition-privilege_escalation",
804
+ category: "privilege_escalation",
805
+ stage: "cognition",
806
+ label: "Role escalation in reasoning",
807
+ embedding: [0.4, 0.55, 0.05],
808
+ },
809
+ {
810
+ id: "s2b-action-prompt_injection",
811
+ category: "prompt_injection",
812
+ stage: "action",
813
+ label: "Action hijack via injected tool call",
814
+ embedding: [0.0, 0.05, 0.95],
815
+ },
816
+ {
817
+ id: "s2b-action-jailbreak",
818
+ category: "jailbreak",
819
+ stage: "action",
820
+ label: "Unauthorized action execution",
821
+ embedding: [0.05, 0.1, 0.9],
822
+ },
823
+ {
824
+ id: "s2b-action-social_engineering",
825
+ category: "social_engineering",
826
+ stage: "action",
827
+ label: "Deceptive output generation",
828
+ embedding: [0.1, 0.2, 0.8],
829
+ },
830
+ {
831
+ id: "s2b-action-data_poisoning",
832
+ category: "data_poisoning",
833
+ stage: "action",
834
+ label: "Malicious file write",
835
+ embedding: [0.15, 0.25, 0.7],
836
+ },
837
+ {
838
+ id: "s2b-action-evasion",
839
+ category: "evasion",
840
+ stage: "action",
841
+ label: "Detection bypass in tool use",
842
+ embedding: [0.1, 0.1, 0.85],
843
+ },
844
+ {
845
+ id: "s2b-action-reconnaissance",
846
+ category: "reconnaissance",
847
+ stage: "action",
848
+ label: "Environment probing via tools",
849
+ embedding: [0.2, 0.15, 0.75],
850
+ },
851
+ {
852
+ id: "s2b-action-supply_chain",
853
+ category: "supply_chain",
854
+ stage: "action",
855
+ label: "Dependency download from untrusted source",
856
+ embedding: [0.1, 0.3, 0.65],
857
+ },
858
+ {
859
+ id: "s2b-action-data_exfiltration",
860
+ category: "data_exfiltration",
861
+ stage: "action",
862
+ label: "Data exfiltration via network egress",
863
+ embedding: [0.05, 0.35, 0.6],
864
+ },
865
+ {
866
+ id: "s2b-action-privilege_escalation",
867
+ category: "privilege_escalation",
868
+ stage: "action",
869
+ label: "Shell escape for privilege escalation",
870
+ embedding: [0.05, 0.4, 0.55],
871
+ },
872
+ {
873
+ id: "s2b-feedback-prompt_injection",
874
+ category: "prompt_injection",
875
+ stage: "feedback",
876
+ label: "Feedback loop injection",
877
+ embedding: [0.5, 0.05, 0.45],
878
+ },
879
+ {
880
+ id: "s2b-feedback-jailbreak",
881
+ category: "jailbreak",
882
+ stage: "feedback",
883
+ label: "Self-reinforcing jailbreak via feedback",
884
+ embedding: [0.45, 0.1, 0.5],
885
+ },
886
+ {
887
+ id: "s2b-feedback-social_engineering",
888
+ category: "social_engineering",
889
+ stage: "feedback",
890
+ label: "Trust amplification via repeated feedback",
891
+ embedding: [0.4, 0.2, 0.45],
892
+ },
893
+ {
894
+ id: "s2b-feedback-data_poisoning",
895
+ category: "data_poisoning",
896
+ stage: "feedback",
897
+ label: "Feedback-driven model drift",
898
+ embedding: [0.35, 0.25, 0.4],
899
+ },
900
+ {
901
+ id: "s2b-feedback-evasion",
902
+ category: "evasion",
903
+ stage: "feedback",
904
+ label: "Adaptive evasion from feedback",
905
+ embedding: [0.42, 0.12, 0.48],
906
+ },
907
+ {
908
+ id: "s2b-feedback-reconnaissance",
909
+ category: "reconnaissance",
910
+ stage: "feedback",
911
+ label: "Response analysis for reconnaissance",
912
+ embedding: [0.4, 0.15, 0.5],
913
+ },
914
+ {
915
+ id: "s2b-feedback-supply_chain",
916
+ category: "supply_chain",
917
+ stage: "feedback",
918
+ label: "Supply chain persistence via feedback",
919
+ embedding: [0.35, 0.3, 0.4],
920
+ },
921
+ {
922
+ id: "s2b-feedback-data_exfiltration",
923
+ category: "data_exfiltration",
924
+ stage: "feedback",
925
+ label: "Gradual data leak via feedback",
926
+ embedding: [0.3, 0.35, 0.4],
927
+ },
928
+ {
929
+ id: "s2b-feedback-privilege_escalation",
930
+ category: "privilege_escalation",
931
+ stage: "feedback",
932
+ label: "Incremental privilege gain via feedback",
933
+ embedding: [0.25, 0.4, 0.4],
934
+ },
935
+ ];
936
+ function parseSpiderSensePatterns(config, policyBaseDir) {
937
+ const normalizeHex = (value) => {
938
+ const trimmed = value.trim().toLowerCase();
939
+ return trimmed.startsWith("0x") ? trimmed.slice(2) : trimmed;
940
+ };
941
+ const hasNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
942
+ const hasNonEmptyArray = (value) => Array.isArray(value) && value.length > 0;
943
+ const parsePattern = (entry) => {
944
+ if (!isRecord(entry) || !Array.isArray(entry.embedding) || entry.embedding.length === 0) {
945
+ return null;
946
+ }
947
+ const embedding = [];
948
+ for (const value of entry.embedding) {
949
+ if (typeof value !== "number" || !Number.isFinite(value)) {
950
+ return null;
951
+ }
952
+ embedding.push(value);
953
+ }
954
+ return {
955
+ id: typeof entry.id === "string" ? entry.id : "",
956
+ category: typeof entry.category === "string" ? entry.category : "",
957
+ stage: typeof entry.stage === "string" ? entry.stage : "",
958
+ label: typeof entry.label === "string" ? entry.label : "",
959
+ embedding,
960
+ };
961
+ };
962
+ const assertConsistentEmbeddingDimensions = (patterns, source) => {
963
+ if (patterns.length === 0)
964
+ return;
965
+ const expectedDim = patterns[0].embedding.length;
966
+ for (let i = 0; i < patterns.length; i++) {
967
+ const dim = patterns[i].embedding.length;
968
+ if (dim !== expectedDim) {
969
+ throw new Error(`${source} contains embedding dimension mismatch at index ${i}: expected ${expectedDim}, got ${dim}`);
970
+ }
971
+ }
972
+ };
973
+ const rawPatterns = config.patterns;
974
+ if (Array.isArray(rawPatterns)) {
975
+ if (rawPatterns.length > 0) {
976
+ const parsed = rawPatterns.map((entry, index) => {
977
+ const pattern = parsePattern(entry);
978
+ if (!pattern) {
979
+ throw new Error(`spider_sense inline patterns contain invalid entry at index ${index}`);
980
+ }
981
+ return pattern;
982
+ });
983
+ assertConsistentEmbeddingDimensions(parsed, "spider_sense inline patterns");
984
+ return parsed;
985
+ }
986
+ }
987
+ const rawPatternDbPath = typeof config.pattern_db_path === "string" ? config.pattern_db_path.trim() : "";
988
+ if (rawPatternDbPath === "builtin:s2bench-v1") {
989
+ assertConsistentEmbeddingDimensions(SPIDER_SENSE_BUILTIN_S2BENCH, "builtin:s2bench-v1");
990
+ return SPIDER_SENSE_BUILTIN_S2BENCH;
991
+ }
992
+ const patternDbPath = rawPatternDbPath.length > 0 && !path.isAbsolute(rawPatternDbPath)
993
+ ? path.resolve(policyBaseDir ?? process.cwd(), rawPatternDbPath)
994
+ : rawPatternDbPath;
995
+ if (patternDbPath.length > 0) {
996
+ const raw = readFileSync(patternDbPath, "utf8");
997
+ const expectedChecksum = hasNonEmptyString(config.pattern_db_checksum)
998
+ ? normalizeHex(String(config.pattern_db_checksum))
999
+ : "";
1000
+ if (expectedChecksum.length > 0) {
1001
+ const actualChecksum = createHash("sha256").update(raw).digest("hex").toLowerCase();
1002
+ if (actualChecksum !== expectedChecksum) {
1003
+ throw new Error(`spider_sense pattern DB checksum mismatch for '${patternDbPath}': expected ${expectedChecksum}, got ${actualChecksum}`);
1004
+ }
1005
+ }
1006
+ const hasSignatureMetadata = hasNonEmptyString(config.pattern_db_signature) ||
1007
+ hasNonEmptyString(config.pattern_db_signature_key_id) ||
1008
+ hasNonEmptyString(config.pattern_db_public_key) ||
1009
+ hasNonEmptyString(config.pattern_db_trust_store_path) ||
1010
+ hasNonEmptyArray(config.pattern_db_trusted_keys);
1011
+ const hasManifestMetadata = hasNonEmptyString(config.pattern_db_manifest_path) ||
1012
+ hasNonEmptyString(config.pattern_db_manifest_trust_store_path) ||
1013
+ hasNonEmptyArray(config.pattern_db_manifest_trusted_keys);
1014
+ if (hasSignatureMetadata || hasManifestMetadata) {
1015
+ throw new Error("spider_sense signature/manifest integrity metadata is not executable in OpenClaw runtime");
1016
+ }
1017
+ const parsed = JSON.parse(raw);
1018
+ if (!Array.isArray(parsed) || parsed.length === 0) {
1019
+ throw new Error(`spider_sense pattern DB at '${patternDbPath}' is empty or invalid`);
1020
+ }
1021
+ const patterns = parsed.map((entry, index) => {
1022
+ const pattern = parsePattern(entry);
1023
+ if (!pattern) {
1024
+ throw new Error(`spider_sense pattern DB at '${patternDbPath}' contains invalid entry at index ${index}`);
1025
+ }
1026
+ return pattern;
1027
+ });
1028
+ assertConsistentEmbeddingDimensions(patterns, `spider_sense pattern DB at '${patternDbPath}'`);
1029
+ return patterns;
1030
+ }
1031
+ return [];
1032
+ }
1033
+ function buildSpiderSenseRuntimeConfig(spec, options = {}) {
1034
+ const record = isRecord(spec) ? spec : {};
1035
+ const config = isRecord(record.config) ? record.config : {};
1036
+ const similarityThreshold = typeof config.similarity_threshold === "number"
1037
+ ? config.similarity_threshold
1038
+ : SPIDER_SENSE_DEFAULT_THRESHOLD;
1039
+ const ambiguityBand = typeof config.ambiguity_band === "number"
1040
+ ? config.ambiguity_band
1041
+ : SPIDER_SENSE_DEFAULT_AMBIGUITY_BAND;
1042
+ const topK = typeof config.top_k === "number"
1043
+ ? Math.max(1, Math.trunc(config.top_k))
1044
+ : SPIDER_SENSE_DEFAULT_TOP_K;
1045
+ if (!Number.isFinite(similarityThreshold) || similarityThreshold < 0 || similarityThreshold > 1) {
1046
+ throw new Error(`spider_sense similarity_threshold must be in [0, 1], got ${String(similarityThreshold)}`);
1047
+ }
1048
+ if (!Number.isFinite(ambiguityBand) || ambiguityBand < 0 || ambiguityBand > 1) {
1049
+ throw new Error(`spider_sense ambiguity_band must be in [0, 1], got ${String(ambiguityBand)}`);
1050
+ }
1051
+ const upperBound = similarityThreshold + ambiguityBand;
1052
+ const lowerBound = similarityThreshold - ambiguityBand;
1053
+ if (upperBound > 1 || lowerBound < 0) {
1054
+ throw new Error(`spider_sense threshold/band produce invalid decision range: lower=${lowerBound.toFixed(3)}, upper=${upperBound.toFixed(3)}`);
1055
+ }
1056
+ const patterns = parseSpiderSensePatterns(config, options.policyBaseDir);
1057
+ if (patterns.length === 0) {
1058
+ throw new Error("spider_sense requires non-empty patterns or pattern_db_path");
1059
+ }
1060
+ const embeddingApiUrl = typeof config.embedding_api_url === "string" ? config.embedding_api_url : undefined;
1061
+ const embeddingApiKey = typeof config.embedding_api_key === "string" ? config.embedding_api_key : undefined;
1062
+ const embeddingModel = typeof config.embedding_model === "string" ? config.embedding_model : undefined;
1063
+ return {
1064
+ enabled: record.enabled !== false,
1065
+ similarityThreshold,
1066
+ ambiguityBand,
1067
+ topK,
1068
+ patterns,
1069
+ embeddingApiUrl,
1070
+ embeddingApiKey,
1071
+ embeddingModel,
1072
+ };
1073
+ }
1074
+ function cosineSimilarity(a, b) {
1075
+ if (a.length === 0 || a.length !== b.length)
1076
+ return 0;
1077
+ let dot = 0;
1078
+ let normA = 0;
1079
+ let normB = 0;
1080
+ for (let i = 0; i < a.length; i++) {
1081
+ const av = a[i];
1082
+ const bv = b[i];
1083
+ dot += av * bv;
1084
+ normA += av * av;
1085
+ normB += bv * bv;
1086
+ }
1087
+ if (normA === 0 || normB === 0)
1088
+ return 0;
1089
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
1090
+ }
1091
+ function extractEmbeddingFromEvent(event) {
1092
+ const data = isRecord(event.data) ? event.data : {};
1093
+ const customData = isRecord(data.customData) ? data.customData : null;
1094
+ const maybeEmbedding = (Array.isArray(data.embedding) ? data.embedding : undefined) ??
1095
+ (customData && Array.isArray(customData.embedding)
1096
+ ? customData.embedding
1097
+ : undefined);
1098
+ if (!maybeEmbedding)
1099
+ return null;
1100
+ const embedding = maybeEmbedding.filter((value) => typeof value === "number" && Number.isFinite(value));
1101
+ return embedding.length > 0 ? embedding : null;
1102
+ }
1103
+ function eventToSpiderSenseText(event) {
1104
+ return `[event:${event.eventType}] ${JSON.stringify(event.data ?? null)}`;
1105
+ }
1106
+ async function fetchSpiderSenseEmbedding(runtime, event) {
1107
+ if (!runtime.embeddingApiUrl || !runtime.embeddingApiKey || !runtime.embeddingModel) {
1108
+ return null;
1109
+ }
1110
+ const response = await fetch(runtime.embeddingApiUrl, {
1111
+ method: "POST",
1112
+ headers: {
1113
+ "content-type": "application/json",
1114
+ authorization: `Bearer ${runtime.embeddingApiKey}`,
1115
+ },
1116
+ body: JSON.stringify({
1117
+ model: runtime.embeddingModel,
1118
+ input: eventToSpiderSenseText(event),
1119
+ }),
1120
+ });
1121
+ if (!response.ok) {
1122
+ throw new Error(`spider_sense embedding request failed (${response.status})`);
1123
+ }
1124
+ const json = (await response.json());
1125
+ const data = Array.isArray(json.data) ? json.data : [];
1126
+ const first = isRecord(data[0]) ? data[0] : {};
1127
+ const embedding = Array.isArray(first.embedding) ? first.embedding : [];
1128
+ const out = embedding.filter((value) => typeof value === "number" && Number.isFinite(value));
1129
+ return out.length > 0 ? out : null;
1130
+ }
1131
+ function evaluateSpiderSenseEmbedding(runtime, embedding) {
1132
+ const expectedDim = runtime.patterns[0]?.embedding.length ?? 0;
1133
+ if (expectedDim === 0) {
1134
+ return denyDecision(POLICY_REASON_CODES.GUARD_ERROR, "Spider-Sense pattern DB is empty (fail-closed)", "clawdstrike-spider-sense", "high");
1135
+ }
1136
+ if (embedding.length !== expectedDim) {
1137
+ return denyDecision(POLICY_REASON_CODES.GUARD_ERROR, `Spider-Sense embedding dimension mismatch (${embedding.length} vs ${expectedDim})`, "clawdstrike-spider-sense", "high");
1138
+ }
1139
+ const topMatches = runtime.patterns
1140
+ .map((entry) => ({ entry, score: cosineSimilarity(embedding, entry.embedding) }))
1141
+ .sort((a, b) => b.score - a.score)
1142
+ .slice(0, runtime.topK);
1143
+ const top = topMatches[0];
1144
+ const topScore = top?.score ?? 0;
1145
+ const upper = runtime.similarityThreshold + runtime.ambiguityBand;
1146
+ const lower = runtime.similarityThreshold - runtime.ambiguityBand;
1147
+ if (top && topScore >= upper) {
1148
+ return denyDecision(POLICY_REASON_CODES.GUARD_ERROR, `Spider-Sense high similarity (${topScore.toFixed(3)}) to pattern '${top.entry.label}'`, "clawdstrike-spider-sense", "high");
1149
+ }
1150
+ if (topScore <= lower) {
1151
+ return { status: "allow" };
1152
+ }
1153
+ return warnDecision(POLICY_REASON_CODES.POLICY_WARN, `Spider-Sense ambiguous similarity (${topScore.toFixed(3)})`, "clawdstrike-spider-sense", "medium");
1154
+ }
1155
+ async function evaluateSpiderSenseRuntime(runtime, event) {
1156
+ if (!runtime.enabled)
1157
+ return { status: "allow" };
1158
+ try {
1159
+ const embedding = extractEmbeddingFromEvent(event) ?? (await fetchSpiderSenseEmbedding(runtime, event));
1160
+ if (!embedding) {
1161
+ return { status: "allow" };
1162
+ }
1163
+ return evaluateSpiderSenseEmbedding(runtime, embedding);
1164
+ }
1165
+ catch (error) {
1166
+ const detail = error instanceof Error ? error.message : String(error);
1167
+ return denyDecision(POLICY_REASON_CODES.GUARD_ERROR, `Spider-Sense runtime error: ${detail}`, "clawdstrike-spider-sense", "high");
1168
+ }
1169
+ }
1170
+ function buildThreatIntelEngine(policy, guardToggles, policyBaseDir) {
646
1171
  const custom = policy.guards?.custom;
647
1172
  if (!Array.isArray(custom) || custom.length === 0) {
648
1173
  return null;
649
1174
  }
650
- // The openclaw Policy types `custom` as `unknown`; the canonical Policy
651
- // expects `CustomGuardSpec[]`. We've validated it's an array above.
652
- // GuardConfigs has an index signature so `unknown[]` is assignable.
653
- const canonicalPolicy = {
654
- version: "1.1.0",
655
- guards: { custom },
1175
+ const policySpiderSenseDisabled = isPolicySpiderSenseDisabled(policy);
1176
+ const spiderSenseRuntimes = custom
1177
+ .filter((entry) => {
1178
+ if (!isSpiderSenseCustomGuard(entry))
1179
+ return false;
1180
+ if (!guardToggles.spider_sense || policySpiderSenseDisabled)
1181
+ return false;
1182
+ if (isRecord(entry) && entry.enabled === false)
1183
+ return false;
1184
+ return true;
1185
+ })
1186
+ .map((entry) => buildSpiderSenseRuntimeConfig(entry, { policyBaseDir }));
1187
+ const filteredCustom = custom.filter((entry) => {
1188
+ if (isSpiderSenseCustomGuard(entry)) {
1189
+ return false;
1190
+ }
1191
+ return true;
1192
+ });
1193
+ let canonicalEngine = null;
1194
+ if (filteredCustom.length > 0) {
1195
+ // The openclaw Policy types `custom` as `unknown`; the canonical Policy
1196
+ // expects `CustomGuardSpec[]`. We've validated it's an array above.
1197
+ // GuardConfigs has an index signature so `unknown[]` is assignable.
1198
+ const canonicalPolicy = {
1199
+ version: "1.1.0",
1200
+ guards: { custom: filteredCustom },
1201
+ };
1202
+ canonicalEngine = createPolicyEngineFromPolicy(canonicalPolicy);
1203
+ }
1204
+ if (!canonicalEngine && spiderSenseRuntimes.length === 0) {
1205
+ return null;
1206
+ }
1207
+ if (!canonicalEngine) {
1208
+ return {
1209
+ evaluate: async (event) => {
1210
+ let out = { status: "allow" };
1211
+ for (const runtime of spiderSenseRuntimes) {
1212
+ const decision = await evaluateSpiderSenseRuntime(runtime, event);
1213
+ out = combineDecisions(out, decision);
1214
+ if (out.status === "deny")
1215
+ return out;
1216
+ }
1217
+ return out;
1218
+ },
1219
+ };
1220
+ }
1221
+ if (spiderSenseRuntimes.length === 0) {
1222
+ return canonicalEngine;
1223
+ }
1224
+ return {
1225
+ evaluate: async (event) => {
1226
+ const canonicalDecision = (await canonicalEngine.evaluate(event));
1227
+ if (canonicalDecision.status === "deny")
1228
+ return canonicalDecision;
1229
+ let spiderDecision = { status: "allow" };
1230
+ for (const runtime of spiderSenseRuntimes) {
1231
+ const decision = await evaluateSpiderSenseRuntime(runtime, event);
1232
+ spiderDecision = combineDecisions(spiderDecision, decision);
1233
+ if (spiderDecision.status === "deny")
1234
+ break;
1235
+ }
1236
+ return combineDecisions(canonicalDecision, spiderDecision);
1237
+ },
656
1238
  };
657
- return createPolicyEngineFromPolicy(canonicalPolicy);
658
1239
  }
659
1240
  function combineDecisions(base, next) {
660
1241
  const rank = { deny: 2, warn: 1, allow: 0 };