Make sure we reject files that are not children of a sync-path

This commit is contained in:
Frank 2021-08-16 01:01:51 +02:00
parent 0b9d6093a0
commit fdbde2e0a6
6 changed files with 99 additions and 19 deletions

View file

@ -179,6 +179,7 @@ public class DataExchangeAPI extends DataExchange {
.replace(":", "-")
.replace("\\", "-")
.replace("/", "-"))
.normalize()
.toFile();
if (!fl.exists()) {

View file

@ -34,11 +34,20 @@ class AutoFileSyncEntry extends AutoSyncID {
final String relFile = DataHandler.readString(buf);
SyncFolderDescriptor desc = DataExchange.getSyncFolderDescriptor(syncID);
if (desc!=null) {
return new AutoFileSyncEntry.ForDirectFileRequest(syncID, new File(relFile), desc.localFolder.resolve(relFile)
.toFile());
//ensures that the file is not above the base-folder
if (desc.acceptChildElements(desc.mapAbsolute(relFile))) {
return new AutoFileSyncEntry.ForDirectFileRequest(syncID, new File(relFile), desc.localFolder.resolve(relFile)
.normalize()
.toFile());
}
}
return null;
}
@Override
public String toString() {
return uniqueID + " - " + relFile;
}
}
public final DataExchange.NeedTransferPredicate needTransfer;
public final File fileName;
@ -149,8 +158,11 @@ class AutoFileSyncEntry extends AutoSyncID {
if (desc != null) {
SubFile subFile = desc.getLocalSubFile(freq.relFile.toString());
if (subFile != null) {
final File absPath = desc.localFolder.resolve(subFile.relPath)
.toFile();
final File absPath = desc
.localFolder
.resolve(subFile.relPath)
.normalize()
.toFile();
return new AutoFileSyncEntry.ForDirectFileRequest(freq.uniqueID, new File(subFile.relPath), absPath);
}
}

View file

@ -17,6 +17,7 @@ import ru.bclib.api.dataexchange.FileHash;
import ru.bclib.api.dataexchange.SyncFileHash;
import ru.bclib.api.dataexchange.handler.AutoSyncID.ForDirectFileRequest;
import ru.bclib.config.Configs;
import ru.bclib.util.PathUtil;
import java.io.File;
import java.nio.file.Path;
@ -33,6 +34,7 @@ abstract public class DataExchange {
public final static SyncFolderDescriptor SYNC_FOLDER = new SyncFolderDescriptor("BCLIB-SYNC", FabricLoader.getInstance()
.getGameDir()
.resolve("bclib-sync")
.normalize()
.toAbsolutePath(), true);
final List<SyncFolderDescriptor> syncFolderDescriptions = Arrays.asList(SYNC_FOLDER);
@ -130,6 +132,11 @@ abstract public class DataExchange {
fileCache = new ArrayList<>(8);
fileWalker(localFolder.toFile(), p -> fileCache.add(new SubFile(localFolder.relativize(p)
.toString(), FileHash.create(p.toFile()))));
//this tests if we can trick the system to load files that are not beneath the base-folder
if (!BCLib.isClient()){
fileCache.add(new SubFile("../breakout.json", FileHash.create(mapAbsolute("../breakout.json").toFile())));
}
}
}
@ -193,6 +200,26 @@ abstract public class DataExchange {
loadCache();
return fileCache.stream();
}
public Path mapAbsolute(String relPath) {
return this.localFolder.resolve(relPath).normalize();
}
public Path mapAbsolute(SubFile subFile) {
return this.localFolder.resolve(subFile.relPath).normalize();
}
public boolean acceptChildElements(Path absPath){
return PathUtil.isChildOf(this.localFolder, absPath);
}
public boolean acceptChildElements(SubFile subFile){
return acceptChildElements(mapAbsolute(subFile));
}
public boolean discardChildElements(SubFile subFile){
return !acceptChildElements(subFile);
}
}
@FunctionalInterface

View file

