refactor: optimize security baseline, task logging, frontend maven packaging, and redirect root page to v2

This commit is contained in:
liumangmang
2026-06-10 16:06:10 +08:00
parent 1b182c2930
commit 42214b33e3
10 changed files with 307 additions and 20 deletions
@@ -0,0 +1,83 @@
package com.svnlog.web.service;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* AiApiService 中 truncateErrorBody / redactSensitiveFields 的单元测试。
* truncateErrorBody 为 package-private,可直接测试。
*/
class AiApiServiceTest {
// settingsService 未用到 truncateErrorBody,传 null 安全
private final AiApiService service = new AiApiService(null);
@Test
void shouldReturnEmptyPlaceholderWhenBodyIsNull() {
Assertions.assertEquals("(empty)", service.truncateErrorBody(null));
}
@Test
void shouldReturnEmptyPlaceholderWhenBodyIsBlank() {
Assertions.assertEquals("(empty)", service.truncateErrorBody(" "));
}
@Test
void shouldReturnShortBodyWithoutSensitiveDataAsIs() {
final String body = "{\"error\": \"model not found\", \"code\": 404}";
Assertions.assertEquals(body, service.truncateErrorBody(body));
}
@Test
void shouldTruncateLongBodyAndAppendMarker() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < 600; i++) {
sb.append('x');
}
final String result = service.truncateErrorBody(sb.toString());
Assertions.assertTrue(result.contains("[已截断"), "should contain truncation marker");
Assertions.assertTrue(result.length() < 600, "result should be shorter than original");
}
@Test
void shouldRedactApiKeyFieldInJsonBody() {
final String body = "{\"error\": \"invalid\", \"api_key\": \"sk-secret-123456\"}";
final String result = service.truncateErrorBody(body);
Assertions.assertFalse(result.contains("sk-secret-123456"), "api_key value must be redacted");
Assertions.assertTrue(result.contains("***"), "redaction marker must be present");
Assertions.assertTrue(result.contains("api_key"), "field name must be preserved");
}
@Test
void shouldRedactTokenFieldInJsonBody() {
final String body = "{\"token\": \"eyJhbGciOiJIUzI1NiJ9.payload\", \"status\": 401}";
final String result = service.truncateErrorBody(body);
Assertions.assertFalse(result.contains("eyJhbGciOiJIUzI1NiJ9.payload"), "token value must be redacted");
Assertions.assertTrue(result.contains("***"));
}
@Test
void shouldRedactPasswordFieldInJsonBody() {
final String body = "{\"password\": \"my-secret-pw\", \"code\": 401}";
final String result = service.truncateErrorBody(body);
Assertions.assertFalse(result.contains("my-secret-pw"), "password value must be redacted");
Assertions.assertTrue(result.contains("***"));
}
@Test
void shouldRedactAuthorizationBearerToken() {
final String body = "Authorization: Bearer sk-real-token-abc\nError: unauthorized";
final String result = service.truncateErrorBody(body);
Assertions.assertFalse(result.contains("sk-real-token-abc"), "bearer token must be redacted");
Assertions.assertTrue(result.contains("***"), "redaction marker must be present");
Assertions.assertTrue(result.contains("Authorization"), "header name must be preserved");
}
@Test
void shouldPreserveNonSensitiveContentAfterRedaction() {
final String body = "{\"error\": \"rate limit exceeded\", \"code\": 429}";
final String result = service.truncateErrorBody(body);
Assertions.assertTrue(result.contains("rate limit exceeded"), "non-sensitive content must be preserved");
Assertions.assertTrue(result.contains("429"), "non-sensitive content must be preserved");
}
}
@@ -30,14 +30,15 @@ class AiWorkflowServiceTest {
final OutputFileService outputFileService = buildOutputFileService();
final SettingsService settingsService = buildSettingsService(outputFileService);
final AiApiService aiApiService = buildAiApiService(settingsService);
final AiWorkflowService service = new AiWorkflowService(
new AiWorkflowService(
outputFileService,
new AiInputValidator(),
aiApiService,
buildExcelExportService()
);
final AiApiService.AiProviderContext context = aiApiService.resolveProviderContext(null);
// DeepSeek provider:通过请求临时传入 key 来解析 context(不依赖内置默认 key
final AiApiService.AiProviderContext context = aiApiService.resolveProviderContext("sk-test-key");
Assertions.assertEquals(SettingsService.PROVIDER_DEEPSEEK, context.getProvider());
Assertions.assertEquals("deepseek-chat", context.getStageOneModel());