Skip to content
12 changes: 11 additions & 1 deletion common/src/main/java/com/genexus/util/GXFileInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public boolean createNewFile() throws IOException{
return fileSource.createNewFile();
}
public boolean createNewFile(InputStream input) throws IOException{
fromBytes(SpecificImplementation.GXutil.toByteArray(input));
fromInputStream(input);
return true;
}
public boolean delete(){
Expand Down Expand Up @@ -180,6 +180,16 @@ public void fromBytes(byte[] data) throws IOException
destination.write(data, 0, data.length);
}
}
private void fromInputStream(InputStream input) throws IOException
{
try (OutputStream output = new BufferedOutputStream(new FileOutputStream(fileSource))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}
}
public String readAllText(String encoding)throws IOException{
return SpecificImplementation.FileUtils.readFileToString(fileSource, CommonUtil.normalizeEncodingName(encoding));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.util.IOUtils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
Expand Down Expand Up @@ -220,23 +219,31 @@ else if (acl == ResourceAccessControlList.PublicReadWrite) {
}

public String upload(String externalFileName, InputStream input, ResourceAccessControlList acl) {
byte[] bytes;
ExternalProviderHelper.InputStreamWithLength streamInfo = null;
try {
bytes = IOUtils.toByteArray(input);
streamInfo = ExternalProviderHelper.getInputStreamContentLength(input);

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(bytes.length);
metadata.setContentLength(streamInfo.contentLength);

if (externalFileName.endsWith(".tmp")) {
metadata.setContentType("image/jpeg");
}
String upload = "";
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
client.putObject(new PutObjectRequest(bucket, externalFileName, byteArrayInputStream, metadata).withCannedAcl(internalToAWSACL(acl)));
upload = getResourceUrl(externalFileName, acl, defaultExpirationMinutes);
}
client.putObject(new PutObjectRequest(bucket, externalFileName, streamInfo.inputStream, metadata).withCannedAcl(internalToAWSACL(acl)));
upload = getResourceUrl(externalFileName, acl, defaultExpirationMinutes);
return upload;
} catch (IOException ex) {
logger.error("Error while uploading file to the external provider.", ex);
return "";
} finally {
if (streamInfo != null && streamInfo.tempFile != null && streamInfo.tempFile.exists()) {
try {
streamInfo.tempFile.delete();
} catch (Exception e) {
logger.warn("Could not delete temporary file: " + streamInfo.tempFile.getAbsolutePath(), e);
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
import com.genexus.util.StorageUtils;
import com.genexus.StructSdtMessages_Message;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.utils.IoUtils;

import java.io.*;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -603,8 +601,10 @@ else if (acl == ResourceAccessControlList.PublicReadWrite)
}

private String uploadWithACL(String externalFileName, InputStream input, ResourceAccessControlList acl) {
ExternalProviderHelper.InputStreamWithLength streamInfo = null;
try {
ByteBuffer byteBuffer = ByteBuffer.wrap(IoUtils.toByteArray(input));
streamInfo = ExternalProviderHelper.getInputStreamContentLength(input);

PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder()
.bucket(bucket)
.key(externalFileName)
Expand All @@ -613,7 +613,7 @@ private String uploadWithACL(String externalFileName, InputStream input, Resourc
putObjectRequestBuilder = putObjectRequestBuilder.acl(internalToAWSACLWithACL(acl));
PutObjectRequest putObjectRequest = putObjectRequestBuilder.build();

PutObjectResponse response = client.putObject(putObjectRequest, RequestBody.fromByteBuffer(byteBuffer));
PutObjectResponse response = client.putObject(putObjectRequest, RequestBody.fromInputStream(streamInfo.inputStream, streamInfo.contentLength));
if (!response.sdkHttpResponse().isSuccessful()) {
logger.error("Error while uploading file: " + response.sdkHttpResponse().statusText().orElse("Unknown error"));
}
Expand All @@ -622,6 +622,15 @@ private String uploadWithACL(String externalFileName, InputStream input, Resourc
} catch (IOException ex) {
logger.error("Error while uploading file to the external provider.", ex);
return "";
} finally {
// Clean up the temporary file if it was created
if (streamInfo != null && streamInfo.tempFile != null && streamInfo.tempFile.exists()) {
try {
streamInfo.tempFile.delete();
} catch (Exception e) {
logger.warn("Could not delete temporary file: " + streamInfo.tempFile.getAbsolutePath(), e);
}
}
}
}

Expand Down Expand Up @@ -727,15 +736,17 @@ private String uploadWithoutACL(String localFile, String externalFileName) {
}

private String uploadWithoutACL(String externalFileName, InputStream input) {
ExternalProviderHelper.InputStreamWithLength streamInfo = null;
try {
ByteBuffer byteBuffer = ByteBuffer.wrap(IoUtils.toByteArray(input));
streamInfo = ExternalProviderHelper.getInputStreamContentLength(input);

PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder()
.bucket(bucket)
.key(externalFileName)
.contentType(externalFileName.endsWith(".tmp") ? "image/jpeg" : null);
PutObjectRequest putObjectRequest = putObjectRequestBuilder.build();

PutObjectResponse response = client.putObject(putObjectRequest, RequestBody.fromByteBuffer(byteBuffer));
PutObjectResponse response = client.putObject(putObjectRequest, RequestBody.fromInputStream(streamInfo.inputStream, streamInfo.contentLength));
if (!response.sdkHttpResponse().isSuccessful()) {
logger.error("Error while uploading file: " + response.sdkHttpResponse().statusText().orElse("Unknown error"));
}
Expand All @@ -744,6 +755,15 @@ private String uploadWithoutACL(String externalFileName, InputStream input) {
} catch (IOException ex) {
logger.error("Error while uploading file to the external provider.", ex);
return "";
} finally {
// Clean up the temporary file if it was created
if (streamInfo != null && streamInfo.tempFile != null && streamInfo.tempFile.exists()) {
try {
streamInfo.tempFile.delete();
} catch (Exception e) {
logger.warn("Could not delete temporary file: " + streamInfo.tempFile.getAbsolutePath(), e);
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,10 @@ public String upload(String externalFileName, InputStream input, ResourceAccessC
blob.getProperties().setContentType("image/jpeg");
}
try (BlobOutputStream blobOutputStream = blob.openOutputStream()) {
int next = input.read();
while (next != -1) {
blobOutputStream.write(next);
next = input.read();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
blobOutputStream.write(buffer, 0, bytesRead);
}
}
return getResourceUrl(externalFileName, acl, DEFAULT_EXPIRATION_MINUTES);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.genexus.util.Encryption;
import com.genexus.util.GXService;

import java.io.*;

public class ExternalProviderHelper {

public static String getServicePropertyValue(GXService s, String propName, boolean isSecure){
Expand All @@ -23,4 +25,30 @@ public static String getEnvironmentVariable(String name, boolean required) throw
}
return value;
}

public static InputStreamWithLength getInputStreamContentLength(InputStream input) throws IOException {
Copy link
Preview

Copilot AI Jul 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an exception occurs after creating the temporary file but before returning the InputStreamWithLength, the temporary file will not be cleaned up, causing a resource leak. Consider adding a try-catch block to clean up the temp file on error.

Copilot uses AI. Check for mistakes.

File tempFile = File.createTempFile("upload-", ".tmp");
Copy link
Preview

Copilot AI Jul 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating temporary files without specifying a secure directory could create files in a world-readable location. Consider using a secure temporary directory or setting appropriate file permissions.

Suggested change
File tempFile = File.createTempFile("upload-", ".tmp");
File tempFile = File.createTempFile("upload-", ".tmp");
tempFile.setReadable(false, false); // Disable read for all users
tempFile.setWritable(false, false); // Disable write for all users
tempFile.setExecutable(false, false); // Disable execute for all users
tempFile.setReadable(true, true); // Enable read for owner only
tempFile.setWritable(true, true); // Enable write for owner only

Copilot uses AI. Check for mistakes.

try (OutputStream out = new FileOutputStream(tempFile)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
long size = tempFile.length();
InputStream newInput = new FileInputStream(tempFile);
return new InputStreamWithLength(newInput, size, tempFile);
}

public static class InputStreamWithLength {
public final InputStream inputStream;
public final long contentLength;
public final File tempFile; // nullable

public InputStreamWithLength(InputStream inputStream, long contentLength, File tempFile) {
this.inputStream = inputStream;
this.contentLength = contentLength;
this.tempFile = tempFile;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import com.google.api.services.storage.model.StorageObject;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.storage.*;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand Down Expand Up @@ -170,16 +169,28 @@ private void setBlobAcl(BlobId blobId, ResourceAccessControlList acl) {
}

public String upload(String externalFileName, InputStream input, ResourceAccessControlList acl) {
ExternalProviderHelper.InputStreamWithLength streamInfo = null;
try {
streamInfo = ExternalProviderHelper.getInputStreamContentLength(input);

BlobId blobId = BlobId.of(bucket, externalFileName);
BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build();
byte[] targetArray = IOUtils.toByteArray(input);
storageClient.create(blobInfo, targetArray);

storageClient.createFrom(blobInfo, streamInfo.tempFile.toPath());
Copy link
Preview

Copilot AI Jul 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no null check for streamInfo.tempFile before calling toPath(). If the helper method fails to create a temp file, this could result in a NullPointerException.

Copilot uses AI. Check for mistakes.


setBlobAcl(blobId, acl);
return getResourceUrl(blobInfo, acl);
} catch (IOException ex) {
handleIOException(ex);
return "";
} finally {
if (streamInfo != null && streamInfo.tempFile != null && streamInfo.tempFile.exists()) {
try {
streamInfo.tempFile.delete();
} catch (Exception e) {
logger.warn("Could not delete temporary file: " + streamInfo.tempFile.getAbsolutePath(), e);
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import com.ibm.cloud.objectstorage.services.s3.AmazonS3Client;
import com.ibm.cloud.objectstorage.services.s3.AmazonS3ClientBuilder;
import com.ibm.cloud.objectstorage.services.s3.model.*;
import com.ibm.cloud.objectstorage.util.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand Down Expand Up @@ -150,24 +149,30 @@ else if (acl == ResourceAccessControlList.PublicReadWrite) {
}

public String upload(String externalFileName, InputStream input, ResourceAccessControlList acl) {
byte[] bytes;
try {
bytes = IOUtils.toByteArray(input);
ExternalProviderHelper.InputStreamWithLength streamInfo = null;
try {
streamInfo = ExternalProviderHelper.getInputStreamContentLength(input);
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(bytes.length);
metadata.setContentLength(streamInfo.contentLength);
if (externalFileName.endsWith(".tmp")) {
metadata.setContentType("image/jpeg");
}
String upload = "";
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
client.putObject(new PutObjectRequest(bucket, externalFileName, byteArrayInputStream, metadata).withCannedAcl(internalToAWSACL(acl)));
upload = getResourceUrl(externalFileName, acl, defaultExpirationMinutes);
}
client.putObject(new PutObjectRequest(bucket, externalFileName, streamInfo.inputStream, metadata).withCannedAcl(internalToAWSACL(acl)));
upload = getResourceUrl(externalFileName, acl, defaultExpirationMinutes);
return upload;
} catch (IOException ex) {
logger.error("Error while uploading file to the external provider.", ex);
return "";
}
} finally {
if (streamInfo != null && streamInfo.tempFile != null && streamInfo.tempFile.exists()) {
try {
streamInfo.tempFile.delete();
} catch (Exception e) {
logger.warn("Could not delete temporary file: " + streamInfo.tempFile.getAbsolutePath(), e);
}
}
}
}

public String get(String externalFileName, ResourceAccessControlList acl, int expirationMinutes) {
Expand Down
Loading