evnict-kit 0.2.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.
Files changed (138) hide show
  1. package/README.md +19 -0
  2. package/bin/cli.js +38 -0
  3. package/package.json +48 -0
  4. package/src/commands/add.js +129 -0
  5. package/src/commands/init-check.js +19 -0
  6. package/src/commands/init-context.js +37 -0
  7. package/src/commands/init-rules.js +42 -0
  8. package/src/commands/init-workflow.js +36 -0
  9. package/src/commands/init.js +722 -0
  10. package/src/utils/config.js +167 -0
  11. package/src/utils/file.js +53 -0
  12. package/templates/GETTING-STARTED.md +196 -0
  13. package/templates/content/context/AGENTS.md.template +462 -0
  14. package/templates/content/rules/01-evnict-kit-general-rules.md +303 -0
  15. package/templates/content/rules/02-evnict-kit-security-rules.md +423 -0
  16. package/templates/content/rules/03-evnict-kit-backend-conventions.md +383 -0
  17. package/templates/content/rules/04-evnict-kit-frontend-conventions.md +402 -0
  18. package/templates/content/rules/05-evnict-kit-project-conventions.md +228 -0
  19. package/templates/content/skills/evnict-kit-brainstorm/SKILL.md +140 -0
  20. package/templates/content/skills/evnict-kit-bug-fix/SKILL.md +108 -0
  21. package/templates/content/skills/evnict-kit-checkpoint/SKILL.md +156 -0
  22. package/templates/content/skills/evnict-kit-code-review/SKILL.md +158 -0
  23. package/templates/content/skills/evnict-kit-coordinate/SKILL.md +274 -0
  24. package/templates/content/skills/evnict-kit-create-api/SKILL.md +281 -0
  25. package/templates/content/skills/evnict-kit-create-component/SKILL.md +263 -0
  26. package/templates/content/skills/evnict-kit-create-page/SKILL.md +247 -0
  27. package/templates/content/skills/evnict-kit-database-migration/SKILL.md +164 -0
  28. package/templates/content/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
  29. package/templates/content/skills/evnict-kit-finish-branch/SKILL.md +87 -0
  30. package/templates/content/skills/evnict-kit-fix-attt/SKILL.md +129 -0
  31. package/templates/content/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
  32. package/templates/content/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
  33. package/templates/content/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
  34. package/templates/content/skills/evnict-kit-onboard/SKILL.md +143 -0
  35. package/templates/content/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
  36. package/templates/content/skills/evnict-kit-receiving-review/SKILL.md +89 -0
  37. package/templates/content/skills/evnict-kit-security-audit/SKILL.md +190 -0
  38. package/templates/content/skills/evnict-kit-spec/SKILL.md +237 -0
  39. package/templates/content/skills/evnict-kit-tdd/SKILL.md +413 -0
  40. package/templates/content/skills/evnict-kit-wiki/SKILL.md +412 -0
  41. package/templates/content/workflows/evnict-kit-archive-wiki.md +100 -0
  42. package/templates/content/workflows/evnict-kit-attt.md +100 -0
  43. package/templates/content/workflows/evnict-kit-bug-fix.md +107 -0
  44. package/templates/content/workflows/evnict-kit-feature-large.md +393 -0
  45. package/templates/content/workflows/evnict-kit-feature-small.md +86 -0
  46. package/templates/content/workflows/evnict-kit-handoff.md +243 -0
  47. package/templates/content/workflows/evnict-kit-implement.md +247 -0
  48. package/templates/content/workflows/evnict-kit-init-check.md +76 -0
  49. package/templates/content/workflows/evnict-kit-init-context.md +58 -0
  50. package/templates/content/workflows/evnict-kit-init-rules.md +114 -0
  51. package/templates/content/workflows/evnict-kit-init-wiki.md +80 -0
  52. package/templates/content/workflows/evnict-kit-plan.md +308 -0
  53. package/templates/content/workflows/evnict-kit-review.md +53 -0
  54. package/templates/content/workflows/evnict-kit-spec-archive.md +53 -0
  55. package/templates/content/workflows/evnict-kit-wiki-archive-feature.md +164 -0
  56. package/templates/content/workflows/evnict-kit-wiki-query.md +91 -0
  57. package/templates/content/workflows/evnict-kit-wiki-scan-project.md +272 -0
  58. package/templates/context/AGENT.md.template +9 -0
  59. package/templates/context/AGENTS.md.template +462 -0
  60. package/templates/context/CLAUDE.md.template +301 -0
  61. package/templates/context/copilot-instructions.md.template +60 -0
  62. package/templates/context/cursorrules.template +114 -0
  63. package/templates/instruct/Instruct-Agent-AI.be.md +96 -0
  64. package/templates/instruct/Instruct-Agent-AI.fe.md +79 -0
  65. package/templates/rules/antigravity/01-evnict-kit-general-rules.md +303 -0
  66. package/templates/rules/antigravity/02-evnict-kit-security-rules.md +423 -0
  67. package/templates/rules/antigravity/03-evnict-kit-backend-conventions.md +383 -0
  68. package/templates/rules/antigravity/04-evnict-kit-frontend-conventions.md +402 -0
  69. package/templates/rules/antigravity/05-evnict-kit-project-conventions.md +228 -0
  70. package/templates/rules/claude/README.md +8 -0
  71. package/templates/rules/cursor/01-evnict-kit-general-rules.mdc +46 -0
  72. package/templates/rules/cursor/02-evnict-kit-security-rules.mdc +46 -0
  73. package/templates/rules/cursor/03-evnict-kit-backend-conventions.mdc +50 -0
  74. package/templates/rules/cursor/04-evnict-kit-frontend-conventions.mdc +43 -0
  75. package/templates/rules/cursor/05-evnict-kit-project-conventions.mdc +63 -0
  76. package/templates/rules/cursor/README.md +7 -0
  77. package/templates/skills/evnict-kit-brainstorm/SKILL.md +140 -0
  78. package/templates/skills/evnict-kit-bug-fix/SKILL.md +108 -0
  79. package/templates/skills/evnict-kit-checkpoint/SKILL.md +156 -0
  80. package/templates/skills/evnict-kit-code-review/SKILL.md +158 -0
  81. package/templates/skills/evnict-kit-coordinate/SKILL.md +274 -0
  82. package/templates/skills/evnict-kit-create-api/SKILL.md +281 -0
  83. package/templates/skills/evnict-kit-create-component/SKILL.md +263 -0
  84. package/templates/skills/evnict-kit-create-page/SKILL.md +247 -0
  85. package/templates/skills/evnict-kit-database-migration/SKILL.md +164 -0
  86. package/templates/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
  87. package/templates/skills/evnict-kit-finish-branch/SKILL.md +87 -0
  88. package/templates/skills/evnict-kit-fix-attt/SKILL.md +129 -0
  89. package/templates/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
  90. package/templates/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
  91. package/templates/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
  92. package/templates/skills/evnict-kit-onboard/SKILL.md +143 -0
  93. package/templates/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
  94. package/templates/skills/evnict-kit-receiving-review/SKILL.md +89 -0
  95. package/templates/skills/evnict-kit-security-audit/SKILL.md +190 -0
  96. package/templates/skills/evnict-kit-spec/SKILL.md +237 -0
  97. package/templates/skills/evnict-kit-tdd/SKILL.md +413 -0
  98. package/templates/skills/evnict-kit-wiki/SKILL.md +412 -0
  99. package/templates/wiki/README.md +35 -0
  100. package/templates/wiki/config.example.yaml +17 -0
  101. package/templates/wiki/package.json +17 -0
  102. package/templates/wiki/raw/notes/.gitkeep +1 -0
  103. package/templates/wiki/scripts/ingest.js +66 -0
  104. package/templates/workflows/antigravity/evnict-kit-archive-wiki.md +100 -0
  105. package/templates/workflows/antigravity/evnict-kit-attt.md +100 -0
  106. package/templates/workflows/antigravity/evnict-kit-bug-fix.md +107 -0
  107. package/templates/workflows/antigravity/evnict-kit-feature-large.md +393 -0
  108. package/templates/workflows/antigravity/evnict-kit-feature-small.md +86 -0
  109. package/templates/workflows/antigravity/evnict-kit-handoff.md +243 -0
  110. package/templates/workflows/antigravity/evnict-kit-implement.md +247 -0
  111. package/templates/workflows/antigravity/evnict-kit-init-check.md +76 -0
  112. package/templates/workflows/antigravity/evnict-kit-init-context.md +58 -0
  113. package/templates/workflows/antigravity/evnict-kit-init-rules.md +114 -0
  114. package/templates/workflows/antigravity/evnict-kit-init-wiki.md +80 -0
  115. package/templates/workflows/antigravity/evnict-kit-plan.md +308 -0
  116. package/templates/workflows/antigravity/evnict-kit-review.md +53 -0
  117. package/templates/workflows/antigravity/evnict-kit-spec-archive.md +53 -0
  118. package/templates/workflows/antigravity/evnict-kit-wiki-archive-feature.md +164 -0
  119. package/templates/workflows/antigravity/evnict-kit-wiki-query.md +91 -0
  120. package/templates/workflows/antigravity/evnict-kit-wiki-scan-project.md +272 -0
  121. package/templates/workflows/claude/README.md +6 -0
  122. package/templates/workflows/claude/evnict-kit-archive-wiki.md +98 -0
  123. package/templates/workflows/claude/evnict-kit-attt.md +98 -0
  124. package/templates/workflows/claude/evnict-kit-bug-fix.md +105 -0
  125. package/templates/workflows/claude/evnict-kit-feature-large.md +391 -0
  126. package/templates/workflows/claude/evnict-kit-feature-small.md +84 -0
  127. package/templates/workflows/claude/evnict-kit-handoff.md +240 -0
  128. package/templates/workflows/claude/evnict-kit-implement.md +245 -0
  129. package/templates/workflows/claude/evnict-kit-init-check.md +74 -0
  130. package/templates/workflows/claude/evnict-kit-init-context.md +56 -0
  131. package/templates/workflows/claude/evnict-kit-init-rules.md +112 -0
  132. package/templates/workflows/claude/evnict-kit-init-wiki.md +78 -0
  133. package/templates/workflows/claude/evnict-kit-plan.md +305 -0
  134. package/templates/workflows/claude/evnict-kit-review.md +51 -0
  135. package/templates/workflows/claude/evnict-kit-spec-archive.md +51 -0
  136. package/templates/workflows/claude/evnict-kit-wiki-archive-feature.md +162 -0
  137. package/templates/workflows/claude/evnict-kit-wiki-query.md +89 -0
  138. package/templates/workflows/claude/evnict-kit-wiki-scan-project.md +270 -0
