@libs-ui/pipes-escape-html 0.2.356-8 → 0.2.357-0
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.
- package/README.md +171 -86
- package/fesm2022/libs-ui-pipes-escape-html.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
# @libs-ui/pipes-escape-html
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Angular pipe escape các ký tự HTML đặc biệt, ngăn chặn XSS khi hiển thị nội dung không tin cậy.
|
|
4
4
|
|
|
5
5
|
## Giới thiệu
|
|
6
6
|
|
|
7
|
-
`LibsUiPipesEscapeHtmlPipe` là một standalone Angular pipe
|
|
7
|
+
`LibsUiPipesEscapeHtmlPipe` là một standalone Angular pipe bọc lại hàm `escapeHtml` từ `@libs-ui/utils`, chuyển đổi các ký tự HTML đặc biệt (`<`, `>`, `&`, `"`, `'`) thành HTML entities an toàn. Pipe xử lý `null` và `undefined` một cách an toàn — trả về chuỗi rỗng thay vì throw lỗi. Dùng trực tiếp trong Angular template bằng cú pháp pipe chuẩn.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Tính năng
|
|
10
10
|
|
|
11
|
-
- Escape
|
|
12
|
-
- Xử lý null
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- Angular Signals compatible
|
|
11
|
+
- ✅ Escape 5 ký tự HTML đặc biệt: `<` `>` `&` `"` `'`
|
|
12
|
+
- ✅ Xử lý an toàn `null` và `undefined` (trả về `''`)
|
|
13
|
+
- ✅ Standalone pipe — import trực tiếp, không cần module
|
|
14
|
+
- ✅ Thin wrapper của `escapeHtml` từ `@libs-ui/utils` (có thể dùng hàm trực tiếp trong TS)
|
|
15
|
+
- ✅ Compatible với Angular Signals và OnPush Change Detection
|
|
17
16
|
|
|
18
17
|
## Khi nào sử dụng
|
|
19
18
|
|
|
20
|
-
- Hiển thị nội dung
|
|
21
|
-
- Escape
|
|
22
|
-
-
|
|
23
|
-
-
|
|
19
|
+
- Hiển thị nội dung từ user input để tránh XSS attacks
|
|
20
|
+
- Escape code snippets hoặc HTML examples trong UI
|
|
21
|
+
- Render an toàn dữ liệu từ API hoặc database có thể chứa HTML tags
|
|
22
|
+
- Truyền tham số vào i18n translate pipe khi tham số đến từ nguồn không tin cậy
|
|
24
23
|
|
|
25
24
|
## Cài đặt
|
|
26
25
|
|
|
27
26
|
```bash
|
|
28
|
-
# npm
|
|
29
27
|
npm install @libs-ui/pipes-escape-html
|
|
30
|
-
|
|
31
|
-
# yarn
|
|
32
|
-
yarn add @libs-ui/pipes-escape-html
|
|
33
28
|
```
|
|
34
29
|
|
|
35
30
|
## Import
|
|
@@ -41,103 +36,192 @@ import { LibsUiPipesEscapeHtmlPipe } from '@libs-ui/pipes-escape-html';
|
|
|
41
36
|
standalone: true,
|
|
42
37
|
imports: [LibsUiPipesEscapeHtmlPipe],
|
|
43
38
|
})
|
|
44
|
-
export class
|
|
39
|
+
export class MyComponent {}
|
|
45
40
|
```
|
|
46
41
|
|
|
47
|
-
## Ví dụ
|
|
42
|
+
## Ví dụ sử dụng
|
|
48
43
|
|
|
49
|
-
###
|
|
44
|
+
### 1. Escape XSS script tag
|
|
50
45
|
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
```typescript
|
|
47
|
+
// component.ts
|
|
48
|
+
import { Component } from '@angular/core';
|
|
49
|
+
import { LibsUiPipesEscapeHtmlPipe } from '@libs-ui/pipes-escape-html';
|
|
50
|
+
|
|
51
|
+
@Component({
|
|
52
|
+
standalone: true,
|
|
53
|
+
imports: [LibsUiPipesEscapeHtmlPipe],
|
|
54
|
+
template: `
|
|
55
|
+
<p>{{ userInput | LibsUiPipesEscapeHtmlPipe }}</p>
|
|
56
|
+
`,
|
|
57
|
+
})
|
|
58
|
+
export class CommentComponent {
|
|
59
|
+
userInput = '<script>alert("xss")</script>';
|
|
60
|
+
// Output: <script>alert("xss")</script>
|
|
61
|
+
}
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
###
|
|
64
|
+
### 2. Hiển thị an toàn user input chứa ký tự đặc biệt
|
|
63
65
|
|
|
64
66
|
```typescript
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
// component.ts
|
|
68
|
+
import { Component, signal } from '@angular/core';
|
|
69
|
+
import { LibsUiPipesEscapeHtmlPipe } from '@libs-ui/pipes-escape-html';
|
|
70
|
+
|
|
71
|
+
@Component({
|
|
72
|
+
standalone: true,
|
|
73
|
+
imports: [LibsUiPipesEscapeHtmlPipe],
|
|
74
|
+
template: `
|
|
75
|
+
<div class="user-comment">
|
|
76
|
+
<p>{{ userComment() | LibsUiPipesEscapeHtmlPipe }}</p>
|
|
77
|
+
</div>
|
|
78
|
+
`,
|
|
79
|
+
})
|
|
80
|
+
export class UserCommentComponent {
|
|
81
|
+
userComment = signal('<img src=x onerror="alert(1)">');
|
|
82
|
+
// Output: <img src=x onerror="alert(1)">
|
|
67
83
|
}
|
|
68
84
|
```
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
### 3. Escape ký tự đặc biệt trong chuỗi văn bản
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// component.ts
|
|
90
|
+
import { Component } from '@angular/core';
|
|
91
|
+
import { LibsUiPipesEscapeHtmlPipe } from '@libs-ui/pipes-escape-html';
|
|
92
|
+
|
|
93
|
+
@Component({
|
|
94
|
+
standalone: true,
|
|
95
|
+
imports: [LibsUiPipesEscapeHtmlPipe],
|
|
96
|
+
template: `
|
|
97
|
+
<code>{{ specialChars | LibsUiPipesEscapeHtmlPipe }}</code>
|
|
98
|
+
`,
|
|
99
|
+
})
|
|
100
|
+
export class CodeDisplayComponent {
|
|
101
|
+
specialChars = '<div class="test">Hello & "World"</div>';
|
|
102
|
+
// Output: <div class="test">Hello & "World"</div>
|
|
103
|
+
}
|
|
75
104
|
```
|
|
76
105
|
|
|
77
|
-
###
|
|
106
|
+
### 4. Xử lý null/undefined gracefully
|
|
78
107
|
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
108
|
+
```typescript
|
|
109
|
+
// component.ts
|
|
110
|
+
import { Component, signal } from '@angular/core';
|
|
111
|
+
import { LibsUiPipesEscapeHtmlPipe } from '@libs-ui/pipes-escape-html';
|
|
112
|
+
|
|
113
|
+
@Component({
|
|
114
|
+
standalone: true,
|
|
115
|
+
imports: [LibsUiPipesEscapeHtmlPipe],
|
|
116
|
+
template: `
|
|
117
|
+
<p>{{ nullValue | LibsUiPipesEscapeHtmlPipe }}</p>
|
|
118
|
+
<!-- Output: '' (chuỗi rỗng, không throw lỗi) -->
|
|
119
|
+
<p>{{ undefinedValue | LibsUiPipesEscapeHtmlPipe }}</p>
|
|
120
|
+
<!-- Output: '' -->
|
|
121
|
+
`,
|
|
122
|
+
})
|
|
123
|
+
export class SafeComponent {
|
|
124
|
+
nullValue: string | null = null;
|
|
125
|
+
undefinedValue: string | undefined = undefined;
|
|
126
|
+
}
|
|
86
127
|
```
|
|
87
128
|
|
|
88
|
-
###
|
|
129
|
+
### 5. Dùng hàm escapeHtml trực tiếp trong TypeScript (không cần pipe)
|
|
130
|
+
|
|
131
|
+
Khi cần escape trong logic TS thay vì template, import trực tiếp hàm `escapeHtml` từ `@libs-ui/utils` — pipe chỉ là thin wrapper của hàm này.
|
|
89
132
|
|
|
90
133
|
```typescript
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
134
|
+
import { Component, computed, signal } from '@angular/core';
|
|
135
|
+
import { escapeHtml } from '@libs-ui/utils';
|
|
136
|
+
|
|
137
|
+
@Component({
|
|
138
|
+
standalone: true,
|
|
139
|
+
template: `<p>{{ safeText() }}</p>`,
|
|
140
|
+
})
|
|
141
|
+
export class MyComponent {
|
|
142
|
+
private rawInput = signal('<script>alert("xss")</script>');
|
|
143
|
+
|
|
144
|
+
protected safeText = computed(() => escapeHtml(this.rawInput()));
|
|
145
|
+
// Output: <script>alert("xss")</script>
|
|
94
146
|
}
|
|
95
147
|
```
|
|
96
148
|
|
|
149
|
+
## Transform
|
|
150
|
+
|
|
151
|
+
### Cú pháp
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
value | LibsUiPipesEscapeHtmlPipe
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Tham số
|
|
158
|
+
|
|
159
|
+
| Tham số | Type | Bắt buộc | Mô tả | Ví dụ |
|
|
160
|
+
|---------|-------------------------------|----------|----------------------------------------------------|--------------------------------------------------|
|
|
161
|
+
| `value` | `string \| null \| undefined` | Có | Chuỗi cần escape các ký tự HTML đặc biệt | `'<script>alert(1)</script>'` |
|
|
162
|
+
|
|
163
|
+
### Giá trị trả về
|
|
164
|
+
|
|
165
|
+
| Type | Mô tả |
|
|
166
|
+
|----------|---------------------------------------------------------------------|
|
|
167
|
+
| `string` | Chuỗi đã escape HTML entities. Trả về `''` nếu input là `null`/`undefined` |
|
|
168
|
+
|
|
169
|
+
### Ví dụ transform — template
|
|
170
|
+
|
|
97
171
|
```html
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
</div>
|
|
172
|
+
<!-- Escape XSS -->
|
|
173
|
+
{{ '<script>alert(1)</script>' | LibsUiPipesEscapeHtmlPipe }}
|
|
174
|
+
<!-- Kết quả: <script>alert(1)</script> -->
|
|
175
|
+
|
|
176
|
+
<!-- Escape ký tự đặc biệt -->
|
|
177
|
+
{{ '<div class="test">Hello & World</div>' | LibsUiPipesEscapeHtmlPipe }}
|
|
178
|
+
<!-- Kết quả: <div class="test">Hello & World</div> -->
|
|
179
|
+
|
|
180
|
+
<!-- Null trả về chuỗi rỗng -->
|
|
181
|
+
{{ null | LibsUiPipesEscapeHtmlPipe }}
|
|
182
|
+
<!-- Kết quả: '' -->
|
|
104
183
|
```
|
|
105
184
|
|
|
106
|
-
|
|
185
|
+
### Ví dụ transform — standalone (pipe.transform)
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { LibsUiPipesEscapeHtmlPipe } from '@libs-ui/pipes-escape-html';
|
|
189
|
+
|
|
190
|
+
const pipe = new LibsUiPipesEscapeHtmlPipe();
|
|
107
191
|
|
|
108
|
-
|
|
192
|
+
pipe.transform('<script>alert(1)</script>');
|
|
193
|
+
// → '<script>alert(1)</script>'
|
|
109
194
|
|
|
110
|
-
|
|
195
|
+
pipe.transform('<div class="a">Hello & "World"</div>');
|
|
196
|
+
// → '<div class="a">Hello & "World"</div>'
|
|
111
197
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
| `value` | `string \| null \| undefined` | - | Chuỗi cần escape HTML entities |
|
|
198
|
+
pipe.transform(null);
|
|
199
|
+
// → ''
|
|
115
200
|
|
|
116
|
-
|
|
201
|
+
pipe.transform(undefined);
|
|
202
|
+
// → ''
|
|
203
|
+
|
|
204
|
+
pipe.transform('');
|
|
205
|
+
// → ''
|
|
206
|
+
```
|
|
117
207
|
|
|
118
|
-
|
|
119
|
-
| -------- | ---------------------------------- |
|
|
120
|
-
| `string` | Chuỗi đã được escape HTML entities |
|
|
208
|
+
## Bảng ký tự được escape
|
|
121
209
|
|
|
122
|
-
|
|
210
|
+
| Ký tự | HTML Entity |
|
|
211
|
+
|-------|-------------|
|
|
212
|
+
| `<` | `<` |
|
|
213
|
+
| `>` | `>` |
|
|
214
|
+
| `&` | `&` |
|
|
215
|
+
| `"` | `"` |
|
|
216
|
+
| `'` | `'` |
|
|
123
217
|
|
|
124
|
-
|
|
218
|
+
## Lưu ý quan trọng
|
|
125
219
|
|
|
126
|
-
|
|
127
|
-
| --------- | ---------- |
|
|
128
|
-
| `<` | `<` |
|
|
129
|
-
| `>` | `>` |
|
|
130
|
-
| `&` | `&` |
|
|
131
|
-
| `"` | `"` |
|
|
132
|
-
| `'` | `'` |
|
|
220
|
+
⚠️ **Pipe trả về escaped text, không phải HTML an toàn để bind vào `[innerHTML]`**: Sau khi escape, chuỗi sẽ hiển thị HTML entities dưới dạng text thuần. Nếu bind vào `[innerHTML]`, nội dung sẽ hiển thị là text (entities sẽ được decode thành ký tự gốc dạng text), không render thành HTML. Đây là hành vi đúng — mục tiêu là ngăn render HTML nguy hiểm.
|
|
133
221
|
|
|
134
|
-
|
|
222
|
+
⚠️ **Dùng hàm `escapeHtml` thay vì inject pipe trong TypeScript**: Pipe là thin wrapper — khi cần escape trong logic TS, import trực tiếp `escapeHtml` từ `@libs-ui/utils` thay vì inject `LibsUiPipesEscapeHtmlPipe`. Không cần khai báo trong `providers[]`.
|
|
135
223
|
|
|
136
|
-
|
|
137
|
-
| --------------- | ------- | ---------------- |
|
|
138
|
-
| Angular | 18+ | Framework |
|
|
139
|
-
| Angular Signals | - | State management |
|
|
140
|
-
| TailwindCSS | 3.x | Styling |
|
|
224
|
+
⚠️ **Không dùng pipe này để sanitize HTML cho phép render**: Nếu cần cho phép một số HTML tags an toàn (bold, italic...) mà vẫn block XSS, dùng `xssFilter` từ `@libs-ui/utils` thay vì pipe này.
|
|
141
225
|
|
|
142
226
|
## Demo
|
|
143
227
|
|
|
@@ -145,16 +229,17 @@ Pipe escape các ký tự HTML đặc biệt sau:
|
|
|
145
229
|
npx nx serve core-ui
|
|
146
230
|
```
|
|
147
231
|
|
|
148
|
-
|
|
149
|
-
- Production: `https://libs-ui.mobio.vn/pipes/escape-html`
|
|
232
|
+
Truy cập: http://localhost:4500/pipes/escape-html
|
|
150
233
|
|
|
151
234
|
## Unit Tests
|
|
152
235
|
|
|
153
236
|
```bash
|
|
237
|
+
# Chạy test cho lib
|
|
154
238
|
npx nx test pipes-escape-html
|
|
155
|
-
npx nx test pipes-escape-html --coverage
|
|
156
|
-
```
|
|
157
239
|
|
|
158
|
-
|
|
240
|
+
# Chạy test với coverage
|
|
241
|
+
npx nx test pipes-escape-html --coverage
|
|
159
242
|
|
|
160
|
-
|
|
243
|
+
# Chạy test single file
|
|
244
|
+
npx nx test pipes-escape-html --testFile=libs-ui/pipes/escape-html/src/escape-html.pipe.spec.ts
|
|
245
|
+
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libs-ui-pipes-escape-html.mjs","sources":["../../../../../libs-ui/pipes/escape-html/src/escape-html.pipe.ts","../../../../../libs-ui/pipes/escape-html/src/libs-ui-pipes-escape-html.ts"],"sourcesContent":["import { Pipe, PipeTransform } from '@angular/core';\nimport { escapeHtml } from '@libs-ui/utils';\n@Pipe({\n name: 'LibsUiPipesEscapeHtmlPipe',\n standalone: true,\n})\nexport class LibsUiPipesEscapeHtmlPipe implements PipeTransform {\n transform(value: string | null | undefined): string {\n if (!value) {\n return '';\n }\n\n return escapeHtml(value);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAMa,yBAAyB,CAAA;AACpC,IAAA,SAAS,CAAC,KAAgC,EAAA;QACxC,IAAI,CAAC,KAAK,EAAE;AACV,YAAA,OAAO,EAAE;
|
|
1
|
+
{"version":3,"file":"libs-ui-pipes-escape-html.mjs","sources":["../../../../../libs-ui/pipes/escape-html/src/escape-html.pipe.ts","../../../../../libs-ui/pipes/escape-html/src/libs-ui-pipes-escape-html.ts"],"sourcesContent":["import { Pipe, PipeTransform } from '@angular/core';\nimport { escapeHtml } from '@libs-ui/utils';\n@Pipe({\n name: 'LibsUiPipesEscapeHtmlPipe',\n standalone: true,\n})\nexport class LibsUiPipesEscapeHtmlPipe implements PipeTransform {\n transform(value: string | null | undefined): string {\n if (!value) {\n return '';\n }\n\n return escapeHtml(value);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAMa,yBAAyB,CAAA;AACpC,IAAA,SAAS,CAAC,KAAgC,EAAA;QACxC,IAAI,CAAC,KAAK,EAAE;AACV,YAAA,OAAO,EAAE,CAAC;SACX;AAED,QAAA,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;KAC1B;wGAPU,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,IAAA,EAAA,CAAA,CAAA;sGAAzB,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,IAAA,EAAA,2BAAA,EAAA,CAAA,CAAA;;4FAAzB,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAJrC,IAAI;AAAC,YAAA,IAAA,EAAA,CAAA;AACJ,oBAAA,IAAI,EAAE,2BAA2B;AACjC,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA,CAAA;;;ACLD;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libs-ui/pipes-escape-html",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.357-0",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/core": ">=18.0.0",
|
|
6
|
-
"@libs-ui/utils": "0.2.
|
|
6
|
+
"@libs-ui/utils": "0.2.357-0"
|
|
7
7
|
},
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"module": "fesm2022/libs-ui-pipes-escape-html.mjs",
|