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