Skip to content
2 changes: 1 addition & 1 deletion common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
Expand Down
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,19 +219,14 @@ else if (acl == ResourceAccessControlList.PublicReadWrite) {
}

public String upload(String externalFileName, InputStream input, ResourceAccessControlList acl) {
byte[] bytes;
try {
bytes = IOUtils.toByteArray(input);
try (ExternalProviderHelper.InputStreamWithLength streamInfo = ExternalProviderHelper.getInputStreamContentLength(input)) {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(bytes.length);
if (externalFileName.endsWith(".tmp")) {
metadata.setContentType("image/jpeg");
}
metadata.setContentLength(streamInfo.contentLength);
metadata.setContentType((externalFileName.endsWith(".tmp") && "application/octet-stream".equals(streamInfo.detectedContentType)) ? "image/jpeg" : streamInfo.detectedContentType);

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);
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,7 @@ else if (acl == ResourceAccessControlList.PublicReadWrite)
}

private String uploadWithACL(String externalFileName, InputStream input, ResourceAccessControlList acl) {
try {
ByteBuffer byteBuffer = ByteBuffer.wrap(IoUtils.toByteArray(input));
try (ExternalProviderHelper.InputStreamWithLength streamInfo = ExternalProviderHelper.getInputStreamContentLength(input)) {
PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder()
.bucket(bucket)
.key(externalFileName)
Expand All @@ -613,7 +610,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 Down Expand Up @@ -727,15 +724,14 @@ private String uploadWithoutACL(String localFile, String externalFileName) {
}

private String uploadWithoutACL(String externalFileName, InputStream input) {
try {
ByteBuffer byteBuffer = ByteBuffer.wrap(IoUtils.toByteArray(input));
try (ExternalProviderHelper.InputStreamWithLength streamInfo = ExternalProviderHelper.getInputStreamContentLength(input)) {
PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder()
.bucket(bucket)
.key(externalFileName)
.contentType(externalFileName.endsWith(".tmp") ? "image/jpeg" : null);
.contentType((externalFileName.endsWith(".tmp") && "application/octet-stream".equals(streamInfo.detectedContentType)) ? "image/jpeg" : streamInfo.detectedContentType);
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,14 @@ public String upload(String localFile, String externalFileName, ResourceAccessCo
}

public String upload(String externalFileName, InputStream input, ResourceAccessControlList acl) {

try {
try (ExternalProviderHelper.InputStreamWithLength streamInfo = ExternalProviderHelper.getInputStreamContentLength(input)) {
CloudBlockBlob blob = getCloudBlockBlob(externalFileName, acl);
if (externalFileName.endsWith(".tmp")) {
blob.getProperties().setContentType("image/jpeg");
}
blob.getProperties().setContentType((externalFileName.endsWith(".tmp") && "application/octet-stream".equals(streamInfo.detectedContentType)) ? "image/jpeg" : streamInfo.detectedContentType);
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 = streamInfo.inputStream.read(buffer)) != -1) {
blobOutputStream.write(buffer, 0, bytesRead);
}
}
return getResourceUrl(externalFileName, acl, DEFAULT_EXPIRATION_MINUTES);
Expand Down
10 changes: 10 additions & 0 deletions gxcloudstorage-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.9.4</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
import com.genexus.util.Encryption;
import com.genexus.util.GXService;

import java.io.*;
import org.apache.tika.Tika;

public class ExternalProviderHelper {

public static final int BUFFER_MARK_LIMIT = 128 * 1024;

public static String getServicePropertyValue(GXService s, String propName, boolean isSecure){
String value = s.getProperties().get(propName);
if (value != null){
Expand All @@ -23,4 +28,49 @@ public static String getEnvironmentVariable(String name, boolean required) throw
}
return value;
}

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

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

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 bufferedInput = new BufferedInputStream(new FileInputStream(tempFile));
bufferedInput.mark(BUFFER_MARK_LIMIT);
Tika tika = new Tika();
String detectedContentType = tika.detect(bufferedInput);
bufferedInput.reset();
return new InputStreamWithLength(bufferedInput, size, tempFile, detectedContentType);
}

public static class InputStreamWithLength implements AutoCloseable {
public final InputStream inputStream;
public final long contentLength;
public final File tempFile; // nullable
public final String detectedContentType;

public InputStreamWithLength(InputStream inputStream, long contentLength, File tempFile, String detectedContentType) {
this.inputStream = inputStream;
this.contentLength = contentLength;
this.tempFile = tempFile;
this.detectedContentType = detectedContentType;
}

@Override
public void close() throws IOException {
try {
if (inputStream != null) {
inputStream.close();
}
} finally {
if (tempFile != null && tempFile.exists()) {
tempFile.delete();
}
}
}
}
}
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,11 +169,12 @@ private void setBlobAcl(BlobId blobId, ResourceAccessControlList acl) {
}

public String upload(String externalFileName, InputStream input, ResourceAccessControlList acl) {
try {
try (ExternalProviderHelper.InputStreamWithLength 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

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) {
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,19 +149,13 @@ else if (acl == ResourceAccessControlList.PublicReadWrite) {
}

public String upload(String externalFileName, InputStream input, ResourceAccessControlList acl) {
byte[] bytes;
try {
bytes = IOUtils.toByteArray(input);
try (ExternalProviderHelper.InputStreamWithLength streamInfo = ExternalProviderHelper.getInputStreamContentLength(input)) {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(bytes.length);
if (externalFileName.endsWith(".tmp")) {
metadata.setContentType("image/jpeg");
}
metadata.setContentLength(streamInfo.contentLength);
metadata.setContentType((externalFileName.endsWith(".tmp") && "application/octet-stream".equals(streamInfo.detectedContentType)) ? "image/jpeg" : streamInfo.detectedContentType);
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);
Expand Down
5 changes: 0 additions & 5 deletions java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,6 @@
<artifactId>javax.jms</artifactId>
<version>3.1.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
Expand Down
5 changes: 0 additions & 5 deletions wrappercommon/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.ws.security</groupId>
<artifactId>wss4j</artifactId>
Expand Down
Loading