Localize user-facing UI, agent output, and backend messages to Simplified Chinese
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
detections / detections (push) Waiting to run
docs / links (push) Waiting to run
web / web (push) Waiting to run
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
detections / detections (push) Waiting to run
docs / links (push) Waiting to run
web / web (push) Waiting to run
This commit is contained in:
@@ -21,13 +21,35 @@ func containsHan(s string) (rune, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// containsHangul 은 문자열에 한글 음절이 남아 있으면 true 를 돌려준다.
|
||||
// 중국어로 바뀐 뒤 한글이 되돌아오는 회귀를 잡는다.
|
||||
func containsHangul(s string) (rune, bool) {
|
||||
for _, r := range s {
|
||||
if unicode.Is(unicode.Hangul, r) {
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// assertChinese 는 s 에 한자가 있고 한글이 없음을 단언한다.
|
||||
func assertChinese(t *testing.T, label, s string) {
|
||||
t.Helper()
|
||||
if r, ok := containsHangul(s); ok {
|
||||
t.Fatalf("%s: 한글이 남아 있습니다 %q:\n%s", label, string(r), s)
|
||||
}
|
||||
if _, ok := containsHan(s); !ok {
|
||||
t.Fatalf("%s: 한자가 없습니다:\n%s", label, s)
|
||||
}
|
||||
}
|
||||
|
||||
func sampleFindingNode(t *testing.T) *db.Node {
|
||||
t.Helper()
|
||||
payload, err := json.Marshal(map[string]any{
|
||||
"vulnclass": "SQL Injection",
|
||||
"name": "로그인 폼 SQL 주입",
|
||||
"name": "登录表单 SQL 注入",
|
||||
"severity": "high",
|
||||
"summary": "로그인 파라미터에서 오류 기반 SQL 주입이 확인되었습니다.",
|
||||
"summary": "在登录参数中确认了基于错误的 SQL 注入。",
|
||||
"evidence": map[string]string{"poc": "' OR '1'='1"},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -40,20 +62,20 @@ func sampleDBFinding() *db.DBFinding {
|
||||
return &db.DBFinding{
|
||||
ID: 123,
|
||||
VulnClass: "SQL Injection",
|
||||
Name: "로그인 폼 SQL 주입",
|
||||
Name: "登录表单 SQL 注入",
|
||||
Severity: "high",
|
||||
Summary: "로그인 파라미터에서 오류 기반 SQL 주입이 확인되었습니다.",
|
||||
Summary: "在登录参数中确认了基于错误的 SQL 注入。",
|
||||
Evidence: "' OR '1'='1",
|
||||
Status: "confirmed",
|
||||
Report: "상세 분석 본문.",
|
||||
Report: "详细分析正文。",
|
||||
CreatedAt: time.Date(2026, 10, 4, 2, 30, 0, 0, time.UTC),
|
||||
EvidenceVersion: 2,
|
||||
TaskDescription: "데모 대상 침투 테스트",
|
||||
TaskDescription: "演示目标渗透测试",
|
||||
TrafficBindings: []db.FindingTrafficBinding{
|
||||
{
|
||||
ID: 9,
|
||||
Role: "request",
|
||||
Note: "주입 페이로드 전송 지점",
|
||||
Note: "注入载荷发送点",
|
||||
Snapshot: db.TrafficEvidenceSnapshot{
|
||||
Method: "POST",
|
||||
URL: "https://sandbox.local/login",
|
||||
@@ -68,23 +90,21 @@ func sampleDBFinding() *db.DBFinding {
|
||||
// 중국어 한자가 전혀 없음을 검증한다.
|
||||
func TestReportMarkdownLocalized(t *testing.T) {
|
||||
out := Markdown(Input{
|
||||
Title: "데모 작업",
|
||||
Goal: "샌드박스 전수 점검",
|
||||
Title: "演示任务",
|
||||
Goal: "沙箱全面检查",
|
||||
GeneratedAt: time.Date(2026, 10, 4, 2, 30, 0, 0, time.UTC),
|
||||
AssetCounts: map[string]int{"host": 2, "url": 5},
|
||||
Findings: []*db.Node{sampleFindingNode(t)},
|
||||
})
|
||||
if r, ok := containsHan(out); ok {
|
||||
t.Fatalf("보고서 골격에 중국어 한자 %q 가 남아 있다:\n%s", string(r), out)
|
||||
}
|
||||
assertChinese(t, "보고서", out)
|
||||
for _, want := range []string{
|
||||
"# 침투 테스트 보고서: 데모 작업",
|
||||
"- **작업 목표**: 샌드박스 전수 점검",
|
||||
"- **생성 시각**:",
|
||||
"## 요약",
|
||||
"- 확인된 취약점: **1**건",
|
||||
"## 취약점",
|
||||
"**PoC / 증거:**",
|
||||
"# 渗透测试报告: 演示任务",
|
||||
"- **任务目标**: 沙箱全面检查",
|
||||
"- **生成时间**:",
|
||||
"## 摘要",
|
||||
"- 已确认漏洞: **1** 个",
|
||||
"## 漏洞",
|
||||
"**PoC / 证据:**",
|
||||
} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("보고서 골격에 %q 가 없다", want)
|
||||
@@ -94,12 +114,10 @@ func TestReportMarkdownLocalized(t *testing.T) {
|
||||
|
||||
// TestReportMarkdownEmptyLocalized 는 취약점이 없을 때의 안내 문구가 한국어임을 검증한다.
|
||||
func TestReportMarkdownEmptyLocalized(t *testing.T) {
|
||||
out := Markdown(Input{Title: "빈 작업", GeneratedAt: time.Now()})
|
||||
if r, ok := containsHan(out); ok {
|
||||
t.Fatalf("빈 보고서에 중국어 한자 %q 가 남아 있다:\n%s", string(r), out)
|
||||
}
|
||||
if !strings.Contains(out, "_이번에 확인된 취약점이 없습니다._") {
|
||||
t.Errorf("빈 보고서 안내 문구가 한국어가 아니다:\n%s", out)
|
||||
out := Markdown(Input{Title: "空任务", GeneratedAt: time.Now()})
|
||||
assertChinese(t, "빈 보고서", out)
|
||||
if !strings.Contains(out, "_本次未确认到任何漏洞。_") {
|
||||
t.Errorf("빈 보고서 안내 문구가 중국어가 아니다:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,25 +126,23 @@ func TestReportMarkdownEmptyLocalized(t *testing.T) {
|
||||
func TestFindingsMarkdownLocalized(t *testing.T) {
|
||||
f := sampleDBFinding()
|
||||
out := FindingsMarkdown([]*db.DBFinding{f}, time.Date(2026, 10, 4, 2, 30, 0, 0, time.UTC))
|
||||
if r, ok := containsHan(out); ok {
|
||||
t.Fatalf("취약점 요약 보고서에 중국어 한자 %q 가 남아 있다:\n%s", string(r), out)
|
||||
}
|
||||
assertChinese(t, "취약점 요약 보고서", out)
|
||||
for _, want := range []string{
|
||||
"# 취약점 요약 보고서",
|
||||
"- **생성 시각**:",
|
||||
"- **취약점 총계**: 1건",
|
||||
"## 요약",
|
||||
"| 심각도 | 개수 |",
|
||||
"| 심각 |", "| 높음 |", "| 중간 |", "| 낮음 |", // 심각도 라벨(UI status.severity 와 동일)
|
||||
"## 취약점 상세",
|
||||
"- **유형**: SQL Injection",
|
||||
"- **상태**: confirmed",
|
||||
"- **소속 작업**: 데모 대상 침투 테스트",
|
||||
"- **발견 시각**:",
|
||||
"**증거:**",
|
||||
"**상세 보고서:**",
|
||||
"## 관련 트래픽 증거",
|
||||
"증거 버전: 2, 바인딩 개수: 1.",
|
||||
"# 漏洞摘要报告",
|
||||
"- **生成时间**:",
|
||||
"- **漏洞总数**: 1 个",
|
||||
"## 摘要",
|
||||
"| 严重程度 | 数量 |",
|
||||
"| 严重 |", "| 高危 |", "| 中危 |", "| 低危 |", // 심각도 라벨(UI status.severity 와 동일)
|
||||
"## 漏洞详情",
|
||||
"- **类型**: SQL Injection",
|
||||
"- **状态**: confirmed",
|
||||
"- **所属任务**: 演示目标渗透测试",
|
||||
"- **发现时间**:",
|
||||
"**证据:**",
|
||||
"**详细报告:**",
|
||||
"## 相关流量证据",
|
||||
"证据版本: 2, 绑定数量: 1.",
|
||||
} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("요약 보고서에 %q 가 없다", want)
|
||||
@@ -134,16 +150,14 @@ func TestFindingsMarkdownLocalized(t *testing.T) {
|
||||
}
|
||||
|
||||
single := SingleFindingMarkdown(f, time.Date(2026, 10, 4, 2, 30, 0, 0, time.UTC))
|
||||
if r, ok := containsHan(single); ok {
|
||||
t.Fatalf("단건 보고서에 중국어 한자 %q 가 남아 있다:\n%s", string(r), single)
|
||||
}
|
||||
assertChinese(t, "단건 보고서", single)
|
||||
for _, want := range []string{
|
||||
"- **심각도**: high",
|
||||
"## 개요",
|
||||
"## 증거",
|
||||
"## 상세 보고서",
|
||||
"[요청 메시지](evidence/123/9/request.http)",
|
||||
"[응답 메시지](evidence/123/9/response.http)",
|
||||
"- **严重程度**: high",
|
||||
"## 概述",
|
||||
"## 证据",
|
||||
"## 详细报告",
|
||||
"[请求消息](evidence/123/9/request.http)",
|
||||
"[响应消息](evidence/123/9/response.http)",
|
||||
} {
|
||||
if !strings.Contains(single, want) {
|
||||
t.Errorf("단건 보고서에 %q 가 없다", want)
|
||||
@@ -157,10 +171,8 @@ func TestFindingsCSVLocalized(t *testing.T) {
|
||||
raw := FindingsCSV([]*db.DBFinding{sampleDBFinding()})
|
||||
out := strings.TrimPrefix(string(raw), "\xEF\xBB\xBF")
|
||||
header := strings.SplitN(out, "\n", 2)[0]
|
||||
if r, ok := containsHan(header); ok {
|
||||
t.Fatalf("CSV 헤더에 중국어 한자 %q 가 남아 있다: %s", string(r), header)
|
||||
}
|
||||
for _, col := range []string{"ID", "이름", "유형", "심각도", "상태", "소속 작업", "발견 시각", "개요", "트래픽 증거 개수", "트래픽 증거 ID"} {
|
||||
assertChinese(t, "CSV 헤더", header)
|
||||
for _, col := range []string{"ID", "名称", "类型", "严重程度", "状态", "所属任务", "发现时间", "概述", "流量证据数量", "流量证据 ID"} {
|
||||
if !strings.Contains(header, col) {
|
||||
t.Errorf("CSV 헤더에 %q 열이 없다: %s", col, header)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user