Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
show-progress: false
- uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
distribution: 'temurin'
cache: 'maven'
- name: Cache SonarCloud packages
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
show-progress: false
- uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
distribution: 'temurin'
cache: 'maven'
- name: Initialize CodeQL
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dependency-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
with:
runner-os: 'ubuntu-latest'
java-distribution: 'temurin'
java-version: 17
java-version: 21
secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }}
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
2 changes: 1 addition & 1 deletion .github/workflows/publish-central.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
show-progress: false
- uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
distribution: 'temurin'
cache: 'maven'
server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-github.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
show-progress: false
- uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
distribution: 'temurin'
cache: 'maven'
- name: Enforce project version ${{ github.event.release.tag_name }}
Expand Down
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptofs/badge.svg)](https://snyk.io/test/github/cryptomator/cryptofs)

**CryptoFS:** Implementation of the [Cryptomator](https://github.com/cryptomator/cryptomator) encryption scheme.
For more info about the encryption scheme, read the [docs](https://docs.cryptomator.org/en/latest/security/vault/).

## Features

Expand Down Expand Up @@ -98,7 +99,7 @@ For more details on how to use the constructed `FileSystem`, you may consult the

### Dependencies

* Java 17
* Java 21
* Maven 3

### Run Maven
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
<maven.compiler.release>21</maven.compiler.release>

<!-- dependencies -->
<cryptolib.version>2.2.0</cryptolib.version>
Expand All @@ -27,7 +27,7 @@

<!-- test dependencies -->
<junit.jupiter.version>5.10.3</junit.jupiter.version>
<mockito.version>5.2.0</mockito.version>
<mockito.version>5.12.0</mockito.version>
<hamcrest.version>3.0</hamcrest.version>
<jimfs.version>1.3.0</jimfs.version>

Expand Down Expand Up @@ -114,7 +114,7 @@
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
Expand Down Expand Up @@ -959,6 +958,7 @@ public void copyDirectory() throws IOException {
when(physicalFsProv.newFileChannel(Mockito.eq(ciphertextDestinationDirFile), Mockito.any(), any(FileAttribute[].class))).thenReturn(ciphertextTargetDirDirFileFileChannel);
when(cryptoPathMapper.getCiphertextFileType(cleartextSource)).thenReturn(CiphertextFileType.DIRECTORY);
when(cryptoPathMapper.getCiphertextFileType(cleartextDestination)).thenThrow(NoSuchFileException.class);
when(physicalFsProv.exists(ciphertextTargetParent)).thenReturn(true);
Mockito.doThrow(new NoSuchFileException("ciphertextDestinationDirFile")).when(physicalFsProv).checkAccess(ciphertextDestinationFile);

inTest.copy(cleartextSource, cleartextDestination);
Expand Down Expand Up @@ -1007,6 +1007,7 @@ public void moveDirectoryCopyBasicAttributes() throws IOException {
when(physicalFsProv.readAttributes(Mockito.same(ciphertextSourceDir), Mockito.same(BasicFileAttributes.class), any(LinkOption[].class))).thenReturn(srcAttrs);
when(physicalFsProv.getFileAttributeView(Mockito.same(ciphertextDestinationDir), Mockito.same(BasicFileAttributeView.class), any(LinkOption[].class))).thenReturn(dstAttrView);
when(physicalFsProv.newFileChannel(Mockito.same(ciphertextDestinationDirFile), Mockito.anySet(), any(FileAttribute[].class))).thenReturn(ciphertextTargetDirDirFileFileChannel);
when(physicalFsProv.exists(ciphertextTargetParent)).thenReturn(true);

inTest.copy(cleartextSource, cleartextDestination, StandardCopyOption.COPY_ATTRIBUTES);

Expand All @@ -1027,6 +1028,7 @@ public void moveDirectoryCopyFileOwnerAttributes() throws IOException {
when(physicalFsProv.getFileAttributeView(Mockito.same(ciphertextSourceDir), Mockito.same(FileOwnerAttributeView.class), any(LinkOption[].class))).thenReturn(srcAttrsView);
when(physicalFsProv.getFileAttributeView(Mockito.same(ciphertextDestinationDir), Mockito.same(FileOwnerAttributeView.class), any(LinkOption[].class))).thenReturn(dstAttrView);
when(physicalFsProv.newFileChannel(Mockito.same(ciphertextDestinationDirFile), Mockito.anySet(), any(FileAttribute[].class))).thenReturn(ciphertextTargetDirDirFileFileChannel);
when(physicalFsProv.exists(ciphertextTargetParent)).thenReturn(true);

inTest.copy(cleartextSource, cleartextDestination, StandardCopyOption.COPY_ATTRIBUTES);

Expand All @@ -1050,6 +1052,7 @@ public void moveDirectoryCopyPosixAttributes() throws IOException {
when(physicalFsProv.readAttributes(Mockito.same(ciphertextSourceDir), Mockito.same(PosixFileAttributes.class), any(LinkOption[].class))).thenReturn(srcAttrs);
when(physicalFsProv.getFileAttributeView(Mockito.same(ciphertextDestinationDir), Mockito.same(PosixFileAttributeView.class), any(LinkOption[].class))).thenReturn(dstAttrView);
when(physicalFsProv.newFileChannel(Mockito.same(ciphertextDestinationDirFile), Mockito.anySet(), any(FileAttribute[].class))).thenReturn(ciphertextTargetDirDirFileFileChannel);
when(physicalFsProv.exists(ciphertextTargetParent)).thenReturn(true);

inTest.copy(cleartextSource, cleartextDestination, StandardCopyOption.COPY_ATTRIBUTES);

Expand All @@ -1073,6 +1076,7 @@ public void moveDirectoryCopyDosAttributes() throws IOException {
when(physicalFsProv.readAttributes(Mockito.same(ciphertextSourceDir), Mockito.same(DosFileAttributes.class), any(LinkOption[].class))).thenReturn(srcAttrs);
when(physicalFsProv.getFileAttributeView(Mockito.same(ciphertextDestinationDir), Mockito.same(DosFileAttributeView.class), any(LinkOption[].class))).thenReturn(dstAttrView);
when(physicalFsProv.newFileChannel(Mockito.same(ciphertextDestinationDirFile), Mockito.anySet(), any(FileAttribute[].class))).thenReturn(ciphertextTargetDirDirFileFileChannel);
when(physicalFsProv.exists(ciphertextTargetParent)).thenReturn(true);

inTest.copy(cleartextSource, cleartextDestination, StandardCopyOption.COPY_ATTRIBUTES);

Expand Down Expand Up @@ -1168,6 +1172,7 @@ public void createDirectoryIfPathCiphertextFileDoesExistThrowsFileAlreadyExcepti
when(cryptoPathMapper.getCiphertextDir(parent)).thenReturn(new CiphertextDirectory("foo", ciphertextParent));
when(ciphertextParent.getFileSystem()).thenReturn(fileSystem);
doThrow(new FileAlreadyExistsException(path.toString())).when(cryptoPathMapper).assertNonExisting(path);
when(provider.exists(ciphertextParent)).thenReturn(true);

FileAlreadyExistsException e = Assertions.assertThrows(FileAlreadyExistsException.class, () -> {
inTest.createDirectory(path);
Expand All @@ -1177,7 +1182,7 @@ public void createDirectoryIfPathCiphertextFileDoesExistThrowsFileAlreadyExcepti

@Test
public void createDirectoryCreatesDirectoryIfConditonsAreMet() throws IOException {
Path ciphertextParent = mock(Path.class, "ciphertextParent");
Path ciphertextParent = mock(Path.class, "d/00/00");
Path ciphertextRawPath = mock(Path.class, "d/00/00/path.c9r");
Path ciphertextDirFile = mock(Path.class, "d/00/00/path.c9r/dir.c9r");
Path ciphertextDirPath = mock(Path.class, "d/FF/FF/");
Expand All @@ -1187,7 +1192,7 @@ public void createDirectoryCreatesDirectoryIfConditonsAreMet() throws IOExceptio
when(ciphertextRawPath.resolve("dir.c9r")).thenReturn(ciphertextDirFile);
when(cryptoPathMapper.getCiphertextFilePath(path)).thenReturn(ciphertextPath);
when(cryptoPathMapper.getCiphertextDir(path)).thenReturn(new CiphertextDirectory(dirId, ciphertextDirPath));
when(cryptoPathMapper.getCiphertextDir(parent)).thenReturn(new CiphertextDirectory("parentDirId", ciphertextDirPath));
when(cryptoPathMapper.getCiphertextDir(parent)).thenReturn(new CiphertextDirectory("parentDirId", ciphertextParent));
when(cryptoPathMapper.getCiphertextFileType(path)).thenThrow(NoSuchFileException.class);
when(ciphertextPath.getRawPath()).thenReturn(ciphertextRawPath);
when(ciphertextPath.getDirFilePath()).thenReturn(ciphertextDirFile);
Expand All @@ -1197,6 +1202,7 @@ public void createDirectoryCreatesDirectoryIfConditonsAreMet() throws IOExceptio
when(ciphertextDirPath.getFileSystem()).thenReturn(fileSystem);
when(ciphertextDirFile.getName(3)).thenReturn(mock(Path.class, "path.c9r"));
when(provider.newFileChannel(ciphertextDirFile, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))).thenReturn(channel);
when(provider.exists(ciphertextParent)).thenReturn(true);

inTest.createDirectory(path);

Expand All @@ -1206,7 +1212,7 @@ public void createDirectoryCreatesDirectoryIfConditonsAreMet() throws IOExceptio

@Test
public void createDirectoryClearsDirIdAndDeletesDirFileIfCreatingDirFails() throws IOException {
Path ciphertextParent = mock(Path.class, "ciphertextParent");
Path ciphertextParent = mock(Path.class, "d/00/00");
Path ciphertextRawPath = mock(Path.class, "d/00/00/path.c9r");
Path ciphertextDirFile = mock(Path.class, "d/00/00/path.c9r/dir.c9r");
Path ciphertextDirPath = mock(Path.class, "d/FF/FF/");
Expand All @@ -1216,7 +1222,7 @@ public void createDirectoryClearsDirIdAndDeletesDirFileIfCreatingDirFails() thro
when(ciphertextRawPath.resolve("dir.c9r")).thenReturn(ciphertextDirFile);
when(cryptoPathMapper.getCiphertextFilePath(path)).thenReturn(ciphertextPath);
when(cryptoPathMapper.getCiphertextDir(path)).thenReturn(new CiphertextDirectory(dirId, ciphertextDirPath));
when(cryptoPathMapper.getCiphertextDir(parent)).thenReturn(new CiphertextDirectory("parentDirId", ciphertextDirPath));
when(cryptoPathMapper.getCiphertextDir(parent)).thenReturn(new CiphertextDirectory("parentDirId", ciphertextParent));
when(cryptoPathMapper.getCiphertextFileType(path)).thenThrow(NoSuchFileException.class);
when(ciphertextPath.getRawPath()).thenReturn(ciphertextRawPath);
when(ciphertextPath.getDirFilePath()).thenReturn(ciphertextDirFile);
Expand All @@ -1226,15 +1232,18 @@ public void createDirectoryClearsDirIdAndDeletesDirFileIfCreatingDirFails() thro
when(ciphertextDirPath.getFileSystem()).thenReturn(fileSystem);
when(ciphertextDirFile.getName(3)).thenReturn(mock(Path.class, "path.c9r"));
when(provider.newFileChannel(ciphertextDirFile, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))).thenReturn(channel);
when(provider.exists(ciphertextParent)).thenReturn(true);

// make createDirectory with an FileSystemException during Files.createDirectories(ciphertextDirPath)
doThrow(new IOException()).when(provider).createDirectory(ciphertextDirPath);
// make createDirectory with an FileSystemException during Files.createDirectories(ciphertextContentDir)
doThrow(new IOException()).when(provider).readAttributesIfExists(ciphertextDirPath, BasicFileAttributes.class);
doThrow(new FileAlreadyExistsException("very specific")).when(provider).createDirectory(ciphertextDirPath);
when(ciphertextDirPath.toAbsolutePath()).thenReturn(ciphertextDirPath);
when(ciphertextDirPath.getParent()).thenReturn(null);

Assertions.assertThrows(IOException.class, () -> {
var exception = Assertions.assertThrows(FileAlreadyExistsException.class, () -> {
inTest.createDirectory(path);
});
Assertions.assertEquals("very specific", exception.getMessage());
verify(readonlyFlag).assertWritable();
verify(provider).delete(ciphertextDirFile);
verify(dirIdProvider).delete(ciphertextDirFile);
Expand All @@ -1243,7 +1252,7 @@ public void createDirectoryClearsDirIdAndDeletesDirFileIfCreatingDirFails() thro

@Test
public void createDirectoryBackupsDirIdInCiphertextDirPath() throws IOException {
Path ciphertextParent = mock(Path.class, "ciphertextParent");
Path ciphertextParent = mock(Path.class, "d/00/00");
Path ciphertextRawPath = mock(Path.class, "d/00/00/path.c9r");
Path ciphertextDirFile = mock(Path.class, "d/00/00/path.c9r/dir.c9r");
Path ciphertextDirPath = mock(Path.class, "d/FF/FF/");
Expand All @@ -1254,7 +1263,7 @@ public void createDirectoryBackupsDirIdInCiphertextDirPath() throws IOException
when(ciphertextRawPath.resolve("dir.c9r")).thenReturn(ciphertextDirFile);
when(cryptoPathMapper.getCiphertextFilePath(path)).thenReturn(ciphertextPath);
when(cryptoPathMapper.getCiphertextDir(path)).thenReturn(cipherDirObject);
when(cryptoPathMapper.getCiphertextDir(parent)).thenReturn(new CiphertextDirectory("parentDirId", ciphertextDirPath));
when(cryptoPathMapper.getCiphertextDir(parent)).thenReturn(new CiphertextDirectory("parentDirId", ciphertextParent));
when(cryptoPathMapper.getCiphertextFileType(path)).thenThrow(NoSuchFileException.class);
when(ciphertextPath.getRawPath()).thenReturn(ciphertextRawPath);
when(ciphertextPath.getDirFilePath()).thenReturn(ciphertextDirFile);
Expand All @@ -1264,6 +1273,7 @@ public void createDirectoryBackupsDirIdInCiphertextDirPath() throws IOException
when(ciphertextDirPath.getFileSystem()).thenReturn(fileSystem);
when(ciphertextDirFile.getName(3)).thenReturn(mock(Path.class, "path.c9r"));
when(provider.newFileChannel(ciphertextDirFile, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))).thenReturn(channel);
when(provider.exists(ciphertextParent)).thenReturn(true);

inTest.createDirectory(path);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public void testGetCiphertextFileTypeForDirectory() throws IOException {
Mockito.when(underlyingFileSystemProvider.readAttributes(dirFilePath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)).thenReturn(Mockito.mock(BasicFileAttributes.class));
Mockito.when(underlyingFileSystemProvider.readAttributes(symlinkFilePath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)).thenThrow(NoSuchFileException.class);
Mockito.when(underlyingFileSystemProvider.readAttributes(contentsFilePath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)).thenThrow(NoSuchFileException.class);
Mockito.when(underlyingFileSystemProvider.exists(dirFilePath, LinkOption.NOFOLLOW_LINKS)).thenReturn(true);

CryptoPathMapper mapper = new CryptoPathMapper(pathToVault, cryptor, dirIdProvider, longFileNameProvider, vaultConfig);

Expand All @@ -305,6 +306,7 @@ public void testGetCiphertextFileTypeForSymlink() throws IOException {
Mockito.when(underlyingFileSystemProvider.readAttributes(dirFilePath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)).thenThrow(NoSuchFileException.class);
Mockito.when(underlyingFileSystemProvider.readAttributes(symlinkFilePath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)).thenReturn(Mockito.mock(BasicFileAttributes.class));
Mockito.when(underlyingFileSystemProvider.readAttributes(contentsFilePath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)).thenThrow(NoSuchFileException.class);
Mockito.when(underlyingFileSystemProvider.exists(symlinkFilePath, LinkOption.NOFOLLOW_LINKS)).thenReturn(true);

CryptoPathMapper mapper = new CryptoPathMapper(pathToVault, cryptor, dirIdProvider, longFileNameProvider, vaultConfig);

Expand All @@ -322,6 +324,7 @@ public void testGetCiphertextFileTypeForShortenedFile() throws IOException {
Mockito.when(underlyingFileSystemProvider.readAttributes(contentsFilePath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)).thenReturn(Mockito.mock(BasicFileAttributes.class));
Mockito.when(fileNameCryptor.encryptFilename(Mockito.any(), Mockito.eq("LONGCLEAR"), Mockito.any())).thenReturn(Strings.repeat("A", 1000));
Mockito.when(longFileNameProvider.deflate(Mockito.any())).thenReturn(new LongFileNameProvider.DeflatedFileName(c9rPath, null, null));
Mockito.when(underlyingFileSystemProvider.exists(contentsFilePath, LinkOption.NOFOLLOW_LINKS)).thenReturn(true);

CryptoPathMapper mapper = new CryptoPathMapper(pathToVault, cryptor, dirIdProvider, longFileNameProvider, vaultConfig);

Expand Down