Skip to content

Commit

Permalink
Begin fixing XXE vulnerability
Browse files Browse the repository at this point in the history
- partial fix for the #261, marker for curves not implemented
- initial fix for the #262, fixes the resolution of URL in entities
  • Loading branch information
paulushub committed Oct 2, 2023
1 parent 404fe93 commit fe13f96
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 22 deletions.
30 changes: 30 additions & 0 deletions Source/SharpVectorCore/Utils/Xml/DynamicXmlUrlResolver.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Xml;

namespace SharpVectors.Xml
Expand Down Expand Up @@ -28,6 +29,26 @@ public sealed class DynamicXmlUrlResolver : XmlUrlResolver
/// </summary>
public event GettingEntityEventHandler GettingEntity;

public static readonly UrlResolvePolicy UrlDefaultPolicy = new UrlResolvePolicy(DtdProcessing.Parse);

private static UrlResolvePolicy _urlPolicy;

public DynamicXmlUrlResolver() { }

public static UrlResolvePolicy UrlPolicy
{
get {
if (_urlPolicy == null)
{
_urlPolicy = UrlDefaultPolicy;
}
return _urlPolicy;
}
set {
_urlPolicy = value;
}
}

/// <summary>
/// Maps a URI to an object that contains the actual resource.
/// </summary>
Expand All @@ -44,6 +65,15 @@ public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToRe
return entity;
}

UrlResolvePolicy urlPolicy = DynamicXmlUrlResolver.UrlPolicy;
if (urlPolicy.Processing == DtdProcessing.Parse)
{
if (urlPolicy.Entity.HasFlag(UrlResolveTypes.Resource))
{
return new MemoryStream();
}
}

return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}

Expand Down
119 changes: 119 additions & 0 deletions Source/SharpVectorCore/Utils/Xml/UrlResolvePolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Xml;
using System.Linq;
using System.Text;
using System.IO;

namespace SharpVectors.Xml
{
public class UrlResolvePolicy
{
private readonly DtdProcessing _processing;
private readonly UrlResolveTypes _entity;
private readonly UrlResolveTypes _element;
private readonly UrlResolveTypes _document;
private readonly UrlResolveTypes _font;
private readonly UrlResolveTypes _image;
private readonly UrlResolveTypes _style;
private readonly UrlResolveTypes _script;

public UrlResolvePolicy(DtdProcessing processing)
{
_processing = processing;
_entity = processing == DtdProcessing.Parse ? UrlResolveTypes.Resource : UrlResolveTypes.None;
_element = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_document = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_font = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_image = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_style = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_script = UrlResolveTypes.Local | UrlResolveTypes.Remote;
}

public UrlResolvePolicy(UrlResolveArgs args)
{
_processing = args.Processing;
_entity = args.Entity;
_element = args.Element;
_document = args.Document;
_font = args.Font;
_image = args.Image;
_style = args.Style;
_script = args.Script;
}

public DtdProcessing Processing { get => _processing; }
public UrlResolveTypes Entity { get => _entity; }
public UrlResolveTypes Element { get => _element; }
public UrlResolveTypes Document { get => _document; }
public UrlResolveTypes Font { get => _font; }
public UrlResolveTypes Image { get => _image; }
public UrlResolveTypes Style { get => _style; }
public UrlResolveTypes Script { get => _script; }

public virtual bool Supports(Uri uri, UrlResolveSource source)
{
if (uri == null || !uri.IsAbsoluteUri)
{
return false;
}
UrlResolveTypes types = UrlResolveTypes.None;
switch (source)
{
case UrlResolveSource.Entity:
types = this.Entity;
break;
case UrlResolveSource.Element:
break;
case UrlResolveSource.Document:
break;
case UrlResolveSource.Font:
break;
case UrlResolveSource.Image:
break;
case UrlResolveSource.Style:
break;
case UrlResolveSource.Script:
break;
}
if (uri.Scheme.StartsWith("pack") && types.HasFlag(UrlResolveTypes.Resource))
{
return true;
}
if (uri.IsFile && types.HasFlag(UrlResolveTypes.Local))
{
return true;
}

return !uri.IsFile && types.HasFlag(UrlResolveTypes.Remote);
}

/// <summary>
/// Maps a URI to an object that contains the actual resource.
/// </summary>
/// <param name="absoluteUri">The URI returned from <see cref="ResolveUri(Uri, string)"/>.</param>
/// <param name="role">Currently not used.</param>
/// <param name="ofObjectToReturn">The type of object to return. The current implementation only returns <see cref="Stream"/> objects.</param>
/// <returns>A stream object or <see langword="null"/> if a type other than stream is specified.</returns>
public virtual object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
return null;
}

/// <summary>
/// Resolves the absolute URI from the base and relative URIs.
/// </summary>
/// <param name="baseUri">The base URI used to resolve the relative URI.</param>
/// <param name="relativeUri">The URI to resolve. The URI can be absolute or relative.
/// If absolute, this value effectively replaces the <paramref name="baseUri"/> value.
/// If relative, it combines with the <paramref name="baseUri"/> to make an absolute URI.</param>
/// <returns>
/// A <see cref="T:System.Uri"/> representing the absolute URI, or <see langword="null"/> if the relative URI cannot be resolved.
/// </returns>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="baseUri"/>is null or <paramref name="relativeUri"/> is null</exception>
public virtual Uri ResolveUri(Uri baseUri, string relativeUri)
{
return null;
}
}
}
63 changes: 63 additions & 0 deletions Source/SharpVectorCore/Utils/Xml/UrlResolveSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Xml;

