From 80b293d4a66e2f9c56ff1b06a0ae8d1d186d0f65 Mon Sep 17 00:00:00 2001 From: Mikkel Nylander Bundgaard Date: Sun, 14 May 2017 20:53:34 +0200 Subject: [PATCH] Correct parsing of 'Format Version' header in sln files Prior to this change the exception on line 138 could not be thrown, as far as I can tell. The reason for this is that`HeaderPrefix` contained a trailing space, and we had verified that `line` (trimmed) started with `HeaderPrefix`. Hence `line` must contain something more than `HeaderPrefix` otherwise the tailing space would have been removed, so the length of `line` could not be less than or equal to the length of `HeaderPrefix`. Added and changed tests to ensure that both exceptions regarding `FormatVersion` are thrown. Fixes #5978 --- .../LocalizableStrings.cs | 4 +++- .../SlnFile.cs | 15 +++++++----- ...Microsoft.DotNet.Cli.Sln.Internal.Tests.cs | 24 +++++++++++++++++-- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.cs index f340f9373..013260ed8 100644 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.cs +++ b/src/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.cs @@ -17,7 +17,9 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public const string GlobalSectionNotClosedError = "Global section not closed"; - public const string FileHeaderMissingError = "File header is missing"; + public const string FileHeaderMissingVersionError = "File header is missing version"; + + public const string FileHeaderMissingError = "Expected file header not found"; public const string ProjectSectionNotClosedError = "Project section not closed"; diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs index 93df94b85..87fad297e 100644 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs +++ b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs @@ -120,7 +120,7 @@ namespace Microsoft.DotNet.Cli.Sln.Internal private void Read(TextReader reader) { - const string HeaderPrefix = "Microsoft Visual Studio Solution File, Format Version "; + const string HeaderPrefix = "Microsoft Visual Studio Solution File, Format Version"; string line; int curLineNum = 0; @@ -137,10 +137,10 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { throw new InvalidSolutionFormatException( curLineNum, - LocalizableStrings.FileHeaderMissingError); + LocalizableStrings.FileHeaderMissingVersionError); } - FormatVersion = line.Substring(HeaderPrefix.Length); + FormatVersion = line.Substring(HeaderPrefix.Length).Trim(); _prefixBlankLines = curLineNum - 1; } if (line.StartsWith("# ", StringComparison.Ordinal)) @@ -199,9 +199,7 @@ namespace Microsoft.DotNet.Cli.Sln.Internal } if (FormatVersion == null) { - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.FileHeaderMissingError); + throw new InvalidSolutionFormatException(LocalizableStrings.FileHeaderMissingError); } } @@ -1167,6 +1165,11 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public class InvalidSolutionFormatException : Exception { + public InvalidSolutionFormatException(string details) + : base(details) + { + } + public InvalidSolutionFormatException(int line, string details) : base(string.Format(LocalizableStrings.ErrorMessageFormatString, line, details)) { diff --git a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs index 1a2155266..0734a8b55 100644 --- a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs +++ b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs @@ -287,9 +287,29 @@ EndGlobal .Should().Be(SolutionModified); } + [Theory] + [InlineData("Microsoft Visual Studio Solution File, Format Version ", 1)] + [InlineData("First Line\nMicrosoft Visual Studio Solution File, Format Version ", 2)] + [InlineData("First Line\nMicrosoft Visual Studio Solution File, Format Version \nThird Line", 2)] + [InlineData("First Line\nSecondLine\nMicrosoft Visual Studio Solution File, Format Version \nFourth Line", 3)] + public void WhenGivenASolutionWithMissingHeaderVersionItThrows(string fileContents, int lineNum) + { + var tmpFile = Temp.CreateFile(); + tmpFile.WriteAllText(fileContents); + + Action action = () => + { + SlnFile.Read(tmpFile.Path); + }; + + action.ShouldThrow() + .WithMessage($"Invalid format in line {lineNum}: File header is missing version"); + } + [Theory] [InlineData("Invalid Solution")] - [InlineData("Microsoft Visual Studio Solution File, Format Version ")] + [InlineData("Invalid Solution\nSpanning Multiple Lines")] + [InlineData("Microsoft Visual\nStudio Solution File,\nFormat Version ")] public void WhenGivenASolutionWithMissingHeaderItThrows(string fileContents) { var tmpFile = Temp.CreateFile(); @@ -301,7 +321,7 @@ EndGlobal }; action.ShouldThrow() - .WithMessage("Invalid format in line 1: File header is missing"); + .WithMessage("Expected file header not found"); } [Fact]