mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(workspace): reject impossible calendar dates in memory snapshot import
This commit is contained in:
parent
69fb03968b
commit
f56f4b059a
@ -17,6 +17,8 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -210,7 +212,23 @@ public class WorkspaceMemoryArchiveService {
|
||||
}
|
||||
|
||||
private static boolean isAllowedFilename(String name) {
|
||||
return TOP_LEVEL_WHITELIST.contains(name) || DAILY_FILENAME.matcher(name).matches();
|
||||
if (TOP_LEVEL_WHITELIST.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
if (!DAILY_FILENAME.matcher(name).matches()) {
|
||||
return false;
|
||||
}
|
||||
// The regex only constrains digit shape, so structurally-valid but
|
||||
// non-existent dates (memory/2026-13-99.md, memory/2026-02-30.md)
|
||||
// would slip through. Parse the date to reject calendar dates that
|
||||
// cannot occur, keeping the daily ledger namespace clean.
|
||||
String date = name.substring("memory/".length(), name.length() - ".md".length());
|
||||
try {
|
||||
LocalDate.parse(date);
|
||||
return true;
|
||||
} catch (DateTimeParseException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -173,6 +173,33 @@ class WorkspaceMemoryArchiveServiceTest {
|
||||
org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Daily filename matching the digit shape but not a real date is rejected")
|
||||
void impossibleCalendarDateRejected() throws Exception {
|
||||
wireAgent(1L, 10L);
|
||||
when(workspaceFileService.getFile(1L, "memory/2026-05-12.md"))
|
||||
.thenReturn(null);
|
||||
|
||||
byte[] zip = makeZip(Map.of(
|
||||
"memory/2026-05-12.md", "real date → create",
|
||||
"memory/2026-13-99.md", "month 13 / day 99 → reject",
|
||||
"memory/2026-02-30.md", "feb 30 does not exist → reject"));
|
||||
|
||||
WorkspaceMemoryArchiveService.ImportPreview preview =
|
||||
service.previewImport(1L, 10L, zip);
|
||||
|
||||
assertThat(preview.willCreate()).containsExactlyInAnyOrder("memory/2026-05-12.md");
|
||||
assertThat(preview.willSkip())
|
||||
.extracting(WorkspaceMemoryArchiveService.SkipEntry::filename,
|
||||
WorkspaceMemoryArchiveService.SkipEntry::reason)
|
||||
.contains(
|
||||
org.assertj.core.groups.Tuple.tuple("memory/2026-13-99.md", "not in whitelist"),
|
||||
org.assertj.core.groups.Tuple.tuple("memory/2026-02-30.md", "not in whitelist"));
|
||||
|
||||
verify(workspaceFileService, never()).saveFile(org.mockito.ArgumentMatchers.anyLong(),
|
||||
org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Preview surfaces old vs new hash + size for updated files")
|
||||
void previewExposesDiffMetadata() throws Exception {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user