From 433ed56c9f80ad3560d376c7b036ea5e44e20482 Mon Sep 17 00:00:00 2001 From: Sun Serega Date: Sat, 11 Mar 2023 13:45:24 +0200 Subject: [PATCH] Better valid path checks --- CodeCompletion/DomSyntaxTreeVisitor.cs | 2 + Compiler/Compiler.cs | 109 +++++++++++++------------ pabcnetc_clear/ConsoleCompiler.cs | 2 + 3 files changed, 62 insertions(+), 51 deletions(-) diff --git a/CodeCompletion/DomSyntaxTreeVisitor.cs b/CodeCompletion/DomSyntaxTreeVisitor.cs index 64d2d506c..b57cd5605 100644 --- a/CodeCompletion/DomSyntaxTreeVisitor.cs +++ b/CodeCompletion/DomSyntaxTreeVisitor.cs @@ -2585,6 +2585,7 @@ namespace CodeCompletion else if (dir.Name.text.ToLower() == "includenamespace") { string directive = dir.Directive.text.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + Compiler.TryThrowInvalidPath(directive, dir.source_context); if (directive == "*.pas" || directive.EndsWith(Path.DirectorySeparatorChar + "*.pas")) { @@ -2843,6 +2844,7 @@ namespace CodeCompletion else if (dir.Name.text.ToLower() == "includenamespace") { string directive = dir.Directive.text.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + Compiler.TryThrowInvalidPath(directive, dir.source_context); if (directive == "*.pas" || directive.EndsWith(Path.DirectorySeparatorChar + "*.pas")) { diff --git a/Compiler/Compiler.cs b/Compiler/Compiler.cs index 2407e2efe..28771ce59 100644 --- a/Compiler/Compiler.cs +++ b/Compiler/Compiler.cs @@ -1820,17 +1820,14 @@ namespace PascalABCCompiler CompilerOptions.OutputDirectory = project.output_directory; } - - private static string CombinePathWithCheck(string path1, string path2, location loc) - { - try - { - return Path.Combine(path1, path2); - } - catch (Exception ex) - { - throw new InvalidPathError(loc); - } + + public static bool CheckPathValid(string path) { + return !Path.GetInvalidPathChars().Any(path.Contains); + } + + public static void TryThrowInvalidPath(string path, SyntaxTree.SourceContext loc) { + if (CheckPathValid(path)) return; + throw new InvalidPathError(loc); } public string Compile() @@ -2028,11 +2025,13 @@ namespace PascalABCCompiler { ErrorsList.Add(new MainResourceNotAllowed(cds[0].location)); } - cdo.MainResourceFileName = CombinePathWithCheck(Path.GetDirectoryName(cds[0].source_file), cds[0].directive, cds[0].location); - if (!File.Exists(cdo.MainResourceFileName)) - { + TryThrowInvalidPath(cds[0].directive, cds[0].location); + // Тут не обязательно нормализовывать путь + // И если он слишком длинный - File.Exists вернёт false + if (File.Exists(cdo.MainResourceFileName)) + cdo.MainResourceFileName = Path.Combine(Path.GetDirectoryName(cds[0].source_file), cds[0].directive); + else ErrorsList.Add(new ResourceFileNotFound(cds[0].directive, cds[0].location)); - } } List ResourceFiles = null; @@ -2040,10 +2039,11 @@ namespace PascalABCCompiler { ResourceFiles = new List(); List ResourceDirectives = compilerDirectives[TreeConverter.compiler_string_consts.compiler_directive_resource]; - foreach (TreeRealization.compiler_directive cd in ResourceDirectives) - { - var resource_fname = CombinePathWithCheck(Path.GetDirectoryName(cd.source_file), cd.directive, cd.location); - + foreach (TreeRealization.compiler_directive cd in ResourceDirectives) { + TryThrowInvalidPath(cd.directive, cd.location); + var resource_fname = Path.Combine(Path.GetDirectoryName(cd.source_file), cd.directive); + + // Так же как с main_resource if (File.Exists(resource_fname)) ResourceFiles.Add(resource_fname); else @@ -2578,21 +2578,21 @@ namespace PascalABCCompiler return null; } - public static string CombinePath(string dir, string path) - { - if (Path.IsPathRooted(path)) return path; + public static string CombinePathsRelatively(string path1, string path2) { + if (Path.IsPathRooted(path2)) return path2; int i = 0; - for (; dir!="" && i < path.Length && path[i] == '.' && path[i + 1] == '.'; ) - { - if (Path.GetFileName(dir) == "..") break; - dir = Path.GetDirectoryName(dir); - if (dir == null) return null; // Path.GetDirectoryName("C:\") возвращает null - i += 2; - if (path[i] == Path.DirectorySeparatorChar || path[i] == Path.AltDirectorySeparatorChar) i += 1; + foreach (var s in path2.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar })) { + if (s==".") continue; + if (s==".." && !string.IsNullOrWhiteSpace(path1) && !path1.EndsWith("..")) { + path1 = Path.GetDirectoryName(path1); + if (path1 == null) return null; // Path.GetDirectoryName("C:\") возвращает null + } + else + path1 = Path.Combine(path1, s); } - return Path.Combine(dir, path.Substring(i)); + return path1; } public static string GetUnitPath(CompilationUnit u1, CompilationUnit u2) @@ -2616,23 +2616,18 @@ namespace PascalABCCompiler { var next = new Dictionary(); - foreach (var u in last.Keys) - { - foreach (var used_u in u.DirectInterfaceCompilationUnits.Values) - { - if (!done.Add(used_u)) continue; - var path = CombinePath(Path.GetDirectoryName(last[u]), u.InterfaceUsedUnits.unit_uses_paths[used_u.SemanticTree]); - if (used_u == u2) return path; - next.Add(used_u, path); + foreach (var u in last.Keys) { + var intr_units = u.DirectInterfaceCompilationUnits.Select(kvp => new { u = kvp.Value, path = u.InterfaceUsedUnits.unit_uses_paths[kvp.Key] }); + var impl_units = u.DirectImplementationCompilationUnits.Select(kvp => new { u = kvp.Value, path = u.ImplementationUsedUnits.unit_uses_paths[kvp.Key] }); + + foreach (var used_u in intr_units.Concat(impl_units)) { + if (!done.Add(used_u.u)) continue; + // Важно "..\a\b" + "..\c\d" превращать в "..\a\c\d", а не полный путь + var path = CombinePathsRelatively(Path.GetDirectoryName(last[u]), used_u.path); + if (used_u.u == u2) return path; + next.Add(used_u.u, path); } - foreach (var used_u in u.DirectImplementationCompilationUnits.Values) - { - if (!done.Add(used_u)) continue; - var path = CombinePath(Path.GetDirectoryName(last[u]), u.ImplementationUsedUnits.unit_uses_paths[used_u.SemanticTree]); - if (used_u == u2) return path; - next.Add(used_u, path); - } } last = next; @@ -2643,12 +2638,22 @@ namespace PascalABCCompiler private string FindFileInDirs(string FileName, params string[] Dirs) { + if (Path.IsPathRooted(FileName)) + return File.Exists(FileName) ? FileName : null; + foreach (string Dir in Dirs) - { - var res = CombinePath(Dir, FileName); - if (File.Exists(res)) - return res; - } + try { + var res = Path.Combine(Dir, FileName); + if (File.Exists(res)) + // Path.GetFullPath чтобы нормализовать + // File.Exists не может кинуть исключение или дать true + // если путь слишком длинный или содержит нерпавильные знаки + return Path.GetFullPath(res); + } + catch (PathTooLongException) { + continue; + } + return null; } @@ -2715,7 +2720,9 @@ namespace PascalABCCompiler if (SyntaxUsesUnit is SyntaxTree.uses_unit_in uui) { - + + TryThrowInvalidPath(uui.in_file.Value, uui.in_file.source_context); + if (UnitName.ToLower() != Path.GetFileNameWithoutExtension(uui.in_file.Value).ToLower()) throw new UsesInWrongName(CurrentCompilationUnit.SyntaxTree.file_name, UnitName, Path.GetFileNameWithoutExtension(uui.in_file.Value), uui.in_file.source_context); diff --git a/pabcnetc_clear/ConsoleCompiler.cs b/pabcnetc_clear/ConsoleCompiler.cs index 2d257230f..d6473451e 100644 --- a/pabcnetc_clear/ConsoleCompiler.cs +++ b/pabcnetc_clear/ConsoleCompiler.cs @@ -81,6 +81,8 @@ namespace PascalABCCompiler return true; case "output": + if (!Compiler.CheckPathValid(value)) + return false; co.OutputFileName = Path.GetFileName(value); if (Path.IsPathRooted(value)) {