type_evaporation_scan¶
Detect Type System Evaporation vulnerabilities where TypeScript types are lost at API boundaries, leaving backend code vulnerable to type confusion attacks.
Quick Reference¶
type_evaporation_scan(
frontend_code: str, # TypeScript/JavaScript frontend code
backend_code: str, # Python/JS backend code
frontend_file: str = "frontend.ts", # Frontend filename
backend_file: str = "backend.py" # Backend filename
) -> TypeEvaporationResult
User Stories¶
| Persona | Story | Tool Value |
|---|---|---|
| 🛡️ Marcus (Security Engineer) | "Detect TypeScript type safety violations at runtime boundaries" | Type system integrity |
| 🏢 Jennifer (Enterprise Architect) | "Ensure type safety across frontend/backend boundaries at scale" | Architectural integrity |
| 👥 David (Team Lead) | "Verify API contracts are properly validated" | API safety |
Parameters¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
frontend_code | string | Yes | - | Frontend TypeScript/JavaScript code |
backend_code | string | Yes | - | Backend Python or JavaScript code |
frontend_file | string | No | "frontend.ts" | Frontend filename for context |
backend_file | string | No | "backend.py" | Backend filename for context |
What is Type Evaporation?¶
Type Evaporation occurs when: 1. Frontend defines strict TypeScript types for API requests 2. Types are "evaporated" (lost) when data crosses network boundary 3. Backend receives untyped any data via request.json() 4. Backend assumes types match without validation 5. Attackers send malformed data that bypasses frontend validation
graph LR
A[TypeScript] -->|Strict Types| B[API Call]
B -->|Network| C[Backend]
C -->|request.json| D[Untyped Data]
D -->|Type Confusion| E[Vulnerability]
style B fill:#ff9
style D fill:#f99 Response Schema¶
{
"data": {
"vulnerabilities": [
{
"type": "string",
"severity": "string",
"frontend_type": "string",
"backend_usage": "string",
"frontend_line": "integer",
"backend_line": "integer",
"description": "string",
"recommendation": "string"
}
],
"frontend_interfaces": [
{
"name": "string",
"fields": [{"name": "string", "type": "string"}],
"line": "integer"
}
],
"backend_endpoints": [
{
"path": "string",
"method": "string",
"expects_type": "string | null",
"validates_type": "boolean",
"line": "integer"
}
],
"type_coverage": {
"frontend_types": "integer",
"backend_validated": "integer",
"percentage": "float"
}
},
"tier_applied": "string",
"duration_ms": "integer"
}
Examples¶
Detect Type Evaporation¶
# backend.py
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.json # Type evaporates here!
# Assumes 'age' is a number, but no validation
if data['age'] >= 18:
# If attacker sends age="18", this breaks
create_adult_user(data)
# Assumes 'isAdmin' is boolean
if data.get('isAdmin'):
# Attacker can send isAdmin="true" (string truthy)
grant_admin_privileges(data['name'])
return jsonify({"status": "created"})
{
"frontend_code": "interface CreateUserRequest {\n name: string;\n email: string;\n age: number;\n isAdmin: boolean;\n}\n\nasync function createUser(user: CreateUserRequest) {\n const response = await fetch('/api/users', {\n method: 'POST',\n body: JSON.stringify(user)\n });\n return response.json();\n}",
"backend_code": "@app.route('/api/users', methods=['POST'])\ndef create_user():\n data = request.json\n \n if data['age'] >= 18:\n create_adult_user(data)\n \n if data.get('isAdmin'):\n grant_admin_privileges(data['name'])\n \n return jsonify({'status': 'created'})",
"frontend_file": "frontend.ts",
"backend_file": "routes/users.py"
}
```bash codescalpel type-evaporation-scan \ --frontend-code "interface CreateUserRequest {
name: string; email: string; age: number; isAdmin: boolean; }
async function createUser(user: CreateUserRequest) { const response = await fetch('/api/users', { method: 'POST', body: JSON.stringify(user) }); return response.json(); }" \ --backend-code "@app.route('/api/users', methods=['POST']) def create_user(): data = request.json
if data['age'] >= 18:
create_adult_user(data)
if data.get('isAdmin'):
grant_admin_privileges(data['name'])
return jsonify({'status': 'created'})" \
--frontend-file frontend.ts \
--backend-file routes/users.py
```
{
"data": {
"vulnerabilities": [
{
"type": "TYPE_EVAPORATION",
"severity": "HIGH",
"frontend_type": "CreateUserRequest.age: number",
"backend_usage": "data['age'] >= 18 (numeric comparison)",
"frontend_line": 4,
"backend_line": 5,
"description": "Frontend expects 'age' as number, but backend does not validate type. Attacker can send string '18' which may cause unexpected behavior in comparison.",
"recommendation": "Add runtime validation: isinstance(data['age'], int)"
},
{
"type": "TYPE_EVAPORATION",
"severity": "CRITICAL",
"frontend_type": "CreateUserRequest.isAdmin: boolean",
"backend_usage": "data.get('isAdmin') (truthy check for admin)",
"frontend_line": 5,
"backend_line": 8,
"description": "Frontend expects 'isAdmin' as boolean, but backend uses truthy check. Attacker can send isAdmin='yes' (truthy string) to gain admin access.",
"recommendation": "Validate strict boolean: data.get('isAdmin') is True"
}
],
"frontend_interfaces": [
{
"name": "CreateUserRequest",
"fields": [
{"name": "name", "type": "string"},
{"name": "email", "type": "string"},
{"name": "age", "type": "number"},
{"name": "isAdmin", "type": "boolean"}
],
"line": 1
}
],
"backend_endpoints": [
{
"path": "/api/users",
"method": "POST",
"expects_type": "CreateUserRequest",
"validates_type": false,
"line": 1
}
],
"type_coverage": {
"frontend_types": 4,
"backend_validated": 0,
"percentage": 0.0
}
},
"tier_applied": "pro",
"duration_ms": 85
}
Safe Pattern Detection¶
from pydantic import BaseModel
class CreateUserRequest(BaseModel):
name: str
email: str
age: int
isAdmin: bool
@app.route('/api/users', methods=['POST'])
def create_user():
data = CreateUserRequest(**request.json) # Validated!
if data.age >= 18:
create_adult_user(data)
if data.isAdmin:
grant_admin_privileges(data.name)
Vulnerability Types¶
| Type | Severity | Description |
|---|---|---|
TYPE_EVAPORATION | HIGH | Type lost at API boundary |
BOOLEAN_CONFUSION | CRITICAL | Boolean type bypassed with truthy string |
NUMBER_STRING_CONFUSION | HIGH | Number expected but string accepted |
ARRAY_INJECTION | MEDIUM | Single value where array expected |
NULL_INJECTION | MEDIUM | Null where value expected |
PROTOTYPE_POLLUTION | CRITICAL | proto or constructor injection |
Mitigation Strategies¶
1. Pydantic Validation (Python)¶
from pydantic import BaseModel, validator
class UserRequest(BaseModel):
name: str
age: int
isAdmin: bool
@validator('isAdmin', pre=True)
def strict_bool(cls, v):
if not isinstance(v, bool):
raise ValueError('must be boolean')
return v
2. Zod Validation (Node.js)¶
import { z } from 'zod';
const UserSchema = z.object({
name: z.string(),
age: z.number().int(),
isAdmin: z.boolean().strict()
});
app.post('/api/users', (req, res) => {
const data = UserSchema.parse(req.body); // Throws if invalid
});
3. TypeBox (Shared Types)¶
// shared/types.ts
import { Type, Static } from '@sinclair/typebox';
export const UserRequest = Type.Object({
name: Type.String(),
age: Type.Integer(),
isAdmin: Type.Boolean()
});
export type UserRequest = Static<typeof UserRequest>;
Tier Limits¶
type_evaporation_scan capabilities vary by tier:
| Feature | Community | Pro | Enterprise |
|---|---|---|---|
| Max files analyzed | 50 | 500 | Unlimited |
| Frontend languages | TypeScript, JavaScript | TypeScript, JavaScript | TypeScript, JavaScript |
| Backend languages | Python, JavaScript | Python, JavaScript | Python, JavaScript |
| Frontend-only mode | ✅ | ❌ (full analysis) | ❌ (full analysis) |
| Validation detection | ✅ Basic | ✅ Advanced | ✅ Full |
| Pydantic/Zod support | ✅ Basic | ✅ Full | ✅ Full |
| Schema generation | ❌ | ❌ | ✅ |
| Custom validators | ❌ | ❌ | ✅ |
Community Tier¶
- ✅ Detect type evaporation at API boundaries
- ✅ Parse TypeScript interfaces
- ✅ Detect Python/JS backend usage
- ✅ Basic validation detection (Pydantic, Zod)
- ✅ Identify boolean confusion vulnerabilities
- ⚠️ Limited to 50 files - Small projects only
- ⚠️ Frontend-only mode - Can't analyze backend separately
- ❌ No automatic schema generation
- ❌ No custom validator detection
Pro Tier¶
- ✅ All Community features
- ✅ 500 files analyzed - Production applications
- ✅ Full frontend/backend analysis - Separate or combined
- ✅ Advanced validation detection - FastAPI, Flask-Pydantic, etc.
- ✅ Enhanced type coverage metrics - Better reporting
- ✅ Framework-specific patterns - Django REST, NestJS, etc.
Enterprise Tier¶
- ✅ All Pro features
- ✅ Unlimited files - Enterprise-scale applications
- ✅ Automatic schema generation - Generate Pydantic/Zod from TypeScript
- ✅ Custom validator detection - Organization-specific validators
- ✅ Multi-repo analysis - Scan across microservices
- ✅ Remediation code generation - Auto-generate fixes
Key Difference: File Coverage and Schema Generation - Community: 50 files, frontend-only - Basic type safety check - Pro: 500 files, full analysis - Production API validation - Enterprise: Unlimited, schema generation - Enterprise type safety
Best Practices¶
- Always validate backend input - Never trust frontend types
- Use validation libraries - Pydantic, Zod, TypeBox
- Match types exactly - Don't rely on truthy checks
- Test with malformed data - Send strings where numbers expected
- Share type definitions - Single source of truth
Related Tools¶
- security_scan - General security analysis
- unified_sink_detect - Sink detection