mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
84 lines
2.3 KiB
Zig
84 lines
2.3 KiB
Zig
// https://github.com/MasterQ32/ftz/blob/3183b582211f8e38c1c3363c56753026ca45c11f/src/main.zig#L431-L509
|
|
// Thanks, Felix! We should get this into std perhaps.
|
|
|
|
const std = @import("std");
|
|
|
|
/// Resolves a unix-like path and removes all "." and ".." from it. Will not escape the root and can be used to sanitize inputs.
|
|
pub fn resolvePath(buffer: []u8, src_path: []const u8) ?[]u8 {
|
|
var end: usize = 0;
|
|
buffer[0] = '.';
|
|
|
|
var iter = std.mem.tokenize(src_path, "/");
|
|
while (iter.next()) |segment| {
|
|
if (end >= buffer.len) break;
|
|
|
|
if (std.mem.eql(u8, segment, ".")) {
|
|
continue;
|
|
} else if (std.mem.eql(u8, segment, "..")) {
|
|
while (true) {
|
|
if (end == 0)
|
|
break;
|
|
if (buffer[end] == '/') {
|
|
break;
|
|
}
|
|
end -= 1;
|
|
}
|
|
} else {
|
|
if (end + segment.len + 1 > buffer.len)
|
|
return error.BufferTooSmall;
|
|
|
|
const start = end;
|
|
buffer[end] = '/';
|
|
end += segment.len + 1;
|
|
std.mem.copy(u8, buffer[start + 1 .. end], segment);
|
|
}
|
|
}
|
|
|
|
const result = if (end == 0)
|
|
buffer[0 .. end + 1]
|
|
else
|
|
buffer[0..end];
|
|
|
|
if (std.mem.eql(u8, result, src_path)) {
|
|
return null;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
fn testResolve(expected: []const u8, input: []const u8) !void {
|
|
var buffer: [1024]u8 = undefined;
|
|
|
|
const actual = try resolvePath(&buffer, input);
|
|
std.testing.expectEqualStrings(expected, actual);
|
|
}
|
|
|
|
test "resolvePath" {
|
|
try testResolve("/", "");
|
|
try testResolve("/", "/");
|
|
try testResolve("/", "////////////");
|
|
|
|
try testResolve("/a", "a");
|
|
try testResolve("/a", "/a");
|
|
try testResolve("/a", "////////////a");
|
|
try testResolve("/a", "////////////a///");
|
|
|
|
try testResolve("/a/b/c/d", "/a/b/c/d");
|
|
|
|
try testResolve("/a/b/d", "/a/b/c/../d");
|
|
|
|
try testResolve("/", "..");
|
|
try testResolve("/", "/..");
|
|
try testResolve("/", "/../../../..");
|
|
try testResolve("/a/b/c", "a/b/c/");
|
|
|
|
try testResolve("/new/date.txt", "/new/../../new/date.txt");
|
|
}
|
|
|
|
test "resolvePath overflow" {
|
|
var buf: [1]u8 = undefined;
|
|
|
|
std.testing.expectEqualStrings("/", try resolvePath(&buf, "/"));
|
|
std.testing.expectError(error.BufferTooSmall, resolvePath(&buf, "a")); // will resolve to "/a"
|
|
}
|