- 43 tests across SlugGuard, ReplContext navigation, NotesCache.TimeAgo - SlugGuard moved to NozCli.Core.Security (shared between REPL and tests) - Bug fix: FoldersInView() now returns only actual sub-folders, not note segments (previously cd tab-completion suggested note names as navigable folders) - .forgejo/workflows/ci.yml: build + test on every push/PR
68 lines
2.8 KiB
C#
68 lines
2.8 KiB
C#
using Xunit;
|
|
using NozCli.Core.Security;
|
|
|
|
namespace NozCli.Tests;
|
|
|
|
public class SlugGuardTests
|
|
{
|
|
// ── IsValid ──────────────────────────────────────────────────────────────
|
|
|
|
[Theory]
|
|
[InlineData("simple-note", true)]
|
|
[InlineData("psychologie/test", true)]
|
|
[InlineData("biosysteme/sub/deep", true)]
|
|
[InlineData("note123", true)]
|
|
[InlineData("a", true)]
|
|
public void IsValid_AcceptsValidSlugs(string slug, bool expected)
|
|
=> Assert.Equal(expected, SlugGuard.IsValid(slug));
|
|
|
|
[Theory]
|
|
[InlineData("../../etc/passwd", false)] // path traversal
|
|
[InlineData("../escape", false)] // path traversal
|
|
[InlineData("note/../other", false)] // embedded traversal
|
|
[InlineData("UPPERCASE", false)] // uppercase not allowed
|
|
[InlineData("has space", false)] // spaces not allowed
|
|
[InlineData("", false)] // empty
|
|
[InlineData(" ", false)] // whitespace
|
|
[InlineData("note//double", false)] // double slash
|
|
[InlineData("/leading-slash", false)] // leading slash
|
|
[InlineData("trailing/", false)] // trailing slash
|
|
[InlineData("Sonderzeichen!", false)] // special chars
|
|
public void IsValid_RejectsInvalidSlugs(string slug, bool expected)
|
|
=> Assert.Equal(expected, SlugGuard.IsValid(slug));
|
|
|
|
// ── ToSafePath ───────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void ToSafePath_ValidSlug_ReturnsPathInsideBase()
|
|
{
|
|
var baseDir = Path.Combine(Path.GetTempPath(), "noz-test");
|
|
var result = SlugGuard.ToSafePath(baseDir, "psychologie/test");
|
|
|
|
Assert.NotNull(result);
|
|
Assert.StartsWith(baseDir, result);
|
|
Assert.EndsWith(".md", result);
|
|
Assert.Contains("psychologie", result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("../../etc/passwd")]
|
|
[InlineData("../escape")]
|
|
[InlineData("sub/../../outside")]
|
|
public void ToSafePath_TraversalSlug_ReturnsNull(string slug)
|
|
{
|
|
var baseDir = Path.Combine(Path.GetTempPath(), "noz-test");
|
|
Assert.Null(SlugGuard.ToSafePath(baseDir, slug));
|
|
}
|
|
|
|
[Fact]
|
|
public void ToSafePath_NestedSlug_BuildsCorrectPath()
|
|
{
|
|
var baseDir = "/tmp/vault";
|
|
var result = SlugGuard.ToSafePath(baseDir, "a/b/c");
|
|
|
|
Assert.NotNull(result);
|
|
var expected = Path.Combine("/tmp/vault", "a", "b", "c") + ".md";
|
|
Assert.Equal(expected, result);
|
|
}
|
|
}
|