-
Notifications
You must be signed in to change notification settings - Fork 145
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for JRE provisioning: Jre tar.gz unpack
- Loading branch information
1 parent
ffd1271
commit 03d3f67
Showing
7 changed files
with
223 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
Tests/SonarScanner.MSBuild.PreProcessor.Test/JreCaching/TarGzUnpackTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
* SonarScanner for .NET | ||
* Copyright (C) 2016-2024 SonarSource SA | ||
* mailto: info AT sonarsource DOT com | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 3 of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public License | ||
* along with this program; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
using System; | ||
using System.IO; | ||
using FluentAssertions; | ||
using ICSharpCode.SharpZipLib.Core; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using NSubstitute; | ||
using SonarScanner.MSBuild.Common; | ||
using SonarScanner.MSBuild.PreProcessor.JreCaching; | ||
using TestUtilities; | ||
|
||
namespace SonarScanner.MSBuild.PreProcessor.Test.JreCaching; | ||
|
||
[TestClass] | ||
public class TarGzUnpackTests | ||
{ | ||
[TestMethod] | ||
public void TarGzUnpacking_Success() | ||
{ | ||
// A sample zip file with the following content: | ||
// Main | ||
// ├── Sub | ||
// └── Sub2 | ||
// └── Sample.txt | ||
const string sampleTarGzFile = """ | ||
H4sICL04jWYEAE1haW4udGFyAO3SUQrDIAyA4RzFE2wao55iTz2BBccK3Ribw | ||
nb7iVDKnkqh+mK+l4S8/rn46XGGumTmnMuz+JvLrsiSRk1ImO/WkgRhoIH0jv | ||
4lBHSq9B/SWPMHdvVHk+9OO+T+LSz9seID7OpPpT8Zy/1bWPsP/v6cwyl+Ihx | ||
ss78ya3+T70qR1iAkNNB5/1v4ijH4FKdrmoExxlgvfmqGu7oADgAA | ||
"""; | ||
var baseDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
var main = Path.Combine(baseDirectory, "Main"); | ||
var sub1 = Path.Combine(baseDirectory, "Main", "Sub"); | ||
var sub2 = Path.Combine(baseDirectory, "Main", "Sub2"); | ||
var sampleTxt = Path.Combine(baseDirectory, "Main", "Sub2", "Sample.txt"); | ||
var osProvider = Substitute.For<IOperatingSystemProvider>(); | ||
osProvider.OperatingSystem().Returns(PlatformOS.MacOSX); | ||
using var archive = new MemoryStream(Convert.FromBase64String(sampleTarGzFile)); | ||
var sut = new TarGzUnpacker(DirectoryWrapper.Instance, FileWrapper.Instance, osProvider); | ||
try | ||
{ | ||
sut.Unpack(archive, baseDirectory); | ||
|
||
Directory.Exists(main).Should().BeTrue(); | ||
Directory.Exists(sub1).Should().BeTrue(); | ||
Directory.Exists(sub2).Should().BeTrue(); | ||
File.Exists(sampleTxt).Should().BeTrue(); | ||
var content = File.ReadAllText(sampleTxt).NormalizeLineEndings(); | ||
content.Should().Be("hey beautiful"); | ||
} | ||
finally | ||
{ | ||
Directory.Delete(baseDirectory, true); | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public void TarGzUnpacking_Fails_InvalidZipFile() | ||
{ | ||
var baseDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
using var archive = new MemoryStream([1, 2, 3]); // Invalid archive content | ||
var sut = new TarGzUnpacker(DirectoryWrapper.Instance, FileWrapper.Instance, Substitute.For<IOperatingSystemProvider>()); | ||
|
||
var action = () => sut.Unpack(archive, baseDirectory); | ||
|
||
action.Should().Throw<Exception>().WithMessage("Error GZIP header, first magic byte doesn't match"); | ||
Directory.Exists(baseDirectory).Should().BeFalse(); | ||
} | ||
|
||
[TestMethod] | ||
public void TarGzUnpacking_ZipSlip_IsDetected() | ||
{ | ||
// zip-slip.zip from https://github.com/kevva/decompress/issues/71 | ||
// google "Zip Slip Vulnerability" for details | ||
const string zipSlip = """ | ||
H4sICJDill0C/215LXNsaXAudGFyAO3TvQrCMBSG4cxeRa4gTdKk | ||
XRUULHQo2MlNUET8K7aC9OrFFsTFn0ELlffhwDmcZEngU4EKhunx | ||
sE43h634Dd161rWL3X1u9sZYa4VMRQfOZbU4Sfn1R/aEUgH1YVX7 | ||
Iih3m6JYLVV1qcQ/6OLnbnmIoibjJvb6sbesESb0znsfGh8Kba1z | ||
XkjdZf6Pdb1bvbj37ryn+Z8nmcyno1zO0iTLJuOBAAAAAAAAAAAA | ||
AAAAQJ9cAZCup/MAKAAA | ||
"""; | ||
var baseDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
using var zipStream = new MemoryStream(Convert.FromBase64String(zipSlip)); | ||
var sut = new TarGzUnpacker(DirectoryWrapper.Instance, FileWrapper.Instance, Substitute.For<IOperatingSystemProvider>()); | ||
|
||
var action = () => sut.Unpack(zipStream, baseDirectory); | ||
|
||
action.Should().Throw<InvalidNameException>().WithMessage("Parent traversal in paths is not allowed"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
src/SonarScanner.MSBuild.PreProcessor/JreCaching/TarGzUnpacker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* SonarScanner for .NET | ||
* Copyright (C) 2016-2024 SonarSource SA | ||
* mailto: info AT sonarsource DOT com | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 3 of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public License | ||
* along with this program; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
using System; | ||
using System.IO; | ||
using ICSharpCode.SharpZipLib.Core; | ||
using ICSharpCode.SharpZipLib.GZip; | ||
using ICSharpCode.SharpZipLib.Tar; | ||
using SonarScanner.MSBuild.Common; | ||
|
||
namespace SonarScanner.MSBuild.PreProcessor.JreCaching; | ||
|
||
public class TarGzUnpacker(IDirectoryWrapper directoryWrapper, IFileWrapper fileWrapper, IOperatingSystemProvider operatingSystemProvider) : IUnpacker | ||
{ | ||
// ref https://github.com/icsharpcode/SharpZipLib/blob/ff2d7c30bdb2474d507f001bc555405e9f02a0bb/src/ICSharpCode.SharpZipLib/Tar/TarArchive.cs#L608 | ||
public void Unpack(Stream archive, string destinationDirectory) | ||
{ | ||
using var gzipStream = new GZipInputStream(archive); | ||
using var tarIn = new TarInputStream(gzipStream, null); | ||
|
||
var destinationFullPath = Path.GetFullPath(destinationDirectory).TrimEnd('/', '\\'); | ||
while (tarIn.GetNextEntry() is {} entry) | ||
{ | ||
if (entry.TarHeader.TypeFlag is not TarHeader.LF_LINK or TarHeader.LF_SYMLINK) | ||
{ | ||
ExtractEntry(tarIn, destinationFullPath, entry); | ||
} | ||
} | ||
} | ||
|
||
// ref https://github.com/icsharpcode/SharpZipLib/blob/ff2d7c30bdb2474d507f001bc555405e9f02a0bb/src/ICSharpCode.SharpZipLib/Tar/TarArchive.cs#L644 | ||
private void ExtractEntry(TarInputStream tar, string destinationFullPath, TarEntry entry) | ||
{ | ||
var name = entry.Name; | ||
|
||
if (Path.IsPathRooted(name)) | ||
{ | ||
// NOTE: | ||
// for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt | ||
name = name.Substring(Path.GetPathRoot(name).Length); | ||
} | ||
|
||
name = name.Replace('/', Path.DirectorySeparatorChar); | ||
|
||
var destinationFile = Path.Combine(destinationFullPath, name); | ||
var destinationFileDirectory = Path.GetDirectoryName(Path.GetFullPath(destinationFile)) ?? string.Empty; | ||
|
||
var isRootDir = entry.IsDirectory && entry.Name == string.Empty; | ||
|
||
if (!isRootDir && !destinationFileDirectory.StartsWith(destinationFullPath, StringComparison.InvariantCultureIgnoreCase)) | ||
{ | ||
throw new InvalidNameException("Parent traversal in paths is not allowed"); | ||
} | ||
|
||
if (entry.IsDirectory) | ||
{ | ||
directoryWrapper.CreateDirectory(destinationFile); | ||
} | ||
else | ||
{ | ||
directoryWrapper.CreateDirectory(destinationFileDirectory); | ||
|
||
using var outputStream = fileWrapper.Create(destinationFile); | ||
// If translation is disabled, just copy the entry across directly. | ||
tar.CopyEntryContents(outputStream); | ||
|
||
#if NETSTANDARD | ||
if (operatingSystemProvider.OperatingSystem() is PlatformOS.Linux or PlatformOS.Alpine) | ||
{ | ||
_ = new Mono.Unix.UnixFileInfo(destinationFile) | ||
{ | ||
FileAccessPermissions = (Mono.Unix.FileAccessPermissions)entry.TarHeader.Mode // set the same permissions as inside the archive | ||
}; | ||
} | ||
#endif | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters