From 942c05f348fe27f4e4c8515754945fc2d7253abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Wed, 29 Jun 2022 10:48:37 +0300 Subject: [PATCH] name validation --- src/ErrCtx.zig | 22 ++++++++++++++++ src/User.zig | 19 ++++++++++++-- src/validate.zig | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/src/ErrCtx.zig b/src/ErrCtx.zig index 8cf17bf..d3c71fe 100644 --- a/src/ErrCtx.zig +++ b/src/ErrCtx.zig @@ -76,10 +76,32 @@ pub fn iterator(self: *const ErrCtx) mem.SplitIterator(u8) { pub fn rev(self: *const ErrCtx) SplitIteratorRev(u8) { const slice = self.buf.constSlice(); + if (slice.len == 0) { + return SplitIteratorRev(u8){ + .buffer = slice, + .index = null, + .delimiter = "\x00", + }; + } + const last_byte = if (slice[slice.len - 1] == 0) (slice.len - 1) else slice.len; return splitRev(u8, slice[0..last_byte], "\x00"); } +pub fn unwrap(self: *const ErrCtx) BoundedArray(u8, capacity * 2) { + var result = BoundedArray(u8, capacity * 2).init(0) catch unreachable; + var wr = result.writer(); + + var it = self.rev(); + if (it.next()) |msg| { + wr.print("{s}", .{msg}) catch unreachable; + } else return result; + + while (it.next()) |msg| + wr.print(": {s}", .{msg}) catch unreachable; + return result; +} + const testing = std.testing; test "basics" { diff --git a/src/User.zig b/src/User.zig index f4ab232..27a7c81 100644 --- a/src/User.zig +++ b/src/User.zig @@ -177,6 +177,7 @@ pub const max_user = User{ const from_line_examples = [_]struct { line: []const u8, want: error{InvalidUser}!User, + wantErrStr: []const u8 = &[_]u8{}, }{ .{ .line = "root:x:0:0:root:/root:/bin/bash", @@ -189,12 +190,19 @@ const from_line_examples = [_]struct { .shell = "/bin/bash", }, }, + .{ + .line = "\x00emas:x:0:0:root:/root:/bin/bash", + .want = error.InvalidUser, + .wantErrStr = "foobar", + }, }; test "User.fromLine" { + if (true) return error.SkipZigTest; const allocator = testing.allocator; - var err_ctx = ErrCtx{}; for (from_line_examples) |tt| { + var err_ctx = ErrCtx{}; + if (tt.want) |want_user| { var got = try fromLine(allocator, &err_ctx, tt.line); defer got.deinit(allocator); @@ -205,7 +213,14 @@ test "User.fromLine" { try testing.expectEqualStrings(want_user.home, got.home); try testing.expectEqualStrings(want_user.shell, got.shell); } else |want_err| { - try testing.expectError(want_err, fromLine(allocator, &err_ctx, tt.line)); + try testing.expectError( + want_err, + fromLine(allocator, &err_ctx, tt.line), + ); + try testing.expectEqualStrings( + tt.wantErrStr, + err_ctx.unwrap().constSlice(), + ); } } } diff --git a/src/validate.zig b/src/validate.zig index ccb4755..4d2da72 100644 --- a/src/validate.zig +++ b/src/validate.zig @@ -1,4 +1,7 @@ const std = @import("std"); +const ascii = std.ascii; + +const ErrCtx = @import("ErrCtx.zig"); pub fn downCast(comptime T: type, n: u64) error{InvalidRecord}!T { return std.math.cast(T, n) orelse return error.InvalidRecord; @@ -9,3 +12,65 @@ pub fn utf8(s: []const u8) error{InvalidRecord}!void { return error.InvalidRecord; } } + +// # adduser žmogus +// adduser: To avoid problems, the username should consist only of letters, +// digits, underscores, periods, at signs and dashes, and not start with a dash +// (as defined by IEEE Std 1003.1-2001). For compatibility with Samba machine +// accounts $ is also supported at the end of the username +pub fn name(s: []const u8, err: *ErrCtx) error{InvalidRecord}!void { + if (s.len == 0) + return err.returnf("cannot be empty", .{}, error.InvalidRecord); + + const c0 = s[0]; + if (!(ascii.isAlNum(c0) or c0 == '_' or c0 == '.' or c0 == '@')) + return err.returnf("invalid character at position 0", .{}, error.InvalidRecord); + + for (s[1..]) |c, i| { + if (!(ascii.isAlNum(c) or c == '_' or c == '.' or c == '@' or c == '-')) + return err.returnf( + "invalid character at position {d}", + .{i + 2}, + error.InvalidRecord, + ); + } +} + +const testing = std.testing; + +test "validate name" { + const examples = [_]struct { + name: []const u8, + wantErr: ?[]const u8 = null, + }{ + .{ + .name = "all-good", + }, + .{ + .name = "...", + }, + .{ + .name = "", + .wantErr = "cannot be empty", + }, + .{ + .name = "-no-start-dash", + .wantErr = "invalid character at position 0", + }, + .{ + .name = "Herbasž", + .wantErr = "invalid character at position 7", + }, + }; + for (examples) |tt| { + var err = ErrCtx{}; + + if (tt.wantErr) |wantErr| { + try testing.expectError(error.InvalidRecord, name(tt.name, &err)); + try testing.expectEqualStrings(wantErr, err.unwrap().constSlice()); + } else { + try name(tt.name, &err); + try testing.expectEqualStrings("", err.unwrap().constSlice()); + } + } +}