@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/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: [...activeSecrets, ...customSecrets, ...activePII],
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
- function clearPatternCache() {
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
- id: "secret:aws-access-key",
622
- name: "AWS Access Key",
623
- category: "secret",
624
- pattern: /AKIA[0-9A-Z]{16}/g,
625
- placeholder: "[AWS_KEY_REDACTED]"
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
- INTERNAL_SENSITIVE_FILE_PATTERNS = [
799
- { id: "file:env", pattern: /\.env(?:\.|$)/i },
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
- SENSITIVE_FILE_PATTERNS = INTERNAL_SENSITIVE_FILE_PATTERNS.map((p) => p.pattern);
840
- INTERNAL_DESTRUCTIVE_COMMAND_PATTERNS = [
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.17";
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.RUNTIME_EVENT, "Berry.Root layer registered (Prompt Guard)");
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
- function redactString(text, patterns) {
2007
- const unescaped = unescapeString(text);
2008
- let redactionCount = 0;
2009
- const redactedTypes = [];
2010
- let currentResult = unescaped;
2011
- for (const pattern of patterns) {
2012
- pattern.pattern.lastIndex = 0;
2013
- const matches = currentResult.match(pattern.pattern);
2014
- if (matches && matches.length > 0) {
2015
- currentResult = currentResult.replace(pattern.pattern, pattern.placeholder);
2016
- redactionCount += matches.length;
2017
- if (!redactedTypes.includes(pattern.name)) {
2018
- redactedTypes.push(pattern.name);
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: redactionCount > 0 ? currentResult : text,
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
- for (const p of patterns) {
2124
- const regex = p instanceof RegExp ? p : p.pattern;
2125
- const name = p instanceof RegExp ? "Sensitive Pattern" : p.name;
2126
- regex.lastIndex = 0;
2127
- if (regex.test(unescaped)) {
2128
- matchedNames.add(name);
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.RUNTIME_EVENT, "Berry.Pulp layer registered (Output Scanner)");
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.RUNTIME_EVENT, "Berry.Thorn layer registered (Tool Blocker)");
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.name));
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.RUNTIME_EVENT, "Berry.Leaf layer registered (Input Audit)");
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 createHash2, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
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 createHash("sha256").update(JSON.stringify(toSignatureShape(intent))).digest("hex");
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 createHash2("sha256").update(`${operation}
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.RUNTIME_EVENT, "Berry.Stem layer registered (Security Gate)");
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.RUNTIME_EVENT, "Berry.Vine layer registered (External Content Guard)");
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
- // Upstream naming drift example: old short name -> current explicit name.
5394
- "secret:gitleaks:gitlab-runner-token": "secret:gitleaks:gitlab-runner-authentication-token"
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 built-in rule id: ${id}` };
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 generatePlaceholder(name) {
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 ?? generatePlaceholder(name),
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 (builtins or full scope)").option("--scope <scope>", "Reset scope (builtins | all)").option("--yes", "Skip confirmation prompt").action(async (target, options) => {
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
  );