askill
security

securitySafety 100Repository

Guide secure coding practices including authentication, input validation, and common vulnerability prevention.

0 stars
1.2k downloads
Updated 2/8/2026

Package Files

Loading files...
SKILL.md

Security

Use this skill when implementing authentication, handling user input, or reviewing code for security issues.

1. Authentication & Authorization

JWT Token Handling

This project uses Passport JWT for authentication with a hexagonal architecture (see ADR-005). Key patterns:

// ✅ Correct: Use AuthGuardAdapter from adapters layer
import { AuthGuardAdapter } from '../../shared/adapters/auth';

@UseGuards(AuthGuardAdapter)
@Get('protected-endpoint')
async getProtectedData() { ... }

// ❌ Wrong: Direct import from shared module internals
import { JwtAuthGuard } from '../../shared/modules/auth/jwt-auth.guard';

// ❌ Wrong: Manual token parsing
const token = request.headers.authorization?.split(' ')[1];

Token Storage Best Practices:

Storage MethodSecurity LevelUse Case
HttpOnly Cookie✅ Most SecureProduction apps
Memory (state)✅ SecureSPA session
localStorage❌ InsecureNever store sensitive tokens

This Project's Pattern:

  • Frontend: AuthInterceptor handles token injection automatically
  • Backend: AuthGuardAdapter delegates to internal JWT guard
  • Never manually add Authorization headers in hooks

Guard Usage

import { AuthGuardAdapter } from '../../shared/adapters/auth';

// Single endpoint protection
@UseGuards(AuthGuardAdapter)
@Post()
create(@Body() dto: CreateDto) { ... }

// Controller-level protection
@UseGuards(AuthGuardAdapter)
@Controller('api/admin')
export class AdminController { ... }

Check User Ownership:

// Verify user owns the resource before modifying
@UseGuards(AuthGuardAdapter)
@Delete(':id')
async delete(@Param('id') id: string, @Req() request) {
  const userId = request.user.sub;
  const resource = await this.service.findOne(id);

  if (resource.userId !== userId) {
    throw new ForbiddenException('Not authorized to delete this resource');
  }

  return this.service.delete(id);
}

2. Input Validation

Backend (NestJS)

Always use class-validator DTOs:

// ✅ Correct: DTO with validation decorators
import { IsString, IsNotEmpty, MinLength, MaxLength, IsOptional } from 'class-validator';

export class CreateBlogPostDto {
  @IsString()
  @IsNotEmpty()
  @MinLength(3)
  @MaxLength(200)
  title: string;

  @IsString()
  @IsNotEmpty()
  content: string;

  @IsOptional()
  @IsString()
  slug?: string;
}

// ❌ Wrong: No validation
async create(data: any) {
  return this.repository.save(data);
}

Global ValidationPipe (already configured):

// main.ts - DO NOT REMOVE
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true, // Strip unknown properties
    forbidNonWhitelisted: true, // Throw on unknown properties
    transform: true, // Transform payloads to DTO instances
  }),
);

Frontend (React)

  • Validate on client for UX, but NEVER trust client validation
  • React automatically escapes strings (XSS protection by default)
  • Use controlled inputs to prevent unexpected values
// ✅ Client validation for UX
const handleSubmit = (e: FormEvent) => {
  e.preventDefault();

  if (title.length < 3) {
    setError('Title must be at least 3 characters');
    return;
  }

  // Server will validate again
  mutation.mutate({ title, content });
};

3. Common Vulnerabilities

SQL Injection

TypeORM Protection (Already in Place):

// ✅ Safe: TypeORM parameterizes automatically
const user = await this.repository.findOne({ where: { email } });

// ✅ Safe: Query builder with parameters
const posts = await this.repository
  .createQueryBuilder('post')
  .where('post.title LIKE :search', { search: `%${term}%` })
  .getMany();

// ❌ DANGEROUS: String concatenation
const result = await this.repository.query(
  `SELECT * FROM users WHERE email = '${email}'`, // SQL INJECTION!
);

XSS (Cross-Site Scripting)

React's Built-in Protection:

// ✅ Safe: React escapes by default
return <div>{userContent}</div>;

// ⚠️ Dangerous: Only use with sanitized content
return <div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />;

// ✅ If you must render HTML, sanitize first:
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userHtml);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;

Markdown Rendering (Blog Feature): This project uses react-markdown which is safe by default:

// ✅ Safe: react-markdown sanitizes HTML
import ReactMarkdown from 'react-markdown';
<ReactMarkdown>{content}</ReactMarkdown>

CSRF (Cross-Site Request Forgery)

Mitigation Strategies:

  1. SameSite Cookies: Set SameSite=Strict or Lax
  2. Origin Verification: Check Origin header on mutations
  3. Token-based Auth: JWTs in Authorization header (not cookies) are CSRF-immune

4. Dependency Security

Regular Audits

# Check for vulnerabilities
npm audit

# Only high/critical (CI uses this)
npm audit --audit-level=high

# Auto-fix where possible
npm audit fix

Dependabot (Already Configured)

This project has Dependabot enabled in .github/dependabot.yml. It will:

  • Check for dependency updates weekly
  • Create PRs for security patches
  • Group minor updates to reduce noise

Before Adding Dependencies

  1. Check npm for last update date (avoid abandoned packages)
  2. Review GitHub issues for security concerns
  3. Check bundle size impact (npx bundlephobia <package>)
  4. Prefer well-maintained alternatives

5. Environment & Secrets

Never Commit Secrets

# .gitignore already includes:
.env
.env.local
*.pem

Environment Variable Pattern

// ✅ Correct: Access via process.env
const secret = process.env.JWT_AUTH_SECRET;

// ❌ Wrong: Hardcoded secrets
const secret = 'my-super-secret-key';

Required Environment Variables

See .env.example for all required variables:

VariableRequiredPurpose
JWT_AUTH_SECRETYesJWT signing key
SENTRY_DSNNoError tracking (optional)
NODE_ENVNoEnvironment (default: dev)

Secret Rotation

  • Rotate JWT_AUTH_SECRET periodically
  • On rotation, expect brief auth failures as tokens become invalid
  • Consider using short-lived tokens with refresh token pattern for production

6. Security ESLint Rules

This project has eslint-plugin-security configured with these rules:

RuleLevelPurpose
detect-object-injectionwarnBracket notation security
detect-non-literal-regexpwarnDynamic regex (ReDoS)
detect-unsafe-regexerrorCatastrophic backtracking
detect-buffer-noasserterrorBuffer bounds checking
detect-eval-with-expressionerrorDynamic eval() calls
detect-no-csrf-before-method-overrideerrorExpress CSRF vulnerability
detect-possible-timing-attackswarnConstant-time comparisons

Suppressing False Positives:

// When you're certain the code is safe:
// eslint-disable-next-line security/detect-object-injection
const value = obj[key];

7. Security Checklist

Before deploying or reviewing PRs, verify:

  • All user input validated with DTOs
  • Protected endpoints use @UseGuards(AuthGuardAdapter) (not direct JwtAuthGuard)
  • No hardcoded secrets
  • No dangerouslySetInnerHTML without sanitization
  • No string concatenation in SQL queries
  • npm audit shows no high/critical vulnerabilities
  • Sensitive data not logged

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

92/100Analyzed 2/10/2026

A comprehensive and highly actionable security guide tailored for a NestJS/React stack, featuring clear code examples, tool configurations, and a final checklist.

100
95
80
95
95

Metadata

Licenseunknown
Version-
Updated2/8/2026
PublisherReillySteere

Tags

apici-cddatabasegithubsecurity