Skip to content

Instantly share code, notes, and snippets.

@jhy
Created November 22, 2023 23:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jhy/aa12c4c07b8a4bfaba59055180f60b6c to your computer and use it in GitHub Desktop.
Save jhy/aa12c4c07b8a4bfaba59055180f60b6c to your computer and use it in GitHub Desktop.
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
import java.util.Arrays;
class Scratch {
public static void main(String[] args) {
cleanSvg();
}
static void cleanSvg() {
String svg = getSvg();
Safelist safelist = Safelist.none();
String[] lowercaseElementsArray = Arrays.stream(DefaultSvgElements)
.map(String::toLowerCase)
.toList()
.toArray(new String[0]);
String[] lowercaseAttributesArray = Arrays.stream(DefaultSvgAttributes)
.map(String::toLowerCase)
.toList()
.toArray(new String[0]);
safelist.addTags(lowercaseElementsArray);
safelist.addAttributes(":all", lowercaseAttributesArray);
String parsedSvg = Jsoup.parseBodyFragment(svg).body().outerHtml();
print("Parsed:", parsedSvg);
String cleanedSvg = Jsoup.clean(svg, safelist);
print("Cleaned:", cleanedSvg);
}
static String getSvg() {
return """
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="feOffset" x="-40" y="-20" width="100" height="200">
<feOffset in="SourceGraphic" dx="60" dy="60" />
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur2" />
<feMerge>
<feMergeNode
in="blur2"></feMergeNode>
<feMergeNode></feMergeNode>
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="noise2" x="0" y="0" width="100%" height="100%">
<feTurbulence baseFrequency="0.05" />
</filter>
<clipPath id="myClip2" clipPathUnits="objectBoundingBox">
<circle cx=".5" cy=".5" r=".35" />
</clipPath>
<filter id="convolveMatrix2" x="0" y="0" width="100%" height="100%">
<feConvolveMatrix
kernelMatrix="-1 0 0 0 0 0 0 0 1" />
</filter>
<rect
x="40"
y="40"
width="100"
height="100"
style="stroke: #000000; fill: green; filter: url(#feOffset);" />
<rect
x="40"
y="40"
width="100"
height="100"
style="stroke: #000000; fill: green;" />
</svg>
""";
}
static final String[] DefaultSvgElements = new String[]{
"a",
"animate",
"animateMotion",
"animateTransform",
"circle",
"clipPath",
"defs",
"desc",
"ellipse",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"filter",
"g",
"hatch",
"hatchpath",
"image",
"line",
"linearGradient",
"marker",
"mask",
"metadata",
"mpath",
"path",
"pattern",
"polygon",
"polyline",
"radialGradient",
"rect",
"set",
"stop",
"switch",
"symbol",
"text",
"textPath",
"title",
"tspan",
"use",
"view",
"altGlyph",
"altGlyphDef",
"altGlyphItem",
"animateColor",
"discard",
"font",
"glyph",
"glyphRef",
"hkern",
"svg",
"tref",
"vkern"
};
static final String[] DefaultSvgAttributes = new String[]{
"accent-height",
"accumulate",
"additive",
"alignment-baseline",
"alphabetic",
"amplitude",
"arabic-form",
"ascent",
"attributeName",
"attributeType",
"azimuth",
"baseFrequency",
"baseline-shift",
"baseProfile",
"bbox",
"begin",
"bias",
"by",
"calcMode",
"cap-height",
"class",
"clip",
"clipPathUnits",
"clip-path",
"clip-rule",
"color",
"color-interpolation",
"color-interpolation-filters",
"color-profile",
"color-rendering",
"contentScriptType",
"contentStyleType",
"crossorigin",
"cursor",
"cx",
"cy",
"d",
"decelerate",
"descent",
"diffuseConstant",
"direction",
"display",
"divisor",
"dominant-baseline",
"dur",
"dx",
"dy",
"edgeMode",
"elevation",
"enable-background",
"end",
"exponent",
"fill",
"fill-opacity",
"fill-rule",
"filter",
"filterRes",
"filterUnits",
"flood-color",
"flood-opacity",
"font-family",
"font-size",
"font-size-adjust",
"font-stretch",
"font-style",
"font-variant",
"font-weight",
"format",
"from",
"fr",
"fx",
"fy",
"g1",
"g2",
"glyph-name",
"glyph-orientation-horizontal",
"glyph-orientation-vertical",
"glyphRef",
"gradientTransform",
"gradientUnits",
"hanging",
"height",
"href",
"hreflang",
"horiz-adv-x",
"horiz-origin-x",
"id",
"ideographic",
"image-rendering",
"in",
"in2",
"intercept",
"k",
"k1",
"k2",
"k3",
"k4",
"kernelMatrix",
"kernelUnitLength",
"kerning",
"keyPoints",
"keySplines",
"keyTimes",
"lang",
"lengthAdjust",
"letter-spacing",
"lighting-color",
"limitingConeAngle",
"local",
"marker-end",
"marker-mid",
"marker-start",
"markerHeight",
"markerUnits",
"markerWidth",
"mask",
"maskContentUnits",
"maskUnits",
"mathematical",
"max",
"media",
"method",
"min",
"mode",
"name",
"numOctaves",
"offset",
"opacity",
"operator",
"order",
"orient",
"orientation",
"origin",
"overflow",
"overline-position",
"overline-thickness",
"panose-1",
"paint-order",
"path",
"pathLength",
"patternContentUnits",
"patternTransform",
"patternUnits",
"ping",
"pointer-events",
"points",
"pointsAtX",
"pointsAtY",
"pointsAtZ",
"preserveAlpha",
"preserveAspectRatio",
"primitiveUnits",
"r",
"radius",
"referrerPolicy",
"refX",
"refY",
"rel",
"rendering-intent",
"repeatCount",
"repeatDur",
"requiredExtensions",
"requiredFeatures",
"restart",
"result",
"rotate",
"rx",
"ry",
"scale",
"seed",
"shape-rendering",
"slope",
"spacing",
"specularConstant",
"specularExponent",
"speed",
"spreadMethod",
"startOffset",
"stdDeviation",
"stemh",
"stemv",
"stitchTiles",
"stop-color",
"stop-opacity",
"strikethrough-position",
"strikethrough-thickness",
"string",
"stroke",
"stroke-dasharray",
"stroke-dashoffset",
"stroke-linecap",
"stroke-linejoin",
"stroke-miterlimit",
"stroke-opacity",
"stroke-width",
"style",
"surfaceScale",
"systemLanguage",
"tabindex",
"tableValues",
"target",
"targetX",
"targetY",
"text-anchor",
"text-decoration",
"text-rendering",
"textLength",
"to",
"transform",
"transform-origin",
"type",
"u1",
"u2",
"underline-position",
"underline-thickness",
"unicode",
"unicode-bidi",
"unicode-range",
"units-per-em",
"v-alphabetic",
"v-hanging",
"v-ideographic",
"v-mathematical",
"values",
"vector-effect",
"version",
"vert-adv-y",
"vert-origin-x",
"vert-origin-y",
"viewBox",
"viewTarget",
"visibility",
"width",
"widths",
"word-spacing",
"writing-mode",
"x",
"x-height",
"x1",
"x2",
"xChannelSelector",
"xlink:actuate",
"xlink:arcrole",
"xlink:href",
"xlink:role",
"xlink:show",
"xlink:title",
"xlink:type",
"xml:base",
"xml:lang",
"xml:space",
"y",
"y1",
"y2",
"yChannelSelector",
"z",
"zoomAndPan",
"wrap",
"xmlns",
"xmlns:xlink"
};
static void print(String msg) {
System.out.println(msg);
}
static void print(String title, String msg) {
System.out.println(title);
System.out.println(msg);
System.out.println("\n\n");
}
}
@OlivierJaquemet
Copy link

Hi @jhy , this code is very interesting because it seems to provide a way to properly clean SVG from unsafe data.
Are you aware of any project/repo dedicated to this purpose ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment