diff --git a/eslint.config.mjs b/eslint.config.mjs index 0f2e41f4..4100420b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -9,7 +9,6 @@ export default tseslint.config( }, { rules: { - "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-extraneous-class": ["error", { allowWithDecorator: true }] diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index be3c7b3a..1a4e707b 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -72,9 +72,9 @@ export class AuthGuard implements CanActivate { ); try { - return await this.authService.validateToken(token, processorType); + return !!(await this.authService.validateToken(token, processorType)); } catch { - return this.authService.validateToken(token, JwtProcessorType.BEARER); + return !!(await this.authService.validateToken(token, JwtProcessorType.BEARER)); } } diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts index 0b6b5d19..002d3b7f 100644 --- a/src/auth/auth.service.spec.ts +++ b/src/auth/auth.service.spec.ts @@ -30,6 +30,7 @@ describe('AuthService', () => { AuthService, { provide: EntityManager, + // eslint-disable-next-line @typescript-eslint/no-explicit-any useFactory: () => new (EntityManager as any)(), }, { diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 5ae1688d..9ac7773f 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -121,7 +121,7 @@ export class AuthService { ); } - validateToken(token: string, processor: JwtProcessorType): Promise { + validateToken(token: string, processor: JwtProcessorType): Promise { return this.processors.get(processor).validateToken(token); } diff --git a/src/auth/jwt/jwt.header.ts b/src/auth/jwt/jwt.header.ts index 2c251593..4c176de0 100644 --- a/src/auth/jwt/jwt.header.ts +++ b/src/auth/jwt/jwt.header.ts @@ -1,7 +1,9 @@ +import type { JWK } from 'jose'; + export class JwtHeader { alg: 'HS256' | 'HS384' | 'HS512' | 'RS256' | 'none'; jku?: string; - jwk?: any; + jwk?: JWK; kid?: string; x5u?: string; x5c?: string[]; diff --git a/src/auth/jwt/jwt.token.bearer.processor.ts b/src/auth/jwt/jwt.token.bearer.processor.ts index 045eded8..f23331b2 100644 --- a/src/auth/jwt/jwt.token.bearer.processor.ts +++ b/src/auth/jwt/jwt.token.bearer.processor.ts @@ -12,7 +12,7 @@ export class JwtBearerTokenProcessor extends JwtTokenProcessor { super(new Logger(JwtBearerTokenProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { const [header, payload] = this.parse(token); if (!header || !payload) { this.log.debug(`Invalid JWT token. parse() failure.`); @@ -56,7 +56,7 @@ export class JwtBearerTokenProcessor extends JwtTokenProcessor { } } - private async decodeAndVerifyToken(token: string, kid: string): Promise { + private async decodeAndVerifyToken(token: string, kid: string): Promise { try { return await this.keyCloakService.verifyToken(token, kid); } catch (e) { diff --git a/src/auth/jwt/jwt.token.processor.ts b/src/auth/jwt/jwt.token.processor.ts index 81728178..d69f129f 100644 --- a/src/auth/jwt/jwt.token.processor.ts +++ b/src/auth/jwt/jwt.token.processor.ts @@ -10,7 +10,7 @@ export abstract class JwtTokenProcessor { this.log = log; } - protected parse(token: string): [header: JwtHeader, payload: any] { + protected parse(token: string): [header: JwtHeader, payload: unknown] { this.log.debug('Call parse'); const parts = token.split('.'); @@ -23,7 +23,7 @@ export abstract class JwtTokenProcessor { const payloadStr = Buffer.from(parts[1], 'base64').toString('ascii'); this.log.debug(`Jwt token (None alg) payload is ${payloadStr}`); - const payload: any = JSON.parse(payloadStr); + const payload = JSON.parse(payloadStr); return [header, payload]; } @@ -50,7 +50,7 @@ export abstract class JwtTokenProcessor { return key; } - abstract validateToken(token: string): Promise; + abstract validateToken(token: string): Promise; abstract createToken(payload: unknown): Promise; } diff --git a/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts b/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts index 72f5abe3..444771e1 100644 --- a/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts +++ b/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts @@ -7,7 +7,7 @@ export class JwtTokenWithHMACKeysProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithHMACKeysProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); return decode(token, this.publicKey, false, 'HS256'); diff --git a/src/auth/jwt/jwt.token.with.jku.processor.ts b/src/auth/jwt/jwt.token.with.jku.processor.ts index c9d35a6c..c98f0b14 100644 --- a/src/auth/jwt/jwt.token.with.jku.processor.ts +++ b/src/auth/jwt/jwt.token.with.jku.processor.ts @@ -12,7 +12,7 @@ export class JwtTokenWithJKUProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithJKUProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); const [header, payload] = this.parse(token); diff --git a/src/auth/jwt/jwt.token.with.jwk.processor.ts b/src/auth/jwt/jwt.token.with.jwk.processor.ts index b01ffb91..a9a4eb2b 100644 --- a/src/auth/jwt/jwt.token.with.jwk.processor.ts +++ b/src/auth/jwt/jwt.token.with.jwk.processor.ts @@ -7,7 +7,7 @@ export class JwtTokenWithJWKProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithJWKProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); const [header, payload] = this.parse(token); diff --git a/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts b/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts index 97024cf7..28f47d07 100644 --- a/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts +++ b/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts @@ -7,7 +7,7 @@ export class JwtTokenWithRSAKeysProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithRSAKeysProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); const [header, payload] = this.parse(token); diff --git a/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts b/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts index c390e245..c9fc0bf5 100644 --- a/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts +++ b/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts @@ -7,7 +7,7 @@ export class JwtTokenWithRSASignatureKeysProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithRSASignatureKeysProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); return decode(token, this.publicKey, true, 'RS256'); diff --git a/src/auth/jwt/jwt.token.with.sql.kid.processor.ts b/src/auth/jwt/jwt.token.with.sql.kid.processor.ts index ef893e31..71b843d1 100644 --- a/src/auth/jwt/jwt.token.with.sql.kid.processor.ts +++ b/src/auth/jwt/jwt.token.with.sql.kid.processor.ts @@ -13,7 +13,7 @@ export class JwtTokenWithSqlKIDProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithSqlKIDProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); const [header] = this.parse(token); diff --git a/src/auth/jwt/jwt.token.with.weak.key.processor.ts b/src/auth/jwt/jwt.token.with.weak.key.processor.ts index 6c24bfb8..30ae4689 100644 --- a/src/auth/jwt/jwt.token.with.weak.key.processor.ts +++ b/src/auth/jwt/jwt.token.with.weak.key.processor.ts @@ -7,7 +7,7 @@ export class JwtTokenWithWeakKeyProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithWeakKeyProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); return decode(token, this.key, false); } diff --git a/src/auth/jwt/jwt.token.with.x5c.key.processor.ts b/src/auth/jwt/jwt.token.with.x5c.key.processor.ts index c5090c5b..38b21dd0 100644 --- a/src/auth/jwt/jwt.token.with.x5c.key.processor.ts +++ b/src/auth/jwt/jwt.token.with.x5c.key.processor.ts @@ -7,7 +7,7 @@ export class JwtTokenWithX5CKeyProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithX5CKeyProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); const [header] = this.parse(token); diff --git a/src/auth/jwt/jwt.token.with.x5u.key.processor.ts b/src/auth/jwt/jwt.token.with.x5u.key.processor.ts index 90c30c28..30c6568d 100644 --- a/src/auth/jwt/jwt.token.with.x5u.key.processor.ts +++ b/src/auth/jwt/jwt.token.with.x5u.key.processor.ts @@ -11,7 +11,7 @@ export class JwtTokenWithX5UKeyProcessor extends JwtTokenProcessor { super(new Logger(JwtTokenWithX5UKeyProcessor.name)); } - async validateToken(token: string): Promise { + async validateToken(token: string): Promise { this.log.debug('Call validateToken'); const [header] = this.parse(token); diff --git a/src/components/any-files.interceptor.ts b/src/components/any-files.interceptor.ts index 6a014a2d..4c700b0f 100644 --- a/src/components/any-files.interceptor.ts +++ b/src/components/any-files.interceptor.ts @@ -12,8 +12,8 @@ import { FastifyReply, FastifyRequest } from 'fastify'; export class AnyFilesInterceptor implements NestInterceptor { public async intercept( context: ExecutionContext, - next: CallHandler, - ): Promise> { + next: CallHandler, + ): Promise> { const req = context.switchToHttp().getRequest() as FastifyRequest; const res = context.switchToHttp().getResponse() as FastifyReply; diff --git a/src/components/headers.configurator.interceptor.ts b/src/components/headers.configurator.interceptor.ts index 9be12948..00cb78ce 100644 --- a/src/components/headers.configurator.interceptor.ts +++ b/src/components/headers.configurator.interceptor.ts @@ -25,7 +25,7 @@ export class HeadersConfiguratorInterceptor implements NestInterceptor { public static readonly COUNTER_COOKIE_NAME = 'bc-calls-counter'; private readonly logger = new Logger(HeadersConfiguratorInterceptor.name); - intercept(context: ExecutionContext, next: CallHandler): Observable { + intercept(context: ExecutionContext, next: CallHandler): Observable { const req = this.getRequest(context); const cookies: string[] = req.headers.cookie diff --git a/src/email/email.controller.ts b/src/email/email.controller.ts index afcbc787..6a55e94b 100644 --- a/src/email/email.controller.ts +++ b/src/email/email.controller.ts @@ -84,9 +84,9 @@ export class EmailController { this.logger.debug(`Raw query ${rawQuery}`); // "Use" the status code - const uriParams: any = splitUriIntoParamsPPVulnerable(rawQuery); + const uriParams = splitUriIntoParamsPPVulnerable(rawQuery); if (uriParams?.status) { - responseJson.status = uriParams.status; + responseJson.status = uriParams.status as HttpStatus; } const mailSubject = `Support email regarding "${subject}"`; @@ -118,8 +118,8 @@ export class EmailController { example: 'true', required: true, }) - async getEmails(@Query('withSource') withSource: any) { - withSource = withSource === 'true'; + async getEmails(@Query('withSource') withSourceStr: string) { + const withSource = withSourceStr === 'true'; this.logger.log(`Getting Emails (withSource=${withSource})`); return await this.emailService.getEmails(withSource); diff --git a/src/email/email.service.ts b/src/email/email.service.ts index b3e99933..5986de0f 100644 --- a/src/email/email.service.ts +++ b/src/email/email.service.ts @@ -60,11 +60,11 @@ export class EmailService { to = to.replace('\n', '%0A'); this.logger.debug(`Creating vulnerable mailOptions. "to" param is: ${to}`); - let parsedSubject: any = subject; - let parsedFrom: any = from; - let parsedTo: any = to; - let parsedCc: any = []; - let parsedBcc: any = []; + let parsedSubject: string | RegExpExecArray | null = subject; + let parsedFrom: string | RegExpExecArray | null = from; + let parsedTo: string | RegExpExecArray | null = to; + let parsedCc: string | RegExpExecArray | null = null; + let parsedBcc: string | RegExpExecArray | null = null; // This is intentional to support email injection if ( @@ -124,7 +124,7 @@ export class EmailService { return mailOptions; } - async getEmails(withSource): Promise { + async getEmails(withSource): Promise { this.logger.debug(`Fetching all emails from MailCatcher`); const emails = await axios diff --git a/src/httpclient/httpclient.service.ts b/src/httpclient/httpclient.service.ts index c204e7cd..afe5f4dc 100644 --- a/src/httpclient/httpclient.service.ts +++ b/src/httpclient/httpclient.service.ts @@ -22,7 +22,7 @@ export class HttpClientService { async post( url: string, - data: any, + data: unknown, config?: AxiosRequestConfig, ): Promise { const resp = await axios.post(url, data, config); diff --git a/src/utils/url.ts b/src/utils/url.ts index 70a17133..6b502e8d 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -1,11 +1,11 @@ // Taken from PortSwigger's prototype pollution labs // VULNERABLE TO PROTOTYPE POLLUTION! -const splitUriIntoParamsPPVulnerable = (params, coerce = undefined) => { +const splitUriIntoParamsPPVulnerable = (params, coerce = undefined): Record => { if (params.charAt(0) === '?') { params = params.substring(1); } - const obj = {}; + const obj: Record = {}; const coerce_types = { true: !0, false: !1, null: null }; if (!params) { @@ -21,6 +21,7 @@ const splitUriIntoParamsPPVulnerable = (params, coerce = undefined) => { let keys = key.split(']['); let keys_last = keys.length - 1; let val; + // eslint-disable-next-line @typescript-eslint/no-explicit-any let cur: any = obj; let i = 0; @@ -52,12 +53,13 @@ const splitUriIntoParamsPPVulnerable = (params, coerce = undefined) => { cur = cur[key] = i < keys_last ? cur[key] || + // eslint-disable-next-line @typescript-eslint/no-explicit-any (keys[i + 1] && isNaN(keys[i + 1] as any) ? {} : []) : val; } } else { if (Object.prototype.toString.call(obj[key]) === '[object Array]') { - obj[key].push(val); + (obj[key] as unknown[]).push(val); } else if ({}.hasOwnProperty.call(obj, key)) { obj[key] = [obj[key], val]; } else {