a0d759364e
Build & Release APK / build (push) Successful in 12m42s
WebDAV already sanitizes server-supplied names, but SFTP passed entry.name through unfiltered, and the engine had no central guard — a malicious or compromised remote could return '../../x' and (on the JavaFile backend) write outside the sync root. - SyncEngine: isUnsafeSyncPath() rejects empty, absolute, and any '..'-segment path; every file is checked before any read/write/delete (covers all providers). - SftpProvider.listFiles: drop '.'/'..' and names containing path separators. - PathSafetyTest covers traversal, backslash, absolute, and empty cases.
37 lines
1.2 KiB
Kotlin
37 lines
1.2 KiB
Kotlin
package com.syncflow.domain.sync
|
|
|
|
import org.junit.Assert.assertFalse
|
|
import org.junit.Assert.assertTrue
|
|
import org.junit.Test
|
|
|
|
/**
|
|
* Path-traversal guard: a hostile/compromised remote must not be able to make the engine read
|
|
* or write outside the sync root via "..", absolute, or separator-smuggled paths.
|
|
*/
|
|
class PathSafetyTest {
|
|
|
|
@Test fun `normal relative paths are allowed`() {
|
|
assertFalse(isUnsafeSyncPath("photo.jpg"))
|
|
assertFalse(isUnsafeSyncPath("sub/dir/photo.jpg"))
|
|
assertFalse(isUnsafeSyncPath("a.b..c/file.txt")) // ".." only inside a name, not a segment
|
|
}
|
|
|
|
@Test fun `parent-dir traversal is rejected`() {
|
|
assertTrue(isUnsafeSyncPath(".."))
|
|
assertTrue(isUnsafeSyncPath("../evil"))
|
|
assertTrue(isUnsafeSyncPath("a/../../etc/passwd"))
|
|
assertTrue(isUnsafeSyncPath("sub/../../escape"))
|
|
}
|
|
|
|
@Test fun `backslash traversal is rejected`() {
|
|
assertTrue(isUnsafeSyncPath("..\\evil"))
|
|
assertTrue(isUnsafeSyncPath("a\\..\\..\\escape"))
|
|
}
|
|
|
|
@Test fun `absolute and empty paths are rejected`() {
|
|
assertTrue(isUnsafeSyncPath("/etc/passwd"))
|
|
assertTrue(isUnsafeSyncPath(""))
|
|
assertTrue(isUnsafeSyncPath(" "))
|
|
}
|
|
}
|