@libs-ui/pipes-escape-html 0.2.356-9 → 0.2.357-1

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 CHANGED
@@ -1,35 +1,30 @@
1
1
  # @libs-ui/pipes-escape-html
2
2
 
3
- > Pipe để escape các ký tự HTML đặc biệt, ngăn chặn XSS attacks khi hiển thị nội dung không tin cậy.
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 để escape các ký tự HTML đặc biệt, giúp ngăn chặn XSS (Cross-Site Scripting) attacks khi hiển thị nội dung từ user input hoặc nguồn không tin cậy.
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ử `null` `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
- ### Tính năng
9
+ ## Tính năng
10
10
 
11
- - Escape các ký tự HTML đặc biệt: `<`, `>`, `&`, `"`, `'`
12
- - Xử lý null/undefined một cách an toàn
13
- - Ngăn chặn XSS attacks
14
- - OnPush Change Detection
15
- - Standalone
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 HTML từ user input để tránh XSS attacks
21
- - Escape các tự đặc biệt như `<`, `>`, `&`, `"`, `'` trong text
22
- - Hiển thị code snippets hoặc HTML examples trong UI
23
- - Render an toàn nội dung từ API hoặc database
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 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 YourComponent {}
39
+ export class MyComponent {}
45
40
  ```
46
41
 
47
- ## Ví dụ
42
+ ## Ví dụ sử dụng
48
43
 
49
- ### Basic
44
+ ### 1. Escape XSS script tag
50
45
 
51
- ```html
52
- <div>
53
- {{ '
54
- <script>
55
- alert('xss');
56
- </script>
57
- ' | LibsUiPipesEscapeHtmlPipe }}
58
- </div>
59
- <!-- Output: &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt; -->
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: &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;
61
+ }
60
62
  ```
61
63
 
62
- ### User Input
64
+ ### 2. Hiển thị an toàn user input chứa ký tự đặc biệt
63
65
 
64
66
  ```typescript
65
- export class CommentComponent {
66
- userComment = '<img src=x onerror="alert(1)">';
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: &lt;img src=x onerror=&quot;alert(1)&quot;&gt;
67
83
  }
68
84
  ```
69
85
 
70
- ```html
71
- <div class="user-comment">
72
- <p>{{ userComment | LibsUiPipesEscapeHtmlPipe }}</p>
73
- </div>
74
- <!-- Output: &lt;img src=x onerror=&quot;alert(1)&quot;&gt; -->
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: &lt;div class=&quot;test&quot;&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;
103
+ }
75
104
  ```
76
105
 
77
- ### Special Characters
106
+ ### 4. Xử lý null/undefined gracefully
78
107
 
79
- ```html
80
- <div>
81
- {{ '
82
- <div class="test">Hello & "World"</div>
83
- ' | LibsUiPipesEscapeHtmlPipe }}
84
- </div>
85
- <!-- Output: &lt;div class=&quot;test&quot;&gt;Hello &amp; &quot;World&quot;&lt;/div&gt; -->
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
- ### Null Handling
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
- export class ExampleComponent {
92
- nullValue = null;
93
- undefinedValue = undefined;
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: &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;
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
- <div>
99
- {{ nullValue | LibsUiPipesEscapeHtmlPipe }}
100
- <!-- Output: '' -->
101
- {{ undefinedValue | LibsUiPipesEscapeHtmlPipe }}
102
- <!-- Output: '' -->
103
- </div>
172
+ <!-- Escape XSS -->
173
+ {{ '<script>alert(1)</script>' | LibsUiPipesEscapeHtmlPipe }}
174
+ <!-- Kết quả: &lt;script&gt;alert(1)&lt;/script&gt; -->
175
+
176
+ <!-- Escape tự đặc biệt -->
177
+ {{ '<div class="test">Hello & World</div>' | LibsUiPipesEscapeHtmlPipe }}
178
+ <!-- Kết quả: &lt;div class=&quot;test&quot;&gt;Hello &amp; World&lt;/div&gt; -->
179
+
180
+ <!-- Null trả về chuỗi rỗng -->
181
+ {{ null | LibsUiPipesEscapeHtmlPipe }}
182
+ <!-- Kết quả: '' -->
104
183
  ```
105
184
 
106
- ## API
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
- ### LibsUiPipesEscapeHtmlPipe
192
+ pipe.transform('<script>alert(1)</script>');
193
+ // → '&lt;script&gt;alert(1)&lt;/script&gt;'
109
194
 
110
- #### Parameters
195
+ pipe.transform('<div class="a">Hello & "World"</div>');
196
+ // → '&lt;div class=&quot;a&quot;&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;'
111
197
 
112
- | Property | Type | Default | Description |
113
- | -------- | ----------------------------- | ------- | ------------------------------ |
114
- | `value` | `string \| null \| undefined` | - | Chuỗi cần escape HTML entities |
198
+ pipe.transform(null);
199
+ // ''
115
200
 
116
- #### Returns
201
+ pipe.transform(undefined);
202
+ // → ''
203
+
204
+ pipe.transform('');
205
+ // → ''
206
+ ```
117
207
 
118
- | Type | Description |
119
- | -------- | ---------------------------------- |
120
- | `string` | Chuỗi đã được escape HTML entities |
208
+ ## Bảng ký tự được escape
121
209
 
122
- ## Escaped Characters
210
+ | tự | HTML Entity |
211
+ |-------|-------------|
212
+ | `<` | `&lt;` |
213
+ | `>` | `&gt;` |
214
+ | `&` | `&amp;` |
215
+ | `"` | `&quot;` |
216
+ | `'` | `&#x27;` |
123
217
 
124
- Pipe escape các tự HTML đặc biệt sau:
218
+ ## Lưu ý quan trọng
125
219
 
126
- | Character | Escaped To |
127
- | --------- | ---------- |
128
- | `<` | `&lt;` |
129
- | `>` | `&gt;` |
130
- | `&` | `&amp;` |
131
- | `"` | `&quot;` |
132
- | `'` | `&#x27;` |
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
- ## Công nghệ
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
- | Technology | Version | Purpose |
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
- - Local: `http://localhost:4500/pipes/escape-html`
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
- ## License
240
+ # Chạy test với coverage
241
+ npx nx test pipes-escape-html --coverage
159
242
 
160
- MIT
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;QACX;AAEA,QAAA,OAAO,UAAU,CAAC,KAAK,CAAC;IAC1B;wGAPW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,IAAA,EAAA,CAAA;sGAAzB,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,IAAA,EAAA,2BAAA,EAAA,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;;;ACLD;;AAEG;;;;"}
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.356-9",
3
+ "version": "0.2.357-1",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=18.0.0",
6
- "@libs-ui/utils": "0.2.356-9"
6
+ "@libs-ui/utils": "0.2.357-1"
7
7
  },
8
8
  "sideEffects": false,
9
9
  "module": "fesm2022/libs-ui-pipes-escape-html.mjs",