fix(wiki): add fail-closed option for empty source-path allow-list

This commit is contained in:
matevip 2026-05-31 07:57:44 +08:00
parent 56b49e9cbf
commit 4c56df2ed3
3 changed files with 28 additions and 0 deletions

View File

@ -80,6 +80,16 @@ public class WikiProperties {
*/
private java.util.List<String> allowedSourceRoots = new java.util.ArrayList<>();
/**
* Fail-closed switch for source-path validation. When {@code true} and
* {@link #allowedSourceRoots} is empty, every source directory is rejected
* (no path is allowed until a root is configured) recommended for
* multi-tenant servers so a missing allow-list cannot silently re-open
* full-filesystem reads. Default {@code false} keeps the opt-in behaviour
* for desktop / single-tenant where no roots are configured.
*/
private boolean requireAllowedRoots = false;
/**
* When {@code true}, a scheduled job (single-owner via ShedLock) scans each
* KB's configured source directory and auto-ingests new files. Off by

View File

@ -47,6 +47,11 @@ public class WikiSourcePathValidator {
Path resolved = canonicalize(Paths.get(rawPath));
List<String> roots = properties.getAllowedSourceRoots();
if (roots == null || roots.isEmpty()) {
if (properties.isRequireAllowedRoots()) {
throw new IllegalArgumentException(
"No allowed source roots are configured; refusing the path (fail-closed). "
+ "Set mate.wiki.allowed-source-roots to permit directories.");
}
return resolved;
}
for (String root : roots) {

View File

@ -27,6 +27,12 @@ class WikiSourcePathValidatorTest {
return new WikiSourcePathValidator(props);
}
private WikiSourcePathValidator failClosedValidator() {
WikiProperties props = new WikiProperties();
props.setRequireAllowedRoots(true);
return new WikiSourcePathValidator(props);
}
@Test
void blankPath_rejected() {
assertThrows(IllegalArgumentException.class, () -> validator(List.of()).validateDirectory(" "));
@ -38,6 +44,13 @@ class WikiSourcePathValidatorTest {
assertEquals(tmp.toRealPath(), resolved);
}
@Test
void emptyRoots_failClosed_rejectsEverything(@TempDir Path tmp) {
// With require-allowed-roots enabled, an empty allow-list denies all.
assertThrows(IllegalArgumentException.class,
() -> failClosedValidator().validateDirectory(tmp.toString()));
}
@Test
void insideAllowedRoot_isAccepted(@TempDir Path root) throws IOException {
Path sub = Files.createDirectory(root.resolve("kb-source"));