{"record":{"id":"3e9caa59dd02e799","repo":"spring-projects/spring-ai","slug":"url-is-not-valid-under-strict-validation-rules","errorCode":null,"errorMessage":"URL is not valid under strict validation rules: ","messagePattern":"URL is not valid under strict validation rules: ","errorType":"exception","errorClass":"SecurityException","httpStatus":null,"severity":"error","filePath":"models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java","lineNumber":550,"sourceCode":"\t\t\t}\n\t\t\telse if (media.getData() instanceof String text) {\n\n\t\t\t\tif (text.startsWith(\"s3://\")) {\n\t\t\t\t\tsourceBuilder.s3Location(S3Location.builder().uri(text).build()).build();\n\t\t\t\t}\n\t\t\t\telse if (text.startsWith(\"http://\") || text.startsWith(\"https://\")) {\n\t\t\t\t\t// Not base64\n\t\t\t\t\tif (URLValidator.isValidURLStrict(text)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tbyte[] bytes = this.mediaFetcher.fetch(URI.create(text));\n\t\t\t\t\t\t\tsourceBuilder.bytes(SdkBytes.fromByteArrayUnsafe(bytes)).build();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (SecurityException | RestClientException e) {\n\t\t\t\t\t\t\tthrow new RuntimeException(\"Failed to read media data from URL: \" + text, e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthrow new SecurityException(\"URL is not valid under strict validation rules: \" + text);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Assume it's base64-encoded image data\n\t\t\t\t\tsourceBuilder.bytes(SdkBytes.fromByteArray(Base64.getDecoder().decode(text)));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (media.getData() instanceof URL url) {\n\n\t\t\t\ttry {\n\t\t\t\t\tString protocol = url.getProtocol();\n\t\t\t\t\tif (!\"http\".equalsIgnoreCase(protocol) && !\"https\".equalsIgnoreCase(protocol)) {\n\t\t\t\t\t\tthrow new SecurityException(\"Unsupported URL protocol: \" + protocol);\n\t\t\t\t\t}\n\t\t\t\t\tbyte[] bytes = this.mediaFetcher.fetch(url.toURI());\n\t\t\t\t\tsourceBuilder.bytes(SdkBytes.fromByteArrayUnsafe(bytes)).build();\n\t\t\t\t}\n\t\t\t\tcatch (SecurityException | RestClientException | URISyntaxException e) {","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java#L532-L568","documentation":"Thrown as SecurityException when a String media value fails URLValidator.isValidURLStrict in mapMediaToContentBlock. The library only accepts Strings that are either strictly valid http/https URLs or base64 data; anything failing strict URL validation is treated as base64, and if decode fails or validation is intended as URL this guard fires for rejected URLs (e.g. file://, ftp://, or otherwise non-conforming strings).","triggerScenarios":"Passing a Media with String data like 'file:///etc/passwd', 'ftp://host/img.png', or any string that strict validation rejects (missing scheme, disallowed characters). The else-branch throws this SecurityException before any base64 decoding is attempted.","commonSituations":"Developers try to load local files via file:// URLs into Bedrock prompts; others paste URLs with spaces or non-ASCII characters that fail strict validation; SSRF hardening intentionally rejects ftp/file schemes.","solutions":["Read the file yourself and pass base64-encoded bytes as the media data.","Use a proper http(s):// URL that passes strict validation.","Use a java.net.URL object via Media with URL data (which goes through the URL branch) only with http/https.","If the string is meant to be base64, verify it decodes cleanly (no 'data:' prefix, no whitespace)."],"exampleFix":"// before\nnew Media(MimeTypeUtils.IMAGE_PNG, \"file:///images/cat.png\");\n// after\nbyte[] bytes = Files.readAllBytes(Path.of(\"/images/cat.png\"));\nnew Media(MimeTypeUtils.IMAGE_PNG, Base64.getEncoder().encodeToString(bytes));","handlingStrategy":"validation","validationCode":"if (data != null && !URLValidator.isValidURLStrict(data) && !isBase64(data)) {\n    throw new IllegalArgumentException(\"Media string is neither a valid strict URL nor base64\");\n}","typeGuard":"boolean isSafeMediaString(String s) {\n    return s != null && (s.startsWith(\"https://\") || s.startsWith(\"http://\") || s.matches(\"[A-Za-z0-9+/=]+\"));\n}","tryCatchPattern":"try {\n    model.call(prompt);\n} catch (SecurityException e) {\n    if (e.getMessage().startsWith(\"URL is not valid\")) { /* use base64 instead */ }\n    else throw e;\n}","preventionTips":["Read local files into memory and send base64 instead of file:// URLs","Only use http/https URLs as String media data","Strip whitespace and data: prefixes from base64 strings","Unit-test media strings against strict URL validation before deployment"],"tags":["security","url-validation","ssrf-protection","bedrock"],"backgroundTag":"invalid-url-format","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}