hi-secure 1.0.0 → 1.0.3
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/adapters/ExpressRLAdapter.d.ts.map +1 -1
- package/dist/adapters/ExpressRLAdapter.js +0 -29
- package/dist/adapters/ExpressRLAdapter.js.map +1 -1
- package/dist/adapters/GoogleAdapter.d.ts.map +1 -1
- package/dist/adapters/GoogleAdapter.js +4 -3
- package/dist/adapters/GoogleAdapter.js.map +1 -1
- package/dist/adapters/JWTAdapter.d.ts.map +1 -1
- package/dist/adapters/JWTAdapter.js +3 -1
- package/dist/adapters/JWTAdapter.js.map +1 -1
- package/dist/core/HiSecure.d.ts +3 -18
- package/dist/core/HiSecure.d.ts.map +1 -1
- package/dist/core/HiSecure.js +29 -132
- package/dist/core/HiSecure.js.map +1 -1
- package/dist/core/errors/HttpError.d.ts +17 -0
- package/dist/core/errors/HttpError.d.ts.map +1 -0
- package/dist/core/errors/HttpError.js +36 -0
- package/dist/core/errors/HttpError.js.map +1 -0
- package/dist/core/useSecure.d.ts +0 -7
- package/dist/core/useSecure.d.ts.map +1 -1
- package/dist/core/useSecure.js +65 -21
- package/dist/core/useSecure.js.map +1 -1
- package/dist/index.d.ts +3 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -9
- package/dist/index.js.map +1 -1
- package/dist/managers/AuthManager.d.ts.map +1 -1
- package/dist/managers/AuthManager.js +18 -17
- package/dist/managers/AuthManager.js.map +1 -1
- package/dist/managers/ValidatorManager.d.ts +4 -6
- package/dist/managers/ValidatorManager.d.ts.map +1 -1
- package/dist/managers/ValidatorManager.js +97 -144
- package/dist/managers/ValidatorManager.js.map +1 -1
- package/dist/middlewares/errorHandler.js +2 -2
- package/dist/middlewares/errorHandler.js.map +1 -1
- package/dist/utils/normalizeOptions.d.ts.map +1 -1
- package/dist/utils/normalizeOptions.js +14 -4
- package/dist/utils/normalizeOptions.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +195 -0
- package/src/adapters/ExpressRLAdapter.ts +0 -38
- package/src/adapters/GoogleAdapter.ts +5 -3
- package/src/adapters/JWTAdapter.ts +3 -1
- package/src/core/HiSecure.ts +414 -175
- package/src/core/useSecure.ts +91 -36
- package/src/index.ts +28 -12
- package/src/managers/AuthManager.ts +15 -13
- package/src/managers/ValidatorManager.ts +120 -182
- package/src/middlewares/errorHandler.ts +1 -1
- package/src/utils/normalizeOptions.ts +24 -9
- /package/src/core/errors/{HttpErrror.ts → HttpError.ts} +0 -0
package/src/core/HiSecure.ts
CHANGED
|
@@ -595,7 +595,373 @@
|
|
|
595
595
|
// }
|
|
596
596
|
|
|
597
597
|
|
|
598
|
-
//
|
|
598
|
+
// =================
|
|
599
|
+
|
|
600
|
+
// // src/core/HiSecure.ts - COMPLETELY FIXED
|
|
601
|
+
// import { HiSecureConfig } from "./types/HiSecureConfig.js";
|
|
602
|
+
// import { defaultConfig } from "./config.js";
|
|
603
|
+
// import { LIB_NAME, LIB_VERSION } from "./constants.js";
|
|
604
|
+
// import { deepMerge } from "../utils/deepMerge.js";
|
|
605
|
+
// import { deepFreeze } from "../utils/deepFreeze.js";
|
|
606
|
+
// import { logger } from "../logging/index.js";
|
|
607
|
+
|
|
608
|
+
// // Adapters
|
|
609
|
+
// import { ArgonAdapter } from "../adapters/ArgonAdapter.js";
|
|
610
|
+
// import { BcryptAdapter } from "../adapters/BcryptAdapter.js";
|
|
611
|
+
// import { RLFlexibleAdapter } from "../adapters/RLFlexibleAdapter.js";
|
|
612
|
+
// import { ExpressRLAdapter } from "../adapters/ExpressRLAdapter.js";
|
|
613
|
+
// import { ZodAdapter } from "../adapters/ZodAdapter.js";
|
|
614
|
+
// import { ExpressValidatorAdapter } from "../adapters/ExpressValidatorAdapter.js";
|
|
615
|
+
// import { SanitizeHtmlAdapter } from "../adapters/SanitizeHtmlAdapter.js";
|
|
616
|
+
// import { XSSAdapter } from "../adapters/XSSAdapter.js";
|
|
617
|
+
|
|
618
|
+
// // Managers
|
|
619
|
+
// import { HashManager } from "../managers/HashManager.js";
|
|
620
|
+
// import { RateLimitManager } from "../managers/RateLimitManager.js";
|
|
621
|
+
// import { ValidatorManager } from "../managers/ValidatorManager.js";
|
|
622
|
+
// import { SanitizerManager } from "../managers/SanitizerManager.js";
|
|
623
|
+
// import { JsonManager } from "../managers/JsonManager.js";
|
|
624
|
+
// import { CorsManager } from "../managers/CorsManager.js";
|
|
625
|
+
// import { AuthManager } from "../managers/AuthManager.js";
|
|
626
|
+
|
|
627
|
+
// // Middlewares
|
|
628
|
+
// import helmet from "helmet";
|
|
629
|
+
// import hpp from "hpp";
|
|
630
|
+
// import compression from "compression";
|
|
631
|
+
// import { errorHandler } from "../middlewares/errorHandler.js";
|
|
632
|
+
|
|
633
|
+
// // Types
|
|
634
|
+
// import { SecureOptions, ValidationSchema, RateLimitOptions } from "./types/SecureOptions.js";
|
|
635
|
+
|
|
636
|
+
// export class HiSecure {
|
|
637
|
+
// private static instance: HiSecure | null = null;
|
|
638
|
+
// private config: HiSecureConfig;
|
|
639
|
+
// private initialized = false;
|
|
640
|
+
|
|
641
|
+
// // Managers
|
|
642
|
+
// public hashManager!: HashManager;
|
|
643
|
+
// public rateLimitManager!: RateLimitManager;
|
|
644
|
+
// public validatorManager!: ValidatorManager;
|
|
645
|
+
// public sanitizerManager!: SanitizerManager;
|
|
646
|
+
// public jsonManager!: JsonManager;
|
|
647
|
+
// public corsManager!: CorsManager;
|
|
648
|
+
// public authManager?: AuthManager;
|
|
649
|
+
|
|
650
|
+
// // Internal adapters
|
|
651
|
+
// private hashingPrimary: any;
|
|
652
|
+
// private hashingFallback: any;
|
|
653
|
+
// private rateLimiterPrimary: any;
|
|
654
|
+
// private rateLimiterFallback: any;
|
|
655
|
+
// private validatorPrimary: any;
|
|
656
|
+
// private validatorFallback: any;
|
|
657
|
+
// private sanitizerPrimary: any;
|
|
658
|
+
// private sanitizerFallback: any;
|
|
659
|
+
|
|
660
|
+
// // Private constructor for singleton
|
|
661
|
+
// private constructor(userConfig: Partial<HiSecureConfig> = {}) {
|
|
662
|
+
// this.config = deepMerge(defaultConfig, userConfig);
|
|
663
|
+
// }
|
|
664
|
+
|
|
665
|
+
// // =====================================================
|
|
666
|
+
// // SINGLETON & INITIALIZATION
|
|
667
|
+
// // =====================================================
|
|
668
|
+
|
|
669
|
+
// static getInstance(config?: Partial<HiSecureConfig>): HiSecure {
|
|
670
|
+
// if (!HiSecure.instance) {
|
|
671
|
+
// HiSecure.instance = new HiSecure(config);
|
|
672
|
+
// HiSecure.instance.init();
|
|
673
|
+
// }
|
|
674
|
+
// return HiSecure.instance;
|
|
675
|
+
// }
|
|
676
|
+
|
|
677
|
+
// static resetInstance(): void {
|
|
678
|
+
// HiSecure.instance = null;
|
|
679
|
+
// }
|
|
680
|
+
|
|
681
|
+
// init(): void {
|
|
682
|
+
// if (this.initialized) {
|
|
683
|
+
// logger.warn("⚠ HiSecure already initialized");
|
|
684
|
+
// return;
|
|
685
|
+
// }
|
|
686
|
+
|
|
687
|
+
// logger.info(`🔐 ${LIB_NAME} v${LIB_VERSION} initializing...`);
|
|
688
|
+
|
|
689
|
+
// this.setupAdapters();
|
|
690
|
+
// this.setupManagers();
|
|
691
|
+
// this.setupDynamicManagers();
|
|
692
|
+
|
|
693
|
+
// // Make everything immutable
|
|
694
|
+
// deepFreeze(this.config);
|
|
695
|
+
// deepFreeze(this.hashManager);
|
|
696
|
+
// deepFreeze(this.rateLimitManager);
|
|
697
|
+
// deepFreeze(this.validatorManager);
|
|
698
|
+
// deepFreeze(this.sanitizerManager);
|
|
699
|
+
// deepFreeze(this.jsonManager);
|
|
700
|
+
// deepFreeze(this.corsManager);
|
|
701
|
+
// if (this.authManager) deepFreeze(this.authManager);
|
|
702
|
+
|
|
703
|
+
// this.initialized = true;
|
|
704
|
+
// logger.info("✅ HiSecure initialized successfully");
|
|
705
|
+
// }
|
|
706
|
+
|
|
707
|
+
// isInitialized(): boolean {
|
|
708
|
+
// return this.initialized;
|
|
709
|
+
// }
|
|
710
|
+
|
|
711
|
+
// // =====================================================
|
|
712
|
+
// // FLUENT API METHODS (Route-level security)
|
|
713
|
+
// // =====================================================
|
|
714
|
+
|
|
715
|
+
// static auth(options?: { required?: boolean; roles?: string[] }) {
|
|
716
|
+
// const instance = this.getInstance();
|
|
717
|
+
// if (!instance.authManager) {
|
|
718
|
+
// throw new Error("Auth not enabled. Set auth.enabled=true in config.");
|
|
719
|
+
// }
|
|
720
|
+
// return instance.authManager.protect(options);
|
|
721
|
+
// }
|
|
722
|
+
|
|
723
|
+
// static validate(schema: ValidationSchema) {
|
|
724
|
+
// return this.getInstance().validatorManager.validate(schema);
|
|
725
|
+
// }
|
|
726
|
+
|
|
727
|
+
// static sanitize(options?: any) {
|
|
728
|
+
// return this.getInstance().sanitizerManager.middleware(options);
|
|
729
|
+
// }
|
|
730
|
+
|
|
731
|
+
// static rateLimit(preset: "strict" | "relaxed" | "api" | object) {
|
|
732
|
+
// const instance = this.getInstance();
|
|
733
|
+
|
|
734
|
+
// if (typeof preset === "string") {
|
|
735
|
+
// const presets: Record<string, { mode?: "strict" | "relaxed" | "api"; options?: any }> = {
|
|
736
|
+
// strict: { mode: "strict" },
|
|
737
|
+
// relaxed: { mode: "relaxed" },
|
|
738
|
+
// api: { mode: "api", options: { max: 100, windowMs: 60000 } }
|
|
739
|
+
// };
|
|
740
|
+
// return instance.rateLimitManager.middleware(presets[preset] || {});
|
|
741
|
+
// }
|
|
742
|
+
|
|
743
|
+
// return instance.rateLimitManager.middleware({ options: preset });
|
|
744
|
+
// }
|
|
745
|
+
|
|
746
|
+
// static cors(options?: any) {
|
|
747
|
+
// return this.getInstance().corsManager.middleware(options);
|
|
748
|
+
// }
|
|
749
|
+
|
|
750
|
+
// static json(options?: any) {
|
|
751
|
+
// const instance = this.getInstance();
|
|
752
|
+
// const chain = [];
|
|
753
|
+
// chain.push(instance.jsonManager.middleware(options));
|
|
754
|
+
// chain.push(instance.jsonManager.urlencoded());
|
|
755
|
+
// return chain;
|
|
756
|
+
// }
|
|
757
|
+
|
|
758
|
+
// // =====================================================
|
|
759
|
+
// // UTILITY METHODS (Direct usage)
|
|
760
|
+
// // =====================================================
|
|
761
|
+
|
|
762
|
+
// static async hash(password: string): Promise<string> {
|
|
763
|
+
// const instance = this.getInstance();
|
|
764
|
+
// const result = await instance.hashManager.hash(password, { allowFallback: true });
|
|
765
|
+
// return result.hash;
|
|
766
|
+
// }
|
|
767
|
+
|
|
768
|
+
// static async verify(password: string, hash: string): Promise<boolean> {
|
|
769
|
+
// return this.getInstance().hashManager.verify(password, hash);
|
|
770
|
+
// }
|
|
771
|
+
|
|
772
|
+
// static jwt = {
|
|
773
|
+
// sign: (payload: object, options?: any) => {
|
|
774
|
+
// const instance = HiSecure.getInstance();
|
|
775
|
+
// if (!instance.authManager) {
|
|
776
|
+
// throw new Error("Auth not enabled");
|
|
777
|
+
// }
|
|
778
|
+
// return instance.authManager.sign(payload, options);
|
|
779
|
+
// },
|
|
780
|
+
|
|
781
|
+
// verify: (token: string) => {
|
|
782
|
+
// const instance = HiSecure.getInstance();
|
|
783
|
+
// if (!instance.authManager) {
|
|
784
|
+
// throw new Error("Auth not enabled");
|
|
785
|
+
// }
|
|
786
|
+
// return instance.authManager.verify(token);
|
|
787
|
+
// },
|
|
788
|
+
|
|
789
|
+
// google: {
|
|
790
|
+
// verifyIdToken: (idToken: string) => {
|
|
791
|
+
// const instance = HiSecure.getInstance();
|
|
792
|
+
// if (!instance.authManager) {
|
|
793
|
+
// throw new Error("Auth not enabled");
|
|
794
|
+
// }
|
|
795
|
+
// return instance.authManager.verifyGoogleIdToken(idToken);
|
|
796
|
+
// }
|
|
797
|
+
// }
|
|
798
|
+
// };
|
|
799
|
+
|
|
800
|
+
// // =====================================================
|
|
801
|
+
// // GLOBAL MIDDLEWARE (app.use())
|
|
802
|
+
// // =====================================================
|
|
803
|
+
|
|
804
|
+
// static middleware(options?: SecureOptions | "api" | "strict" | "public") {
|
|
805
|
+
// const instance = this.getInstance();
|
|
806
|
+
|
|
807
|
+
// // Handle preset strings
|
|
808
|
+
// if (typeof options === "string") {
|
|
809
|
+
// const presets: Record<string, SecureOptions> = {
|
|
810
|
+
// api: { cors: true, rateLimit: "relaxed" as any, sanitize: true },
|
|
811
|
+
// strict: { cors: true, rateLimit: "strict" as any, sanitize: true, auth: true },
|
|
812
|
+
// public: { cors: true, rateLimit: true as any, sanitize: false }
|
|
813
|
+
// };
|
|
814
|
+
// const presetOptions = presets[options];
|
|
815
|
+
// if (presetOptions) {
|
|
816
|
+
// return instance.createMiddlewareChain(presetOptions);
|
|
817
|
+
// }
|
|
818
|
+
// return instance.createMiddlewareChain({});
|
|
819
|
+
// }
|
|
820
|
+
|
|
821
|
+
// return instance.createMiddlewareChain(options || {});
|
|
822
|
+
// }
|
|
823
|
+
|
|
824
|
+
// // =====================================================
|
|
825
|
+
// // INTERNAL METHODS
|
|
826
|
+
// // =====================================================
|
|
827
|
+
|
|
828
|
+
// private setupAdapters(): void {
|
|
829
|
+
// logger.info("🧩 Setting up adapters...");
|
|
830
|
+
|
|
831
|
+
// // Hashing
|
|
832
|
+
// this.hashingPrimary = this.config.hashing.primary === "argon2"
|
|
833
|
+
// ? new ArgonAdapter()
|
|
834
|
+
// : new BcryptAdapter(this.config.hashing.saltRounds);
|
|
835
|
+
|
|
836
|
+
// this.hashingFallback = this.config.hashing.fallback === "bcrypt"
|
|
837
|
+
// ? new BcryptAdapter(this.config.hashing.saltRounds)
|
|
838
|
+
// : null;
|
|
839
|
+
|
|
840
|
+
// // Rate limiting
|
|
841
|
+
// this.rateLimiterPrimary = this.config.rateLimiter.useAdaptiveMode
|
|
842
|
+
// ? new RLFlexibleAdapter()
|
|
843
|
+
// : new ExpressRLAdapter();
|
|
844
|
+
// this.rateLimiterFallback = new ExpressRLAdapter();
|
|
845
|
+
|
|
846
|
+
// // Validation
|
|
847
|
+
// this.validatorPrimary = this.config.validation.mode === "zod"
|
|
848
|
+
// ? new ZodAdapter()
|
|
849
|
+
// : new ExpressValidatorAdapter();
|
|
850
|
+
// this.validatorFallback = this.config.validation.fallback === "express-validator"
|
|
851
|
+
// ? new ExpressValidatorAdapter()
|
|
852
|
+
// : null;
|
|
853
|
+
|
|
854
|
+
// // Sanitization
|
|
855
|
+
// this.sanitizerPrimary = new SanitizeHtmlAdapter(this.config.sanitizer);
|
|
856
|
+
// this.sanitizerFallback = new XSSAdapter(this.config.sanitizer);
|
|
857
|
+
|
|
858
|
+
// logger.info("✅ Adapters ready");
|
|
859
|
+
// }
|
|
860
|
+
|
|
861
|
+
// private setupManagers(): void {
|
|
862
|
+
// this.hashManager = new HashManager(
|
|
863
|
+
// this.config.hashing,
|
|
864
|
+
// this.hashingPrimary,
|
|
865
|
+
// this.hashingFallback
|
|
866
|
+
// );
|
|
867
|
+
|
|
868
|
+
// this.rateLimitManager = new RateLimitManager(
|
|
869
|
+
// this.config.rateLimiter,
|
|
870
|
+
// this.rateLimiterPrimary,
|
|
871
|
+
// this.rateLimiterFallback
|
|
872
|
+
// );
|
|
873
|
+
|
|
874
|
+
// this.validatorManager = new ValidatorManager(
|
|
875
|
+
// // this.config.validation,
|
|
876
|
+
// // this.validatorPrimary,
|
|
877
|
+
// // this.validatorFallback
|
|
878
|
+
// new ZodAdapter(),
|
|
879
|
+
// new ExpressValidatorAdapter()
|
|
880
|
+
// );
|
|
881
|
+
|
|
882
|
+
// this.sanitizerManager = new SanitizerManager(
|
|
883
|
+
// this.sanitizerPrimary,
|
|
884
|
+
// this.sanitizerFallback
|
|
885
|
+
// );
|
|
886
|
+
// }
|
|
887
|
+
|
|
888
|
+
// private setupDynamicManagers(): void {
|
|
889
|
+
// this.jsonManager = new JsonManager();
|
|
890
|
+
// this.corsManager = new CorsManager();
|
|
891
|
+
|
|
892
|
+
// // Auth manager (only if enabled)
|
|
893
|
+
// if (this.config.auth.enabled) {
|
|
894
|
+
// const jwtSecret = process.env.JWT_SECRET || this.config.auth.jwtSecret;
|
|
895
|
+
// if (!jwtSecret) {
|
|
896
|
+
// throw new Error("JWT_SECRET environment variable or jwtSecret in config is required when auth.enabled=true");
|
|
897
|
+
// }
|
|
898
|
+
|
|
899
|
+
// this.authManager = new AuthManager({
|
|
900
|
+
// jwtSecret,
|
|
901
|
+
// jwtExpiresIn: this.config.auth.jwtExpiresIn,
|
|
902
|
+
// googleClientId: process.env.GOOGLE_CLIENT_ID || this.config.auth.googleClientId
|
|
903
|
+
// // Removed algorithm - handled in AuthManager
|
|
904
|
+
// });
|
|
905
|
+
// }
|
|
906
|
+
// }
|
|
907
|
+
|
|
908
|
+
// private createMiddlewareChain(options: SecureOptions): any[] {
|
|
909
|
+
// const chain: any[] = [];
|
|
910
|
+
|
|
911
|
+
// // JSON parsing
|
|
912
|
+
// chain.push(this.jsonManager.middleware(this.config.json));
|
|
913
|
+
// chain.push(this.jsonManager.urlencoded(this.config.urlencoded));
|
|
914
|
+
|
|
915
|
+
// // Security headers
|
|
916
|
+
// if (this.config.enableHelmet) chain.push(helmet());
|
|
917
|
+
// if (this.config.enableHPP) chain.push(hpp());
|
|
918
|
+
|
|
919
|
+
// // Compression (check if compression config exists)
|
|
920
|
+
// if (this.config.enableCompression && this.config.compression) {
|
|
921
|
+
// chain.push(compression(this.config.compression));
|
|
922
|
+
// } else if (this.config.enableCompression) {
|
|
923
|
+
// chain.push(compression()); // Use defaults
|
|
924
|
+
// }
|
|
925
|
+
|
|
926
|
+
// // CORS
|
|
927
|
+
// if (this.config.enableCORS || options.cors) {
|
|
928
|
+
// const corsOptions = options.cors === true ? this.config.cors :
|
|
929
|
+
// (typeof options.cors === 'object' ? options.cors : this.config.cors);
|
|
930
|
+
// chain.push(this.corsManager.middleware(corsOptions));
|
|
931
|
+
// }
|
|
932
|
+
|
|
933
|
+
// // Sanitization
|
|
934
|
+
// if (this.config.enableSanitizer || options.sanitize) {
|
|
935
|
+
// const sanitizeOptions = options.sanitize === true ? undefined :
|
|
936
|
+
// (typeof options.sanitize === 'object' ? options.sanitize : undefined);
|
|
937
|
+
// chain.push(this.sanitizerManager.middleware(sanitizeOptions));
|
|
938
|
+
// }
|
|
939
|
+
|
|
940
|
+
// // Rate limiting
|
|
941
|
+
// if (this.config.enableRateLimiter || options.rateLimit) {
|
|
942
|
+
// const rateLimitOpts = typeof options.rateLimit === 'object' ?
|
|
943
|
+
// { options: options.rateLimit } : {};
|
|
944
|
+
// chain.push(this.rateLimitManager.middleware(rateLimitOpts));
|
|
945
|
+
// }
|
|
946
|
+
|
|
947
|
+
// // Authentication
|
|
948
|
+
// if (options.auth && this.authManager) {
|
|
949
|
+
// const authOpts = options.auth === true ? undefined :
|
|
950
|
+
// (typeof options.auth === 'object' ? options.auth : undefined);
|
|
951
|
+
// chain.push(this.authManager.protect(authOpts));
|
|
952
|
+
// }
|
|
953
|
+
|
|
954
|
+
// // Error handler (always last)
|
|
955
|
+
// chain.push(errorHandler);
|
|
956
|
+
|
|
957
|
+
// return chain;
|
|
958
|
+
// }
|
|
959
|
+
// }
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
// ===================
|
|
963
|
+
|
|
964
|
+
// src/core/HiSecure.ts - FINAL VERSION
|
|
599
965
|
import { HiSecureConfig } from "./types/HiSecureConfig.js";
|
|
600
966
|
import { defaultConfig } from "./config.js";
|
|
601
967
|
import { LIB_NAME, LIB_VERSION } from "./constants.js";
|
|
@@ -629,7 +995,7 @@ import compression from "compression";
|
|
|
629
995
|
import { errorHandler } from "../middlewares/errorHandler.js";
|
|
630
996
|
|
|
631
997
|
// Types
|
|
632
|
-
import { SecureOptions, ValidationSchema
|
|
998
|
+
import { SecureOptions, ValidationSchema } from "./types/SecureOptions.js";
|
|
633
999
|
|
|
634
1000
|
export class HiSecure {
|
|
635
1001
|
private static instance: HiSecure | null = null;
|
|
@@ -650,20 +1016,14 @@ export class HiSecure {
|
|
|
650
1016
|
private hashingFallback: any;
|
|
651
1017
|
private rateLimiterPrimary: any;
|
|
652
1018
|
private rateLimiterFallback: any;
|
|
653
|
-
private validatorPrimary: any;
|
|
654
|
-
private validatorFallback: any;
|
|
655
|
-
private sanitizerPrimary: any;
|
|
656
|
-
private sanitizerFallback: any;
|
|
657
1019
|
|
|
658
|
-
// Private constructor for singleton
|
|
659
1020
|
private constructor(userConfig: Partial<HiSecureConfig> = {}) {
|
|
660
1021
|
this.config = deepMerge(defaultConfig, userConfig);
|
|
661
1022
|
}
|
|
662
1023
|
|
|
663
1024
|
// =====================================================
|
|
664
|
-
// SINGLETON
|
|
1025
|
+
// SINGLETON
|
|
665
1026
|
// =====================================================
|
|
666
|
-
|
|
667
1027
|
static getInstance(config?: Partial<HiSecureConfig>): HiSecure {
|
|
668
1028
|
if (!HiSecure.instance) {
|
|
669
1029
|
HiSecure.instance = new HiSecure(config);
|
|
@@ -677,10 +1037,7 @@ export class HiSecure {
|
|
|
677
1037
|
}
|
|
678
1038
|
|
|
679
1039
|
init(): void {
|
|
680
|
-
if (this.initialized)
|
|
681
|
-
logger.warn("⚠ HiSecure already initialized");
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
1040
|
+
if (this.initialized) return;
|
|
684
1041
|
|
|
685
1042
|
logger.info(`🔐 ${LIB_NAME} v${LIB_VERSION} initializing...`);
|
|
686
1043
|
|
|
@@ -688,34 +1045,20 @@ export class HiSecure {
|
|
|
688
1045
|
this.setupManagers();
|
|
689
1046
|
this.setupDynamicManagers();
|
|
690
1047
|
|
|
691
|
-
//
|
|
1048
|
+
// ❌ DO NOT FREEZE MANAGERS
|
|
1049
|
+
// ✔ Only freeze config
|
|
692
1050
|
deepFreeze(this.config);
|
|
693
|
-
deepFreeze(this.hashManager);
|
|
694
|
-
deepFreeze(this.rateLimitManager);
|
|
695
|
-
deepFreeze(this.validatorManager);
|
|
696
|
-
deepFreeze(this.sanitizerManager);
|
|
697
|
-
deepFreeze(this.jsonManager);
|
|
698
|
-
deepFreeze(this.corsManager);
|
|
699
|
-
if (this.authManager) deepFreeze(this.authManager);
|
|
700
1051
|
|
|
701
1052
|
this.initialized = true;
|
|
702
1053
|
logger.info("✅ HiSecure initialized successfully");
|
|
703
1054
|
}
|
|
704
1055
|
|
|
705
|
-
isInitialized(): boolean {
|
|
706
|
-
return this.initialized;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
1056
|
// =====================================================
|
|
710
|
-
// FLUENT API
|
|
1057
|
+
// FLUENT API (Route-level)
|
|
711
1058
|
// =====================================================
|
|
712
|
-
|
|
1059
|
+
|
|
713
1060
|
static auth(options?: { required?: boolean; roles?: string[] }) {
|
|
714
|
-
|
|
715
|
-
if (!instance.authManager) {
|
|
716
|
-
throw new Error("Auth not enabled. Set auth.enabled=true in config.");
|
|
717
|
-
}
|
|
718
|
-
return instance.authManager.protect(options);
|
|
1061
|
+
return this.getInstance().authManager!.protect(options);
|
|
719
1062
|
}
|
|
720
1063
|
|
|
721
1064
|
static validate(schema: ValidationSchema) {
|
|
@@ -726,107 +1069,39 @@ export class HiSecure {
|
|
|
726
1069
|
return this.getInstance().sanitizerManager.middleware(options);
|
|
727
1070
|
}
|
|
728
1071
|
|
|
1072
|
+
static cors(options?: any) {
|
|
1073
|
+
return this.getInstance().corsManager.middleware(options);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
729
1076
|
static rateLimit(preset: "strict" | "relaxed" | "api" | object) {
|
|
730
1077
|
const instance = this.getInstance();
|
|
731
|
-
|
|
1078
|
+
|
|
732
1079
|
if (typeof preset === "string") {
|
|
733
|
-
const presets:
|
|
1080
|
+
const presets: any = {
|
|
734
1081
|
strict: { mode: "strict" },
|
|
735
1082
|
relaxed: { mode: "relaxed" },
|
|
736
1083
|
api: { mode: "api", options: { max: 100, windowMs: 60000 } }
|
|
737
1084
|
};
|
|
738
1085
|
return instance.rateLimitManager.middleware(presets[preset] || {});
|
|
739
1086
|
}
|
|
740
|
-
|
|
741
1087
|
return instance.rateLimitManager.middleware({ options: preset });
|
|
742
1088
|
}
|
|
743
1089
|
|
|
744
|
-
static cors(options?: any) {
|
|
745
|
-
return this.getInstance().corsManager.middleware(options);
|
|
746
|
-
}
|
|
747
|
-
|
|
748
1090
|
static json(options?: any) {
|
|
749
1091
|
const instance = this.getInstance();
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
1092
|
+
return [
|
|
1093
|
+
instance.jsonManager.middleware(options),
|
|
1094
|
+
instance.jsonManager.urlencoded()
|
|
1095
|
+
];
|
|
754
1096
|
}
|
|
755
1097
|
|
|
756
1098
|
// =====================================================
|
|
757
|
-
//
|
|
1099
|
+
// INTERNAL SETUP
|
|
758
1100
|
// =====================================================
|
|
759
|
-
|
|
760
|
-
static async hash(password: string): Promise<string> {
|
|
761
|
-
const instance = this.getInstance();
|
|
762
|
-
const result = await instance.hashManager.hash(password, { allowFallback: true });
|
|
763
|
-
return result.hash;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
static async verify(password: string, hash: string): Promise<boolean> {
|
|
767
|
-
return this.getInstance().hashManager.verify(password, hash);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
static jwt = {
|
|
771
|
-
sign: (payload: object, options?: any) => {
|
|
772
|
-
const instance = HiSecure.getInstance();
|
|
773
|
-
if (!instance.authManager) {
|
|
774
|
-
throw new Error("Auth not enabled");
|
|
775
|
-
}
|
|
776
|
-
return instance.authManager.sign(payload, options);
|
|
777
|
-
},
|
|
778
|
-
|
|
779
|
-
verify: (token: string) => {
|
|
780
|
-
const instance = HiSecure.getInstance();
|
|
781
|
-
if (!instance.authManager) {
|
|
782
|
-
throw new Error("Auth not enabled");
|
|
783
|
-
}
|
|
784
|
-
return instance.authManager.verify(token);
|
|
785
|
-
},
|
|
786
|
-
|
|
787
|
-
google: {
|
|
788
|
-
verifyIdToken: (idToken: string) => {
|
|
789
|
-
const instance = HiSecure.getInstance();
|
|
790
|
-
if (!instance.authManager) {
|
|
791
|
-
throw new Error("Auth not enabled");
|
|
792
|
-
}
|
|
793
|
-
return instance.authManager.verifyGoogleIdToken(idToken);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
1101
|
|
|
798
|
-
// =====================================================
|
|
799
|
-
// GLOBAL MIDDLEWARE (app.use())
|
|
800
|
-
// =====================================================
|
|
801
|
-
|
|
802
|
-
static middleware(options?: SecureOptions | "api" | "strict" | "public") {
|
|
803
|
-
const instance = this.getInstance();
|
|
804
|
-
|
|
805
|
-
// Handle preset strings
|
|
806
|
-
if (typeof options === "string") {
|
|
807
|
-
const presets: Record<string, SecureOptions> = {
|
|
808
|
-
api: { cors: true, rateLimit: "relaxed" as any, sanitize: true },
|
|
809
|
-
strict: { cors: true, rateLimit: "strict" as any, sanitize: true, auth: true },
|
|
810
|
-
public: { cors: true, rateLimit: true as any, sanitize: false }
|
|
811
|
-
};
|
|
812
|
-
const presetOptions = presets[options];
|
|
813
|
-
if (presetOptions) {
|
|
814
|
-
return instance.createMiddlewareChain(presetOptions);
|
|
815
|
-
}
|
|
816
|
-
return instance.createMiddlewareChain({});
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
return instance.createMiddlewareChain(options || {});
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// =====================================================
|
|
823
|
-
// INTERNAL METHODS
|
|
824
|
-
// =====================================================
|
|
825
|
-
|
|
826
1102
|
private setupAdapters(): void {
|
|
827
1103
|
logger.info("🧩 Setting up adapters...");
|
|
828
1104
|
|
|
829
|
-
// Hashing
|
|
830
1105
|
this.hashingPrimary = this.config.hashing.primary === "argon2"
|
|
831
1106
|
? new ArgonAdapter()
|
|
832
1107
|
: new BcryptAdapter(this.config.hashing.saltRounds);
|
|
@@ -835,33 +1110,17 @@ export class HiSecure {
|
|
|
835
1110
|
? new BcryptAdapter(this.config.hashing.saltRounds)
|
|
836
1111
|
: null;
|
|
837
1112
|
|
|
838
|
-
// Rate limiting
|
|
839
1113
|
this.rateLimiterPrimary = this.config.rateLimiter.useAdaptiveMode
|
|
840
1114
|
? new RLFlexibleAdapter()
|
|
841
1115
|
: new ExpressRLAdapter();
|
|
842
|
-
this.rateLimiterFallback = new ExpressRLAdapter();
|
|
843
|
-
|
|
844
|
-
// Validation
|
|
845
|
-
this.validatorPrimary = this.config.validation.mode === "zod"
|
|
846
|
-
? new ZodAdapter()
|
|
847
|
-
: new ExpressValidatorAdapter();
|
|
848
|
-
this.validatorFallback = this.config.validation.fallback === "express-validator"
|
|
849
|
-
? new ExpressValidatorAdapter()
|
|
850
|
-
: null;
|
|
851
1116
|
|
|
852
|
-
|
|
853
|
-
this.sanitizerPrimary = new SanitizeHtmlAdapter(this.config.sanitizer);
|
|
854
|
-
this.sanitizerFallback = new XSSAdapter(this.config.sanitizer);
|
|
1117
|
+
this.rateLimiterFallback = new ExpressRLAdapter();
|
|
855
1118
|
|
|
856
1119
|
logger.info("✅ Adapters ready");
|
|
857
1120
|
}
|
|
858
1121
|
|
|
859
1122
|
private setupManagers(): void {
|
|
860
|
-
this.hashManager = new HashManager(
|
|
861
|
-
this.config.hashing,
|
|
862
|
-
this.hashingPrimary,
|
|
863
|
-
this.hashingFallback
|
|
864
|
-
);
|
|
1123
|
+
this.hashManager = new HashManager(this.config.hashing, this.hashingPrimary, this.hashingFallback);
|
|
865
1124
|
|
|
866
1125
|
this.rateLimitManager = new RateLimitManager(
|
|
867
1126
|
this.config.rateLimiter,
|
|
@@ -869,15 +1128,15 @@ export class HiSecure {
|
|
|
869
1128
|
this.rateLimiterFallback
|
|
870
1129
|
);
|
|
871
1130
|
|
|
1131
|
+
// ✔ AUTO-DETECT VALIDATION (ZOD + EXPRESS-VALIDATOR)
|
|
872
1132
|
this.validatorManager = new ValidatorManager(
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
this.validatorFallback
|
|
1133
|
+
new ZodAdapter(),
|
|
1134
|
+
new ExpressValidatorAdapter()
|
|
876
1135
|
);
|
|
877
1136
|
|
|
878
1137
|
this.sanitizerManager = new SanitizerManager(
|
|
879
|
-
this.
|
|
880
|
-
this.
|
|
1138
|
+
new SanitizeHtmlAdapter(this.config.sanitizer),
|
|
1139
|
+
new XSSAdapter(this.config.sanitizer)
|
|
881
1140
|
);
|
|
882
1141
|
}
|
|
883
1142
|
|
|
@@ -885,71 +1144,51 @@ export class HiSecure {
|
|
|
885
1144
|
this.jsonManager = new JsonManager();
|
|
886
1145
|
this.corsManager = new CorsManager();
|
|
887
1146
|
|
|
888
|
-
// Auth manager (only if enabled)
|
|
889
1147
|
if (this.config.auth.enabled) {
|
|
890
1148
|
const jwtSecret = process.env.JWT_SECRET || this.config.auth.jwtSecret;
|
|
891
|
-
if (!jwtSecret)
|
|
892
|
-
throw new Error("JWT_SECRET environment variable or jwtSecret in config is required when auth.enabled=true");
|
|
893
|
-
}
|
|
1149
|
+
if (!jwtSecret) throw new Error("JWT_SECRET is required when auth.enabled=true");
|
|
894
1150
|
|
|
895
1151
|
this.authManager = new AuthManager({
|
|
896
1152
|
jwtSecret,
|
|
897
1153
|
jwtExpiresIn: this.config.auth.jwtExpiresIn,
|
|
898
|
-
googleClientId:
|
|
899
|
-
// Removed algorithm - handled in AuthManager
|
|
1154
|
+
googleClientId: this.config.auth.googleClientId
|
|
900
1155
|
});
|
|
901
1156
|
}
|
|
902
1157
|
}
|
|
903
1158
|
|
|
904
1159
|
private createMiddlewareChain(options: SecureOptions): any[] {
|
|
905
1160
|
const chain: any[] = [];
|
|
906
|
-
|
|
907
|
-
// JSON parsing
|
|
1161
|
+
|
|
908
1162
|
chain.push(this.jsonManager.middleware(this.config.json));
|
|
909
1163
|
chain.push(this.jsonManager.urlencoded(this.config.urlencoded));
|
|
910
|
-
|
|
911
|
-
// Security headers
|
|
1164
|
+
|
|
912
1165
|
if (this.config.enableHelmet) chain.push(helmet());
|
|
913
1166
|
if (this.config.enableHPP) chain.push(hpp());
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
chain.push(compression(this.config.compression));
|
|
918
|
-
} else if (this.config.enableCompression) {
|
|
919
|
-
chain.push(compression()); // Use defaults
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// CORS
|
|
1167
|
+
|
|
1168
|
+
if (this.config.enableCompression) chain.push(compression(this.config.compression));
|
|
1169
|
+
|
|
923
1170
|
if (this.config.enableCORS || options.cors) {
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
chain.push(this.corsManager.middleware(corsOptions));
|
|
1171
|
+
const opts = typeof options.cors === "object" ? options.cors : this.config.cors;
|
|
1172
|
+
chain.push(this.corsManager.middleware(opts));
|
|
927
1173
|
}
|
|
928
|
-
|
|
929
|
-
// Sanitization
|
|
1174
|
+
|
|
930
1175
|
if (this.config.enableSanitizer || options.sanitize) {
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
chain.push(this.sanitizerManager.middleware(sanitizeOptions));
|
|
1176
|
+
const opts = typeof options.sanitize === "object" ? options.sanitize : undefined;
|
|
1177
|
+
chain.push(this.sanitizerManager.middleware(opts));
|
|
934
1178
|
}
|
|
935
|
-
|
|
936
|
-
// Rate limiting
|
|
1179
|
+
|
|
937
1180
|
if (this.config.enableRateLimiter || options.rateLimit) {
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
chain.push(this.rateLimitManager.middleware(rateLimitOpts));
|
|
1181
|
+
const opts = typeof options.rateLimit === "object" ? { options: options.rateLimit } : {};
|
|
1182
|
+
chain.push(this.rateLimitManager.middleware(opts));
|
|
941
1183
|
}
|
|
942
|
-
|
|
943
|
-
// Authentication
|
|
1184
|
+
|
|
944
1185
|
if (options.auth && this.authManager) {
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
chain.push(this.authManager.protect(authOpts));
|
|
1186
|
+
const opts = typeof options.auth === "object" ? options.auth : undefined;
|
|
1187
|
+
chain.push(this.authManager.protect(opts));
|
|
948
1188
|
}
|
|
949
|
-
|
|
950
|
-
// Error handler (always last)
|
|
1189
|
+
|
|
951
1190
|
chain.push(errorHandler);
|
|
952
|
-
|
|
1191
|
+
|
|
953
1192
|
return chain;
|
|
954
1193
|
}
|
|
955
|
-
}
|
|
1194
|
+
}
|