# NestJS Security CLI

Advanced IP blocking, role-based security, and attack detection for NestJS applications.

## Requirements

- Node.js >= 22.x
- NestJS >= 11.x
- TypeScript >= 5.x
- You'll need to maintain the `@nestjs/cache-manager` and `cache-manager` packages.

## Installation

```bash
npm install nestjs-security-cli
```

## Quick Start

```typescript
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common'
import { MongooseModule } from '@nestjs/mongoose'
import { SecurityModule, BlacklistedIp, BlacklistedIpSchema, SecurityMiddleware } from 'nestjs-security-cli'

@Module( {
	imports: [
		SecurityModule.forRootAsync( {
			enableAdminPanel: true,
			useFactory: () => ({
				enableDatabase: true,
				defaultBlockDurationHours: 24,
				enableAutoBlocking: true,
				// example of customizing the suspicious patterns
				suspiciousPatterns: [
					{ pattern: '/vendor', name: 'Composer Attempt' },
					{ pattern: '/phpunit', name: 'PHPUnit Attempt' },
					{ pattern: '/lib', name: 'Lib Attempt' },
					{ pattern: '/laravel', name: 'Laravel Attempt' },
					{ pattern: '/www', name: 'WWW Attempt' },
					{ pattern: '/ws', name: 'Web Socket Attempt' },
					{ pattern: '/yii', name: 'Yii Attempt' },
					{ pattern: '/zend', name: 'Zend Attempt' },
					{ pattern: '/test', name: 'Test Attempt' },
					{ pattern: '/tests', name: 'Tests Attempt' },
					{ pattern: '/testing', name: 'Testing Attempt' },
					{ pattern: '/cms', name: 'CMS Attempt' },
					{ pattern: '/crm', name: 'CRM Attempt' }
				]
			}),
			imports: [ MongoDbModule ],
			providers: [ ...IpBlockerProvider ]
		} )
	]
} )
export class AppModule {
}
```

## DATABASE_CONNECTION provider

if you're needing to use the database connection, and the import options isn't working, you can try to pass it directly
into the providers array.

```text
    {
        provide: 'DATABASE_CONNECTION',
        inject: [ConfigService],
        useFactory: (configService: ConfigService): Promise<typeof mongoose> => {
        return mongoose.connect(configService.get<string>('DATABASE_HOST'), {
            dbName: configService.get<string>('DATABASE_NAME')
        })
    }
```

## with forRoot

```typescript
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { MongooseModule } from '@nestjs/mongoose'
import { SecurityModule, BlacklistedIp, BlacklistedIpSchema } from 'nestjs-security-cli'

@Module( {
	imports: [
		ConfigModule.forRoot(),
		MongoDbModule,
		SecurityModule.forRoot( {
			enableDatabase: true,
			defaultBlockDurationHours: 24,
			enableAutoBlocking: true,
			enableAdminPanel: true,
		} ),
	],
	providers: [ ...IpBlockerProvider ], // Required if enableDatabase is true
} )
export class AppModule {
}

export class AppModule {}
```

## Cache-Only Mode (No Database)

If you don't want to use MongoDB, you can skip the schema registration:

```typescript
import { Module } from '@nestjs/common'
import { SecurityModule } from 'nestjs-security-cli'

@Module( {
	imports: [
		SecurityModule.forRoot( {
			enableDatabase: false, // This will use only cache
			defaultBlockDurationHours: 24,
			enableAutoBlocking: true
		} )
	]
} )
export class AppModule {}
````

## SecurityService

Since this has switched to using the `@Inject('IP_BLOCKER')` model to be registered in your app. So by passing the
IpBlockerProvider, it will automatically register the model. More on this down below.

## Clean up cron

There's a cron that runs to clean up old blocks. By default, it runs every day at midnight.
`CLEANUP_CRON` environment variable.

```text
	// Cleanup expired entries (runs daily)
	@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
	async cleanupExpiredBlocks(): Promise<void> {
		const result = await this.blacklistedIpModel.updateMany(
			{ active: true, expiresAt: { $lt: new Date() } },
			{ active: false }
		)
		console.log(`🧹 Cleaned up ${result.modifiedCount} expired IP blocks`)
	}
```

## Role type

The roles that can be defined in your app. Persist the roles in your user model as an array of strings.

```text
export type Role = 'Admin' | 'User' | 'Moderator' | 'Guest'
```

## Register the IpBlacklistGuard globally
### this is no longer needed, since the SecurityModule is now registered globally by default
All you have to do is apply the guard to your controllers.
```
@UseGuards(IpBlacklistGuard) // add this to any, or all controller endpoints
@Controller()
export class AppController {
	@Get()
	someEndpoint(): object {
		return { message: 'Hello World!' }
	}
}
```


## Admin & Roles guards

* The Admin guard accepts ONLY users with the role `Admin`.
* The Role guard accepts users with ane of (or all) the type "Roles" [as mentioned above](#role-type).
* To leverage the admin panel, you'll need to create a role called `Admin`. The user model in your app should have a
  field called "Roles", which is an array of strings.

To use the `AdminGuard` just add the decorator to your controller method.

```typescript
@UseGuards( AdminGuard )
@Controller( 'my-contoller' )
```

To use the `RoleGuard` just add the decorator to your controller method.

```typescript
@Roles( 'Admin', 'User' )
@UseGuards( RoleGuard )
```

The `AdminGuard` uses both `JwtService` and `ConfigService` to verify the JWT token. So if you're using this in your
app,
just make sure that the env var is `JWT_SECRET` and this will work. ( This could be dynamic later ). Also, when passing
the token to the methods, you'll need to pass the token as `access_token` in either the request cookies,
or the header authorization bearer token.

```typescript
const user = await this.jwtService.verifyAsync( token, {
	secret: this.configService.get<string>( 'JWT_SECRET' )
} )
```

## The admin panel

To use the built-in admin panel, you can set the `enableAdminPanel: true` in the
configs [as shown in the](#quick-start) to set the available endpoints.

Available endpoints:

- POST `/security/blacklist`. This will accept a JSON object with the following fields:
    - `ip`: The IP address to blacklist
    - `hours`: The duration of the blacklist in hours
    - `reason`: The reason for the blacklist
- GET `/security/blacklist`. This will return a list of all blacklisted IPs
- GET `/security/blacklist/:ip`. This will return the details of a specific IP address
- DELETE `/security/blacklist/:ip`. This will delete a specific IP address
- GET `/security/analytics`. This will return analytics for the last 24 hours

## Features

- 🛡️ IP Blacklisting with MongoDB persistence
- 🚫 Automatic attack pattern detection
- ⚡ Redis/Memory caching for fast lookups
- 👥 Role-based access control (RBAC)
- 📊 Security analytics and reporting
- ⏰ Scheduled cleanup of expired blocks

## Configuration Options

```typescript
export interface SecurityConfigInterface {
	enableDatabase?: boolean
	mongooseConnection?: string
	cache?: {
		ttl?: number
		max?: number
		store?: any
	}
	enableAdminPanel?: boolean
	adminPath?: string
	enableAutoBlocking?: boolean
	suspiciousPatterns?: Array<{
		pattern: string
		name: string
		blockDurationHours?: number
	}>
	defaultBlockDurationHours?: number
	enableRateLimit?: boolean
	rateLimitOptions?: {
		windowMs?: number
		max?: number
	}
	enableLogging?: boolean
	logLevel?: 'error' | 'warn' | 'info' | 'debug'
}
```

## API Documentation

[coming soon]

## License

MIT