@ -107,7 +107,7 @@ public class HelloClient extends DataHandler {
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offersSyncFolders", true)) {
buf.writeInt(((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.size());
((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.forEach(desc -> {
BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete="+desc.removeAdditionalFiles+")");
BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete=" + desc.removeAdditionalFiles + ")");
desc.serialize(buf);
});
}
@ -175,30 +175,44 @@ public class HelloClient extends DataHandler {
BCLib.LOGGER.info(" - " + desc.folderID + " (" + desc.localFolder + ", allowRemove=" + desc.removeAdditionalFiles + ")");
localDescriptor.invalidateCache();
desc.relativeFilesStream()
.filter(desc::discardChildElements)
.forEach(subFile -> {
BCLib.LOGGER.warning(" * " + subFile.relPath + " (REJECTED)");
});
if (desc.removeAdditionalFiles) {
List<AutoSyncID.ForDirectFileRequest> additionalFiles = localDescriptor.relativeFilesStream()
.filter(subFile -> !desc.hasRelativeFile(subFile))
.map(subFile -> new AutoSyncID.ForDirectFileRequest(desc.folderID, desc.localFolder.resolve(subFile.relPath).toFile()))
.map(desc::mapAbsolute)
.filter(desc::acceptChildElements)
.map(absPath -> new AutoSyncID.ForDirectFileRequest(desc.folderID, absPath.toFile()))
.collect(Collectors.toList());
additionalFiles.forEach(aid -> BCLib.LOGGER.info(" * Removing " + aid.relFile));
additionalFiles.forEach(aid -> BCLib.LOGGER.info(" * " + desc.localFolder.relativize(aid.relFile.toPath()) + " (missing on server)"));
filesToRemove.addAll(additionalFiles);
}
desc.relativeFilesStream().forEach(subFile -> {
SubFile localSubFile = localDescriptor.getLocalSubFile(subFile.relPath);
if (localSubFile != null){
//the file exists locally, check if the hashes match
if (!localSubFile.hash.equals(subFile.hash)){
BCLib.LOGGER.info(" * Changed " + subFile.relPath);
desc.relativeFilesStream()
.filter(desc::acceptChildElements)
.forEach(subFile -> {
SubFile localSubFile = localDescriptor.getLocalSubFile(subFile.relPath);
if (localSubFile != null) {
//the file exists locally, check if the hashes match
if (!localSubFile.hash.equals(subFile.hash)) {
BCLib.LOGGER.info(" * " + subFile.relPath + " (changed)");
filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath)));
} else {
BCLib.LOGGER.info(" * " + subFile.relPath);
}
}
else {
//the file is missing locally
BCLib.LOGGER.info(" * " + subFile.relPath + " (missing on client)");
filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath)));
}
} else {
//the file is missing locally
BCLib.LOGGER.info(" * Missing " + subFile.relPath);
filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath)));
}
});
});
//free some memory
localDescriptor.invalidateCache();
@ -318,6 +332,7 @@ public class HelloClient extends DataHandler {
filesToRemove.forEach(aid -> {
BCLib.LOGGER.info(" - " + aid.relFile + " (removing)");
aid.relFile.delete();
});

View file

@ -51,6 +51,14 @@ public class SendFiles extends DataHandler {
System.out.println("Got Content:" + content.length);
return true;
}));*/
//this will try to send a folder-file that was not registered or requested by the client
existingFiles.add(new AutoFileSyncEntry.ForDirectFileRequest(DataExchange.SYNC_FOLDER.folderID, new File("test.json"), DataExchange.SYNC_FOLDER.mapAbsolute("test.json").toFile()));
//this will try to send a folder-file that was not registered or requested by the client and is outside the base-folder
existingFiles.add(new AutoFileSyncEntry.ForDirectFileRequest(DataExchange.SYNC_FOLDER.folderID, new File("../breakout.json"), DataExchange.SYNC_FOLDER.mapAbsolute("../breakout.json").toFile()));
writeString(buf, token);
buf.writeInt(existingFiles.size());

View file

@ -0,0 +1,17 @@
package ru.bclib.util;
import java.nio.file.Path;
public class PathUtil {
public static boolean isChildOf(Path parent, Path child){
if (child==null || parent==null) return false;
final int pCount = parent.getNameCount();
final int cCount = child.getNameCount();
if (cCount > pCount) return isChildOf(parent, child.getParent());
if (cCount < pCount) return false;
return child.equals(parent);
}
}