7
7
import com .appsmith .server .services .AnalyticsService ;
8
8
import lombok .RequiredArgsConstructor ;
9
9
import lombok .extern .slf4j .Slf4j ;
10
+ import org .apache .commons .io .IOUtils ;
10
11
import org .springframework .core .io .buffer .DataBuffer ;
11
12
import org .springframework .core .io .buffer .DataBufferUtils ;
12
13
import org .springframework .core .io .buffer .DefaultDataBufferFactory ;
25
26
import java .awt .image .BufferedImage ;
26
27
import java .io .ByteArrayOutputStream ;
27
28
import java .io .IOException ;
29
+ import java .nio .charset .StandardCharsets ;
28
30
import java .util .List ;
29
31
import java .util .Objects ;
30
32
import java .util .Set ;
33
+ import java .util .regex .Pattern ;
31
34
import java .util .stream .Collectors ;
32
35
33
36
import static com .appsmith .server .constants .Constraint .THUMBNAIL_PHOTO_DIMENSION ;
@@ -43,11 +46,12 @@ public class AssetServiceCEImpl implements AssetServiceCE {
43
46
private static final Set <MediaType > ALLOWED_CONTENT_TYPES = Set .of (
44
47
MediaType .IMAGE_JPEG ,
45
48
MediaType .IMAGE_PNG ,
49
+ MediaType .valueOf ("image/svg+xml" ),
46
50
MediaType .valueOf ("image/x-icon" ),
47
51
MediaType .valueOf ("image/vnd.microsoft.icon" ));
48
52
49
53
private static final Set <String > ALLOWED_CONTENT_TYPES_STR =
50
- Set .of (MediaType .IMAGE_JPEG_VALUE , MediaType .IMAGE_PNG_VALUE );
54
+ Set .of (MediaType .IMAGE_JPEG_VALUE , MediaType .IMAGE_PNG_VALUE , "image/svg+xml" );
51
55
52
56
@ Override
53
57
public Mono <Asset > getById (String id ) {
@@ -65,13 +69,46 @@ private Boolean checkImageTypeValidation(DataBuffer dataBuffer, MediaType conten
65
69
means the file is not an image type file rather any other corrupted file but the extension has been
66
70
changed to .png or .jpeg to upload the flawed file. This is a security vulnerability hence reject
67
71
*/
68
- if (ALLOWED_CONTENT_TYPES_STR .contains (contentType .toString ())) {
72
+ Boolean isSvgType = MediaType .valueOf ("image/svg+xml" ).equals (contentType );
73
+ if (isSvgType ) {
74
+ String svgContent = IOUtils .toString (dataBuffer .asInputStream (), StandardCharsets .UTF_8 );
75
+ dataBuffer .readPosition (0 );
76
+
77
+ if (!isSvgSafe (svgContent )) {
78
+ // Throwing validation exception to return separate response for svg type.
79
+ throw new AppsmithException (AppsmithError .VALIDATION_FAILURE , "Please upload a valid svg." );
80
+ }
81
+ } else if (ALLOWED_CONTENT_TYPES_STR .contains (contentType .toString ())) {
69
82
return false ;
70
83
}
71
84
}
72
85
return true ;
73
86
}
74
87
88
+ private boolean isSvgSafe (String svgContent ) {
89
+ // Array of regex patterns to check any malicious content like(style,link,script etc.) against SVG content
90
+ String [] patterns = {
91
+ "<script\\ b[^<]*(?:(?!<\\ /script>)<[^<]*)*<\\ /script>" ,
92
+ "<style\\ b[^<]*(?:(?!<\\ /style>)<[^<]*)*<\\ /style>" ,
93
+ "<a\\ b[^>]*>" ,
94
+ "</a>" ,
95
+ "\\ s(on[a-zA-Z]+|style)\\ s*=\\ s*(['\" ]).*?\\ 2" ,
96
+ "\\ s(href|src)\\ s*=\\ s*(['\" ])javascript:[^'\" ]*\\ 2"
97
+ };
98
+
99
+ // Compile patterns and check each against svgContent to detect any malicious code.
100
+ for (String pattern : patterns ) {
101
+ if (Pattern .compile (pattern , Pattern .CASE_INSENSITIVE | Pattern .DOTALL )
102
+ .matcher (svgContent )
103
+ .find ()) {
104
+ return false ; // Unsafe SVG
105
+ }
106
+ }
107
+
108
+ // If none of the patterns match, consider the SVG safe
109
+ return true ;
110
+ }
111
+
75
112
@ Override
76
113
public Mono <Asset > upload (List <Part > fileParts , int maxFileSizeKB , boolean isThumbnail ) {
77
114
fileParts = fileParts .stream ().filter (Objects ::nonNull ).collect (Collectors .toList ());
0 commit comments