Initial commit
This commit is contained in:
429
commands/audit-security.md
Normal file
429
commands/audit-security.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# Audit Security Command
|
||||
|
||||
Comprehensive security audit for Angular applications following OWASP guidelines.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/angular-security:audit-security
|
||||
```
|
||||
|
||||
## Audit Categories
|
||||
|
||||
### 1. Dependency Vulnerabilities
|
||||
|
||||
```bash
|
||||
# Check for vulnerable packages
|
||||
npm audit
|
||||
|
||||
# Fix automatically
|
||||
npm audit fix
|
||||
|
||||
# View detailed report
|
||||
npm audit --json
|
||||
|
||||
# Check specific severity
|
||||
npm audit --audit-level=high
|
||||
```
|
||||
|
||||
### 2. XSS Vulnerabilities
|
||||
|
||||
```typescript
|
||||
// Scan for dangerous patterns:
|
||||
// - innerHTML with user data
|
||||
// - bypassSecurityTrust* methods
|
||||
// - [style], [href], [src] with user input
|
||||
// - Dynamic script/style injection
|
||||
|
||||
// ❌ Found issues:
|
||||
[innerHTML]="userContent" // XSS risk
|
||||
[src]="untrustedUrl" // URL injection
|
||||
[style]="userStyle" // Style injection
|
||||
bypassSecurityTrustHtml(userHtml) // Bypassing protection
|
||||
```
|
||||
|
||||
### 3. Authentication & Authorization
|
||||
|
||||
```typescript
|
||||
// ✅ Secure auth implementation
|
||||
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const token = inject(AuthService).getToken();
|
||||
|
||||
if (token) {
|
||||
req = req.clone({
|
||||
setHeaders: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'X-Requested-With': 'XMLHttpRequest' // CSRF protection
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return next(req).pipe(
|
||||
catchError((error: HttpErrorResponse) => {
|
||||
if (error.status === 401) {
|
||||
inject(Router).navigate(['/login']);
|
||||
}
|
||||
return throwError(() => error);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// ✅ Secure route guard
|
||||
export const authGuard: CanActivateFn = (route, state) => {
|
||||
const authService = inject(AuthService);
|
||||
const router = inject(Router);
|
||||
|
||||
if (authService.isAuthenticated()) {
|
||||
// Check role-based access
|
||||
const requiredRole = route.data['role'];
|
||||
if (requiredRole && !authService.hasRole(requiredRole)) {
|
||||
return router.parseUrl('/unauthorized');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return router.parseUrl('/login');
|
||||
};
|
||||
```
|
||||
|
||||
### 4. CSRF Protection
|
||||
|
||||
```typescript
|
||||
// ✅ CSRF token implementation
|
||||
export class CsrfInterceptor implements HttpInterceptor {
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// Get CSRF token from cookie
|
||||
const csrfToken = this.getCookie('XSRF-TOKEN');
|
||||
|
||||
// Add to header for state-changing requests
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
||||
req = req.clone({
|
||||
setHeaders: {
|
||||
'X-XSRF-TOKEN': csrfToken
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
private getCookie(name: string): string {
|
||||
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
||||
return match ? match[2] : '';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Token Storage
|
||||
|
||||
```typescript
|
||||
// ❌ INSECURE: localStorage
|
||||
localStorage.setItem('token', jwt); // Vulnerable to XSS
|
||||
|
||||
// ✅ SECURE: HttpOnly cookie (server-side)
|
||||
// Set-Cookie: token=xxx; HttpOnly; Secure; SameSite=Strict
|
||||
|
||||
// ✅ ALTERNATIVE: Memory + refresh token
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TokenService {
|
||||
private accessToken: string | null = null;
|
||||
|
||||
setToken(token: string) {
|
||||
this.accessToken = token;
|
||||
// Refresh token in HttpOnly cookie
|
||||
}
|
||||
|
||||
getToken(): string | null {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
clearToken() {
|
||||
this.accessToken = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Input Validation
|
||||
|
||||
```typescript
|
||||
// ✅ Comprehensive validation
|
||||
export class SecureFormComponent {
|
||||
form = this.fb.group({
|
||||
username: ['', [
|
||||
Validators.required,
|
||||
Validators.minLength(3),
|
||||
Validators.maxLength(20),
|
||||
Validators.pattern(/^[a-zA-Z0-9_]+$/)
|
||||
]],
|
||||
email: ['', [
|
||||
Validators.required,
|
||||
Validators.email
|
||||
]],
|
||||
age: ['', [
|
||||
Validators.required,
|
||||
Validators.min(18),
|
||||
Validators.max(120)
|
||||
]],
|
||||
website: ['', [
|
||||
this.urlValidator()
|
||||
]]
|
||||
});
|
||||
|
||||
private urlValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
if (!control.value) return null;
|
||||
|
||||
try {
|
||||
const url = new URL(control.value);
|
||||
// Only allow https
|
||||
if (url.protocol !== 'https:') {
|
||||
return { invalidProtocol: true };
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return { invalidUrl: true };
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Secure HTTP Communication
|
||||
|
||||
```typescript
|
||||
// ✅ HTTPS enforcement
|
||||
export class AppComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
// Redirect HTTP to HTTPS
|
||||
if (location.protocol !== 'https:' &&
|
||||
!location.hostname.includes('localhost')) {
|
||||
location.replace(`https:${location.href.substring(location.protocol.length)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Security headers (server-side config)
|
||||
// Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
// X-Content-Type-Options: nosniff
|
||||
// X-Frame-Options: DENY
|
||||
// X-XSS-Protection: 1; mode=block
|
||||
// Referrer-Policy: strict-origin-when-cross-origin
|
||||
```
|
||||
|
||||
### 8. Content Security Policy
|
||||
|
||||
```html
|
||||
<!-- Strict CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'self';
|
||||
script-src 'self';
|
||||
style-src 'self';
|
||||
img-src 'self' data: https:;
|
||||
font-src 'self';
|
||||
connect-src 'self' https://api.example.com;
|
||||
frame-ancestors 'none';
|
||||
base-uri 'self';
|
||||
form-action 'self';
|
||||
">
|
||||
```
|
||||
|
||||
### 9. Rate Limiting
|
||||
|
||||
```typescript
|
||||
// ✅ Client-side rate limiting
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class RateLimitService {
|
||||
private requests = new Map<string, number[]>();
|
||||
private readonly WINDOW_MS = 60000; // 1 minute
|
||||
private readonly MAX_REQUESTS = 10;
|
||||
|
||||
canMakeRequest(key: string): boolean {
|
||||
const now = Date.now();
|
||||
const timestamps = this.requests.get(key) || [];
|
||||
|
||||
// Remove old timestamps
|
||||
const validTimestamps = timestamps.filter(ts =>
|
||||
now - ts < this.WINDOW_MS
|
||||
);
|
||||
|
||||
if (validTimestamps.length >= this.MAX_REQUESTS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
validTimestamps.push(now);
|
||||
this.requests.set(key, validTimestamps);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10. Logging & Monitoring
|
||||
|
||||
```typescript
|
||||
// ✅ Security event logging
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SecurityLogger {
|
||||
logSecurityEvent(event: SecurityEvent) {
|
||||
const log = {
|
||||
timestamp: new Date().toISOString(),
|
||||
event: event.type,
|
||||
severity: event.severity,
|
||||
user: this.authService.getCurrentUser()?.id,
|
||||
details: event.details,
|
||||
userAgent: navigator.userAgent,
|
||||
ip: this.getClientIp()
|
||||
};
|
||||
|
||||
// Send to security monitoring service
|
||||
this.http.post('/api/security/log', log).subscribe();
|
||||
}
|
||||
|
||||
logFailedLogin(username: string) {
|
||||
this.logSecurityEvent({
|
||||
type: 'FAILED_LOGIN',
|
||||
severity: 'HIGH',
|
||||
details: { username }
|
||||
});
|
||||
}
|
||||
|
||||
logXSSAttempt(payload: string) {
|
||||
this.logSecurityEvent({
|
||||
type: 'XSS_ATTEMPT',
|
||||
severity: 'CRITICAL',
|
||||
details: { payload }
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Audit Report Example
|
||||
|
||||
```
|
||||
🔒 Security Audit Report
|
||||
Generated: 2025-10-21 15:30:00
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ OWASP Top 10 Compliance │
|
||||
├─────────────────────────────────────────┤
|
||||
│ A01: Broken Access Control ✅ PASS │
|
||||
│ A02: Cryptographic Failures ✅ PASS │
|
||||
│ A03: Injection ⚠️ WARN │
|
||||
│ A04: Insecure Design ✅ PASS │
|
||||
│ A05: Security Misconfiguration ❌ FAIL │
|
||||
│ A06: Vulnerable Components ⚠️ WARN │
|
||||
│ A07: Auth Failures ✅ PASS │
|
||||
│ A08: Software/Data Integrity ✅ PASS │
|
||||
│ A09: Logging Failures ⚠️ WARN │
|
||||
│ A10: Server-Side Forgery ✅ PASS │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
📊 Severity Breakdown:
|
||||
┌──────────┬───────┬──────────────────┐
|
||||
│ Severity │ Count │ Issues │
|
||||
├──────────┼───────┼──────────────────┤
|
||||
│ 🔴 CRITICAL │ 0 │ │
|
||||
│ 🔴 HIGH │ 2 │ XSS, CSP missing │
|
||||
│ 🟡 MEDIUM │ 5 │ Various │
|
||||
│ 🟢 LOW │ 3 │ Improvements │
|
||||
└──────────┴───────┴──────────────────┘
|
||||
|
||||
🔴 HIGH Priority Issues:
|
||||
|
||||
1. XSS Vulnerability in UserProfileComponent
|
||||
File: user-profile.component.ts:45
|
||||
Code: [innerHTML]="userBio"
|
||||
Fix: Use DomSanitizer or remove innerHTML
|
||||
|
||||
2. Missing Content-Security-Policy
|
||||
File: index.html
|
||||
Impact: No XSS protection layer
|
||||
Fix: Add CSP meta tag
|
||||
|
||||
🟡 MEDIUM Priority Issues:
|
||||
|
||||
3. Hardcoded API credentials
|
||||
File: environment.ts:8
|
||||
Fix: Use environment variables
|
||||
|
||||
4. No rate limiting on login
|
||||
File: auth.service.ts:23
|
||||
Fix: Implement rate limiting
|
||||
|
||||
5. JWT in localStorage
|
||||
File: token.service.ts:12
|
||||
Fix: Use HttpOnly cookies
|
||||
|
||||
🟢 LOW Priority Issues:
|
||||
|
||||
6. Missing security headers
|
||||
Fix: Configure server headers
|
||||
|
||||
7. No input sanitization
|
||||
Fix: Add validation to forms
|
||||
|
||||
8. Console.log in production
|
||||
Fix: Remove debug logs
|
||||
|
||||
📋 npm audit Results:
|
||||
┌──────────┬───────┐
|
||||
│ Critical │ 0 │
|
||||
│ High │ 1 │
|
||||
│ Moderate │ 3 │
|
||||
│ Low │ 8 │
|
||||
└──────────┴───────┘
|
||||
|
||||
Vulnerable packages:
|
||||
- lodash@4.17.15 (HIGH) → Update to 4.17.21
|
||||
- axios@0.21.1 (MODERATE) → Update to 1.6.0
|
||||
|
||||
✅ Secure Practices Found:
|
||||
- HTTPS enforced
|
||||
- Route guards implemented
|
||||
- Form validation present
|
||||
- CSRF tokens in use
|
||||
- Password hashing (bcrypt)
|
||||
|
||||
🎯 Security Score: 7.2/10
|
||||
|
||||
📈 After Fixes: 9.5/10
|
||||
|
||||
⏱️ Estimated Fix Time: 4-6 hours
|
||||
|
||||
🔧 Action Plan:
|
||||
1. Fix XSS vulnerability (30 min)
|
||||
2. Add CSP headers (15 min)
|
||||
3. Move JWT to HttpOnly cookie (1 hour)
|
||||
4. Update vulnerable dependencies (30 min)
|
||||
5. Add rate limiting (1 hour)
|
||||
6. Implement security logging (1 hour)
|
||||
```
|
||||
|
||||
## Continuous Security
|
||||
|
||||
```yaml
|
||||
# .github/workflows/security.yml
|
||||
name: Security Audit
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: npm audit
|
||||
run: npm audit --audit-level=moderate
|
||||
|
||||
- name: OWASP Dependency Check
|
||||
uses: dependency-check/Dependency-Check_Action@main
|
||||
|
||||
- name: Snyk Security Scan
|
||||
uses: snyk/actions/node@master
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Audit regularly, fix proactively! 🛡️*
|
||||
352
commands/secure-component.md
Normal file
352
commands/secure-component.md
Normal file
@@ -0,0 +1,352 @@
|
||||
# Secure Component Command
|
||||
|
||||
Analyze and secure Angular components against XSS, injection, and other vulnerabilities.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/angular-security:secure-component <ComponentName>
|
||||
```
|
||||
|
||||
## Security Checks
|
||||
|
||||
### 1. XSS Prevention
|
||||
|
||||
```typescript
|
||||
// ❌ CRITICAL: XSS vulnerability
|
||||
@Component({
|
||||
template: `<div [innerHTML]="userInput"></div>`
|
||||
})
|
||||
export class UnsafeComponent {
|
||||
@Input() userInput: string;
|
||||
}
|
||||
|
||||
// ✅ SECURE: Sanitized HTML
|
||||
@Component({
|
||||
template: `<div [innerHTML]="safeContent"></div>`
|
||||
})
|
||||
export class SafeComponent {
|
||||
@Input() set userInput(value: string) {
|
||||
this.safeContent = this.sanitizer.sanitize(
|
||||
SecurityContext.HTML,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
safeContent: SafeHtml;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
}
|
||||
|
||||
// ✅ BEST: Avoid innerHTML
|
||||
@Component({
|
||||
template: `<div>{{ userInput }}</div>` // Auto-escaped
|
||||
})
|
||||
export class BestComponent {
|
||||
@Input() userInput: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. URL Sanitization
|
||||
|
||||
```typescript
|
||||
// ❌ DANGEROUS: Arbitrary URL
|
||||
<a [href]="userUrl">Click</a>
|
||||
|
||||
// ✅ SECURE: Sanitize URLs
|
||||
export class LinkComponent {
|
||||
@Input() set url(value: string) {
|
||||
this.safeUrl = this.sanitizer.sanitize(
|
||||
SecurityContext.URL,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
safeUrl: SafeUrl;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
}
|
||||
|
||||
// Template
|
||||
<a [href]="safeUrl">Click</a>
|
||||
```
|
||||
|
||||
### 3. Resource URL Validation
|
||||
|
||||
```typescript
|
||||
// ❌ DANGEROUS: Untrusted resource
|
||||
<iframe [src]="videoUrl"></iframe>
|
||||
|
||||
// ✅ SECURE: Whitelist trusted domains
|
||||
export class VideoComponent {
|
||||
@Input() set videoUrl(url: string) {
|
||||
if (this.isTrustedDomain(url)) {
|
||||
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
|
||||
} else {
|
||||
console.error('Untrusted video URL:', url);
|
||||
this.safeUrl = null;
|
||||
}
|
||||
}
|
||||
|
||||
safeUrl: SafeResourceUrl | null;
|
||||
|
||||
private trustedDomains = ['youtube.com', 'vimeo.com'];
|
||||
|
||||
private isTrustedDomain(url: string): boolean {
|
||||
try {
|
||||
const domain = new URL(url).hostname;
|
||||
return this.trustedDomains.some(trusted =>
|
||||
domain.includes(trusted)
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Style Injection
|
||||
|
||||
```typescript
|
||||
// ❌ DANGEROUS: User-controlled styles
|
||||
<div [style]="userStyle">Content</div>
|
||||
|
||||
// ✅ SECURE: Sanitize styles
|
||||
export class StyledComponent {
|
||||
@Input() set userStyle(value: string) {
|
||||
this.safeStyle = this.sanitizer.sanitize(
|
||||
SecurityContext.STYLE,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
safeStyle: SafeStyle;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
}
|
||||
|
||||
// ✅ BETTER: Use class binding
|
||||
<div [class]="userClass">Content</div>
|
||||
```
|
||||
|
||||
### 5. Dynamic Script Loading
|
||||
|
||||
```typescript
|
||||
// ❌ NEVER DO THIS
|
||||
eval(userCode);
|
||||
new Function(userCode)();
|
||||
|
||||
// ✅ SECURE: No dynamic code execution
|
||||
// Use configuration objects instead
|
||||
export class ConfigurableComponent {
|
||||
@Input() config: {
|
||||
enabled: boolean;
|
||||
options: string[];
|
||||
};
|
||||
|
||||
executeAction() {
|
||||
// Safe configuration-driven logic
|
||||
if (this.config.enabled) {
|
||||
this.config.options.forEach(opt => this.handleOption(opt));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Form Input Validation
|
||||
|
||||
```typescript
|
||||
// ✅ SECURE: Comprehensive validation
|
||||
export class UserFormComponent {
|
||||
userForm = this.fb.group({
|
||||
username: ['', [
|
||||
Validators.required,
|
||||
Validators.minLength(3),
|
||||
Validators.maxLength(20),
|
||||
Validators.pattern(/^[a-zA-Z0-9_]+$/) // Alphanumeric only
|
||||
]],
|
||||
email: ['', [
|
||||
Validators.required,
|
||||
Validators.email
|
||||
]],
|
||||
bio: ['', [
|
||||
Validators.maxLength(500)
|
||||
]]
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) {}
|
||||
|
||||
onSubmit() {
|
||||
if (this.userForm.valid) {
|
||||
// Server-side validation still required!
|
||||
const data = this.userForm.value;
|
||||
this.api.createUser(data).subscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. File Upload Security
|
||||
|
||||
```typescript
|
||||
// ✅ SECURE: File validation
|
||||
export class FileUploadComponent {
|
||||
private readonly MAX_SIZE = 5 * 1024 * 1024; // 5MB
|
||||
private readonly ALLOWED_TYPES = [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'application/pdf'
|
||||
];
|
||||
|
||||
onFileSelected(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const file = input.files?.[0];
|
||||
|
||||
if (!file) return;
|
||||
|
||||
// Validate file type
|
||||
if (!this.ALLOWED_TYPES.includes(file.type)) {
|
||||
alert('Invalid file type');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size
|
||||
if (file.size > this.MAX_SIZE) {
|
||||
alert('File too large (max 5MB)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file extension
|
||||
const ext = file.name.split('.').pop()?.toLowerCase();
|
||||
const validExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
|
||||
if (!ext || !validExtensions.includes(ext)) {
|
||||
alert('Invalid file extension');
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploadFile(file);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Sensitive Data in Templates
|
||||
|
||||
```typescript
|
||||
// ❌ DANGEROUS: Exposing sensitive data
|
||||
@Component({
|
||||
template: `
|
||||
<div>API Key: {{ apiKey }}</div>
|
||||
<div>Password: {{ password }}</div>
|
||||
`
|
||||
})
|
||||
|
||||
// ✅ SECURE: Never expose sensitive data
|
||||
@Component({
|
||||
template: `
|
||||
<div>Status: {{ isAuthenticated ? 'Connected' : 'Disconnected' }}</div>
|
||||
`
|
||||
})
|
||||
export class SecureComponent {
|
||||
isAuthenticated: boolean;
|
||||
|
||||
// Sensitive data only in memory, never in template
|
||||
private apiKey: string;
|
||||
private password: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Clickjacking Prevention
|
||||
|
||||
```typescript
|
||||
// Set X-Frame-Options header (server-side)
|
||||
// But also check in Angular:
|
||||
|
||||
export class FrameGuard implements OnInit {
|
||||
ngOnInit() {
|
||||
// Prevent loading in iframe
|
||||
if (window.self !== window.top) {
|
||||
console.error('Clickjacking attempt detected');
|
||||
window.top.location = window.self.location;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10. Content Security Policy
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-inline';
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: https:;
|
||||
font-src 'self' data:;
|
||||
connect-src 'self' https://api.example.com;
|
||||
frame-ancestors 'none';
|
||||
">
|
||||
```
|
||||
|
||||
## Security Audit Checklist
|
||||
|
||||
```typescript
|
||||
// Component security audit
|
||||
export class AuditedComponent {
|
||||
// ✅ 1. No innerHTML with user data
|
||||
// ✅ 2. All URLs sanitized
|
||||
// ✅ 3. Form inputs validated
|
||||
// ✅ 4. No eval or Function constructor
|
||||
// ✅ 5. Sensitive data not in template
|
||||
// ✅ 6. File uploads validated
|
||||
// ✅ 7. External resources whitelisted
|
||||
// ✅ 8. CSP headers configured
|
||||
// ✅ 9. HTTPS enforced
|
||||
// ✅ 10. Dependencies audited
|
||||
}
|
||||
```
|
||||
|
||||
## Output Example
|
||||
|
||||
```
|
||||
🔒 Security Audit: UserProfileComponent
|
||||
|
||||
⚠️ Issues Found:
|
||||
|
||||
1. 🔴 CRITICAL: XSS Vulnerability (Line 23)
|
||||
Location: template: `<div [innerHTML]="bio"></div>`
|
||||
Risk: Arbitrary JavaScript execution
|
||||
Fix: Use sanitizer or remove innerHTML
|
||||
|
||||
2. 🟡 HIGH: Unvalidated File Upload (Line 45)
|
||||
Location: onFileUpload(event)
|
||||
Risk: Malicious file upload
|
||||
Fix: Add file type and size validation
|
||||
|
||||
3. 🟡 MEDIUM: Hardcoded API Key (Line 12)
|
||||
Location: apiKey = 'sk_live_123...'
|
||||
Risk: Credential exposure
|
||||
Fix: Use environment variables
|
||||
|
||||
✅ Secure Patterns Detected:
|
||||
- Form validation with Validators
|
||||
- HTTPS enforced
|
||||
- HttpOnly cookies for auth token
|
||||
|
||||
📋 Recommendations:
|
||||
1. Sanitize HTML content
|
||||
2. Validate file uploads
|
||||
3. Move API key to environment config
|
||||
4. Add CSP headers
|
||||
5. Implement rate limiting for API calls
|
||||
|
||||
🎯 Security Score: 6/10 (Medium Risk)
|
||||
After fixes: 9/10 (Low Risk)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Secure every component! 🛡️*
|
||||
Reference in New Issue
Block a user