@@ -779,7 +779,7 @@ class AwsBatchTaskHandler extends TaskHandler implements BatchHandler<String,Job
779779 builder.jobQueue(getJobQueue(task))
780780 builder.jobDefinition(getJobDefinition(task))
781781 if( labels ) {
782- final tags = opts.sanitizeTags() ? sanitizeAwsBatchLabels( labels) : labels
782+ final tags = validateAwsBatchLabels( labels)
783783 builder.tags(tags)
784784 builder.propagateTags(true)
785785 }
@@ -866,76 +866,97 @@ class AwsBatchTaskHandler extends TaskHandler implements BatchHandler<String,Job
866866 }
867867
868868 /**
869- * Sanitize resource labels to comply with AWS Batch tag requirements.
870- * AWS Batch tags have specific constraints:
871- * - Keys and values can contain: letters, numbers, spaces, and characters: _ . : / = + - @
872- * - Maximum key length: 128 characters
869+ * Validate AWS Batch labels for compliance with AWS naming requirements.
870+ * This method validates resource labels against AWS Batch tag constraints and
871+ * handles violations based on the nextflow.enable.strict setting:
872+ *
873+ * - When strict mode is disabled (default): logs warnings for invalid tags but allows them through
874+ * - When strict mode is enabled: throws ProcessUnrecoverableException for invalid tags
875+ *
876+ * AWS Batch tag constraints validated:
877+ * - Keys and values cannot be null
878+ * - Maximum key length: 128 characters
873879 * - Maximum value length: 256 characters
880+ * - Allowed characters: letters, numbers, spaces, and: _ . : / = + - @
874881 *
875882 * @param labels The original resource labels map
876- * @return A new map with sanitized labels suitable for AWS Batch tags
883+ * @return The labels map (unchanged in validation mode)
884+ * @throws ProcessUnrecoverableException when strict mode is enabled and labels are invalid
877885 */
878- protected Map<String, String> sanitizeAwsBatchLabels (Map<String, String> labels) {
886+ protected Map<String, String> validateAwsBatchLabels (Map<String, String> labels) {
879887 if (!labels) return labels
880888
881- final result = new LinkedHashMap<String, String>()
889+ final strictMode = executor.session.config.navigate('nextflow.enable.strict', false)
890+ final violations = []
891+ final result = new HashMap<String, String>()
882892
883893 for (Map.Entry<String, String> entry : labels.entrySet()) {
884894 final key = entry.getKey()
885895 final value = entry.getValue()
886896
887- // Handle null keys or values
888- if (key == null || value == null) {
889- log.warn "AWS Batch label dropped due to null ${key == null ? 'key' : 'value'}: key=${key}, value=${value}"
897+ // Check for null keys or values and filter them out
898+ if (key == null) {
899+ violations << "Label has null key: key=null, value=${value}"
900+ log.warn "AWS Batch label dropped due to null key: key=null, value=${value}"
890901 continue
891902 }
892-
893- final originalKey = key.toString()
894- final originalValue = value.toString()
895- final sanitizedKey = sanitizeAwsBatchLabel(originalKey, 128)
896- final sanitizedValue = sanitizeAwsBatchLabel(originalValue, 256)
897-
898- // Check if sanitization resulted in empty strings
899- if (!sanitizedKey || !sanitizedValue) {
900- log.warn "AWS Batch label dropped after sanitization - key: '${originalKey}' -> '${sanitizedKey ?: ''}', value: '${originalValue}' -> '${sanitizedValue ?: ''}'"
903+ if (value == null) {
904+ violations << "Label has null value: key=${key}, value=null"
905+ log.warn "AWS Batch label dropped due to null value: key=${key}, value=null"
901906 continue
902907 }
903908
904- // Log if values were modified during sanitization
905- if (sanitizedKey != originalKey || sanitizedValue != originalValue) {
906- log.warn "AWS Batch label sanitized - key: '${originalKey}' -> '${sanitizedKey}', value: '${originalValue}' -> '${sanitizedValue}'"
909+ final keyStr = key.toString()
910+ final valueStr = value.toString()
911+
912+ // Validate key length
913+ if (keyStr.length() > 128) {
914+ violations << "Label key exceeds 128 characters: '${keyStr}' (${keyStr.length()} chars)"
915+ }
916+
917+ // Validate value length
918+ if (valueStr.length() > 256) {
919+ violations << "Label value exceeds 256 characters: '${keyStr}' = '${valueStr}' (${valueStr.length()} chars)"
920+ }
921+
922+ // Validate key characters
923+ if (!isValidAwsBatchTagString(keyStr)) {
924+ violations << "Label key contains invalid characters: '${keyStr}' - only letters, numbers, spaces, and _ . : / = + - @ are allowed"
925+ }
926+
927+ // Validate value characters
928+ if (!isValidAwsBatchTagString(valueStr)) {
929+ violations << "Label value contains invalid characters: '${keyStr}' = '${valueStr}' - only letters, numbers, spaces, and _ . : / = + - @ are allowed"
907930 }
908931
909- result.put(sanitizedKey, sanitizedValue)
932+ // Add valid entries to result
933+ result[keyStr] = valueStr
934+ }
935+
936+ // Handle violations based on strict mode (but only for constraint violations, not null filtering)
937+ if (violations) {
938+ final message = "AWS Batch tag validation failed:\n${violations.collect{ ' - ' + it }.join('\n')}"
939+ if (strictMode) {
940+ throw new ProcessUnrecoverableException(message)
941+ } else {
942+ log.warn "${message}\nTags will be used as-is but may cause AWS Batch submission failures"
943+ }
910944 }
911945
912946 return result
913947 }
914948
915949 /**
916- * Sanitize a single label key or value for AWS Batch tags.
917- * Replaces invalid characters with underscores and truncates if necessary.
918- *
919- * @param input The input string to sanitize
920- * @param maxLength The maximum allowed length
921- * @return The sanitized string
950+ * Check if a string contains only characters allowed in AWS Batch tags.
951+ * AWS Batch allows: letters, numbers, spaces, and: _ . : / = + - @
952+ *
953+ * @param input The string to validate
954+ * @return true if the string contains only valid characters
922955 */
923- protected String sanitizeAwsBatchLabel(String input, int maxLength) {
924- if (!input) return input
925-
926- // Replace invalid characters and clean up the string
927- // AWS Batch allows: letters, numbers, spaces, and: _ . : / = + - @
928- final sanitized = input
929- .replaceAll(/[^a-zA-Z0-9\s_.\:\/=+\-@]/, '_') // Replace invalid chars with underscores
930- .replaceAll(/[_\s]{2,}/, '_') // Replace multiple consecutive underscores/spaces
931- .replaceAll(/^[_\s]+|[_\s]+$/, '') // Remove leading/trailing underscores and spaces
932-
933- // Truncate if necessary and clean up any trailing underscores/spaces
934- final result = sanitized.size() > maxLength
935- ? sanitized.substring(0, maxLength).replaceAll(/[_\s]+$/, '')
936- : sanitized
937-
938- return result ?: null
956+ protected boolean isValidAwsBatchTagString(String input, int maxLength = 128) {
957+ if (!input) return false
958+ if (input.length() > maxLength) return false
959+ return input ==~ /^[a-zA-Z0-9\s_.\:\/=+\-@]*$/
939960 }
940961
941962 /**
0 commit comments