From af68db50eb6ab776866b74ac58bad09542774560 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 06:25:23 +0000 Subject: [PATCH 1/5] Bump actions/upload-artifact from 4 to 7 --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dotnet.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 710618b..ae5b676 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -27,13 +27,13 @@ jobs: - name: Test on Ubuntu (no build) run: dotnet test --no-build --verbosity normal --configuration Release - name: Upload build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: release-artifacts path: SanitizeFilename/bin/Release/ retention-days: 1 - name: Upload test artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: test-artifacts path: SanitizeFilenameTests/bin/Release/ From 663882393bd1171968046f211cd3922fd14340af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 06:25:33 +0000 Subject: [PATCH 2/5] Bump actions/download-artifact from 4 to 8 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dotnet.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 710618b..dd179de 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -58,7 +58,7 @@ jobs: - name: Restore dependencies run: dotnet restore - name: Download build artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: test-artifacts path: SanitizeFilenameTests/bin/Release/ @@ -70,7 +70,7 @@ jobs: runs-on: ubuntu-latest needs: [build, test] steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: release-artifacts path: SanitizeFilename/bin/Release/ @@ -93,7 +93,7 @@ jobs: runs-on: ubuntu-latest needs: [build, test] steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: release-artifacts path: SanitizeFilename/bin/Release/ From 091f9a0c5236c9a768cb59f30a22bc51806ccff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 06:25:09 +0000 Subject: [PATCH 3/5] Bump actions/setup-dotnet from 5 to 6 Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5 to 6. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dotnet.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7ae1e01..e430717 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: Setup .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: dotnet-version: | 10.0.x @@ -51,7 +51,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: Setup .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: dotnet-version: | 10.0.x From 57a50f6e02b069cb572edc225e5a8337eb7c0d70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 06:24:31 +0000 Subject: [PATCH 4/5] Bump actions/stale from 10 to 11 Bumps [actions/stale](https://github.com/actions/stale) from 10 to 11. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v10...v11) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '11' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6736c09..9ca7111 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v10 + - uses: actions/stale@v11 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'Stale issue message' From 04b9a3c640a8d03346a2e97e33ecd359be9a4570 Mon Sep 17 00:00:00 2001 From: Stefan Seeland <168659+stesee@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:54:31 +0200 Subject: [PATCH 5/5] Fix reserved filename sanitization only matching whole base name Reserved Windows device names (CON, AUX, NUL, COM1, ...) were being replaced wherever they appeared as a substring anywhere in a filename, instead of only when they formed the entire base name. This mangled ordinary words like ICON, FALCON, CONSOLE and AUXILIARY, and dropped extra characters adjacent to the reserved word instead of leaving them untouched. Also fixes the reserved-name-with-extension prefix handling, which previously discarded everything after the reserved name (e.g. CON.tar.gz became _CON. instead of _.tar.gz). Fixes #46 --- SanitizeFilename/SanitizeFilename.cs | 5 ++-- .../FilenameTests/SanitizeFilenamesTests.cs | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/SanitizeFilename/SanitizeFilename.cs b/SanitizeFilename/SanitizeFilename.cs index 07a8d26..56dd20f 100644 --- a/SanitizeFilename/SanitizeFilename.cs +++ b/SanitizeFilename/SanitizeFilename.cs @@ -245,7 +245,8 @@ private static string InternalSanitizeReservedFileNames(string filename, string { foreach (var reservedFileName in ReservedWindowsFileNames) { - filename = filename.Replace(reservedFileName, replacement, true, CultureInfo.InvariantCulture); + if (string.Equals(filename, reservedFileName, StringComparison.OrdinalIgnoreCase)) + return replacement; } return filename; @@ -255,7 +256,7 @@ private static string InternalSanitizeReservedFileNamePrefix(string filename, st { foreach (var reservedFileNamePrefix in ReservedWindowsFileNamesWithExtension) if (filename.StartsWith(reservedFileNamePrefix, true, CultureInfo.InvariantCulture)) - filename = string.Concat(replacement, filename.AsSpan(0, reservedFileNamePrefix.Length)); + filename = string.Concat(replacement, filename.AsSpan(reservedFileNamePrefix.Length - 1)); return filename; } diff --git a/SanitizeFilenameTests/FilenameTests/SanitizeFilenamesTests.cs b/SanitizeFilenameTests/FilenameTests/SanitizeFilenamesTests.cs index f9d8ed3..d6fa609 100644 --- a/SanitizeFilenameTests/FilenameTests/SanitizeFilenamesTests.cs +++ b/SanitizeFilenameTests/FilenameTests/SanitizeFilenamesTests.cs @@ -22,10 +22,15 @@ public void TearDown() private static readonly string[] InvalidWindowsFileNames = ["invalidfilename", "invalid\"filename", "invalid/filename", "invalid\\filename", "invalid|filename", "invalid?filename", "invalid*filename",]; private static readonly string[] ReservedWindowsFileNames = ["con", "CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "COM\u00B9", "COM\u00B2", "COM\u00B3", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", "LPT\u00B9", "LPT\u00B2", "LPT\u00B3", "endingWithSpace ", "endingWithPeriod."]; - private static readonly string[] ReservedWindowsFileNamePrefixUsed = ["con.txt", "CON.txt", "PRN.txt", "AUX.txt", "NUL.txt", "COM0.txt", "COM1.txt", "COM2.txt", "COM3.txt", "COM4.txt", "COM5.txt", "COM6.txt", "COM7.txt", "COM8.txt", "COM9.txt", "COM\u00B9.txt", "COM\u00B2.txt", "COM\u00B3.txt", "LPT0.txt", "LPT1.txt", "LPT2.txt", "LPT3.txt", "LPT4.txt", "LPT5.txt", "LPT6.txt", "LPT7.txt", "LPT8.txt", "LPT9.txt", "LPT\u00B9.txt", "LPT\u00B2.txt", "LPT\u00B3.txt"]; + private static readonly string[] ReservedWindowsFileNamePrefixUsed = ["con.txt", "CON.txt", "PRN.txt", "AUX.txt", "NUL.txt", "COM0.txt", "COM1.txt", "COM2.txt", "COM3.txt", "COM4.txt", "COM5.txt", "COM6.txt", "COM7.txt", "COM8.txt", "COM9.txt", "COM\u00B9.txt", "COM\u00B2.txt", "COM\u00B3.txt", "LPT0.txt", "LPT1.txt", "LPT2.txt", "LPT3.txt", "LPT4.txt", "LPT5.txt", "LPT6.txt", "LPT7.txt", "LPT8.txt", "LPT9.txt", "LPT\u00B9.txt", "LPT\u00B2.txt", "LPT\u00B3.txt", "NUL.tar.gz", "nul.tar.gz", "CON.tar.gz"]; private static readonly string[] ValidFileNames = ["validFileName"]; + // Regression coverage for https://github.com/Codeuctivity/SanitizeFilename/issues/46 : + // reserved device names must only be sanitized when they are the whole base file name, + // not merely a substring anywhere within an otherwise legal name. + private static readonly string[] ValidFileNamesContainingReservedNamesAsSubstring = ["ICON", "ICON.txt", "FALCON", "RECON.doc", "beacon", "silicon Valley", "CONSOLE.txt", "CONSTRUCTION", "NULLABLE.cs", "AUXILIARY.doc", "COMPANY.docx", "DISCOMFORT", "ENCOMPASS", "LPTOP.txt", "COM10", "LPT10"]; + [Test] [TestCaseSource(nameof(ValidFileNames))] public void ShouldNotTouchASaneFilename(string validFilename) @@ -35,6 +40,24 @@ public void ShouldNotTouchASaneFilename(string validFilename) Assert.That(sanitizedFilename, Is.EqualTo(validFilename)); } + [Test] + [TestCaseSource(nameof(ValidFileNamesContainingReservedNamesAsSubstring))] + public void ShouldNotTouchFileNamesContainingReservedNamesAsSubstring(string validFilename) + { + var sanitizedFilename = validFilename.SanitizeFilename(); + + Assert.That(sanitizedFilename, Is.EqualTo(validFilename)); + } + + [Test] + [TestCase("CONfile*Name", "CONfile_Name")] + public void ShouldOnlyReplaceTheInvalidCharNotTheReservedNameSubstring(string invalidFilename, string expectedOutcome) + { + var sanitizedFilename = invalidFilename.SanitizeFilename(); + + Assert.That(sanitizedFilename, Is.EqualTo(expectedOutcome)); + } + [Test] [TestCase("CO*", 'N', "N")] public void ShouldSanitizeEdgeCase(string invalidFilename, char replacement, string expectedOutcome)