diff --git a/AGENTS.md b/AGENTS.md index 09eac400..5d3b7414 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -173,6 +173,11 @@ To maintain a clear separation between fast, offline unit tests and containerize - Do NOT mix data serialization models (DTOs / JSON metadata objects) with active process or lifecycle management components (such as lock handles with scheduled thread executors or storage clients). - Keep constructors focused and minimal (typically 1 or 2 constructors per class). Classes requiring data models MUST accept the dedicated data object (e.g., `LeaseData`) in their constructor rather than defining multiple telescoping or metadata-only constructor overloads. +### 22. Secure Temporary File Creation +- Do NOT use legacy `java.io.File.createTempFile(...)`. On POSIX systems, it creates files with overly permissive default umask permissions (often world-readable or group-readable, CWE-378 / SonarQube java:S5443). +- Always use `java.nio.file.Files.createTempFile(...)` (or `Utils.createTempSiblingPath(...)` / `Utils.createTempSibling(...)`), which creates temporary files with restricted owner-only permissions (`rw-------` / `0600`) by default. + + ## IETF Resumable Uploads for HTTP (RUFH) Spec Maintenance & Update Playbook ### 1. Spec Diff Review diff --git a/src/main/java/me/desair/tus/server/upload/azure/AzureBlobStorageService.java b/src/main/java/me/desair/tus/server/upload/azure/AzureBlobStorageService.java index c53751c1..c5b33e79 100644 --- a/src/main/java/me/desair/tus/server/upload/azure/AzureBlobStorageService.java +++ b/src/main/java/me/desair/tus/server/upload/azure/AzureBlobStorageService.java @@ -212,7 +212,7 @@ public UploadInfo append(UploadInfo upload, InputStream inputStream) boolean streamFinished = false; IOException streamException = null; - File firstChunkFile = File.createTempFile("tus-azure-chunk-", ".tmp", tempBufferDir.toFile()); + File firstChunkFile = Files.createTempFile(tempBufferDir, "tus-azure-chunk-", ".tmp").toFile(); try { // 4. Read first chunk from incoming payload stream into local disk buffer ReadChunkResult firstChunkResult = readChunk(inputStream, firstChunkFile, optimalBlockSize); @@ -500,7 +500,7 @@ public void removeLastNumberOfBytes(UploadInfo uploadInfo, long byteCount) blockBlobClient.deleteIfExists(); } else { File tempFile = - File.createTempFile("tus-azure-block-trim-", ".tmp", tempBufferDir.toFile()); + Files.createTempFile(tempBufferDir, "tus-azure-block-trim-", ".tmp").toFile(); try { try (InputStream is = BoundedInputStream.builder() @@ -525,7 +525,7 @@ public void removeLastNumberOfBytes(UploadInfo uploadInfo, long byteCount) if (newPartSize <= 0) { partBlob.deleteIfExists(); } else { - File tempFile = File.createTempFile("tus-azure-truncate-", ".tmp", tempBufferDir.toFile()); + File tempFile = Files.createTempFile(tempBufferDir, "tus-azure-truncate-", ".tmp").toFile(); try { try (InputStream is = BoundedInputStream.builder() @@ -862,7 +862,7 @@ private ProcessChunksResult processRemainingChunks( while (!streamFinished) { File chunkFile = null; try { - chunkFile = File.createTempFile("tus-azure-chunk-", ".tmp", tempBufferDir.toFile()); + chunkFile = Files.createTempFile(tempBufferDir, "tus-azure-chunk-", ".tmp").toFile(); ReadChunkResult chunkResult = readChunk(inputStream, chunkFile, optimalBlockSize); long chunkSize = chunkResult.bytesRead; exception = chunkResult.exception; @@ -903,7 +903,7 @@ private void bufferToPartBlob( if (existingPartSize == 0) { partBlob.upload(BinaryData.fromFile(tempFile.toPath()), true); } else { - File combinedTemp = File.createTempFile("tus-azure-part-", ".tmp", tempBufferDir.toFile()); + File combinedTemp = Files.createTempFile(tempBufferDir, "tus-azure-part-", ".tmp").toFile(); try { try (InputStream partIs = partBlob.openInputStream(); InputStream tempIs = new FileInputStream(tempFile); diff --git a/src/main/java/me/desair/tus/server/upload/s3/S3StorageService.java b/src/main/java/me/desair/tus/server/upload/s3/S3StorageService.java index 7a921866..a46286ad 100644 --- a/src/main/java/me/desair/tus/server/upload/s3/S3StorageService.java +++ b/src/main/java/me/desair/tus/server/upload/s3/S3StorageService.java @@ -21,6 +21,7 @@ import java.io.OutputStream; import java.io.SequenceInputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -653,7 +654,7 @@ private InputStream prepareStreamWithExistingIncompletePart( minioClient.getObject( GetObjectArgs.builder().bucket(bucket).object(partObjectKey).build()); File tempPrependedFile = - File.createTempFile("tus-s3-prep-", ".tmp", temporaryDirectory.toFile()); + Files.createTempFile(temporaryDirectory, "tus-s3-prep-", ".tmp").toFile(); tempPrependedFile.deleteOnExit(); try (FileOutputStream fos = new FileOutputStream(tempPrependedFile)) { @@ -691,7 +692,7 @@ private AppendResult processPayloadChunks( boolean streamFinished = false; while (!streamFinished) { File tempChunkFile = - File.createTempFile("tus-s3-chunk-", ".tmp", temporaryDirectory.toFile()); + Files.createTempFile(temporaryDirectory, "tus-s3-chunk-", ".tmp").toFile(); tempChunkFile.deleteOnExit(); long chunkBytesWritten = 0;