namespace SharpVectors.Xml
{
public enum UrlResolveSource
{
None = 0,
Entity = 1,
Element = 2,
Document = 3,
Font = 4,
Image = 5,
Style = 6,
Script = 7
}

public struct UrlResolveArgs
{
private DtdProcessing _processing;
private UrlResolveTypes _entity;
private UrlResolveTypes _element;
private UrlResolveTypes _document;
private UrlResolveTypes _font;
private UrlResolveTypes _image;
private UrlResolveTypes _style;
private UrlResolveTypes _script;

public UrlResolveArgs(DtdProcessing processing)
{
_processing = processing;
_entity = UrlResolveTypes.Resource;
_element = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_document = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_font = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_image = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_style = UrlResolveTypes.Local | UrlResolveTypes.Remote;
_script = UrlResolveTypes.Local | UrlResolveTypes.Remote;
}

public UrlResolveArgs(DtdProcessing processing, UrlResolveTypes entity,
UrlResolveTypes element, UrlResolveTypes document, UrlResolveTypes font,
UrlResolveTypes image, UrlResolveTypes style, UrlResolveTypes script)
{
_processing = processing;
_entity = entity;
_element = element;
_document = document;
_font = font;
_image = image;
_style = style;
_script = script;
}

public DtdProcessing Processing { get => _processing; set => _processing = value; }
public UrlResolveTypes Entity { get => _entity; set => _entity = value; }
public UrlResolveTypes Element { get => _element; set => _element = value; }
public UrlResolveTypes Document { get => _document; set => _document = value; }
public UrlResolveTypes Font { get => _font; set => _font = value; }
public UrlResolveTypes Image { get => _image; set => _image = value; }
public UrlResolveTypes Style { get => _style; set => _style = value; }
public UrlResolveTypes Script { get => _script; set => _script = value; }
}
}
13 changes: 13 additions & 0 deletions Source/SharpVectorCore/Utils/Xml/UrlResolveTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace SharpVectors.Xml
{
[Flags]
public enum UrlResolveTypes
{
None = 0x0,
Local = 0x1,
Remote = 0x2,
Resource = 0x4
}
}
40 changes: 36 additions & 4 deletions Source/SharpVectorModel/DocumentStructure/SvgDocument.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Net;
using System.Xml;
using System.Threading;
using System.Diagnostics;
Expand All @@ -8,6 +9,7 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;