@@ -0,0 +1,423 @@
1
+ ---
2
+ trigger: always_on
3
+ ---
4
+ # Security Rules (ATTT) — EVNICT Standard
5
+ **Activation Mode: Always On**
6
+ **Source: QĐ-TTPM Điều 8, OWASP Top 10:2021**
7
+
8
+ > **Vi phạm các quy tắc sau sẽ gây lỗ hổng bảo mật nghiêm trọng!**
9
+ > Lỗi ATTT Critical/High → Thông báo Tech Lead & Tổ trưởng ANTT ngay lập tức.
10
+
11
+ ---
12
+
13
+ ## 1. SQL INJECTION PREVENTION (ATTT01) — CRITICAL
14
+
15
+ ### Java Spring Boot + JOOQ
16
+ ```java
17
+ // ❌ SAI — String concatenation → SQL Injection
18
+ String sql = "SELECT * FROM CUSTOMER WHERE NAME = '" + name + "'";
19
+ jdbcTemplate.query(sql, mapper);
20
+
21
+ // ❌ SAI — JOOQ nhưng vẫn nối chuỗi
22
+ dsl.fetch("SELECT * FROM CUSTOMER WHERE NAME = '" + name + "'");
23
+ DSL.field("NAME = '" + name + "'");
24
+ DSL.condition("STATUS = " + status);
25
+
26
+ // ✅ ĐÚNG — JOOQ type-safe (mặc định đã an toàn)
27
+ dsl.selectFrom(CUSTOMER)
28
+ .where(CUSTOMER.NAME.eq(name))
29
+ .and(CUSTOMER.STATUS.eq(status))
30
+ .fetch();
31
+
32
+ // ✅ ĐÚNG — JdbcTemplate parameterized
33
+ jdbcTemplate.query("SELECT * FROM CUSTOMER WHERE NAME = ?",
34
+ new Object[]{name}, mapper);
35
+
36
+ // ✅ ĐÚNG — JPA @Query
37
+ @Query("SELECT c FROM Customer c WHERE c.name = :name")
38
+ List<Customer> findByName(@Param("name") String name);
39
+ ```
40
+
41
+ ### ASP.NET Core
42
+ ```csharp
43
+ // ❌ SAI
44
+ var sql = $"SELECT * FROM Users WHERE Name = '{name}'";
45
+ context.Database.ExecuteSqlRaw(sql);
46
+
47
+ // ✅ ĐÚNG
48
+ context.Users.Where(u => u.Name == name).ToList();
49
+ context.Database.ExecuteSqlRaw("SELECT * FROM Users WHERE Name = {0}", name);
50
+ ```
51
+
52
+ ### Scan command
53
+ ```bash
54
+ # Tìm string concatenation trong SQL
55
+ grep -rn "query.*+.*\"" --include="*.java" src/
56
+ grep -rn "createNativeQuery.*+\s" --include="*.java" src/
57
+ grep -rn "DSL\.field(.*+\s" --include="*.java" src/
58
+ grep -rn "DSL\.condition(.*+\s" --include="*.java" src/
59
+ grep -rn "FromSqlRaw.*\$\"" --include="*.cs" src/
60
+ ```
61
+
62
+ ### Dynamic ORDER BY — Case đặc biệt
63
+ ```java
64
+ // ❌ SAI — ORDER BY từ user input
65
+ dsl.selectFrom(TABLE).orderBy(DSL.field(userSortColumn));
66
+
67
+ // ✅ ĐÚNG — Whitelist allowed columns
68
+ private static final Map<String, Field<?>> SORT_FIELDS = Map.of(
69
+ "name", CUSTOMER.NAME,
70
+ "date", CUSTOMER.CREATED_DATE,
71
+ "status", CUSTOMER.STATUS
72
+ );
73
+ Field<?> sortField = SORT_FIELDS.getOrDefault(userSortColumn, CUSTOMER.NAME);
74
+ dsl.selectFrom(TABLE).orderBy(sortField);
75
+ ```
76
+
77
+ ---
78
+
79
+ ## 2. XSS PREVENTION (ATTT02) — CRITICAL
80
+
81
+ ### Angular
82
+ ```typescript
83
+ // ❌ SAI — Bypass sanitizer
84
+ <div [innerHTML]="userContent"></div>
85
+ this.sanitizer.bypassSecurityTrustHtml(userContent);
86
+
87
+ // ✅ ĐÚNG — Interpolation (auto-escaped)
88
+ <div>{{ userContent }}</div>
89
+
90
+ // ✅ Nếu BẮT BUỘC dùng innerHTML → DOMPurify
91
+ import DOMPurify from 'dompurify';
92
+ this.safeHtml = DOMPurify.sanitize(userContent);
93
+ ```
94
+
95
+ ### React
96
+ ```tsx
97
+ // ❌ SAI
98
+ <div dangerouslySetInnerHTML={{ __html: userContent }} />
99
+
100
+ // ✅ ĐÚNG
101
+ <div>{userContent}</div>
102
+ ```
103
+
104
+ ### Backend — Response Headers
105
+ ```java
106
+ // ✅ Spring Security — đặt trong SecurityConfiguration
107
+ http.headers(headers -> headers
108
+ .contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'"))
109
+ .xssProtection(xss -> xss.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK))
110
+ .contentTypeOptions(Customizer.withDefaults()) // X-Content-Type-Options: nosniff
111
+ );
112
+ ```
113
+
114
+ ### Scan command
115
+ ```bash
116
+ grep -rn "\[innerHTML\]" --include="*.html" src/
117
+ grep -rn "bypassSecurityTrust" --include="*.ts" src/
118
+ grep -rn "dangerouslySetInnerHTML" --include="*.tsx" --include="*.jsx" src/
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 3. CSRF PROTECTION (ATTT03)
124
+
125
+ ### Spring Boot
126
+ ```java
127
+ // ✅ Spring Security — CSRF cho stateful sessions
128
+ http.csrf(csrf -> csrf
129
+ .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
130
+ );
131
+
132
+ // ✅ Nếu dùng JWT stateless → có thể disable CSRF
133
+ // (JWT token thay thế CSRF protection)
134
+ http.csrf(csrf -> csrf.disable()); // CHỈ KHI dùng JWT authentication
135
+ ```
136
+
137
+ ### Angular
138
+ ```typescript
139
+ // ✅ Angular HttpClient tự handle XSRF-TOKEN cookie
140
+ // Đảm bảo HttpClientModule đã import
141
+ import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
142
+
143
+ @NgModule({
144
+ imports: [
145
+ HttpClientModule,
146
+ HttpClientXsrfModule.withOptions({
147
+ cookieName: 'XSRF-TOKEN',
148
+ headerName: 'X-XSRF-TOKEN'
149
+ })
150
+ ]
151
+ })
152
+ ```
153
+
154
+ ---
155
+
156
+ ## 4. SENSITIVE DATA HANDLING (R01 + ATTT07) — CRITICAL
157
+
158
+ ### Nghiêm cấm hardcode (QĐ-TTPM Điều 8, Mục 8.2)
159
+ ```java
160
+ // ❌ SAI — TUYỆT ĐỐI KHÔNG
161
+ String password = "admin123";
162
+ String apiKey = "sk-1234567890";
163
+ String dbUrl = "jdbc:oracle:thin:@10.0.1.5:1521:PROD";
164
+ String jwtSecret = "my-super-secret-key";
165
+
166
+ // ✅ ĐÚNG — Environment variables / Application config
167
+ // application.yml:
168
+ // spring.datasource.password: ${DB_PASSWORD}
169
+ // jwt.public-key-path: ${JWT_KEY_PATH}
170
+ String password = System.getenv("DB_PASSWORD");
171
+ @Value("${jwt.public-key-path}") String keyPath;
172
+ ```
173
+
174
+ ### .gitignore BẮT BUỘC chặn
175
+ ```
176
+ .env
177
+ .env.local
178
+ .env.production
179
+ *.key
180
+ *.pem
181
+ *.p12
182
+ *.jks
183
+ application-prod.yml
184
+ ```
185
+
186
+ ### Khi phát hiện secret đã commit
187
+ 1. **DỪNG NGAY** — không push thêm
188
+ 2. **Rotate secret** — đổi password/key mới ngay lập tức
189
+ 3. **Thông báo Tech Lead** + Tổ trưởng ANTT
190
+ 4. `git filter-branch` hoặc `BFG Repo-Cleaner` để xóa khỏi history
191
+
192
+ ---
193
+
194
+ ## 5. ERROR HANDLING — KHÔNG EXPOSE STACK TRACE (RB03)
195
+
196
+ ```java
197
+ // ❌ SAI — Stack trace lộ ra client
198
+ @ExceptionHandler(Exception.class)
199
+ public ResponseEntity<?> handle(Exception ex) {
200
+ return ResponseEntity.status(500).body(ex.toString()); // WRONG!
201
+ return ResponseEntity.status(500).body(ex.getStackTrace()); // WRONG!
202
+ return ResponseEntity.status(500).body(ex.getMessage()); // RISKY!
203
+ }
204
+
205
+ // ✅ ĐÚNG — Log nội bộ, message chung cho client
206
+ @RestControllerAdvice
207
+ public class GlobalExceptionHandler {
208
+
209
+ @ExceptionHandler(BusinessException.class)
210
+ public ResponseEntity<ResponseData> handleBusiness(BusinessException ex) {
211
+ log.warn("Business error: {}", ex.getMessage());
212
+ return ResponseEntity.ok(ResponseData.error(ex.getMessage()));
213
+ }
214
+
215
+ @ExceptionHandler(Exception.class)
216
+ public ResponseEntity<ResponseData> handleGeneral(Exception ex) {
217
+ log.error("Unexpected error", ex); // Log ĐẦY ĐỦ stack trace nội bộ
218
+ return ResponseEntity.status(500)
219
+ .body(ResponseData.error("Có lỗi xảy ra trong hệ thống")); // Message CHUNG
220
+ }
221
+ }
222
+ ```
223
+
224
+ ---
225
+
226
+ ## 6. AUTHENTICATION & AUTHORIZATION (RB05 + ATTT04)
227
+
228
+ ### JWT Checklist
229
+ - ✅ Algorithm: RS256 hoặc RS512 (asymmetric) — **KHÔNG dùng HS256 shared secret**
230
+ - ✅ Key size ≥ 2048 bits (khuyến nghị 4096)
231
+ - ✅ Token expiration ≤ 24h
232
+ - ✅ Refresh token rotation
233
+ - ✅ Validate issuer (iss) + audience (aud)
234
+ - ❌ KHÔNG lưu sensitive data trong JWT payload (password, PII)
235
+
236
+ ### Mọi API endpoint mới
237
+ 1. Đã được bảo vệ bởi authentication filter?
238
+ 2. Role/permission nào được truy cập?
239
+ 3. Cần data-level authorization không? (VD: chỉ xem data đơn vị mình)
240
+ 4. Đã khai báo trong hệ thống phân quyền (Q_FUNCTION)?
241
+
242
+ ### Scan weak JWT
243
+ ```bash
244
+ grep -rn "HS256\|alg.*none\|secret.*=\"" --include="*.java" src/
245
+ grep -rn "parse.*Unsafe\|without.*verification" --include="*.java" src/
246
+ ```
247
+
248
+ ---
249
+
250
+ ## 7. LOGGING — KHÔNG LOG SENSITIVE DATA (R04)
251
+
252
+ ```java
253
+ // ✅ ĐÚNG — Log ID, action, result
254
+ log.info("User {} created customer id={}", userId, customerId);
255
+ log.info("Import completed: {} records processed", count);
256
+ log.warn("Login failed for user: {}", username);
257
+
258
+ // ❌ SAI — Log sensitive data
259
+ log.info("Login: user={}, password={}", username, password); // NEVER!
260
+ log.debug("Token: {}", jwtToken); // NEVER!
261
+ log.info("Customer phone: {}", customer.getPhone()); // PII!
262
+ log.info("Card number: {}", payment.getCardNumber()); // PCI!
263
+ ```
264
+
265
+ ### Danh sách NGHIÊM CẤM log
266
+ - Mật khẩu, tokens, JWT, session IDs
267
+ - Số CMND/CCCD, số điện thoại cá nhân
268
+ - Email cá nhân, địa chỉ nhà
269
+ - Thông tin thẻ/tài khoản ngân hàng
270
+ - Dữ liệu khách hàng EVN (mã KH, số công tơ, điện năng tiêu thụ)
271
+
272
+ ---
273
+
274
+ ## 8. FILE UPLOAD SECURITY (ATTT06)
275
+
276
+ ```java
277
+ // ❌ SAI — Lưu trực tiếp với tên gốc
278
+ Files.copy(file.getInputStream(), Paths.get("/uploads/" + file.getOriginalFilename()));
279
+
280
+ // ✅ ĐÚNG — Validate + rename + lưu MinIO
281
+ public String uploadFile(MultipartFile file) {
282
+ // 1. Validate extension (whitelist)
283
+ String ext = getExtension(file.getOriginalFilename());
284
+ if (!ALLOWED_EXTENSIONS.contains(ext.toLowerCase())) {
285
+ throw new BusinessException("File type not allowed: " + ext);
286
+ }
287
+
288
+ // 2. Validate MIME type
289
+ String mimeType = file.getContentType();
290
+ if (!ALLOWED_MIMES.contains(mimeType)) {
291
+ throw new BusinessException("Invalid MIME type");
292
+ }
293
+
294
+ // 3. Validate size
295
+ if (file.getSize() > MAX_FILE_SIZE) {
296
+ throw new BusinessException("File too large");
297
+ }
298
+
299
+ // 4. Rename với UUID
300
+ String storageName = UUID.randomUUID() + "." + ext;
301
+
302
+ // 5. Lưu ngoài web root (MinIO/S3)
303
+ minioClient.putObject(PutObjectArgs.builder()
304
+ .bucket(bucket).object(storageName)
305
+ .stream(file.getInputStream(), file.getSize(), -1)
306
+ .contentType(mimeType)
307
+ .build());
308
+
309
+ return storageName;
310
+ }
311
+ ```
312
+
313
+ ### Checklist upload
314
+ - [ ] Extension whitelist (pdf, docx, xlsx, png, jpg)
315
+ - [ ] MIME type check (không chỉ dựa vào extension)
316
+ - [ ] File size limit (config, không hardcode)
317
+ - [ ] UUID rename — KHÔNG dùng tên gốc cho storage path
318
+ - [ ] Lưu MinIO/S3, KHÔNG lưu trong static/
319
+ - [ ] KHÔNG execute uploaded files
320
+
321
+ ---
322
+
323
+ ## 9. DATA EXPOSURE PREVENTION (ATTT07)
324
+
325
+ ```java
326
+ // ❌ SAI — Return Entity trực tiếp (chứa password hash, internal IDs)
327
+ @GetMapping("/users/{id}")
328
+ public ResponseEntity<?> getUser(@PathVariable Long id) {
329
+ return ResponseEntity.ok(userRepository.findById(id)); // WRONG!
330
+ }
331
+
332
+ // ✅ ĐÚNG — Dùng DTO, chỉ expose fields cần thiết
333
+ @GetMapping("/users/{id}")
334
+ public ResponseEntity<ResponseData> getUser(@PathVariable Long id) {
335
+ UserDTO dto = userService.getById(id); // DTO không có password, internalId
336
+ return ResponseEntity.ok(ResponseData.ok(dto));
337
+ }
338
+ ```
339
+
340
+ ### Scan command
341
+ ```bash
342
+ grep -rn "ResponseEntity.ok(.*repository\|ResponseEntity.ok(.*entity" --include="*.java" src/
343
+ grep -rn "printStackTrace\|getStackTrace" --include="*.java" src/
344
+ ```
345
+
346
+ ---
347
+
348
+ ## 10. DEPENDENCY CVE (ATTT05)
349
+
350
+ ### Scan commands
351
+ ```bash
352
+ # Java Maven
353
+ ./mvnw org.owasp:dependency-check-maven:check
354
+ ./mvnw versions:display-dependency-updates
355
+
356
+ # Node.js / Angular / React
357
+ npm audit
358
+ npm audit fix
359
+
360
+ # .NET
361
+ dotnet list package --vulnerable
362
+ ```
363
+
364
+ ### Severity response time
365
+ | Severity | Response | Action |
366
+ |----------|----------|--------|
367
+ | Critical | 24h | Hotfix branch, deploy ASAP |
368
+ | High | Sprint hiện tại | Fix trong sprint |
369
+ | Medium | Sprint tiếp theo | Lên kế hoạch |
370
+ | Low | Đánh giá | Theo dõi, fix khi có thể |
371
+
372
+ ---
373
+
374
+ ## 11. OWASP TOP 10:2021 — QUICK REFERENCE
375
+
376
+ | # | Category | Quy tắc | Scan |
377
+ |---|----------|---------|------|
378
+ | A01 | Broken Access Control | Auth mọi route, check ownership | Manual |
379
+ | A02 | Cryptographic Failures | RS256 JWT, key ≥ 2048 | grep HS256 |
380
+ | A03 | Injection | Parameterized queries | grep concat SQL |
381
+ | A04 | Insecure Design | Validate upload, rate limit | Manual |
382
+ | A05 | Security Misconfiguration | No hardcode secrets | grep password= |
383
+ | A06 | Vulnerable Components | Dependency scan | mvnw/npm audit |
384
+ | A07 | Auth Failures | JWT exp, refresh rotation | grep JWT config |
385
+ | A08 | Data Integrity | Migration only, signed JWTs | Manual |
386
+ | A09 | Logging Failures | No PII in logs | grep log.*password |
387
+ | A10 | SSRF | Validate URLs, whitelist | grep URL.*+ |
388
+
389
+ ---
390
+
391
+ ## 12. ANTI-VIBE-CODING (QĐ-TTPM Điều 8, Mục 8.7)
392
+
393
+ ### Dấu hiệu nhận biết
394
+ - Agent sửa cùng đoạn code > **3 lần** mà lỗi chưa fix → **DỪNG NGAY**
395
+ - Code sinh ra chứa logic trùng lặp hoặc mâu thuẫn
396
+ - Module/function phình to bất thường so với yêu cầu
397
+ - Developer KHÔNG giải thích được logic code
398
+
399
+ ### Quy tắc xử lý
400
+ 1. **Dừng phiên AI** khi phát hiện dấu hiệu
401
+ 2. **Chuyển sang phân tích thủ công**
402
+ 3. **Chia nhỏ yêu cầu** — 1 prompt = 1 nhiệm vụ đơn lẻ
403
+ 4. **Giải trình bắt buộc** — code không giải thích được = không merge
404
+
405
+ ---
406
+
407
+ ## 13. CHECKPOINT & ROLLBACK (QĐ-TTPM Điều 8, Mục 8.8)
408
+
409
+ ### TRƯỚC KHI dùng AI Agent — BẮT BUỘC
410
+ ```bash
411
+ # Option 1: Branch riêng
412
+ git checkout -b feature/ai-task-xxx
413
+
414
+ # Option 2: Commit checkpoint
415
+ git add . && git commit -m "checkpoint: before AI session"
416
+
417
+ # Option 3: Stash
418
+ git stash
419
+ ```
420
+
421
+ ### Yêu cầu
422
+ - Revert được trong ≤ **1 thao tác git** (`git revert` hoặc `git reset`)
423
+ - Thay đổi lớn (nhiều file/module) → Rollback plan bằng văn bản trước khi thực hiện