mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 20:08:18 +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.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -210,7 +212,23 @@ public class WorkspaceMemoryArchiveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAllowedFilename(String name) {
|
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());
|
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
|
@Test
|
||||||
@DisplayName("Preview surfaces old vs new hash + size for updated files")
|
@DisplayName("Preview surfaces old vs new hash + size for updated files")
|
||||||
void previewExposesDiffMetadata() throws Exception {
|
void previewExposesDiffMetadata() throws Exception {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user