test(core/browser/platform): 26 extreme RED/GREEN TDD cycles and full regression hardening (3,235 PASS)
This commit is contained in:
parent
bcd242691f
commit
8677853822
100 changed files with 4814 additions and 333 deletions
|
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Paca.Core.Platform;
|
||||
using Paca.Core.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace Paca.Tests.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Cycle 1: 극한 RED 시나리오 - 파일명 정제 및 윈도우 장치 예약어, 트로이목마 Bidi 스푸핑, 경로 누수 방어 검증
|
||||
/// </summary>
|
||||
public class FileNameUtilAndCleanupExtremeRedTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("CON_long_stream_title", 3, "_CON")]
|
||||
[InlineData("PRN_document_capture", 3, "_PRN")]
|
||||
[InlineData("AUX_recording_session", 3, "_AUX")]
|
||||
[InlineData("NUL_blackhole_stream", 3, "_NUL")]
|
||||
[InlineData("COM1_serial_diagnostic", 4, "_COM1")]
|
||||
[InlineData("LPT1_parallel_port", 4, "_LPT1")]
|
||||
[InlineData("con.something.video", 3, "_con")]
|
||||
public void Cycle1_Gate1_TruncatedToReservedName_MustPrependUnderscore(string input, int maxLen, string expectedPrefix)
|
||||
{
|
||||
// Act
|
||||
var sanitized = FileNameUtil.Sanitize(input, maxLen);
|
||||
|
||||
// Assert: 윈도우 장치 예약어(CON, PRN, AUX, NUL, COM1-9, LPT1-9)와 일치해서는 안 됨
|
||||
Assert.StartsWith(expectedPrefix, sanitized, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotMatch(@"^(?i)(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$", sanitized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Cycle1_Gate2_BidiTrojanSourceAndZeroWidth_MustBeSanitized()
|
||||
{
|
||||
// CVE 클래스: Trojan Source / Unicode Bidi Spoofing
|
||||
// \u202E (Right-to-Left Override): "video\u202E4pm.exe" -> 화면에는 "videoexe.mp4" 로 착시 유도
|
||||
var trojanBidi = "my_video\u202E4pm.exe";
|
||||
var sanitizedBidi = FileNameUtil.Sanitize(trojanBidi);
|
||||
|
||||
Assert.DoesNotContain('\u202E', sanitizedBidi);
|
||||
Assert.DoesNotContain('\u202A', sanitizedBidi);
|
||||
Assert.DoesNotContain('\u202B', sanitizedBidi);
|
||||
Assert.DoesNotContain('\u202C', sanitizedBidi);
|
||||
Assert.DoesNotContain('\u202D', sanitizedBidi);
|
||||
|
||||
// 제로 너비 공백(\u200B), BOM(\uFEFF) 등 인비저블 캐릭터 제거
|
||||
var zeroWidth = "video\u200B\u200C\u200D\uFEFFtitle";
|
||||
var sanitizedZero = FileNameUtil.Sanitize(zeroWidth);
|
||||
Assert.DoesNotContain('\u200B', sanitizedZero);
|
||||
Assert.DoesNotContain('\u200C', sanitizedZero);
|
||||
Assert.DoesNotContain('\u200D', sanitizedZero);
|
||||
Assert.DoesNotContain('\uFEFF', sanitizedZero);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("............", 5, "video")]
|
||||
[InlineData(" ", 8, "video")]
|
||||
[InlineData("valid_name", 0, "video")]
|
||||
[InlineData("valid_name", -5, "video")]
|
||||
[InlineData("...", 1, "video")]
|
||||
public void Cycle1_Gate3_DegenerateMaxLenAndDotSpaceCollapse_MustFallbackToVideo(string input, int maxLen, string expected)
|
||||
{
|
||||
// Act
|
||||
var result = FileNameUtil.Sanitize(input, maxLen);
|
||||
|
||||
// Assert: 빈 문자열이나 ArgumentOutOfRangeException 없이 안전한 기본값 반환
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Cycle1_Gate4_DownloadMetaPathResolver_TempAndParts_MustBeInPacaHiddenDirectory()
|
||||
{
|
||||
var tempRoot = Path.Combine(Path.GetTempPath(), "paca_test_" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempRoot);
|
||||
try
|
||||
{
|
||||
var outputBase = Path.Combine(tempRoot, "sample_video");
|
||||
|
||||
var partsDir = DownloadMetaPathResolver.GetPartsDirectory(outputBase);
|
||||
var tempPartFile = DownloadMetaPathResolver.GetTempPartFilePath(outputBase);
|
||||
|
||||
// .paca 격리 서브디렉터리 내부에 생성되어야 함
|
||||
Assert.Contains(".paca", partsDir);
|
||||
Assert.Contains(".paca", tempPartFile);
|
||||
Assert.EndsWith("sample_video.parts", partsDir);
|
||||
Assert.EndsWith("sample_video.part", tempPartFile);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (Directory.Exists(tempRoot)) Directory.Delete(tempRoot, true); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue