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.
- package/README.md +19 -0
- package/bin/cli.js +38 -0
- package/package.json +48 -0
- package/src/commands/add.js +129 -0
- package/src/commands/init-check.js +19 -0
- package/src/commands/init-context.js +37 -0
- package/src/commands/init-rules.js +42 -0
- package/src/commands/init-workflow.js +36 -0
- package/src/commands/init.js +722 -0
- package/src/utils/config.js +167 -0
- package/src/utils/file.js +53 -0
- package/templates/GETTING-STARTED.md +196 -0
- package/templates/content/context/AGENTS.md.template +462 -0
- package/templates/content/rules/01-evnict-kit-general-rules.md +303 -0
- package/templates/content/rules/02-evnict-kit-security-rules.md +423 -0
- package/templates/content/rules/03-evnict-kit-backend-conventions.md +383 -0
- package/templates/content/rules/04-evnict-kit-frontend-conventions.md +402 -0
- package/templates/content/rules/05-evnict-kit-project-conventions.md +228 -0
- package/templates/content/skills/evnict-kit-brainstorm/SKILL.md +140 -0
- package/templates/content/skills/evnict-kit-bug-fix/SKILL.md +108 -0
- package/templates/content/skills/evnict-kit-checkpoint/SKILL.md +156 -0
- package/templates/content/skills/evnict-kit-code-review/SKILL.md +158 -0
- package/templates/content/skills/evnict-kit-coordinate/SKILL.md +274 -0
- package/templates/content/skills/evnict-kit-create-api/SKILL.md +281 -0
- package/templates/content/skills/evnict-kit-create-component/SKILL.md +263 -0
- package/templates/content/skills/evnict-kit-create-page/SKILL.md +247 -0
- package/templates/content/skills/evnict-kit-database-migration/SKILL.md +164 -0
- package/templates/content/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
- package/templates/content/skills/evnict-kit-finish-branch/SKILL.md +87 -0
- package/templates/content/skills/evnict-kit-fix-attt/SKILL.md +129 -0
- package/templates/content/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
- package/templates/content/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
- package/templates/content/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
- package/templates/content/skills/evnict-kit-onboard/SKILL.md +143 -0
- package/templates/content/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
- package/templates/content/skills/evnict-kit-receiving-review/SKILL.md +89 -0
- package/templates/content/skills/evnict-kit-security-audit/SKILL.md +190 -0
- package/templates/content/skills/evnict-kit-spec/SKILL.md +237 -0
- package/templates/content/skills/evnict-kit-tdd/SKILL.md +413 -0
- package/templates/content/skills/evnict-kit-wiki/SKILL.md +412 -0
- package/templates/content/workflows/evnict-kit-archive-wiki.md +100 -0
- package/templates/content/workflows/evnict-kit-attt.md +100 -0
- package/templates/content/workflows/evnict-kit-bug-fix.md +107 -0
- package/templates/content/workflows/evnict-kit-feature-large.md +393 -0
- package/templates/content/workflows/evnict-kit-feature-small.md +86 -0
- package/templates/content/workflows/evnict-kit-handoff.md +243 -0
- package/templates/content/workflows/evnict-kit-implement.md +247 -0
- package/templates/content/workflows/evnict-kit-init-check.md +76 -0
- package/templates/content/workflows/evnict-kit-init-context.md +58 -0
- package/templates/content/workflows/evnict-kit-init-rules.md +114 -0
- package/templates/content/workflows/evnict-kit-init-wiki.md +80 -0
- package/templates/content/workflows/evnict-kit-plan.md +308 -0
- package/templates/content/workflows/evnict-kit-review.md +53 -0
- package/templates/content/workflows/evnict-kit-spec-archive.md +53 -0
- package/templates/content/workflows/evnict-kit-wiki-archive-feature.md +164 -0
- package/templates/content/workflows/evnict-kit-wiki-query.md +91 -0
- package/templates/content/workflows/evnict-kit-wiki-scan-project.md +272 -0
- package/templates/context/AGENT.md.template +9 -0
- package/templates/context/AGENTS.md.template +462 -0
- package/templates/context/CLAUDE.md.template +301 -0
- package/templates/context/copilot-instructions.md.template +60 -0
- package/templates/context/cursorrules.template +114 -0
- package/templates/instruct/Instruct-Agent-AI.be.md +96 -0
- package/templates/instruct/Instruct-Agent-AI.fe.md +79 -0
- package/templates/rules/antigravity/01-evnict-kit-general-rules.md +303 -0
- package/templates/rules/antigravity/02-evnict-kit-security-rules.md +423 -0
- package/templates/rules/antigravity/03-evnict-kit-backend-conventions.md +383 -0
- package/templates/rules/antigravity/04-evnict-kit-frontend-conventions.md +402 -0
- package/templates/rules/antigravity/05-evnict-kit-project-conventions.md +228 -0
- package/templates/rules/claude/README.md +8 -0
- package/templates/rules/cursor/01-evnict-kit-general-rules.mdc +46 -0
- package/templates/rules/cursor/02-evnict-kit-security-rules.mdc +46 -0
- package/templates/rules/cursor/03-evnict-kit-backend-conventions.mdc +50 -0
- package/templates/rules/cursor/04-evnict-kit-frontend-conventions.mdc +43 -0
- package/templates/rules/cursor/05-evnict-kit-project-conventions.mdc +63 -0
- package/templates/rules/cursor/README.md +7 -0
- package/templates/skills/evnict-kit-brainstorm/SKILL.md +140 -0
- package/templates/skills/evnict-kit-bug-fix/SKILL.md +108 -0
- package/templates/skills/evnict-kit-checkpoint/SKILL.md +156 -0
- package/templates/skills/evnict-kit-code-review/SKILL.md +158 -0
- package/templates/skills/evnict-kit-coordinate/SKILL.md +274 -0
- package/templates/skills/evnict-kit-create-api/SKILL.md +281 -0
- package/templates/skills/evnict-kit-create-component/SKILL.md +263 -0
- package/templates/skills/evnict-kit-create-page/SKILL.md +247 -0
- package/templates/skills/evnict-kit-database-migration/SKILL.md +164 -0
- package/templates/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
- package/templates/skills/evnict-kit-finish-branch/SKILL.md +87 -0
- package/templates/skills/evnict-kit-fix-attt/SKILL.md +129 -0
- package/templates/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
- package/templates/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
- package/templates/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
- package/templates/skills/evnict-kit-onboard/SKILL.md +143 -0
- package/templates/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
- package/templates/skills/evnict-kit-receiving-review/SKILL.md +89 -0
- package/templates/skills/evnict-kit-security-audit/SKILL.md +190 -0
- package/templates/skills/evnict-kit-spec/SKILL.md +237 -0
- package/templates/skills/evnict-kit-tdd/SKILL.md +413 -0
- package/templates/skills/evnict-kit-wiki/SKILL.md +412 -0
- package/templates/wiki/README.md +35 -0
- package/templates/wiki/config.example.yaml +17 -0
- package/templates/wiki/package.json +17 -0
- package/templates/wiki/raw/notes/.gitkeep +1 -0
- package/templates/wiki/scripts/ingest.js +66 -0
- package/templates/workflows/antigravity/evnict-kit-archive-wiki.md +100 -0
- package/templates/workflows/antigravity/evnict-kit-attt.md +100 -0
- package/templates/workflows/antigravity/evnict-kit-bug-fix.md +107 -0
- package/templates/workflows/antigravity/evnict-kit-feature-large.md +393 -0
- package/templates/workflows/antigravity/evnict-kit-feature-small.md +86 -0
- package/templates/workflows/antigravity/evnict-kit-handoff.md +243 -0
- package/templates/workflows/antigravity/evnict-kit-implement.md +247 -0
- package/templates/workflows/antigravity/evnict-kit-init-check.md +76 -0
- package/templates/workflows/antigravity/evnict-kit-init-context.md +58 -0
- package/templates/workflows/antigravity/evnict-kit-init-rules.md +114 -0
- package/templates/workflows/antigravity/evnict-kit-init-wiki.md +80 -0
- package/templates/workflows/antigravity/evnict-kit-plan.md +308 -0
- package/templates/workflows/antigravity/evnict-kit-review.md +53 -0
- package/templates/workflows/antigravity/evnict-kit-spec-archive.md +53 -0
- package/templates/workflows/antigravity/evnict-kit-wiki-archive-feature.md +164 -0
- package/templates/workflows/antigravity/evnict-kit-wiki-query.md +91 -0
- package/templates/workflows/antigravity/evnict-kit-wiki-scan-project.md +272 -0
- package/templates/workflows/claude/README.md +6 -0
- package/templates/workflows/claude/evnict-kit-archive-wiki.md +98 -0
- package/templates/workflows/claude/evnict-kit-attt.md +98 -0
- package/templates/workflows/claude/evnict-kit-bug-fix.md +105 -0
- package/templates/workflows/claude/evnict-kit-feature-large.md +391 -0
- package/templates/workflows/claude/evnict-kit-feature-small.md +84 -0
- package/templates/workflows/claude/evnict-kit-handoff.md +240 -0
- package/templates/workflows/claude/evnict-kit-implement.md +245 -0
- package/templates/workflows/claude/evnict-kit-init-check.md +74 -0
- package/templates/workflows/claude/evnict-kit-init-context.md +56 -0
- package/templates/workflows/claude/evnict-kit-init-rules.md +112 -0
- package/templates/workflows/claude/evnict-kit-init-wiki.md +78 -0
- package/templates/workflows/claude/evnict-kit-plan.md +305 -0
- package/templates/workflows/claude/evnict-kit-review.md +51 -0
- package/templates/workflows/claude/evnict-kit-spec-archive.md +51 -0
- package/templates/workflows/claude/evnict-kit-wiki-archive-feature.md +162 -0
- package/templates/workflows/claude/evnict-kit-wiki-query.md +89 -0
- 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
|