using SharpVectors.Net;
using SharpVectors.Xml;
using SharpVectors.Woffs;
using SharpVectors.Dom.Css;
Expand Down Expand Up @@ -423,7 +425,7 @@ public override void Load(string filename)
{
// Provide a support for the .svgz files...
UriBuilder fileUrl = new UriBuilder(filename);
if (string.Equals(fileUrl.Scheme, "file", StringComparison.OrdinalIgnoreCase))
if (string.Equals(fileUrl.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase))
{
string fileExt = Path.GetExtension(filename);
if (string.Equals(fileExt, SvgConstants.FileExtZ, StringComparison.OrdinalIgnoreCase))
Expand Down Expand Up @@ -499,6 +501,7 @@ public void Load(string baseUrl, Stream stream)
/// <param name="txtReader"></param>
public override void Load(TextReader txtReader)
{
_baseURI = string.Empty;
using (XmlReader xmlReader = CreateValidatingXmlReader(txtReader))
{
this.Load(xmlReader);
Expand All @@ -513,6 +516,7 @@ public override void Load(TextReader txtReader)
/// </param>
public override void Load(Stream inStream)
{
_baseURI = string.Empty;
if (inStream.CanSeek && inStream.Position == 0)
{
if (IsGZipped(inStream))
Expand Down Expand Up @@ -581,9 +585,10 @@ private static string GetEntityUri(string uri)
/// Handles DynamicXmlUrlResolver GettingEntity event.
/// </summary>
/// <param name="absoluteUri">The absolute URI.</param>
/// <param name="role">Currently not used.</param>
/// <param name="ofObjectToReturn">The of object to return.</param>
/// <returns></returns>
private object OnXmlResolverGettingEntity(Uri absoluteUri, string cc, Type ofObjectToReturn)
private object OnXmlResolverGettingEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
string fullPath = absoluteUri.ToString();
if (!string.IsNullOrWhiteSpace(fullPath))
Expand Down Expand Up @@ -634,6 +639,31 @@ private object OnXmlResolverGettingEntity(Uri absoluteUri, string cc, Type ofObj
}
}

if (!string.IsNullOrWhiteSpace(this.BaseURI))
{
UriBuilder sourceUri = new UriBuilder(this.BaseURI);
if (sourceUri.Uri == absoluteUri)
{
if (string.Equals(sourceUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase))
{
return new FileStream(absoluteUri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read, 1);
}
else
{
WebRequest request = new ExtendedHttpWebRequest(absoluteUri);
try
{
WebResponse response = request.GetResponse();
return response.GetResponseStream();
}
catch
{
return null;
}
}
}
}

if (absoluteUri.IsFile)
{
return null;
Expand Down Expand Up @@ -1401,14 +1431,16 @@ protected override void OnLoaded()
SvgWindow ownedWindow = _window.CreateOwnedWindow();
ownedWindow.LoadFonts = false;

var comparer = StringComparison.OrdinalIgnoreCase;

for (int i = 0; i < fontUrls.Count; i++)
{
var fontUrl = fontUrls[i].Item1;
var fontFace = fontUrls[i].Item2;
try
{
// remove any hash (won't work for local files)
int hashStart = fontUrl.IndexOf("#", StringComparison.OrdinalIgnoreCase);
int hashStart = fontUrl.IndexOf("#", comparer);
if (hashStart > -1)
{
fontUrl = fontUrl.Substring(0, hashStart);
Expand All @@ -1421,7 +1453,7 @@ protected override void OnLoaded()
continue;
}
string scheme = fileUrl.Scheme;
if (string.Equals(scheme, "file", StringComparison.OrdinalIgnoreCase))
if (string.Equals(scheme, Uri.UriSchemeFile, comparer))
{
this.LoadLocalFont(fileUrl.LocalPath, ownedWindow, fontFace);
}
Expand Down
Loading

0 comments on commit fe13f96

Please sign in to comment.