chore: change output folder (#8447)

This commit is contained in:
Adrián Jesús Peña Rodríguez
2025-08-05 14:07:35 +02:00
committed by GitHub
parent 92bc992e7f
commit a9d16bbbce
5 changed files with 57 additions and 31 deletions
+20 -16
View File
@@ -8909,14 +8909,15 @@ components:
output_directory:
type: string
description: 'The directory path within the bucket where files
will be saved. Path will be normalized to remove excessive slashes
and invalid characters are not allowed (< > : " | ? *). Maximum
length is 900 characters.'
will be saved. Optional - defaults to "output" if not provided.
Path will be normalized to remove excessive slashes and invalid
characters are not allowed (< > : " | ? *). Maximum length is
900 characters.'
maxLength: 900
pattern: ^[^<>:"|?*]+$
default: output
required:
- bucket_name
- output_directory
credentials:
oneOf:
- type: object
@@ -9054,14 +9055,15 @@ components:
output_directory:
type: string
description: 'The directory path within the bucket where files
will be saved. Path will be normalized to remove excessive
slashes and invalid characters are not allowed (< > : "
| ? *). Maximum length is 900 characters.'
will be saved. Optional - defaults to "output" if not provided.
Path will be normalized to remove excessive slashes and
invalid characters are not allowed (< > : " | ? *). Maximum
length is 900 characters.'
maxLength: 900
pattern: ^[^<>:"|?*]+$
default: output
required:
- bucket_name
- output_directory
credentials:
oneOf:
- type: object
@@ -9214,14 +9216,15 @@ components:
output_directory:
type: string
description: 'The directory path within the bucket where files
will be saved. Path will be normalized to remove excessive slashes
and invalid characters are not allowed (< > : " | ? *). Maximum
length is 900 characters.'
will be saved. Optional - defaults to "output" if not provided.
Path will be normalized to remove excessive slashes and invalid
characters are not allowed (< > : " | ? *). Maximum length is
900 characters.'
maxLength: 900
pattern: ^[^<>:"|?*]+$
default: output
required:
- bucket_name
- output_directory
credentials:
oneOf:
- type: object
@@ -10572,14 +10575,15 @@ components:
output_directory:
type: string
description: 'The directory path within the bucket where files
will be saved. Path will be normalized to remove excessive
slashes and invalid characters are not allowed (< > : "
| ? *). Maximum length is 900 characters.'
will be saved. Optional - defaults to "output" if not provided.
Path will be normalized to remove excessive slashes and
invalid characters are not allowed (< > : " | ? *). Maximum
length is 900 characters.'
maxLength: 900
pattern: ^[^<>:"|?*]+$
default: output
required:
- bucket_name
- output_directory
credentials:
oneOf:
- type: object
@@ -30,9 +30,6 @@ class TestS3ConfigSerializer:
"""Test that empty values raise validation errors."""
serializer = S3ConfigSerializer()
with pytest.raises(ValidationError, match="Output directory cannot be empty"):
serializer.validate_output_directory("")
with pytest.raises(
ValidationError, match="Output directory cannot be empty or just"
):
@@ -9,15 +9,17 @@ from api.v1.serializer_utils.base import BaseValidateSerializer
class S3ConfigSerializer(BaseValidateSerializer):
bucket_name = serializers.CharField()
output_directory = serializers.CharField()
output_directory = serializers.CharField(allow_blank=True)
def validate_output_directory(self, value):
"""
Validate the output_directory field to ensure it's a properly formatted path.
Prevents paths with excessive slashes like "///////test".
If empty, sets a default value.
"""
# If empty or None, set default value
if not value:
raise serializers.ValidationError("Output directory cannot be empty.")
return "output"
# Normalize the path to remove excessive slashes
normalized_path = os.path.normpath(value)
@@ -35,7 +37,7 @@ class S3ConfigSerializer(BaseValidateSerializer):
# Check for empty path after normalization
if not normalized_path or normalized_path == ".":
raise serializers.ValidationError(
"Output directory cannot be empty or just '.'."
"Output directory cannot be empty or just '.' or '/'."
)
# Check for paths that are too long (S3 key limit is 1024 characters, leave some room for filename)
@@ -136,12 +138,13 @@ class IntegrationCredentialField(serializers.JSONField):
},
"output_directory": {
"type": "string",
"description": 'The directory path within the bucket where files will be saved. Path will be normalized to remove excessive slashes and invalid characters are not allowed (< > : " | ? *). Maximum length is 900 characters.',
"description": 'The directory path within the bucket where files will be saved. Optional - defaults to "output" if not provided. Path will be normalized to remove excessive slashes and invalid characters are not allowed (< > : " | ? *). Maximum length is 900 characters.',
"maxLength": 900,
"pattern": '^[^<>:"|?*]+$',
"default": "output",
},
},
"required": ["bucket_name", "output_directory"],
"required": ["bucket_name"],
},
]
}
@@ -83,13 +83,6 @@ def upload_s3_integration(
for integration in integrations:
try:
connected, s3 = get_s3_client_from_integration(integration)
# Since many scans will be send to the same S3 bucket, we need to
# add the output directory to the S3 output directory to avoid
# overwriting the files and known the scan origin.
folder = os.getenv("OUTPUT_DIRECTORY", "/tmp/prowler_api_output")
s3._output_directory = (
f"{s3._output_directory}{output_directory.split(folder)[-1]}"
)
except Exception as e:
logger.error(
f"S3 connection failed for integration {integration.id}: {e}"
@@ -331,6 +331,35 @@ class TestS3IntegrationUploads:
# Verify complex path normalization
assert configuration["output_directory"] == "test/folder/subfolder"
@patch("tasks.jobs.integrations.S3")
def test_s3_client_uses_output_directory_in_object_paths(self, mock_s3_class):
"""Test that S3 client uses output_directory correctly when generating object paths."""
mock_integration = MagicMock()
mock_integration.credentials = {
"aws_access_key_id": "AKIA...",
"aws_secret_access_key": "SECRET",
}
mock_integration.configuration = {
"bucket_name": "test-bucket",
"output_directory": "my-custom-prefix/scan-results",
}
mock_s3_instance = MagicMock()
mock_connection = MagicMock()
mock_connection.is_connected = True
mock_s3_instance.test_connection.return_value = mock_connection
mock_s3_class.return_value = mock_s3_instance
connected, s3 = get_s3_client_from_integration(mock_integration)
assert connected is True
# Verify S3 was initialized with the correct output_directory
mock_s3_class.assert_called_once_with(
**mock_integration.credentials,
bucket_name="test-bucket",
output_directory="my-custom-prefix/scan-results",
)
@pytest.mark.django_db
class TestProwlerIntegrationConnectionTest: