diff --git a/Stack/Opc.Ua.Core/Types/Utils/RelativePath.cs b/Stack/Opc.Ua.Core/Types/Utils/RelativePath.cs index 939894158c..ebfb679d16 100644 --- a/Stack/Opc.Ua.Core/Types/Utils/RelativePath.cs +++ b/Stack/Opc.Ua.Core/Types/Utils/RelativePath.cs @@ -740,7 +740,7 @@ namespace Opc.Ua int last = reader.Peek(); - for (int next = last; next != -1; next = reader.Peek(), last=next) + for (int next = last; next != -1; next = reader.Peek(), last = next) { if (!Char.IsDigit((char)next)) { @@ -783,22 +783,37 @@ namespace Opc.Ua } } - // check for invalid character. - if (next == '!' || next == ':' || next == '<' || next == '>' || next == '/' || next == '.') - { - throw new ServiceResultException( - StatusCodes.BadSyntaxError, - Utils.Format("Unexpected character '{0}' in browse path.", next)); - - } - // check for escape character. if (next == '&') { next = reader.Read(); + if (next == -1) + { + throw new ServiceResultException( + StatusCodes.BadSyntaxError, + "Unexpected end after escape character '&'."); + } next = reader.Read(); - buffer.Append((char)next); - continue; + + if (next == '!' || next == ':' || next == '<' || next == '>' || next == '/' || next == '.' || next == '#' || next == '&') + { + buffer.Append((char)next); + continue; + } + else + { + throw new ServiceResultException( + StatusCodes.BadSyntaxError, + Utils.Format("Invalid escape sequence '&{0}' in browse path.", next)); + } + } + + // check for invalid character. + if (next == '!' || next == ':' || next == '<' || next == '>' || next == '/' || next == '.' || next == '#' || next == '&') + { + throw new ServiceResultException( + StatusCodes.BadSyntaxError, + Utils.Format("Unexpected character '{0}' in browse path.", next)); } // append character. @@ -812,8 +827,8 @@ namespace Opc.Ua if (last != '>') { throw new ServiceResultException( - StatusCodes.BadSyntaxError, - Utils.Format("Missing file '>' for reference type name in browse path.")); + StatusCodes.BadSyntaxError, + Utils.Format("Missing closing '>' for reference type name in browse path.")); } } diff --git a/Tests/Opc.Ua.Core.Tests/Types/Utils/UtilTests.cs b/Tests/Opc.Ua.Core.Tests/Types/Utils/UtilTests.cs index 3321792b1c..9f82da6754 100644 --- a/Tests/Opc.Ua.Core.Tests/Types/Utils/UtilTests.cs +++ b/Tests/Opc.Ua.Core.Tests/Types/Utils/UtilTests.cs @@ -290,6 +290,68 @@ namespace Opc.Ua.Core.Tests.Types.UtilsTests Assert.AreEqual(false, settings.CloseInput); } #endregion + + #region RelativePath.Parse Escaping + + /// + /// Parse a path containing non-escaped hash character. + /// + [Test] + public void RelativePathParseNonEscapedHash() + { + TypeTable typeTable = new TypeTable(new NamespaceTable()); + string str = "/abc#def"; + Assert.Throws(() => RelativePath.Parse(str, typeTable).Format(typeTable)); + } + + /// + /// Parse a path containing correctly escaped hash character. + /// + [Test] + public void RelativePathParseEscapedHash() + { + TypeTable typeTable = new TypeTable(new NamespaceTable()); + string str = "/abc&#def"; + string expected = "/abc#def"; + Assert.AreEqual(expected, RelativePath.Parse(str, typeTable).Format(typeTable)); + } + + /// + /// Parse a path containing correctly escaped hash character folowed by exclamation. + /// + [Test] + public void RelativePathParseEscapedHashFollowedByExclamation() + { + TypeTable typeTable = new TypeTable(new NamespaceTable()); + string str = "/abc&#!def"; + Assert.Throws(() => RelativePath.Parse(str, typeTable).Format(typeTable)); + } + + /// + /// Parse a path containing correctly escaped hash character by exclamation within the reference type delimeters. + /// + [Test] + public void RelativePathParseEscapedHashFollowedByExclamationInReferenceType() + { + TypeTable typeTable = new TypeTable(new NamespaceTable()); + string str = ""; + Assert.Throws(() => RelativePath.Parse(str, typeTable).Format(typeTable)); + } + + /// + /// Parse a path containing incorrectly escaped character sequence. + /// + [Test] + public void RelativePathParseInvalidEscapeSequence() + { + TypeTable typeTable = new TypeTable(new NamespaceTable()); + string str = "/abc&$!def"; + Assert.Throws(() => RelativePath.Parse(str, typeTable).Format(typeTable)); + } + + + #endregion + } }