@f4bioo/berry-shield 2026.3.17 → 2026.3.20
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/README.md +3 -3
- package/dist/index.js +1075 -271
- package/docs/wiki/deploy/installation.md +1 -1
- package/docs/wiki/operation/cli/README.md +11 -2
- package/docs/wiki/operation/cli/add.md +1 -1
- package/docs/wiki/operation/cli/remove.md +24 -2
- package/docs/wiki/operation/cli/rules.md +47 -8
- package/docs/wiki/operation/cli/test.md +21 -6
- package/docs/wiki/tutorials/incident-triage-report.md +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -509,16 +509,652 @@ var init_generated = __esm({
|
|
|
509
509
|
}
|
|
510
510
|
});
|
|
511
511
|
|
|
512
|
+
// src/patterns/constants.ts
|
|
513
|
+
var NAMESPACES, CATEGORIES, PREFIXES;
|
|
514
|
+
var init_constants = __esm({
|
|
515
|
+
"src/patterns/constants.ts"() {
|
|
516
|
+
"use strict";
|
|
517
|
+
NAMESPACES = {
|
|
518
|
+
/** Native Berry Shield logic and core patterns */
|
|
519
|
+
BERRY: "berry",
|
|
520
|
+
/** Automated community patterns sourced from Gitleaks baseline */
|
|
521
|
+
GITLEAKS: "gitleaks"
|
|
522
|
+
};
|
|
523
|
+
CATEGORIES = {
|
|
524
|
+
SECRET: "secret",
|
|
525
|
+
PII: "pii",
|
|
526
|
+
FILE: "file",
|
|
527
|
+
COMMAND: "command"
|
|
528
|
+
};
|
|
529
|
+
PREFIXES = {
|
|
530
|
+
BERRY_SECRET: `${NAMESPACES.BERRY}:${CATEGORIES.SECRET}`,
|
|
531
|
+
GITLEAKS_SECRET: `${NAMESPACES.GITLEAKS}:${CATEGORIES.SECRET}`,
|
|
532
|
+
BERRY_PII: `${NAMESPACES.BERRY}:${CATEGORIES.PII}`,
|
|
533
|
+
BERRY_FILE: `${NAMESPACES.BERRY}:${CATEGORIES.FILE}`,
|
|
534
|
+
BERRY_COMMAND: `${NAMESPACES.BERRY}:${CATEGORIES.COMMAND}`
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// src/patterns/extended.ts
|
|
540
|
+
var INTERNAL_SECRET_PATTERNS, INTERNAL_PII_PATTERNS, INTERNAL_SENSITIVE_FILE_PATTERNS, INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS;
|
|
541
|
+
var init_extended = __esm({
|
|
542
|
+
"src/patterns/extended.ts"() {
|
|
543
|
+
"use strict";
|
|
544
|
+
init_constants();
|
|
545
|
+
INTERNAL_SECRET_PATTERNS = [
|
|
546
|
+
{
|
|
547
|
+
id: `${PREFIXES.BERRY_SECRET}:aws-access-key`,
|
|
548
|
+
name: "AWS Access Key",
|
|
549
|
+
category: CATEGORIES.SECRET,
|
|
550
|
+
pattern: /AKIA[0-9A-Z]{16}/g,
|
|
551
|
+
includeHash: true
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
id: `${PREFIXES.BERRY_SECRET}:aws-secret-key`,
|
|
555
|
+
name: "AWS Secret Key",
|
|
556
|
+
category: CATEGORIES.SECRET,
|
|
557
|
+
pattern: /aws_secret_access_key\s*[:=]\s*[A-Za-z0-9/+=]{40}/gi,
|
|
558
|
+
includeHash: true
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
id: `${PREFIXES.BERRY_SECRET}:stripe-key`,
|
|
562
|
+
name: "Stripe Key",
|
|
563
|
+
category: CATEGORIES.SECRET,
|
|
564
|
+
pattern: /[sr]k[-_](?:live|test)[-_][a-zA-Z0-9]{20,}/g,
|
|
565
|
+
includeHash: true
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
id: `${PREFIXES.BERRY_SECRET}:github-token`,
|
|
569
|
+
name: "GitHub Token",
|
|
570
|
+
category: CATEGORIES.SECRET,
|
|
571
|
+
pattern: /gh[pousr]_[a-zA-Z0-9]{36}/g,
|
|
572
|
+
includeHash: true
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
id: `${PREFIXES.BERRY_SECRET}:github-pat`,
|
|
576
|
+
name: "GitHub PAT",
|
|
577
|
+
category: CATEGORIES.SECRET,
|
|
578
|
+
pattern: /github_pat_[a-zA-Z0-9_]{22,}/g,
|
|
579
|
+
includeHash: true
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
id: `${PREFIXES.BERRY_SECRET}:openai-key`,
|
|
583
|
+
name: "OpenAI Key",
|
|
584
|
+
category: CATEGORIES.SECRET,
|
|
585
|
+
pattern: /sk-[a-zA-Z0-9]{20,}/g,
|
|
586
|
+
includeHash: true
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
id: `${PREFIXES.BERRY_SECRET}:anthropic-key`,
|
|
590
|
+
name: "Anthropic Key",
|
|
591
|
+
category: CATEGORIES.SECRET,
|
|
592
|
+
pattern: /sk-ant-[a-zA-Z0-9_-]{20,}/g,
|
|
593
|
+
includeHash: true
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
id: `${PREFIXES.BERRY_SECRET}:slack-token`,
|
|
597
|
+
name: "Slack Token",
|
|
598
|
+
category: CATEGORIES.SECRET,
|
|
599
|
+
pattern: /xox[bpras]-[a-zA-Z0-9-]{10,}/g,
|
|
600
|
+
includeHash: true
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
id: `${PREFIXES.BERRY_SECRET}:sendgrid-key`,
|
|
604
|
+
name: "SendGrid Key",
|
|
605
|
+
category: CATEGORIES.SECRET,
|
|
606
|
+
pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/g,
|
|
607
|
+
includeHash: true
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
id: `${PREFIXES.BERRY_SECRET}:npm-token`,
|
|
611
|
+
name: "NPM Token",
|
|
612
|
+
category: CATEGORIES.SECRET,
|
|
613
|
+
pattern: /npm_[a-zA-Z0-9]{36,}/g,
|
|
614
|
+
includeHash: true
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
id: `${PREFIXES.BERRY_SECRET}:telegram-bot-token`,
|
|
618
|
+
name: "Telegram Bot Token",
|
|
619
|
+
category: CATEGORIES.SECRET,
|
|
620
|
+
pattern: /[0-9]{8,10}:[a-zA-Z0-9_-]{35,}/g,
|
|
621
|
+
includeHash: true,
|
|
622
|
+
// Context-awareness: 8-10 digits + colon + 35 alphanum can collide with
|
|
623
|
+
// composite log identifiers, timestamps:tokens, and similar formats.
|
|
624
|
+
isContextRequired: true,
|
|
625
|
+
contextWords: ["telegram", "bot", "token", "tg", "chat_id", "sendMessage"],
|
|
626
|
+
contextWindow: { before: 30, after: 15 }
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
id: `${PREFIXES.BERRY_SECRET}:google-api-key`,
|
|
630
|
+
name: "Google API Key",
|
|
631
|
+
category: CATEGORIES.SECRET,
|
|
632
|
+
pattern: /AIza[0-9A-Za-z_-]{35}/g,
|
|
633
|
+
includeHash: true
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
id: `${PREFIXES.BERRY_SECRET}:google-cloud-service-account`,
|
|
637
|
+
name: "GCP Service Account",
|
|
638
|
+
category: CATEGORIES.SECRET,
|
|
639
|
+
pattern: /"type"\s*:\s*"service_account"/g,
|
|
640
|
+
includeHash: true
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
id: `${PREFIXES.BERRY_SECRET}:azure-openai-key`,
|
|
644
|
+
name: "Azure OpenAI Key",
|
|
645
|
+
category: CATEGORIES.SECRET,
|
|
646
|
+
pattern: /(?:azure|openai)[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-fA-F0-9]{32}["']?/gi,
|
|
647
|
+
includeHash: true
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
id: `${PREFIXES.BERRY_SECRET}:huggingface-token`,
|
|
651
|
+
name: "HuggingFace Token",
|
|
652
|
+
category: CATEGORIES.SECRET,
|
|
653
|
+
pattern: /hf_[a-zA-Z0-9]{34,}/g,
|
|
654
|
+
includeHash: true
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
id: `${PREFIXES.BERRY_SECRET}:replicate-token`,
|
|
658
|
+
name: "Replicate Token",
|
|
659
|
+
category: CATEGORIES.SECRET,
|
|
660
|
+
pattern: /r8_[a-zA-Z0-9]{36,}/g,
|
|
661
|
+
includeHash: true
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
id: `${PREFIXES.BERRY_SECRET}:cohere-api-key`,
|
|
665
|
+
name: "Cohere API Key",
|
|
666
|
+
category: CATEGORIES.SECRET,
|
|
667
|
+
pattern: /(?:cohere|co[-_]?api)[-_]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{40,}["']?/gi,
|
|
668
|
+
includeHash: true
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
id: `${PREFIXES.BERRY_SECRET}:together-ai-key`,
|
|
672
|
+
name: "Together AI Key",
|
|
673
|
+
category: CATEGORIES.SECRET,
|
|
674
|
+
pattern: /(?:together|together[-_]?ai)[-_]?(?:api)?[-_]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{40,}["']?/gi,
|
|
675
|
+
includeHash: true
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
id: `${PREFIXES.BERRY_SECRET}:mistral-api-key`,
|
|
679
|
+
name: "Mistral API Key",
|
|
680
|
+
category: CATEGORIES.SECRET,
|
|
681
|
+
pattern: /mistral[-_]?(?:api)?[-_]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{32,}["']?/gi,
|
|
682
|
+
includeHash: true
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
id: `${PREFIXES.BERRY_SECRET}:groq-api-key`,
|
|
686
|
+
name: "Groq API Key",
|
|
687
|
+
category: CATEGORIES.SECRET,
|
|
688
|
+
pattern: /gsk_[a-zA-Z0-9_]{48,}/g,
|
|
689
|
+
includeHash: true
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
id: `${PREFIXES.BERRY_SECRET}:deepseek-api-key`,
|
|
693
|
+
name: "DeepSeek API Key",
|
|
694
|
+
category: CATEGORIES.SECRET,
|
|
695
|
+
pattern: /deepseek[-_]?(?:api)?[-_]?key\s*[:=]\s*["']?sk-[a-zA-Z0-9]{48,}["']?/gi,
|
|
696
|
+
includeHash: true
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
id: `${PREFIXES.BERRY_SECRET}:coinbase-api-key`,
|
|
700
|
+
name: "Coinbase API Key",
|
|
701
|
+
category: CATEGORIES.SECRET,
|
|
702
|
+
pattern: /(?:coinbase)[_-]?(?:api)?[_-]?(?:key|secret)\s*[:=]\s*["']?[a-zA-Z0-9_-]{20,}["']?/gi,
|
|
703
|
+
includeHash: true
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
id: `${PREFIXES.BERRY_SECRET}:binance-api-key`,
|
|
707
|
+
name: "Binance API Key",
|
|
708
|
+
category: CATEGORIES.SECRET,
|
|
709
|
+
pattern: /(?:binance)[_-]?(?:api)?[_-]?(?:key|secret)\s*[:=]\s*["']?[a-zA-Z0-9]{64}["']?/gi,
|
|
710
|
+
includeHash: true
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
id: `${PREFIXES.BERRY_SECRET}:alpaca-api-key`,
|
|
714
|
+
name: "Alpaca API Key",
|
|
715
|
+
category: CATEGORIES.SECRET,
|
|
716
|
+
pattern: /(?:AK|PK)[a-zA-Z0-9]{20}/g,
|
|
717
|
+
includeHash: true,
|
|
718
|
+
// Context-awareness required: "AK"/"PK" + 20 alphanum is extremely common
|
|
719
|
+
// in package names, base64 fragments, and generic identifiers.
|
|
720
|
+
isContextRequired: true,
|
|
721
|
+
contextWords: ["alpaca", "trading", "brokerage", "api", "key", "secret", "market"],
|
|
722
|
+
contextWindow: { before: 30, after: 15 }
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
id: `${PREFIXES.BERRY_SECRET}:trading-api-key`,
|
|
726
|
+
name: "Trading API Key",
|
|
727
|
+
category: CATEGORIES.SECRET,
|
|
728
|
+
pattern: /(?:trading|brokerage|alpaca|tradier|interactive[-_]?brokers)[-_]?(?:api)?[-_]?(?:key|secret|token)\s*[:=]\s*["']?[a-zA-Z0-9_-]{20,}["']?/gi,
|
|
729
|
+
includeHash: true
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
id: `${PREFIXES.BERRY_SECRET}:slack-webhook`,
|
|
733
|
+
name: "Slack Webhook",
|
|
734
|
+
category: CATEGORIES.SECRET,
|
|
735
|
+
pattern: /hooks\.slack\.com\/services\/T[a-zA-Z0-9_]+\/B[a-zA-Z0-9_]+\/[a-zA-Z0-9_]+/g,
|
|
736
|
+
includeHash: true
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
id: `${PREFIXES.BERRY_SECRET}:twilio-api-key`,
|
|
740
|
+
name: "Twilio API Key",
|
|
741
|
+
category: CATEGORIES.SECRET,
|
|
742
|
+
pattern: /SK[0-9a-fA-F]{32}/g,
|
|
743
|
+
includeHash: true
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
id: `${PREFIXES.BERRY_SECRET}:mailgun-api-key`,
|
|
747
|
+
name: "Mailgun API Key",
|
|
748
|
+
category: CATEGORIES.SECRET,
|
|
749
|
+
pattern: /key-[a-zA-Z0-9]{32}/g,
|
|
750
|
+
includeHash: true
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
id: `${PREFIXES.BERRY_SECRET}:vault-token`,
|
|
754
|
+
name: "Vault Token",
|
|
755
|
+
category: CATEGORIES.SECRET,
|
|
756
|
+
pattern: /hvs\.[a-zA-Z0-9_-]{24,}/g,
|
|
757
|
+
includeHash: true
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
id: `${PREFIXES.BERRY_SECRET}:database-url`,
|
|
761
|
+
name: "Database URL",
|
|
762
|
+
category: CATEGORIES.SECRET,
|
|
763
|
+
pattern: /(?:postgres|postgresql|mysql|mongodb(?:\+srv)?):\/\/[^\s]{10,}/g,
|
|
764
|
+
includeHash: true
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
id: `${PREFIXES.BERRY_SECRET}:redis-url`,
|
|
768
|
+
name: "Redis URL",
|
|
769
|
+
category: CATEGORIES.SECRET,
|
|
770
|
+
pattern: /rediss?:\/\/[^\s]{10,}/g,
|
|
771
|
+
includeHash: true
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
id: `${PREFIXES.BERRY_SECRET}:supabase-key`,
|
|
775
|
+
name: "Supabase Key",
|
|
776
|
+
category: CATEGORIES.SECRET,
|
|
777
|
+
pattern: /(?:supabase|SUPABASE)[-_]?(?:anon|service[-_]?role)?[-_]?key\s*[:=]\s*["']?eyJ[a-zA-Z0-9_-]{20,}["']?/g,
|
|
778
|
+
includeHash: true
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
id: `${PREFIXES.BERRY_SECRET}:vercel-token`,
|
|
782
|
+
name: "Vercel Token",
|
|
783
|
+
category: CATEGORIES.SECRET,
|
|
784
|
+
pattern: /vercel_[a-zA-Z0-9_-]{24,}/g,
|
|
785
|
+
includeHash: true
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
id: `${PREFIXES.BERRY_SECRET}:heroku-api-key`,
|
|
789
|
+
name: "Heroku API Key",
|
|
790
|
+
category: CATEGORIES.SECRET,
|
|
791
|
+
pattern: /heroku[-_]?(?:api)?[-_]?key\s*[:=]\s*["']?[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}["']?/gi,
|
|
792
|
+
includeHash: true
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
id: `${PREFIXES.BERRY_SECRET}:digitalocean-token`,
|
|
796
|
+
name: "DigitalOcean Token",
|
|
797
|
+
category: CATEGORIES.SECRET,
|
|
798
|
+
pattern: /dop_v1_[a-fA-F0-9]{64}/g,
|
|
799
|
+
includeHash: true
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
id: `${PREFIXES.BERRY_SECRET}:cloudflare-api-token`,
|
|
803
|
+
name: "Cloudflare API Token",
|
|
804
|
+
category: CATEGORIES.SECRET,
|
|
805
|
+
pattern: /cf_[a-zA-Z0-9_-]{40,}/g,
|
|
806
|
+
includeHash: true
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: `${PREFIXES.BERRY_SECRET}:firebase-key`,
|
|
810
|
+
name: "Firebase Key",
|
|
811
|
+
category: CATEGORIES.SECRET,
|
|
812
|
+
pattern: /(?:firebase|FIREBASE)[-_]?(?:api)?[-_]?(?:key|secret|token)\s*[:=]\s*["']?[a-zA-Z0-9_-]{20,}["']?/g,
|
|
813
|
+
includeHash: true
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
id: `${PREFIXES.BERRY_SECRET}:datadog-api-key`,
|
|
817
|
+
name: "Datadog API Key",
|
|
818
|
+
category: CATEGORIES.SECRET,
|
|
819
|
+
pattern: /(?:datadog|DD)[-_]?(?:api)?[-_]?key\s*[:=]\s*["']?[a-fA-F0-9]{32}["']?/gi,
|
|
820
|
+
includeHash: true
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
id: `${PREFIXES.BERRY_SECRET}:sentry-dsn`,
|
|
824
|
+
name: "Sentry DSN",
|
|
825
|
+
category: CATEGORIES.SECRET,
|
|
826
|
+
pattern: /https:\/\/[a-f0-9]{32}@[a-z0-9.]+sentry[a-z.]*\/[0-9]+/g,
|
|
827
|
+
includeHash: true
|
|
828
|
+
},
|
|
829
|
+
// --- High Collision Potential (Raw Formats) ---
|
|
830
|
+
{
|
|
831
|
+
id: `${PREFIXES.BERRY_SECRET}:eth-private-key`,
|
|
832
|
+
name: "ETH Private Key",
|
|
833
|
+
category: CATEGORIES.SECRET,
|
|
834
|
+
pattern: /(?:^|[\s"'`=])(?:0x)?[0-9a-fA-F]{64}(?=\s|$|["'])/g,
|
|
835
|
+
includeHash: true,
|
|
836
|
+
// Context-awareness required: raw 64-char hex collides with SHA-256 hashes,
|
|
837
|
+
// git commit hashes, and other common hex strings.
|
|
838
|
+
// Self-contained vendor patterns (Binance, Coinbase) take priority via selectWinner.
|
|
839
|
+
isContextRequired: true,
|
|
840
|
+
contextWords: ["eth", "ethereum", "private", "wallet", "key", "0x", "web3", "crypto", "mnemonic", "keystore"],
|
|
841
|
+
contextWindow: { before: 40, after: 20 }
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
id: `${PREFIXES.BERRY_SECRET}:mnemonic-seed`,
|
|
845
|
+
name: "Crypto Seed",
|
|
846
|
+
category: CATEGORIES.SECRET,
|
|
847
|
+
pattern: /(?:mnemonic|seed\s*phrase|recovery\s*phrase|backup\s*phrase)\s*[:=]?\s*["']?(?:[a-z]{3,8}\s+){11,23}[a-z]{3,8}["']?/gi,
|
|
848
|
+
includeHash: true
|
|
849
|
+
// Already self-validating: pattern includes "mnemonic"/"seed phrase" in regex
|
|
850
|
+
// No additional context needed
|
|
851
|
+
},
|
|
852
|
+
// --- Generic & Catch-all (Final Fallback) ---
|
|
853
|
+
{
|
|
854
|
+
id: `${PREFIXES.BERRY_SECRET}:private-key`,
|
|
855
|
+
name: "Private Key",
|
|
856
|
+
category: CATEGORIES.SECRET,
|
|
857
|
+
pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
|
|
858
|
+
includeHash: true
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
id: `${PREFIXES.BERRY_SECRET}:jwt`,
|
|
862
|
+
name: "JWT",
|
|
863
|
+
category: CATEGORIES.SECRET,
|
|
864
|
+
pattern: /eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/g,
|
|
865
|
+
includeHash: true
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
id: `${PREFIXES.BERRY_SECRET}:bearer-token`,
|
|
869
|
+
name: "Bearer Token",
|
|
870
|
+
category: CATEGORIES.SECRET,
|
|
871
|
+
pattern: /Authorization\s*[:=]\s*Bearer\s+[a-zA-Z0-9_.\-/+=]{20,}/gi,
|
|
872
|
+
includeHash: true
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
id: `${PREFIXES.BERRY_SECRET}:generic-api-key`,
|
|
876
|
+
name: "Generic API Key",
|
|
877
|
+
category: CATEGORIES.SECRET,
|
|
878
|
+
pattern: /api[-_]?key\s*[:=]\s*[a-zA-Z0-9_.\-/+=]{20,}/gi,
|
|
879
|
+
includeHash: true
|
|
880
|
+
// Self-contained pattern: regex already requires "api_key" literal
|
|
881
|
+
// Context-awareness not needed - the pattern itself IS the context
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
id: `${PREFIXES.BERRY_SECRET}:generic-json-secret`,
|
|
885
|
+
name: "Generic JSON Secret",
|
|
886
|
+
category: CATEGORIES.SECRET,
|
|
887
|
+
pattern: /"(?:apiKey|token|secret|auth|password|passwd)"\s*:\s*"([^"]+)"/gi,
|
|
888
|
+
placeholder: '"key": "[GENERIC_SECRET_REDACTED]"'
|
|
889
|
+
}
|
|
890
|
+
];
|
|
891
|
+
INTERNAL_PII_PATTERNS = [
|
|
892
|
+
{
|
|
893
|
+
id: `${PREFIXES.BERRY_PII}:email`,
|
|
894
|
+
name: "Email",
|
|
895
|
+
category: CATEGORIES.PII,
|
|
896
|
+
pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
897
|
+
includeHash: true
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
id: `${PREFIXES.BERRY_PII}:ssn-us`,
|
|
901
|
+
name: "SSN (USA)",
|
|
902
|
+
category: CATEGORIES.PII,
|
|
903
|
+
pattern: /\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/g,
|
|
904
|
+
includeHash: true
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
id: `${PREFIXES.BERRY_PII}:credit-card`,
|
|
908
|
+
name: "Credit Card",
|
|
909
|
+
category: CATEGORIES.PII,
|
|
910
|
+
pattern: /\b[3-6]\d{3}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{1,7}\b/g,
|
|
911
|
+
includeHash: true
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
id: `${PREFIXES.BERRY_PII}:iban`,
|
|
915
|
+
name: "IBAN",
|
|
916
|
+
category: CATEGORIES.PII,
|
|
917
|
+
pattern: /\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}[A-Z0-9]{0,23}\b/g,
|
|
918
|
+
includeHash: true
|
|
919
|
+
},
|
|
920
|
+
// --- Brazilian PII (BEFORE phone patterns to enable context evaluation) ---
|
|
921
|
+
{
|
|
922
|
+
id: `${PREFIXES.BERRY_PII}:cpf-br`,
|
|
923
|
+
name: "CPF (Brazil)",
|
|
924
|
+
category: CATEGORIES.PII,
|
|
925
|
+
pattern: /\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b/g,
|
|
926
|
+
includeHash: true,
|
|
927
|
+
// Context-awareness: Prevents false positives (11-digit phone numbers)
|
|
928
|
+
isContextRequired: true,
|
|
929
|
+
contextWords: ["cpf", "cadastro", "documento", "contribuinte", "fiscal", "tax"],
|
|
930
|
+
contextWindow: { before: 30, after: 15 }
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
id: `${PREFIXES.BERRY_PII}:cnpj-br`,
|
|
934
|
+
name: "CNPJ (Brazil)",
|
|
935
|
+
category: CATEGORIES.PII,
|
|
936
|
+
pattern: /\b\d{2}\.?\d{3}\.?\d{3}\/?\d{4}-?\d{2}\b/g,
|
|
937
|
+
includeHash: true
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
id: `${PREFIXES.BERRY_PII}:cnh-br`,
|
|
941
|
+
name: "CNH (Brazil)",
|
|
942
|
+
category: CATEGORIES.PII,
|
|
943
|
+
pattern: /\b\d{11}\b/g,
|
|
944
|
+
includeHash: true,
|
|
945
|
+
// Context-awareness: Prevents false positives (timestamps, IDs)
|
|
946
|
+
isContextRequired: true,
|
|
947
|
+
contextWords: ["cnh", "habilita\xE7\xE3o", "habilitacao", "carteira", "motorista", "driver", "license"],
|
|
948
|
+
contextWindow: { before: 40, after: 10 }
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
id: `${PREFIXES.BERRY_PII}:rg-br`,
|
|
952
|
+
name: "RG (Brazil)",
|
|
953
|
+
category: CATEGORIES.PII,
|
|
954
|
+
pattern: /\b\d{1,2}\.?\d{3}\.?\d{3}-?\d{1}\b/g,
|
|
955
|
+
includeHash: true,
|
|
956
|
+
// Context-awareness: Prevents false positives (IDs, counters)
|
|
957
|
+
isContextRequired: true,
|
|
958
|
+
contextWords: ["rg", "identidade", "documento", "carteira", "identity", "card"],
|
|
959
|
+
contextWindow: { before: 30, after: 15 }
|
|
960
|
+
},
|
|
961
|
+
// --- Chinese PII ---
|
|
962
|
+
{
|
|
963
|
+
id: `${PREFIXES.BERRY_PII}:id-cn`,
|
|
964
|
+
name: "Citizen ID (China)",
|
|
965
|
+
category: CATEGORIES.PII,
|
|
966
|
+
pattern: /\b[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]\b/g,
|
|
967
|
+
includeHash: true
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
id: `${PREFIXES.BERRY_PII}:cn-phone`,
|
|
971
|
+
name: "Chinese Phone",
|
|
972
|
+
category: CATEGORIES.PII,
|
|
973
|
+
pattern: /\b1[3-9]\d{9}\b/g,
|
|
974
|
+
includeHash: true,
|
|
975
|
+
// Context-awareness: Prevents collision with Brazilian phones (also 11 digits starting with 1)
|
|
976
|
+
isContextRequired: true,
|
|
977
|
+
contextWords: ["\u624B\u673A", "\u7535\u8BDD", "phone", "mobile", "contact", "tel"],
|
|
978
|
+
contextWindow: { before: 25, after: 10 }
|
|
979
|
+
},
|
|
980
|
+
// --- Generic Passport (international) ---
|
|
981
|
+
{
|
|
982
|
+
id: `${PREFIXES.BERRY_PII}:passport`,
|
|
983
|
+
name: "Passport",
|
|
984
|
+
category: CATEGORIES.PII,
|
|
985
|
+
pattern: /\b(?:passport|护照)[-_\s]*(?:no|number|号)?[-_\s:]*[A-Z0-9]{5,12}\b/gi,
|
|
986
|
+
includeHash: true
|
|
987
|
+
},
|
|
988
|
+
// --- Phone patterns (AFTER context-aware BR/CN to avoid false positives) ---
|
|
989
|
+
{
|
|
990
|
+
id: `${PREFIXES.BERRY_PII}:phone-us`,
|
|
991
|
+
name: "US Phone",
|
|
992
|
+
category: CATEGORIES.PII,
|
|
993
|
+
pattern: /\b(?:\+?1[-.\s]?)?(?:\(?[2-9]\d{2}\)?[-.\s]?)[2-9]\d{2}[-.\s]?\d{4}\b/g,
|
|
994
|
+
includeHash: true
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
id: `${PREFIXES.BERRY_PII}:phone-intl`,
|
|
998
|
+
name: "International Phone",
|
|
999
|
+
category: CATEGORIES.PII,
|
|
1000
|
+
pattern: /(\+[2-9]\d{0,2}[-.\s]?\d{2,4}[-.\s]?\d{3,4}[-.\s]?\d{3,4})\b/g,
|
|
1001
|
+
includeHash: true
|
|
1002
|
+
}
|
|
1003
|
+
];
|
|
1004
|
+
INTERNAL_SENSITIVE_FILE_PATTERNS = [
|
|
1005
|
+
{ id: `${PREFIXES.BERRY_FILE}:env`, name: "Environment File", category: CATEGORIES.FILE, pattern: /\.env(?:\.|$)/i, includeHash: true },
|
|
1006
|
+
{ id: `${PREFIXES.BERRY_FILE}:credentials-json`, name: "Credentials JSON", category: CATEGORIES.FILE, pattern: /credentials\.json$/i, includeHash: true },
|
|
1007
|
+
{ id: `${PREFIXES.BERRY_FILE}:pem`, name: "PEM Key", category: CATEGORIES.FILE, pattern: /\.pem$/i, includeHash: true },
|
|
1008
|
+
{ id: `${PREFIXES.BERRY_FILE}:key`, name: "Private Key File", category: CATEGORIES.FILE, pattern: /\.key$/i, includeHash: true },
|
|
1009
|
+
{ id: `${PREFIXES.BERRY_FILE}:p12`, name: "PKCS#12 File", category: CATEGORIES.FILE, pattern: /\.p12$/i, includeHash: true },
|
|
1010
|
+
{ id: `${PREFIXES.BERRY_FILE}:pfx`, name: "PFX Certificate", category: CATEGORIES.FILE, pattern: /\.pfx$/i, includeHash: true },
|
|
1011
|
+
{ id: `${PREFIXES.BERRY_FILE}:ssh-private-keys`, name: "SSH Private Key", category: CATEGORIES.FILE, pattern: /id_(?:rsa|ed25519|ecdsa|dsa)/i, includeHash: true },
|
|
1012
|
+
{ id: `${PREFIXES.BERRY_FILE}:ssh-known-hosts`, name: "SSH Known Hosts", category: CATEGORIES.FILE, pattern: /known_hosts$/i, includeHash: true },
|
|
1013
|
+
{ id: `${PREFIXES.BERRY_FILE}:ssh-config`, name: "SSH Config", category: CATEGORIES.FILE, pattern: /\.ssh[\\/]config$/i, includeHash: true },
|
|
1014
|
+
{ id: `${PREFIXES.BERRY_FILE}:netrc`, name: "NETRC Config", category: CATEGORIES.FILE, pattern: /\.netrc$/i, includeHash: true },
|
|
1015
|
+
{ id: `${PREFIXES.BERRY_FILE}:npmrc`, name: "NPMRC Config", category: CATEGORIES.FILE, pattern: /\.npmrc$/i, includeHash: true },
|
|
1016
|
+
{ id: `${PREFIXES.BERRY_FILE}:secret-files`, name: "Generic Secret File", category: CATEGORIES.FILE, pattern: /secrets?\.(?:ya?ml|json|toml)$/i, includeHash: true },
|
|
1017
|
+
{ id: `${PREFIXES.BERRY_FILE}:aws-credentials`, name: "AWS Credentials", category: CATEGORIES.FILE, pattern: /\.aws[\\/]credentials$/i, includeHash: true },
|
|
1018
|
+
{ id: `${PREFIXES.BERRY_FILE}:aws-config`, name: "AWS Config", category: CATEGORIES.FILE, pattern: /\.aws[\\/]config$/i, includeHash: true },
|
|
1019
|
+
{ id: `${PREFIXES.BERRY_FILE}:kube-config`, name: "Kube Config", category: CATEGORIES.FILE, pattern: /\.kube[\\/]config$/i, includeHash: true },
|
|
1020
|
+
{ id: `${PREFIXES.BERRY_FILE}:ssh-host-keys`, name: "SSH Host Key", category: CATEGORIES.FILE, pattern: /\/etc\/ssh\/ssh_host_(?:rsa|ed25519|ecdsa)_key$/i, includeHash: true },
|
|
1021
|
+
{ id: `${PREFIXES.BERRY_FILE}:docker-config`, name: "Docker Config", category: CATEGORIES.FILE, pattern: /\.docker[\\/]config\.json$/i, includeHash: true },
|
|
1022
|
+
{ id: `${PREFIXES.BERRY_FILE}:gnupg-keys`, name: "GnuPG Private Key", category: CATEGORIES.FILE, pattern: /\.gnupg[\\/]private-keys-v1\.d(?:[\\/]|$)/i, includeHash: true },
|
|
1023
|
+
{ id: `${PREFIXES.BERRY_FILE}:vault-token`, name: "Vault Token", category: CATEGORIES.FILE, pattern: /\.vault-token$/i, includeHash: true },
|
|
1024
|
+
{ id: `${PREFIXES.BERRY_FILE}:terraform-credentials`, name: "Terraform Credentials", category: CATEGORIES.FILE, pattern: /\.terraform\.d[\\/]credentials\.tfrc\.json$/i, includeHash: true },
|
|
1025
|
+
{ id: `${PREFIXES.BERRY_FILE}:rclone-conf`, name: "Rclone Config", category: CATEGORIES.FILE, pattern: /\.config[\\/]rclone[\\/]rclone\.conf$/i, includeHash: true },
|
|
1026
|
+
{ id: `${PREFIXES.BERRY_FILE}:sops-age-keys`, name: "SOPS Age Keys", category: CATEGORIES.FILE, pattern: /\.config[\\/]sops[\\/]age[\\/]keys\.txt$/i, includeHash: true },
|
|
1027
|
+
{ id: `${PREFIXES.BERRY_FILE}:age-keys`, name: "Age Keys", category: CATEGORIES.FILE, pattern: /\.config[\\/]age[\\/]keys\.txt$/i, includeHash: true },
|
|
1028
|
+
{ id: `${PREFIXES.BERRY_FILE}:pypirc`, name: "PyPI Config", category: CATEGORIES.FILE, pattern: /\.pypirc$/i, includeHash: true },
|
|
1029
|
+
{ id: `${PREFIXES.BERRY_FILE}:gh-hosts`, name: "GitHub CLI Hosts", category: CATEGORIES.FILE, pattern: /\.config[\\/]gh[\\/]hosts\.yml$/i, includeHash: true },
|
|
1030
|
+
{ id: `${PREFIXES.BERRY_FILE}:gcloud-credentials-db`, name: "GCloud Credentials", category: CATEGORIES.FILE, pattern: /\.config[\\/]gcloud[\\/]credentials\.db$/i, includeHash: true },
|
|
1031
|
+
{ id: `${PREFIXES.BERRY_FILE}:gcloud-access-tokens`, name: "GCloud Access Tokens", category: CATEGORIES.FILE, pattern: /\.config[\\/]gcloud[\\/]access_tokens\.db$/i, includeHash: true },
|
|
1032
|
+
{ id: `${PREFIXES.BERRY_FILE}:gcloud-legacy-credentials`, name: "GCloud Legacy Credentials", category: CATEGORIES.FILE, pattern: /\.config[\\/]gcloud[\\/]legacy_credentials(?:[\\/]|$)/i, includeHash: true },
|
|
1033
|
+
{ id: `${PREFIXES.BERRY_FILE}:azure-access-tokens`, name: "Azure Access Tokens", category: CATEGORIES.FILE, pattern: /\.config[\\/]azure[\\/]accessTokens\.json$/i, includeHash: true },
|
|
1034
|
+
{ id: `${PREFIXES.BERRY_FILE}:helm-registry-config`, name: "Helm Registry Config", category: CATEGORIES.FILE, pattern: /\.config[\\/]helm[\\/]registry[\\/]config\.json$/i, includeHash: true },
|
|
1035
|
+
{ id: `${PREFIXES.BERRY_FILE}:poetry-auth`, name: "Poetry Auth", category: CATEGORIES.FILE, pattern: /\.config[\\/]pypoetry[\\/]auth\.toml$/i, includeHash: true },
|
|
1036
|
+
{ id: `${PREFIXES.BERRY_FILE}:linux-keyrings`, name: "Linux Keyrings", category: CATEGORIES.FILE, pattern: /\.local[\\/]share[\\/]keyrings(?:[\\/]|$)/i, includeHash: true },
|
|
1037
|
+
{ id: `${PREFIXES.BERRY_FILE}:windows-roaming-credentials`, name: "Windows Roaming Credentials", category: CATEGORIES.FILE, pattern: /(?:^|[\\/])AppData[\\/]Roaming[\\/]Microsoft[\\/]Credentials(?:[\\/]|$)/i, includeHash: true },
|
|
1038
|
+
{ id: `${PREFIXES.BERRY_FILE}:windows-local-credentials`, name: "Windows Local Credentials", category: CATEGORIES.FILE, pattern: /(?:^|[\\/])AppData[\\/]Local[\\/]Microsoft[\\/]Credentials(?:[\\/]|$)/i, includeHash: true },
|
|
1039
|
+
{ id: `${PREFIXES.BERRY_FILE}:mac-keychains`, name: "Mac Keychains", category: CATEGORIES.FILE, pattern: /(?:^|[\\/])Library[\\/]Keychains(?:[\\/]|$)/i, includeHash: true },
|
|
1040
|
+
{ id: `${PREFIXES.BERRY_FILE}:linux-shadow`, name: "Linux Shadow File", category: CATEGORIES.FILE, pattern: /\/etc\/shadow$/i, includeHash: true },
|
|
1041
|
+
{ id: `${PREFIXES.BERRY_FILE}:linux-passwd`, name: "Linux Passwd File", category: CATEGORIES.FILE, pattern: /\/etc\/passwd$/i, includeHash: true },
|
|
1042
|
+
{ id: `${PREFIXES.BERRY_FILE}:linux-gshadow`, name: "Linux Gshadow File", category: CATEGORIES.FILE, pattern: /\/etc\/gshadow$/i, includeHash: true },
|
|
1043
|
+
{ id: `${PREFIXES.BERRY_FILE}:openclaw-json`, name: "OpenClaw Config File", category: CATEGORIES.FILE, pattern: /openclaw\.json$/i, includeHash: true }
|
|
1044
|
+
];
|
|
1045
|
+
INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS = [
|
|
1046
|
+
// GROUP A: Filesystem (Flags-First)
|
|
1047
|
+
{
|
|
1048
|
+
id: `${PREFIXES.BERRY_COMMAND}:filesystem-rm`,
|
|
1049
|
+
name: "Destructive RM",
|
|
1050
|
+
category: CATEGORIES.COMMAND,
|
|
1051
|
+
pattern: /rm\s+-(?:[rf]{1,2}|-recursive|-force)\b/gi,
|
|
1052
|
+
includeHash: true
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
id: `${PREFIXES.BERRY_COMMAND}:fs-disk`,
|
|
1056
|
+
name: "Filesystem Danger (Disk/Format)",
|
|
1057
|
+
category: CATEGORIES.COMMAND,
|
|
1058
|
+
pattern: /\b(format|mkfs|dd|rmdir)\b/i,
|
|
1059
|
+
isContextRequired: true,
|
|
1060
|
+
contextWords: ["dev", "disk", "sda", "sdb", "nvme", "if=", "of=", "sudo"],
|
|
1061
|
+
contextWindow: { before: 30, after: 30 },
|
|
1062
|
+
includeHash: true
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
id: `${PREFIXES.BERRY_COMMAND}:fs-delete-utils`,
|
|
1066
|
+
name: "Filesystem Danger (Utils)",
|
|
1067
|
+
category: CATEGORIES.COMMAND,
|
|
1068
|
+
pattern: /\b(unlink|del)\b/i,
|
|
1069
|
+
isContextRequired: true,
|
|
1070
|
+
contextWords: ["sudo", "root", "force", "all", "quiet"],
|
|
1071
|
+
contextWindow: { before: 10, after: 10 },
|
|
1072
|
+
includeHash: true
|
|
1073
|
+
},
|
|
1074
|
+
// GROUP B: Database (Semantic Context)
|
|
1075
|
+
{
|
|
1076
|
+
id: `${PREFIXES.BERRY_COMMAND}:db-destructive`,
|
|
1077
|
+
name: "Database Danger",
|
|
1078
|
+
category: CATEGORIES.COMMAND,
|
|
1079
|
+
pattern: /\b(DROP\s+(?:TABLE|DATABASE)|TRUNCATE\s+TABLE|DELETE\s+FROM)\b/i,
|
|
1080
|
+
isContextRequired: true,
|
|
1081
|
+
contextWords: ["sql", "query", "database", "production", "db", "master", "main"],
|
|
1082
|
+
contextWindow: { before: 20, after: 20 },
|
|
1083
|
+
includeHash: true
|
|
1084
|
+
},
|
|
1085
|
+
// GROUP C: DevOps & Cloud
|
|
1086
|
+
{
|
|
1087
|
+
id: `${PREFIXES.BERRY_COMMAND}:devops-k8s`,
|
|
1088
|
+
name: "DevOps Danger (K8s)",
|
|
1089
|
+
category: CATEGORIES.COMMAND,
|
|
1090
|
+
pattern: /\bkubectl\s+delete\b/i,
|
|
1091
|
+
isContextRequired: true,
|
|
1092
|
+
contextWords: ["namespace", "pod", "all", "force", "cluster", "production", "prod"],
|
|
1093
|
+
contextWindow: { before: 20, after: 20 },
|
|
1094
|
+
includeHash: true
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
id: `${PREFIXES.BERRY_COMMAND}:devops-docker`,
|
|
1098
|
+
name: "DevOps Danger (Docker)",
|
|
1099
|
+
category: CATEGORIES.COMMAND,
|
|
1100
|
+
pattern: /\bdocker\s+(?:rm|rmi|system\s+prune)\b/i,
|
|
1101
|
+
isContextRequired: true,
|
|
1102
|
+
contextWords: ["all", "force", "image", "container", "volume", "prune"],
|
|
1103
|
+
contextWindow: { before: 20, after: 20 },
|
|
1104
|
+
includeHash: true
|
|
1105
|
+
},
|
|
1106
|
+
// GROUP D: Network & System
|
|
1107
|
+
{
|
|
1108
|
+
id: `${PREFIXES.BERRY_COMMAND}:sys-network`,
|
|
1109
|
+
name: "System/Network Danger",
|
|
1110
|
+
category: CATEGORIES.COMMAND,
|
|
1111
|
+
pattern: /\b(iptables\s+-F|systemctl\s+stop)\b/i,
|
|
1112
|
+
isContextRequired: true,
|
|
1113
|
+
contextWords: ["sudo", "root", "firewall", "service", "daemon", "stop"],
|
|
1114
|
+
contextWindow: { before: 15, after: 15 },
|
|
1115
|
+
includeHash: true
|
|
1116
|
+
},
|
|
1117
|
+
// GROUP E: Git & Permissions
|
|
1118
|
+
{
|
|
1119
|
+
id: `${PREFIXES.BERRY_COMMAND}:git-force`,
|
|
1120
|
+
name: "Git Danger (Force)",
|
|
1121
|
+
category: CATEGORIES.COMMAND,
|
|
1122
|
+
pattern: /\bgit\s+push\b/i,
|
|
1123
|
+
isContextRequired: true,
|
|
1124
|
+
contextWords: ["--force", "-f", "master", "main", "production"],
|
|
1125
|
+
contextWindow: { before: 10, after: 30 },
|
|
1126
|
+
includeHash: true
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
id: `${PREFIXES.BERRY_COMMAND}:git-reset`,
|
|
1130
|
+
name: "Git Danger (Reset)",
|
|
1131
|
+
category: CATEGORIES.COMMAND,
|
|
1132
|
+
pattern: /\bgit\s+reset\s+--hard\b/i,
|
|
1133
|
+
includeHash: true
|
|
1134
|
+
// Self-contained dangerous command
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
id: `${PREFIXES.BERRY_COMMAND}:permissions-777`,
|
|
1138
|
+
name: "Insecure Permissions (777)",
|
|
1139
|
+
category: CATEGORIES.COMMAND,
|
|
1140
|
+
pattern: /chmod\s+777\b/gi,
|
|
1141
|
+
includeHash: true
|
|
1142
|
+
}
|
|
1143
|
+
];
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
|
|
512
1147
|
// src/patterns/index.ts
|
|
513
1148
|
var patterns_exports = {};
|
|
514
1149
|
__export(patterns_exports, {
|
|
515
1150
|
DESTRUCTIVE_COMMAND_PATTERNS: () => DESTRUCTIVE_COMMAND_PATTERNS,
|
|
516
1151
|
INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS: () => INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS,
|
|
1152
|
+
INTERNAL_PII_PATTERNS: () => INTERNAL_PII_PATTERNS,
|
|
1153
|
+
INTERNAL_SECRET_PATTERNS: () => INTERNAL_SECRET_PATTERNS,
|
|
517
1154
|
INTERNAL_SENSITIVE_FILE_PATTERNS: () => INTERNAL_SENSITIVE_FILE_PATTERNS,
|
|
518
1155
|
PII_PATTERNS: () => PII_PATTERNS,
|
|
519
1156
|
SECRET_PATTERNS: () => SECRET_PATTERNS,
|
|
520
1157
|
SENSITIVE_FILE_PATTERNS: () => SENSITIVE_FILE_PATTERNS,
|
|
521
|
-
clearPatternCache: () => clearPatternCache,
|
|
522
1158
|
getAllDestructiveCommandPatterns: () => getAllDestructiveCommandPatterns,
|
|
523
1159
|
getAllRedactionPatterns: () => getAllRedactionPatterns,
|
|
524
1160
|
getAllSensitiveFilePatterns: () => getAllSensitiveFilePatterns,
|
|
@@ -566,7 +1202,13 @@ function compileCustomPatterns(customRules, disabledBuiltInIds) {
|
|
|
566
1202
|
const activeFiles = INTERNAL_SENSITIVE_FILE_PATTERNS.filter((p) => !disabledIds.has(p.id)).map((p) => p.pattern);
|
|
567
1203
|
const activeCmds = INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS.filter((p) => !disabledIds.has(p.id)).map((p) => p.pattern);
|
|
568
1204
|
return {
|
|
569
|
-
redactionPatterns: [
|
|
1205
|
+
redactionPatterns: [
|
|
1206
|
+
...activeSecrets,
|
|
1207
|
+
...customSecrets,
|
|
1208
|
+
...activePII,
|
|
1209
|
+
...INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS.filter((p) => !disabledIds.has(p.id)),
|
|
1210
|
+
...INTERNAL_SENSITIVE_FILE_PATTERNS.filter((p) => !disabledIds.has(p.id))
|
|
1211
|
+
],
|
|
570
1212
|
filePatterns: [...activeFiles, ...customFiles],
|
|
571
1213
|
commandPatterns: [...activeCmds, ...customCmds]
|
|
572
1214
|
};
|
|
@@ -604,246 +1246,35 @@ function getAllSensitiveFilePatterns() {
|
|
|
604
1246
|
function getAllDestructiveCommandPatterns() {
|
|
605
1247
|
return _cache.commandPatterns;
|
|
606
1248
|
}
|
|
607
|
-
|
|
608
|
-
_cache = {
|
|
609
|
-
redactionPatterns: [...SECRET_PATTERNS, ...PII_PATTERNS],
|
|
610
|
-
filePatterns: [...SENSITIVE_FILE_PATTERNS],
|
|
611
|
-
commandPatterns: [...DESTRUCTIVE_COMMAND_PATTERNS]
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
var SECRET_PATTERNS, PII_PATTERNS, INTERNAL_SENSITIVE_FILE_PATTERNS, SENSITIVE_FILE_PATTERNS, INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS, DESTRUCTIVE_COMMAND_PATTERNS, _cache;
|
|
1249
|
+
var SECRET_PATTERNS, PII_PATTERNS, SENSITIVE_FILE_PATTERNS, DESTRUCTIVE_COMMAND_PATTERNS, _cache;
|
|
615
1250
|
var init_patterns = __esm({
|
|
616
1251
|
"src/patterns/index.ts"() {
|
|
617
1252
|
"use strict";
|
|
618
1253
|
init_generated();
|
|
1254
|
+
init_constants();
|
|
1255
|
+
init_extended();
|
|
1256
|
+
init_extended();
|
|
619
1257
|
SECRET_PATTERNS = [
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
{
|
|
628
|
-
id: "secret:aws-secret-key",
|
|
629
|
-
name: "AWS Secret Key",
|
|
630
|
-
category: "secret",
|
|
631
|
-
pattern: /aws_secret_access_key\s*[:=]\s*[A-Za-z0-9/+=]{40}/gi,
|
|
632
|
-
placeholder: "[AWS_SECRET_REDACTED]"
|
|
633
|
-
},
|
|
634
|
-
{
|
|
635
|
-
id: "secret:stripe-key",
|
|
636
|
-
name: "Stripe Key",
|
|
637
|
-
category: "secret",
|
|
638
|
-
pattern: /[sr]k[-_](?:live|test)[-_][a-zA-Z0-9]{20,}/g,
|
|
639
|
-
placeholder: "[STRIPE_KEY_REDACTED]"
|
|
640
|
-
},
|
|
641
|
-
{
|
|
642
|
-
id: "secret:github-token",
|
|
643
|
-
name: "GitHub Token",
|
|
644
|
-
category: "secret",
|
|
645
|
-
pattern: /gh[pousr]_[a-zA-Z0-9]{36}/g,
|
|
646
|
-
placeholder: "[GITHUB_TOKEN_REDACTED]"
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
id: "secret:github-pat",
|
|
650
|
-
name: "GitHub PAT",
|
|
651
|
-
category: "secret",
|
|
652
|
-
pattern: /github_pat_[a-zA-Z0-9_]{22,}/g,
|
|
653
|
-
placeholder: "[GITHUB_PAT_REDACTED]"
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
id: "secret:openai-key",
|
|
657
|
-
name: "OpenAI Key",
|
|
658
|
-
category: "secret",
|
|
659
|
-
pattern: /sk-[a-zA-Z0-9]{20,}/g,
|
|
660
|
-
placeholder: "[OPENAI_KEY_REDACTED]"
|
|
661
|
-
},
|
|
662
|
-
{
|
|
663
|
-
id: "secret:anthropic-key",
|
|
664
|
-
name: "Anthropic Key",
|
|
665
|
-
category: "secret",
|
|
666
|
-
pattern: /sk-ant-[a-zA-Z0-9_-]{20,}/g,
|
|
667
|
-
placeholder: "[ANTHROPIC_KEY_REDACTED]"
|
|
668
|
-
},
|
|
669
|
-
{
|
|
670
|
-
id: "secret:slack-token",
|
|
671
|
-
name: "Slack Token",
|
|
672
|
-
category: "secret",
|
|
673
|
-
pattern: /xox[bpras]-[a-zA-Z0-9-]{10,}/g,
|
|
674
|
-
placeholder: "[SLACK_TOKEN_REDACTED]"
|
|
675
|
-
},
|
|
676
|
-
{
|
|
677
|
-
id: "secret:sendgrid-key",
|
|
678
|
-
name: "SendGrid Key",
|
|
679
|
-
category: "secret",
|
|
680
|
-
pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/g,
|
|
681
|
-
placeholder: "[SENDGRID_KEY_REDACTED]"
|
|
682
|
-
},
|
|
683
|
-
{
|
|
684
|
-
id: "secret:npm-token",
|
|
685
|
-
name: "NPM Token",
|
|
686
|
-
category: "secret",
|
|
687
|
-
pattern: /npm_[a-zA-Z0-9]{36,}/g,
|
|
688
|
-
placeholder: "[NPM_TOKEN_REDACTED]"
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
id: "secret:private-key",
|
|
692
|
-
name: "Private Key",
|
|
693
|
-
category: "secret",
|
|
694
|
-
pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
|
|
695
|
-
placeholder: "[PRIVATE_KEY_REDACTED]"
|
|
696
|
-
},
|
|
697
|
-
{
|
|
698
|
-
id: "secret:jwt",
|
|
699
|
-
name: "JWT",
|
|
700
|
-
category: "secret",
|
|
701
|
-
pattern: /eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/g,
|
|
702
|
-
placeholder: "[JWT_REDACTED]"
|
|
703
|
-
},
|
|
704
|
-
{
|
|
705
|
-
id: "secret:bearer-token",
|
|
706
|
-
name: "Bearer Token",
|
|
707
|
-
category: "secret",
|
|
708
|
-
pattern: /Authorization\s*[:=]\s*Bearer\s+[a-zA-Z0-9_.\-/+=]{20,}/gi,
|
|
709
|
-
placeholder: "[BEARER_TOKEN_REDACTED]"
|
|
710
|
-
},
|
|
711
|
-
{
|
|
712
|
-
id: "secret:generic-api-key",
|
|
713
|
-
name: "Generic API Key",
|
|
714
|
-
category: "secret",
|
|
715
|
-
pattern: /api[-_]?key\s*[:=]\s*[a-zA-Z0-9_.\-/+=]{20,}/gi,
|
|
716
|
-
placeholder: "[API_KEY_REDACTED]"
|
|
717
|
-
},
|
|
718
|
-
{
|
|
719
|
-
id: "secret:telegram-bot-token",
|
|
720
|
-
name: "Telegram Bot Token",
|
|
721
|
-
category: "secret",
|
|
722
|
-
pattern: /[0-9]{8,10}:[a-zA-Z0-9_-]{35,}/g,
|
|
723
|
-
placeholder: "[TELEGRAM_TOKEN_REDACTED]"
|
|
724
|
-
},
|
|
725
|
-
{
|
|
726
|
-
id: "secret:generic-json-secret",
|
|
727
|
-
name: "Generic JSON Secret",
|
|
728
|
-
category: "secret",
|
|
729
|
-
pattern: /"(?:apiKey|token|secret|auth|password|passwd)"\s*:\s*"([^"]+)"/gi,
|
|
730
|
-
placeholder: '"key": "[GENERIC_SECRET_REDACTED]"'
|
|
731
|
-
},
|
|
732
|
-
...GITLEAKS_PATTERNS.map((rule) => ({
|
|
733
|
-
id: `secret:gitleaks:${rule.id.toLowerCase()}`,
|
|
734
|
-
name: rule.id,
|
|
735
|
-
category: "secret",
|
|
736
|
-
pattern: new RegExp(rule.pattern, "g"),
|
|
737
|
-
placeholder: `[${rule.id.toUpperCase().replace(/-/g, "_")}_REDACTED]`
|
|
1258
|
+
...INTERNAL_SECRET_PATTERNS,
|
|
1259
|
+
...GITLEAKS_PATTERNS.map((entry) => ({
|
|
1260
|
+
id: `${PREFIXES.GITLEAKS_SECRET}:${entry.id.toLowerCase()}`,
|
|
1261
|
+
name: entry.id,
|
|
1262
|
+
category: CATEGORIES.SECRET,
|
|
1263
|
+
pattern: new RegExp(entry.pattern, "g"),
|
|
1264
|
+
placeholder: `[${entry.id.toUpperCase().replace(/-/g, "_")}_REDACTED]`
|
|
738
1265
|
}))
|
|
739
1266
|
];
|
|
740
1267
|
PII_PATTERNS = [
|
|
741
|
-
|
|
742
|
-
id: "pii:email",
|
|
743
|
-
name: "Email",
|
|
744
|
-
category: "pii",
|
|
745
|
-
pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
746
|
-
placeholder: "[EMAIL_REDACTED]"
|
|
747
|
-
},
|
|
748
|
-
{
|
|
749
|
-
id: "pii:ssn-us",
|
|
750
|
-
name: "SSN (USA)",
|
|
751
|
-
category: "pii",
|
|
752
|
-
pattern: /\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/g,
|
|
753
|
-
placeholder: "[SSN_REDACTED]"
|
|
754
|
-
},
|
|
755
|
-
{
|
|
756
|
-
id: "pii:credit-card",
|
|
757
|
-
name: "Credit Card",
|
|
758
|
-
category: "pii",
|
|
759
|
-
pattern: /\b[3-6]\d{3}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{1,7}\b/g,
|
|
760
|
-
placeholder: "[CARD_REDACTED]"
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
id: "pii:phone-us",
|
|
764
|
-
name: "US Phone",
|
|
765
|
-
category: "pii",
|
|
766
|
-
pattern: /\b(?:\+?1[-.\s]?)?(?:\(?[2-9]\d{2}\)?[-.\s]?)[2-9]\d{2}[-.\s]?\d{4}\b/g,
|
|
767
|
-
placeholder: "[PHONE_REDACTED]"
|
|
768
|
-
},
|
|
769
|
-
{
|
|
770
|
-
id: "pii:phone-intl",
|
|
771
|
-
name: "International Phone",
|
|
772
|
-
category: "pii",
|
|
773
|
-
pattern: /(\+[2-9]\d{0,2}[-.\s]?\d{2,4}[-.\s]?\d{3,4}[-.\s]?\d{3,4})\b/g,
|
|
774
|
-
placeholder: "[PHONE_REDACTED]"
|
|
775
|
-
},
|
|
776
|
-
{
|
|
777
|
-
id: "pii:iban",
|
|
778
|
-
name: "IBAN",
|
|
779
|
-
category: "pii",
|
|
780
|
-
pattern: /\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}[A-Z0-9]{0,23}\b/g,
|
|
781
|
-
placeholder: "[IBAN_REDACTED]"
|
|
782
|
-
},
|
|
783
|
-
{
|
|
784
|
-
id: "pii:cpf-br",
|
|
785
|
-
name: "CPF (Brazil)",
|
|
786
|
-
category: "pii",
|
|
787
|
-
pattern: /\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b/g,
|
|
788
|
-
placeholder: "[CPF_REDACTED]"
|
|
789
|
-
},
|
|
790
|
-
{
|
|
791
|
-
id: "pii:cnpj-br",
|
|
792
|
-
name: "CNPJ (Brazil)",
|
|
793
|
-
category: "pii",
|
|
794
|
-
pattern: /\b\d{2}\.?\d{3}\.?\d{3}\/?\d{4}-?\d{2}\b/g,
|
|
795
|
-
placeholder: "[CNPJ_REDACTED]"
|
|
796
|
-
}
|
|
1268
|
+
...INTERNAL_PII_PATTERNS
|
|
797
1269
|
];
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
{ id: "file:credentials-json", pattern: /credentials\.json$/i },
|
|
801
|
-
{ id: "file:pem", pattern: /\.pem$/i },
|
|
802
|
-
{ id: "file:key", pattern: /\.key$/i },
|
|
803
|
-
{ id: "file:p12", pattern: /\.p12$/i },
|
|
804
|
-
{ id: "file:pfx", pattern: /\.pfx$/i },
|
|
805
|
-
{ id: "file:ssh-private-keys", pattern: /id_(?:rsa|ed25519|ecdsa|dsa)/i },
|
|
806
|
-
{ id: "file:ssh-known-hosts", pattern: /known_hosts$/i },
|
|
807
|
-
{ id: "file:ssh-config", pattern: /\.ssh[\\/]config$/i },
|
|
808
|
-
{ id: "file:netrc", pattern: /\.netrc$/i },
|
|
809
|
-
{ id: "file:npmrc", pattern: /\.npmrc$/i },
|
|
810
|
-
{ id: "file:secret-files", pattern: /secrets?\.(?:ya?ml|json|toml)$/i },
|
|
811
|
-
{ id: "file:aws-credentials", pattern: /\.aws[\\/]credentials$/i },
|
|
812
|
-
{ id: "file:aws-config", pattern: /\.aws[\\/]config$/i },
|
|
813
|
-
{ id: "file:kube-config", pattern: /\.kube[\\/]config$/i },
|
|
814
|
-
{ id: "file:ssh-host-keys", pattern: /\/etc\/ssh\/ssh_host_(?:rsa|ed25519|ecdsa)_key$/i },
|
|
815
|
-
{ id: "file:docker-config", pattern: /\.docker[\\/]config\.json$/i },
|
|
816
|
-
{ id: "file:gnupg-keys", pattern: /\.gnupg[\\/]private-keys-v1\.d(?:[\\/]|$)/i },
|
|
817
|
-
{ id: "file:vault-token", pattern: /\.vault-token$/i },
|
|
818
|
-
{ id: "file:terraform-credentials", pattern: /\.terraform\.d[\\/]credentials\.tfrc\.json$/i },
|
|
819
|
-
{ id: "file:rclone-conf", pattern: /\.config[\\/]rclone[\\/]rclone\.conf$/i },
|
|
820
|
-
{ id: "file:sops-age-keys", pattern: /\.config[\\/]sops[\\/]age[\\/]keys\.txt$/i },
|
|
821
|
-
{ id: "file:age-keys", pattern: /\.config[\\/]age[\\/]keys\.txt$/i },
|
|
822
|
-
{ id: "file:pypirc", pattern: /\.pypirc$/i },
|
|
823
|
-
{ id: "file:gh-hosts", pattern: /\.config[\\/]gh[\\/]hosts\.yml$/i },
|
|
824
|
-
{ id: "file:gcloud-credentials-db", pattern: /\.config[\\/]gcloud[\\/]credentials\.db$/i },
|
|
825
|
-
{ id: "file:gcloud-access-tokens", pattern: /\.config[\\/]gcloud[\\/]access_tokens\.db$/i },
|
|
826
|
-
{ id: "file:gcloud-legacy-credentials", pattern: /\.config[\\/]gcloud[\\/]legacy_credentials(?:[\\/]|$)/i },
|
|
827
|
-
{ id: "file:azure-access-tokens", pattern: /\.config[\\/]azure[\\/]accessTokens\.json$/i },
|
|
828
|
-
{ id: "file:helm-registry-config", pattern: /\.config[\\/]helm[\\/]registry[\\/]config\.json$/i },
|
|
829
|
-
{ id: "file:poetry-auth", pattern: /\.config[\\/]pypoetry[\\/]auth\.toml$/i },
|
|
830
|
-
{ id: "file:linux-keyrings", pattern: /\.local[\\/]share[\\/]keyrings(?:[\\/]|$)/i },
|
|
831
|
-
{ id: "file:windows-roaming-credentials", pattern: /(?:^|[\\/])AppData[\\/]Roaming[\\/]Microsoft[\\/]Credentials(?:[\\/]|$)/i },
|
|
832
|
-
{ id: "file:windows-local-credentials", pattern: /(?:^|[\\/])AppData[\\/]Local[\\/]Microsoft[\\/]Credentials(?:[\\/]|$)/i },
|
|
833
|
-
{ id: "file:mac-keychains", pattern: /(?:^|[\\/])Library[\\/]Keychains(?:[\\/]|$)/i },
|
|
834
|
-
{ id: "file:linux-shadow", pattern: /\/etc\/shadow$/i },
|
|
835
|
-
{ id: "file:linux-passwd", pattern: /\/etc\/passwd$/i },
|
|
836
|
-
{ id: "file:linux-gshadow", pattern: /\/etc\/gshadow$/i },
|
|
837
|
-
{ id: "file:openclaw-json", pattern: /openclaw\.json$/i }
|
|
1270
|
+
SENSITIVE_FILE_PATTERNS = [
|
|
1271
|
+
...INTERNAL_SENSITIVE_FILE_PATTERNS.map((entry) => entry.pattern)
|
|
838
1272
|
];
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
{ id: "command:rm-family", pattern: /\b(rm|rmdir|unlink|del|format|mkfs)\b/i },
|
|
842
|
-
{ id: "command:dd", pattern: /\bdd\s+if=/i }
|
|
1273
|
+
DESTRUCTIVE_COMMAND_PATTERNS = [
|
|
1274
|
+
...INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS.map((entry) => entry.pattern)
|
|
843
1275
|
];
|
|
844
|
-
DESTRUCTIVE_COMMAND_PATTERNS = INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS.map((p) => p.pattern);
|
|
845
1276
|
_cache = {
|
|
846
|
-
redactionPatterns: [...SECRET_PATTERNS, ...PII_PATTERNS],
|
|
1277
|
+
redactionPatterns: [...SECRET_PATTERNS, ...PII_PATTERNS, ...INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS, ...INTERNAL_SENSITIVE_FILE_PATTERNS],
|
|
847
1278
|
filePatterns: [...SENSITIVE_FILE_PATTERNS],
|
|
848
1279
|
commandPatterns: [...DESTRUCTIVE_COMMAND_PATTERNS]
|
|
849
1280
|
};
|
|
@@ -909,7 +1340,7 @@ var require_src = __commonJS({
|
|
|
909
1340
|
// src/constants.ts
|
|
910
1341
|
var PLUGIN_ID = "berry-shield";
|
|
911
1342
|
var BRAND_SYMBOL = "\u{1F353}";
|
|
912
|
-
var VERSION = "2026.3.
|
|
1343
|
+
var VERSION = "2026.3.20";
|
|
913
1344
|
var ENV_VARS = {
|
|
914
1345
|
OPENCLAW_BIN: "OPENCLAW_BIN",
|
|
915
1346
|
OPENCLAW_EXECUTABLE: "OPENCLAW_EXECUTABLE"
|
|
@@ -926,7 +1357,9 @@ var SENSITIVE_KEY_EXACT = /* @__PURE__ */ new Set([
|
|
|
926
1357
|
"auth",
|
|
927
1358
|
"credential",
|
|
928
1359
|
"cred",
|
|
929
|
-
"secret"
|
|
1360
|
+
"secret",
|
|
1361
|
+
"mnemonic",
|
|
1362
|
+
"seed"
|
|
930
1363
|
]);
|
|
931
1364
|
var SENSITIVE_KEY_SUFFIXES = [
|
|
932
1365
|
"token",
|
|
@@ -1873,7 +2306,7 @@ function registerBerryRoot(api, config) {
|
|
|
1873
2306
|
},
|
|
1874
2307
|
{ priority: 200 }
|
|
1875
2308
|
);
|
|
1876
|
-
berryLog(api.logger, BERRY_LOG_CATEGORY.
|
|
2309
|
+
berryLog(api.logger, BERRY_LOG_CATEGORY.LAYER_TRACE, "Berry.Root layer registered (Prompt Guard)");
|
|
1877
2310
|
}
|
|
1878
2311
|
|
|
1879
2312
|
// src/types/audit-event.ts
|
|
@@ -1992,6 +2425,25 @@ init_patterns();
|
|
|
1992
2425
|
|
|
1993
2426
|
// src/utils/redaction.ts
|
|
1994
2427
|
init_patterns();
|
|
2428
|
+
import { createHash, randomBytes } from "crypto";
|
|
2429
|
+
var SESSION_SALT = randomBytes(16).toString("hex");
|
|
2430
|
+
function generatePlaceholder(pattern, originalValue) {
|
|
2431
|
+
let baseName = pattern.placeholder;
|
|
2432
|
+
if (!baseName) {
|
|
2433
|
+
const idParts = pattern.id.split(":");
|
|
2434
|
+
const namespacePart = idParts[0].toUpperCase();
|
|
2435
|
+
const namePart = idParts.slice(1).join("_").toUpperCase().replace(/[-]/g, "_");
|
|
2436
|
+
baseName = `[${namespacePart}:${namePart}]`;
|
|
2437
|
+
}
|
|
2438
|
+
if (pattern.includeHash) {
|
|
2439
|
+
const hash = createHash("sha256").update(originalValue + SESSION_SALT).digest("hex").substring(0, 6).toUpperCase();
|
|
2440
|
+
if (baseName.endsWith("]")) {
|
|
2441
|
+
return `${baseName.slice(0, -1)}#${hash}]`;
|
|
2442
|
+
}
|
|
2443
|
+
return `${baseName}#${hash}`;
|
|
2444
|
+
}
|
|
2445
|
+
return baseName;
|
|
2446
|
+
}
|
|
1995
2447
|
function unescapeString(text) {
|
|
1996
2448
|
if (!text.includes("\\") && !text.includes("%")) return text;
|
|
1997
2449
|
try {
|
|
@@ -2003,28 +2455,351 @@ function unescapeString(text) {
|
|
|
2003
2455
|
return text;
|
|
2004
2456
|
}
|
|
2005
2457
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2458
|
+
var MAX_COMPOSITE_SOURCE_LENGTH = 6e4;
|
|
2459
|
+
var ENGINE_CACHE = /* @__PURE__ */ new WeakMap();
|
|
2460
|
+
var PATH_SCAN_HINT = /[\\/]|(?:\.[a-z_][a-z0-9_-]{0,15}\b)|\b(?:appdata|library|credentials|keychains?|known_hosts|id_rsa|id_ed25519|\.env|openclaw\.json)\b/i;
|
|
2461
|
+
function hasPotentialCatastrophicBacktracking(source) {
|
|
2462
|
+
return /\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)[+*{]/.test(source);
|
|
2463
|
+
}
|
|
2464
|
+
function assertPatternSafety(pattern) {
|
|
2465
|
+
if (typeof pattern.id !== "string") {
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
const isUserSuppliedOrTestPattern = pattern.id.startsWith("custom:") || pattern.id.startsWith("test:");
|
|
2469
|
+
if (!isUserSuppliedOrTestPattern) {
|
|
2470
|
+
return;
|
|
2471
|
+
}
|
|
2472
|
+
if (hasPotentialCatastrophicBacktracking(pattern.pattern.source)) {
|
|
2473
|
+
throw new Error(`unsafe_regex_pattern:${pattern.id}`);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
function countCapturingGroups(source) {
|
|
2477
|
+
let count = 0;
|
|
2478
|
+
let escaped = false;
|
|
2479
|
+
let inCharClass = false;
|
|
2480
|
+
for (let i = 0; i < source.length; i++) {
|
|
2481
|
+
const char = source[i];
|
|
2482
|
+
if (escaped) {
|
|
2483
|
+
escaped = false;
|
|
2484
|
+
continue;
|
|
2485
|
+
}
|
|
2486
|
+
if (char === "\\") {
|
|
2487
|
+
escaped = true;
|
|
2488
|
+
continue;
|
|
2489
|
+
}
|
|
2490
|
+
if (char === "[" && !inCharClass) {
|
|
2491
|
+
inCharClass = true;
|
|
2492
|
+
continue;
|
|
2493
|
+
}
|
|
2494
|
+
if (char === "]" && inCharClass) {
|
|
2495
|
+
inCharClass = false;
|
|
2496
|
+
continue;
|
|
2497
|
+
}
|
|
2498
|
+
if (inCharClass || char !== "(") {
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
const next = source[i + 1];
|
|
2502
|
+
if (next !== "?") {
|
|
2503
|
+
count++;
|
|
2504
|
+
continue;
|
|
2505
|
+
}
|
|
2506
|
+
const type = source[i + 2];
|
|
2507
|
+
const nextAfterType = source[i + 3];
|
|
2508
|
+
const isNonCapturing = type === ":" || type === "=" || type === "!" || type === "<" && (nextAfterType === "=" || nextAfterType === "!");
|
|
2509
|
+
if (!isNonCapturing) {
|
|
2510
|
+
count++;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
return count;
|
|
2514
|
+
}
|
|
2515
|
+
function normalizeCompositeFlags(flags) {
|
|
2516
|
+
const baseOrder = ["i", "m", "s", "u", "d", "v"];
|
|
2517
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2518
|
+
for (const flag of flags) {
|
|
2519
|
+
if (flag === "g" || flag === "y") continue;
|
|
2520
|
+
seen.add(flag);
|
|
2521
|
+
}
|
|
2522
|
+
const normalized = baseOrder.filter((flag) => seen.has(flag)).join("");
|
|
2523
|
+
return `${normalized}g`;
|
|
2524
|
+
}
|
|
2525
|
+
function normalizeProbeFlags(flags) {
|
|
2526
|
+
const baseOrder = ["i", "m", "s", "u", "d", "v"];
|
|
2527
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2528
|
+
for (const flag of flags) {
|
|
2529
|
+
if (flag === "g" || flag === "y") continue;
|
|
2530
|
+
seen.add(flag);
|
|
2531
|
+
}
|
|
2532
|
+
const normalized = baseOrder.filter((flag) => seen.has(flag)).join("");
|
|
2533
|
+
return `${normalized}y`;
|
|
2534
|
+
}
|
|
2535
|
+
function isLikelyPathPattern(pattern) {
|
|
2536
|
+
if (pattern.category === "file") {
|
|
2537
|
+
return true;
|
|
2538
|
+
}
|
|
2539
|
+
const source = pattern.pattern.source.toLowerCase();
|
|
2540
|
+
return source.includes("[\\\\/]") || source.includes("\\/") || source.includes("/etc/") || source.includes("appdata") || source.includes("keychain") || source.includes("known_hosts") || source.includes("\\.env") || source.includes("openclaw\\.json");
|
|
2541
|
+
}
|
|
2542
|
+
function compileCompositeBucket(entries, flags) {
|
|
2543
|
+
const shouldProbeAllAlternatives = entries.some((entry) => entry.pattern.isContextRequired) || entries.length <= 24;
|
|
2544
|
+
const orderedEntries = shouldProbeAllAlternatives ? entries : [...entries].sort((a, b) => {
|
|
2545
|
+
const bySourceLength = b.pattern.pattern.source.length - a.pattern.pattern.source.length;
|
|
2546
|
+
if (bySourceLength !== 0) {
|
|
2547
|
+
return bySourceLength;
|
|
2548
|
+
}
|
|
2549
|
+
return a.originalIndex - b.originalIndex;
|
|
2550
|
+
});
|
|
2551
|
+
const alternatives = [];
|
|
2552
|
+
const outerGroupIndexes = [];
|
|
2553
|
+
let groupCursor = 1;
|
|
2554
|
+
for (const entry of orderedEntries) {
|
|
2555
|
+
const source = entry.pattern.pattern.source;
|
|
2556
|
+
alternatives.push(`(${source})`);
|
|
2557
|
+
outerGroupIndexes.push(groupCursor);
|
|
2558
|
+
groupCursor += 1 + countCapturingGroups(source);
|
|
2559
|
+
}
|
|
2560
|
+
const compositeSource = alternatives.join("|");
|
|
2561
|
+
if (compositeSource.length > MAX_COMPOSITE_SOURCE_LENGTH) {
|
|
2562
|
+
throw new Error("composite_regex_too_large");
|
|
2563
|
+
}
|
|
2564
|
+
return {
|
|
2565
|
+
regex: new RegExp(compositeSource, flags),
|
|
2566
|
+
entries: orderedEntries,
|
|
2567
|
+
outerGroupIndexes,
|
|
2568
|
+
shouldProbeAllAlternatives
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
function resolveBucketEntry(match, bucket) {
|
|
2572
|
+
for (let i = 0; i < bucket.outerGroupIndexes.length; i++) {
|
|
2573
|
+
const groupIndex = bucket.outerGroupIndexes[i];
|
|
2574
|
+
if (match[groupIndex] !== void 0) {
|
|
2575
|
+
return bucket.entries[i];
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
return null;
|
|
2579
|
+
}
|
|
2580
|
+
function collectCandidatesFromBucket(text, bucket, candidates) {
|
|
2581
|
+
bucket.regex.lastIndex = 0;
|
|
2582
|
+
let match;
|
|
2583
|
+
while ((match = bucket.regex.exec(text)) !== null) {
|
|
2584
|
+
const entry = resolveBucketEntry(match, bucket);
|
|
2585
|
+
const start = match.index;
|
|
2586
|
+
if (entry) {
|
|
2587
|
+
candidates.push({
|
|
2588
|
+
pattern: entry.pattern,
|
|
2589
|
+
matchText: match[0],
|
|
2590
|
+
start,
|
|
2591
|
+
end: start + match[0].length
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
if (bucket.shouldProbeAllAlternatives) {
|
|
2595
|
+
for (const sibling of bucket.entries) {
|
|
2596
|
+
if (entry && sibling.pattern.id === entry.pattern.id) {
|
|
2597
|
+
continue;
|
|
2598
|
+
}
|
|
2599
|
+
sibling.probeRegex.lastIndex = start;
|
|
2600
|
+
const siblingMatch = sibling.probeRegex.exec(text);
|
|
2601
|
+
if (!siblingMatch) {
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
candidates.push({
|
|
2605
|
+
pattern: sibling.pattern,
|
|
2606
|
+
matchText: siblingMatch[0],
|
|
2607
|
+
start,
|
|
2608
|
+
end: start + siblingMatch[0].length
|
|
2609
|
+
});
|
|
2019
2610
|
}
|
|
2020
2611
|
}
|
|
2612
|
+
if (match.index === bucket.regex.lastIndex) {
|
|
2613
|
+
bucket.regex.lastIndex++;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
function collectCandidatesFallback(text, entries, candidates) {
|
|
2618
|
+
for (const entry of entries) {
|
|
2619
|
+
const pattern = entry.pattern;
|
|
2620
|
+
const regex = new RegExp(pattern.pattern.source, normalizeCompositeFlags(pattern.pattern.flags));
|
|
2621
|
+
let match;
|
|
2622
|
+
while ((match = regex.exec(text)) !== null) {
|
|
2623
|
+
candidates.push({
|
|
2624
|
+
pattern,
|
|
2625
|
+
matchText: match[0],
|
|
2626
|
+
start: match.index,
|
|
2627
|
+
end: match.index + match[0].length
|
|
2628
|
+
});
|
|
2629
|
+
if (match.index === regex.lastIndex) {
|
|
2630
|
+
regex.lastIndex++;
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
function compileRedactionEngine(patterns) {
|
|
2636
|
+
const entries = patterns.map((pattern, originalIndex) => {
|
|
2637
|
+
assertPatternSafety(pattern);
|
|
2638
|
+
return {
|
|
2639
|
+
pattern,
|
|
2640
|
+
originalIndex,
|
|
2641
|
+
compositeFlags: normalizeCompositeFlags(pattern.pattern.flags),
|
|
2642
|
+
isPathLike: isLikelyPathPattern(pattern),
|
|
2643
|
+
probeRegex: new RegExp(pattern.pattern.source, normalizeProbeFlags(pattern.pattern.flags))
|
|
2644
|
+
};
|
|
2645
|
+
});
|
|
2646
|
+
const pathEntries = entries.filter((entry) => entry.isPathLike);
|
|
2647
|
+
const generalEntries = entries.filter((entry) => !entry.isPathLike);
|
|
2648
|
+
const compileBuckets = (bucketEntries) => {
|
|
2649
|
+
const byFlags = /* @__PURE__ */ new Map();
|
|
2650
|
+
for (const entry of bucketEntries) {
|
|
2651
|
+
const current = byFlags.get(entry.compositeFlags);
|
|
2652
|
+
if (current) {
|
|
2653
|
+
current.push(entry);
|
|
2654
|
+
} else {
|
|
2655
|
+
byFlags.set(entry.compositeFlags, [entry]);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
const buckets = [];
|
|
2659
|
+
const fallbackEntries = [];
|
|
2660
|
+
for (const [flags, group] of byFlags.entries()) {
|
|
2661
|
+
try {
|
|
2662
|
+
buckets.push(compileCompositeBucket(group, flags));
|
|
2663
|
+
} catch {
|
|
2664
|
+
fallbackEntries.push(...group);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
return { buckets, fallbackEntries };
|
|
2668
|
+
};
|
|
2669
|
+
const generalCompilation = compileBuckets(generalEntries);
|
|
2670
|
+
const pathCompilation = compileBuckets(pathEntries);
|
|
2671
|
+
return {
|
|
2672
|
+
generalBuckets: generalCompilation.buckets,
|
|
2673
|
+
pathBuckets: pathCompilation.buckets,
|
|
2674
|
+
fallbackGeneralEntries: generalCompilation.fallbackEntries,
|
|
2675
|
+
fallbackPathEntries: pathCompilation.fallbackEntries
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
function getOrCreateEngine(patterns) {
|
|
2679
|
+
const cached = ENGINE_CACHE.get(patterns);
|
|
2680
|
+
if (cached) {
|
|
2681
|
+
return cached;
|
|
2021
2682
|
}
|
|
2683
|
+
const compiled = compileRedactionEngine(patterns);
|
|
2684
|
+
ENGINE_CACHE.set(patterns, compiled);
|
|
2685
|
+
return compiled;
|
|
2686
|
+
}
|
|
2687
|
+
function shouldScanPathLikePatterns(text) {
|
|
2688
|
+
return PATH_SCAN_HINT.test(text);
|
|
2689
|
+
}
|
|
2690
|
+
function applyResolvedRedactions(originalText, resolved) {
|
|
2691
|
+
const parts = [];
|
|
2692
|
+
const redactedTypeSet = /* @__PURE__ */ new Set();
|
|
2693
|
+
let redactionCount = 0;
|
|
2694
|
+
let cursor = 0;
|
|
2695
|
+
for (const candidate of resolved) {
|
|
2696
|
+
const placeholder = generatePlaceholder(candidate.pattern, candidate.matchText);
|
|
2697
|
+
parts.push(originalText.slice(cursor, candidate.start));
|
|
2698
|
+
parts.push(placeholder);
|
|
2699
|
+
cursor = candidate.end;
|
|
2700
|
+
redactionCount++;
|
|
2701
|
+
redactedTypeSet.add(candidate.pattern.id);
|
|
2702
|
+
}
|
|
2703
|
+
parts.push(originalText.slice(cursor));
|
|
2022
2704
|
return {
|
|
2023
|
-
content:
|
|
2705
|
+
content: parts.join(""),
|
|
2024
2706
|
redactionCount,
|
|
2025
|
-
redactedTypes
|
|
2707
|
+
redactedTypes: Array.from(redactedTypeSet)
|
|
2026
2708
|
};
|
|
2027
2709
|
}
|
|
2710
|
+
function evaluateContext(text, match, pattern) {
|
|
2711
|
+
if (!pattern.isContextRequired) {
|
|
2712
|
+
return true;
|
|
2713
|
+
}
|
|
2714
|
+
if (!pattern.contextWords || pattern.contextWords.length === 0) {
|
|
2715
|
+
return false;
|
|
2716
|
+
}
|
|
2717
|
+
const window = pattern.contextWindow || { before: 30, after: 15 };
|
|
2718
|
+
const contextStart = Math.max(0, match.start - window.before);
|
|
2719
|
+
const contextEnd = Math.min(text.length, match.end + window.after);
|
|
2720
|
+
const beforeSlice = text.slice(contextStart, match.start).toLowerCase();
|
|
2721
|
+
const afterSlice = text.slice(match.end, contextEnd).toLowerCase();
|
|
2722
|
+
return pattern.contextWords.some(
|
|
2723
|
+
(word) => beforeSlice.includes(word.toLowerCase()) || afterSlice.includes(word.toLowerCase())
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
function selectWinner(a, b) {
|
|
2727
|
+
const categoryPriority = {
|
|
2728
|
+
command: 5,
|
|
2729
|
+
secret: 4,
|
|
2730
|
+
credential: 3,
|
|
2731
|
+
pii: 2,
|
|
2732
|
+
network: 1,
|
|
2733
|
+
other: 0
|
|
2734
|
+
};
|
|
2735
|
+
const priorityA = categoryPriority[a.pattern.category] || 0;
|
|
2736
|
+
const priorityB = categoryPriority[b.pattern.category] || 0;
|
|
2737
|
+
if (priorityA !== priorityB) {
|
|
2738
|
+
return priorityA > priorityB ? a : b;
|
|
2739
|
+
}
|
|
2740
|
+
const lengthA = a.end - a.start;
|
|
2741
|
+
const lengthB = b.end - b.start;
|
|
2742
|
+
if (lengthA !== lengthB) {
|
|
2743
|
+
return lengthA > lengthB ? a : b;
|
|
2744
|
+
}
|
|
2745
|
+
const isBerryA = a.pattern.id.startsWith("berry:");
|
|
2746
|
+
const isBerryB = b.pattern.id.startsWith("berry:");
|
|
2747
|
+
if (isBerryA && !isBerryB) return a;
|
|
2748
|
+
if (!isBerryA && isBerryB) return b;
|
|
2749
|
+
return b;
|
|
2750
|
+
}
|
|
2751
|
+
function resolveOverlaps(candidates) {
|
|
2752
|
+
if (candidates.length === 0) return [];
|
|
2753
|
+
candidates.sort((a, b) => a.start - b.start);
|
|
2754
|
+
const result = [];
|
|
2755
|
+
let lastEnd = -1;
|
|
2756
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
2757
|
+
const current = candidates[i];
|
|
2758
|
+
if (current.start >= lastEnd) {
|
|
2759
|
+
result.push(current);
|
|
2760
|
+
lastEnd = current.end;
|
|
2761
|
+
continue;
|
|
2762
|
+
}
|
|
2763
|
+
const last = result[result.length - 1];
|
|
2764
|
+
const winner = selectWinner(current, last);
|
|
2765
|
+
if (winner === current) {
|
|
2766
|
+
result[result.length - 1] = current;
|
|
2767
|
+
lastEnd = current.end;
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
return result;
|
|
2771
|
+
}
|
|
2772
|
+
function redactString(text, patterns) {
|
|
2773
|
+
const unescaped = unescapeString(text);
|
|
2774
|
+
const engine = getOrCreateEngine(patterns);
|
|
2775
|
+
const candidates = [];
|
|
2776
|
+
for (const bucket of engine.generalBuckets) {
|
|
2777
|
+
collectCandidatesFromBucket(unescaped, bucket, candidates);
|
|
2778
|
+
}
|
|
2779
|
+
collectCandidatesFallback(unescaped, engine.fallbackGeneralEntries, candidates);
|
|
2780
|
+
if (shouldScanPathLikePatterns(unescaped)) {
|
|
2781
|
+
for (const bucket of engine.pathBuckets) {
|
|
2782
|
+
collectCandidatesFromBucket(unescaped, bucket, candidates);
|
|
2783
|
+
}
|
|
2784
|
+
collectCandidatesFallback(unescaped, engine.fallbackPathEntries, candidates);
|
|
2785
|
+
}
|
|
2786
|
+
if (candidates.length === 0) {
|
|
2787
|
+
return { content: text, redactionCount: 0, redactedTypes: [] };
|
|
2788
|
+
}
|
|
2789
|
+
const validated = candidates.filter((candidate) => {
|
|
2790
|
+
if (!candidate.pattern.isContextRequired) return true;
|
|
2791
|
+
return evaluateContext(
|
|
2792
|
+
unescaped,
|
|
2793
|
+
{ start: candidate.start, end: candidate.end, value: candidate.matchText },
|
|
2794
|
+
candidate.pattern
|
|
2795
|
+
);
|
|
2796
|
+
});
|
|
2797
|
+
if (validated.length === 0) {
|
|
2798
|
+
return { content: text, redactionCount: 0, redactedTypes: [] };
|
|
2799
|
+
}
|
|
2800
|
+
const resolved = resolveOverlaps(validated);
|
|
2801
|
+
return applyResolvedRedactions(unescaped, resolved);
|
|
2802
|
+
}
|
|
2028
2803
|
function isSensitiveKey(key) {
|
|
2029
2804
|
const lower = key.toLowerCase();
|
|
2030
2805
|
if (SENSITIVE_KEY_EXACT.has(lower)) return true;
|
|
@@ -2116,16 +2891,35 @@ function walkAndRedact(obj, patterns, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
2116
2891
|
}
|
|
2117
2892
|
function findMatches(obj, patterns, seen = /* @__PURE__ */ new WeakSet()) {
|
|
2118
2893
|
const matchedNames = /* @__PURE__ */ new Set();
|
|
2894
|
+
const securityPatterns = isSecurityPatternArray(patterns) ? patterns : null;
|
|
2895
|
+
const regexPatterns = securityPatterns ? null : patterns;
|
|
2896
|
+
const engine = securityPatterns ? getOrCreateEngine(securityPatterns) : null;
|
|
2897
|
+
const collectCompiledMatchNames = (text, compiledEngine) => {
|
|
2898
|
+
const candidates = [];
|
|
2899
|
+
for (const bucket of compiledEngine.generalBuckets) {
|
|
2900
|
+
collectCandidatesFromBucket(text, bucket, candidates);
|
|
2901
|
+
}
|
|
2902
|
+
collectCandidatesFallback(text, compiledEngine.fallbackGeneralEntries, candidates);
|
|
2903
|
+
for (const bucket of compiledEngine.pathBuckets) {
|
|
2904
|
+
collectCandidatesFromBucket(text, bucket, candidates);
|
|
2905
|
+
}
|
|
2906
|
+
collectCandidatesFallback(text, compiledEngine.fallbackPathEntries, candidates);
|
|
2907
|
+
for (const candidate of candidates) {
|
|
2908
|
+
matchedNames.add(candidate.pattern.name);
|
|
2909
|
+
}
|
|
2910
|
+
};
|
|
2119
2911
|
const walk = (current) => {
|
|
2120
2912
|
if (!current) return;
|
|
2121
2913
|
if (typeof current === "string") {
|
|
2122
2914
|
const unescaped = unescapeString(current);
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
regex
|
|
2127
|
-
|
|
2128
|
-
|
|
2915
|
+
if (engine) {
|
|
2916
|
+
collectCompiledMatchNames(unescaped, engine);
|
|
2917
|
+
} else {
|
|
2918
|
+
for (const regex of regexPatterns ?? []) {
|
|
2919
|
+
regex.lastIndex = 0;
|
|
2920
|
+
if (regex.test(unescaped)) {
|
|
2921
|
+
matchedNames.add("Sensitive Pattern");
|
|
2922
|
+
}
|
|
2129
2923
|
}
|
|
2130
2924
|
}
|
|
2131
2925
|
return;
|
|
@@ -2147,6 +2941,12 @@ function findMatches(obj, patterns, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
2147
2941
|
walk(obj);
|
|
2148
2942
|
return Array.from(matchedNames);
|
|
2149
2943
|
}
|
|
2944
|
+
function isSecurityPatternArray(patterns) {
|
|
2945
|
+
if (patterns.length === 0) {
|
|
2946
|
+
return false;
|
|
2947
|
+
}
|
|
2948
|
+
return !(patterns[0] instanceof RegExp);
|
|
2949
|
+
}
|
|
2150
2950
|
|
|
2151
2951
|
// src/layers/pulp.ts
|
|
2152
2952
|
function registerBerryPulp(api, config) {
|
|
@@ -2250,7 +3050,7 @@ function registerBerryPulp(api, config) {
|
|
|
2250
3050
|
},
|
|
2251
3051
|
{ priority: 200 }
|
|
2252
3052
|
);
|
|
2253
|
-
berryLog(api.logger, BERRY_LOG_CATEGORY.
|
|
3053
|
+
berryLog(api.logger, BERRY_LOG_CATEGORY.LAYER_TRACE, "Berry.Pulp layer registered (Output Scanner)");
|
|
2254
3054
|
}
|
|
2255
3055
|
|
|
2256
3056
|
// src/ui/decision-card/format-text.ts
|
|
@@ -2552,7 +3352,7 @@ function registerBerryThorn(api, config) {
|
|
|
2552
3352
|
{ priority: 200 }
|
|
2553
3353
|
// High priority - security runs first
|
|
2554
3354
|
);
|
|
2555
|
-
berryLog(api.logger, BERRY_LOG_CATEGORY.
|
|
3355
|
+
berryLog(api.logger, BERRY_LOG_CATEGORY.LAYER_TRACE, "Berry.Thorn layer registered (Tool Blocker)");
|
|
2556
3356
|
}
|
|
2557
3357
|
|
|
2558
3358
|
// src/layers/leaf.ts
|
|
@@ -2572,7 +3372,7 @@ function registerBerryLeaf(api, config) {
|
|
|
2572
3372
|
const sessionKey = context.conversationId;
|
|
2573
3373
|
if (!message) return;
|
|
2574
3374
|
const { redactedTypes } = walkAndRedact(message, patterns);
|
|
2575
|
-
const matchedPatterns = patterns.filter((p) => redactedTypes.includes(p.
|
|
3375
|
+
const matchedPatterns = patterns.filter((p) => redactedTypes.includes(p.id));
|
|
2576
3376
|
const containsSecrets = matchedPatterns.some((p) => p.category === "secret");
|
|
2577
3377
|
const containsPII = matchedPatterns.some((p) => p.category === "pii");
|
|
2578
3378
|
const auditEntry = {
|
|
@@ -2596,7 +3396,7 @@ function registerBerryLeaf(api, config) {
|
|
|
2596
3396
|
{ priority: 50 }
|
|
2597
3397
|
// Lower priority - audit runs after security checks
|
|
2598
3398
|
);
|
|
2599
|
-
berryLog(api.logger, BERRY_LOG_CATEGORY.
|
|
3399
|
+
berryLog(api.logger, BERRY_LOG_CATEGORY.LAYER_TRACE, "Berry.Leaf layer registered (Input Audit)");
|
|
2600
3400
|
}
|
|
2601
3401
|
|
|
2602
3402
|
// src/layers/stem.ts
|
|
@@ -2820,10 +3620,10 @@ function getSharedVineStateManager(retention) {
|
|
|
2820
3620
|
}
|
|
2821
3621
|
|
|
2822
3622
|
// src/vine/confirm-state.ts
|
|
2823
|
-
import { createHash as
|
|
3623
|
+
import { createHash as createHash3, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2824
3624
|
|
|
2825
3625
|
// src/vine/vine-intent.ts
|
|
2826
|
-
import { createHash } from "node:crypto";
|
|
3626
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2827
3627
|
function sortCapabilities(capabilities) {
|
|
2828
3628
|
return [...new Set(capabilities)].sort();
|
|
2829
3629
|
}
|
|
@@ -2885,7 +3685,7 @@ function createIntentKind(capabilities) {
|
|
|
2885
3685
|
return normalizedCapabilities.join("+");
|
|
2886
3686
|
}
|
|
2887
3687
|
function createIntentSignature(intent) {
|
|
2888
|
-
return
|
|
3688
|
+
return createHash2("sha256").update(JSON.stringify(toSignatureShape(intent))).digest("hex");
|
|
2889
3689
|
}
|
|
2890
3690
|
function isEquivalentApprovedIntent(approved, requested) {
|
|
2891
3691
|
if (createIntentSignature(approved) === createIntentSignature(requested)) {
|
|
@@ -2915,7 +3715,7 @@ function normalizeTarget(target) {
|
|
|
2915
3715
|
return target.trim().replace(/\s+/g, " ");
|
|
2916
3716
|
}
|
|
2917
3717
|
function signTarget(operation, target) {
|
|
2918
|
-
return
|
|
3718
|
+
return createHash3("sha256").update(`${operation}
|
|
2919
3719
|
${normalizeTarget(target)}`).digest("hex");
|
|
2920
3720
|
}
|
|
2921
3721
|
function buildLegacyIntent(operation, target, rawTarget) {
|
|
@@ -4170,6 +4970,7 @@ function maybeEscalateFromStem(api, sessionKey, escalationTurns, allowGlobalEsca
|
|
|
4170
4970
|
}
|
|
4171
4971
|
function isDestructiveCommand3(command, customPatterns) {
|
|
4172
4972
|
for (const pattern of getAllDestructiveCommandPatterns()) {
|
|
4973
|
+
pattern.lastIndex = 0;
|
|
4173
4974
|
if (pattern.test(command)) {
|
|
4174
4975
|
return true;
|
|
4175
4976
|
}
|
|
@@ -4188,6 +4989,7 @@ function isDestructiveCommand3(command, customPatterns) {
|
|
|
4188
4989
|
function isSensitiveFile2(filePath, customPatterns) {
|
|
4189
4990
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
4190
4991
|
for (const pattern of getAllSensitiveFilePatterns()) {
|
|
4992
|
+
pattern.lastIndex = 0;
|
|
4191
4993
|
if (pattern.test(normalizedPath)) {
|
|
4192
4994
|
return true;
|
|
4193
4995
|
}
|
|
@@ -4789,7 +5591,7 @@ function registerBerryStem(api, config) {
|
|
|
4789
5591
|
};
|
|
4790
5592
|
}
|
|
4791
5593
|
});
|
|
4792
|
-
berryLog(api.logger, BERRY_LOG_CATEGORY.
|
|
5594
|
+
berryLog(api.logger, BERRY_LOG_CATEGORY.LAYER_TRACE, "Berry.Stem layer registered (Security Gate)");
|
|
4793
5595
|
}
|
|
4794
5596
|
|
|
4795
5597
|
// src/layers/vine.ts
|
|
@@ -5244,7 +6046,7 @@ function registerBerryVine(api, config) {
|
|
|
5244
6046
|
},
|
|
5245
6047
|
{ priority: 190 }
|
|
5246
6048
|
);
|
|
5247
|
-
berryLog(api.logger, BERRY_LOG_CATEGORY.
|
|
6049
|
+
berryLog(api.logger, BERRY_LOG_CATEGORY.LAYER_TRACE, "Berry.Vine layer registered (External Content Guard)");
|
|
5248
6050
|
}
|
|
5249
6051
|
|
|
5250
6052
|
// src/config/wrapper.ts
|
|
@@ -5390,8 +6192,9 @@ var symbols = {
|
|
|
5390
6192
|
|
|
5391
6193
|
// src/patterns/id-aliases.ts
|
|
5392
6194
|
var BASELINE_ID_ALIASES = {
|
|
5393
|
-
//
|
|
5394
|
-
|
|
6195
|
+
// Proof of Concept (POC) - Example of drift in Gitleaks
|
|
6196
|
+
// If an external pattern ID changes in the future, map it here to maintain compatibility.
|
|
6197
|
+
"secret:gitleaks:gitlab-runner-token": "gitleaks:secret:gitlab-runner-authentication-token"
|
|
5395
6198
|
};
|
|
5396
6199
|
function remapDisabledBuiltInIds(ids, aliases = BASELINE_ID_ALIASES) {
|
|
5397
6200
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -5601,7 +6404,7 @@ async function disableBuiltInRule(id) {
|
|
|
5601
6404
|
const normalizedId = id.toLowerCase();
|
|
5602
6405
|
const knownBuiltInIds = await loadKnownBuiltInIds();
|
|
5603
6406
|
if (!knownBuiltInIds.has(normalizedId)) {
|
|
5604
|
-
return { success: false, error: `Unknown
|
|
6407
|
+
return { success: false, error: `Unknown baseline rule id: ${id}` };
|
|
5605
6408
|
}
|
|
5606
6409
|
if (!rules.disabledBuiltInIds.includes(normalizedId)) {
|
|
5607
6410
|
rules.disabledBuiltInIds.push(normalizedId);
|
|
@@ -5629,7 +6432,7 @@ var MAX_PATTERN_LENGTH = 512;
|
|
|
5629
6432
|
function normalizeName3(value) {
|
|
5630
6433
|
return value.trim().toLowerCase();
|
|
5631
6434
|
}
|
|
5632
|
-
function
|
|
6435
|
+
function generatePlaceholder2(name) {
|
|
5633
6436
|
const sanitized = name.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
5634
6437
|
return `[${sanitized}_REDACTED]`;
|
|
5635
6438
|
}
|
|
@@ -5736,7 +6539,7 @@ async function addCustomRuleToConfig(wrapper, type, options) {
|
|
|
5736
6539
|
const rule2 = {
|
|
5737
6540
|
name: name.trim(),
|
|
5738
6541
|
pattern,
|
|
5739
|
-
placeholder: placeholder ??
|
|
6542
|
+
placeholder: placeholder ?? generatePlaceholder2(name),
|
|
5740
6543
|
enabled: true
|
|
5741
6544
|
};
|
|
5742
6545
|
customRules.secrets.push(rule2);
|
|
@@ -7228,9 +8031,10 @@ async function testCommand(input, _config, logger, wrapper) {
|
|
|
7228
8031
|
s.divider(24);
|
|
7229
8032
|
for (const match of matches) {
|
|
7230
8033
|
s.row(match.source.toUpperCase(), match.name);
|
|
7231
|
-
s.row("Redaction", match.placeholder);
|
|
8034
|
+
s.row("Redaction", match.placeholder || theme.dim("(dynamic placeholder)"));
|
|
7232
8035
|
s.divider(24);
|
|
7233
8036
|
}
|
|
8037
|
+
s.divider(24);
|
|
7234
8038
|
}
|
|
7235
8039
|
});
|
|
7236
8040
|
}
|
|
@@ -8885,7 +9689,7 @@ Examples:
|
|
|
8885
9689
|
})
|
|
8886
9690
|
);
|
|
8887
9691
|
attachSubcommandHelp(
|
|
8888
|
-
bshield.command("reset <target>").description("Reset defaults (
|
|
9692
|
+
bshield.command("reset <target>").description("Reset defaults (baseline or full scope)").option("--scope <scope>", "Reset scope (baseline | all)").option("--yes", "Skip confirmation prompt").action(async (target, options) => {
|
|
8889
9693
|
await resetCommand(target, options, context, wrapper);
|
|
8890
9694
|
})
|
|
8891
9695
|
);
|