pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
101 lines (90 loc) • 2.9 kB
YAML
# XSS DOM Sink Detection
# Detects dynamic content assigned to dangerous DOM properties/methods.
id: ts-xss-dom-sink
name: XSS DOM Sink
severity: error
category: security
defect_class: injection
inline_tier: blocking
language: typescript
message: "XSS risk — dynamic value written to innerHTML/outerHTML or document.write()"
description: |
Assigning untrusted content to innerHTML or outerHTML, or calling
document.write() / document.writeln() with a dynamic value, allows an
attacker to inject and execute arbitrary HTML and JavaScript.
❌ NEVER:
element.innerHTML = userInput; // XSS — executes injected scripts!
element.outerHTML = response.data; // XSS
document.write(req.query.msg); // XSS
✅ SAFE:
element.textContent = userInput; // Escapes all HTML automatically
element.innerHTML = DOMPurify.sanitize(userInput); // Explicit sanitisation
query: |
[
(assignment_expression
left: (member_expression
property: (property_identifier) @PROP)
right: (identifier) @VALUE
(#match? @PROP "^(innerHTML|outerHTML)$"))
(assignment_expression
left: (member_expression
property: (property_identifier) @PROP)
right: (member_expression) @VALUE
(#match? @PROP "^(innerHTML|outerHTML)$"))
(assignment_expression
left: (member_expression
property: (property_identifier) @PROP)
right: (call_expression) @VALUE
(#match? @PROP "^(innerHTML|outerHTML)$"))
(assignment_expression
left: (member_expression
property: (property_identifier) @PROP)
right: (await_expression) @VALUE
(#match? @PROP "^(innerHTML|outerHTML)$"))
]
[
(call_expression
function: (member_expression
object: (identifier) @OBJ
property: (property_identifier) @FN)
arguments: (arguments (identifier) @ARG)
(#eq? @OBJ "document")
(#match? @FN "^(write|writeln)$"))
(call_expression
function: (member_expression
object: (identifier) @OBJ
property: (property_identifier) @FN)
arguments: (arguments (member_expression) @ARG)
(#eq? @OBJ "document")
(#match? @FN "^(write|writeln)$"))
(call_expression
function: (member_expression
object: (identifier) @OBJ
property: (property_identifier) @FN)
arguments: (arguments (call_expression) @ARG)
(#eq? @OBJ "document")
(#match? @FN "^(write|writeln)$"))
]
metavars:
- PROP
- VALUE
- OBJ
- FN
- ARG
has_fix: false
tags:
- typescript
- javascript
- security
- xss
- cwe-79
- owasp-a03
examples:
bad: |
element.innerHTML = userInput;
element.outerHTML = getContent();
document.write(req.query.message);
good: |
element.textContent = userInput;
element.innerHTML = DOMPurify.sanitize(userInput);
document.write("<h1>Static content</h1>");