Tweak tests

De-duplicate user agent validation
This commit is contained in:
Karl Seguin
2026-04-14 17:11:54 +08:00
parent 920ae57f9a
commit 5226df9968
3 changed files with 69 additions and 108 deletions

View File

@@ -1098,20 +1098,14 @@ fn parseCommonArg(
return error.InvalidArgument;
};
for (str) |c| {
if (!std.ascii.isPrint(c)) {
log.fatal(.app, "not printable character", .{ .arg = opt });
return error.InvalidArgument;
}
}
if (std.ascii.indexOfIgnoreCase(str, "mozilla") != null) {
validateUserAgent(str) catch |err| {
log.fatal(.app, "invalid value", .{
.detail = "user-agent can't contain Mozilla",
.detail = "invalid user agent",
.arg = opt,
.err = err,
});
return error.InvalidArgument;
}
};
common.user_agent = try allocator.dupe(u8, str);
return true;
@@ -1194,98 +1188,60 @@ fn parseCommonArg(
return false;
}
test "HttpHeaders: default user agent" {
const allocator = std.testing.allocator;
var config = Config{
.mode = .{ .serve = .{} },
.exec_name = "test",
.http_headers = undefined,
};
config.http_headers = try HttpHeaders.init(allocator, &config);
defer config.http_headers.deinit(allocator);
pub fn validateUserAgent(ua: []const u8) !void {
for (ua) |c| {
if (!std.ascii.isPrint(c)) {
return error.NonPrintable;
}
}
try std.testing.expectEqualStrings("Lightpanda/1.0", config.http_headers.user_agent);
try std.testing.expectEqualStrings("User-Agent: Lightpanda/1.0", config.http_headers.user_agent_header);
try std.testing.expect(config.http_headers.proxy_bearer_header == null);
if (std.ascii.indexOfIgnoreCase(ua, "mozilla") != null) {
return error.Reserved;
}
}
test "HttpHeaders: custom user agent override" {
const allocator = std.testing.allocator;
const ua = try allocator.dupe(u8, "MyBot/2.0");
var config = Config{
.mode = .{ .serve = .{ .common = .{ .user_agent = ua } } },
.exec_name = "test",
.http_headers = undefined,
};
config.http_headers = try HttpHeaders.init(allocator, &config);
defer config.http_headers.deinit(allocator);
defer allocator.free(ua);
const testing = @import("testing.zig");
test "Config: HttpHeaders - default user agent" {
var config = try Config.init(testing.allocator, "", .{ .serve = .{} });
defer config.deinit(testing.allocator);
try std.testing.expectEqualStrings("MyBot/2.0", config.http_headers.user_agent);
try std.testing.expectEqualStrings("User-Agent: MyBot/2.0", config.http_headers.user_agent_header);
try testing.expectEqual("Lightpanda/1.0", config.http_headers.user_agent);
try testing.expectEqual("User-Agent: Lightpanda/1.0", config.http_headers.user_agent_header);
try testing.expect(config.http_headers.proxy_bearer_header == null);
}
test "HttpHeaders: user agent suffix" {
const allocator = std.testing.allocator;
const suffix = try allocator.dupe(u8, "CustomSuffix/3.0");
var config = Config{
.mode = .{ .serve = .{ .common = .{ .user_agent_suffix = suffix } } },
.exec_name = "test",
.http_headers = undefined,
};
config.http_headers = try HttpHeaders.init(allocator, &config);
defer config.http_headers.deinit(allocator);
defer allocator.free(suffix);
test "Config: HttpHeaders - custom user agent override" {
var config = try Config.init(testing.allocator, "", .{ .serve = .{ .common = .{ .user_agent = "MyBot/2.0" } } });
defer config.deinit(testing.allocator);
try std.testing.expectEqualStrings("Lightpanda/1.0 CustomSuffix/3.0", config.http_headers.user_agent);
try std.testing.expectEqualStrings("User-Agent: Lightpanda/1.0 CustomSuffix/3.0", config.http_headers.user_agent_header);
try testing.expectEqual("MyBot/2.0", config.http_headers.user_agent);
try testing.expectEqual("User-Agent: MyBot/2.0", config.http_headers.user_agent_header);
}
test "HttpHeaders: fetch mode default user agent" {
const allocator = std.testing.allocator;
const url = try allocator.dupeZ(u8, "https://example.com");
defer allocator.free(url);
var config = Config{
.mode = .{ .fetch = .{ .url = url } },
.exec_name = "test",
.http_headers = undefined,
};
config.http_headers = try HttpHeaders.init(allocator, &config);
defer config.http_headers.deinit(allocator);
test "Config: HttpHeaders - user agent suffix" {
var config = try Config.init(testing.allocator, "", .{ .serve = .{ .common = .{ .user_agent_suffix = "CustomSuffix/3.0" } } });
defer config.deinit(testing.allocator);
try std.testing.expectEqualStrings("Lightpanda/1.0", config.http_headers.user_agent);
try testing.expectEqual("Lightpanda/1.0 CustomSuffix/3.0", config.http_headers.user_agent);
try testing.expectEqual("User-Agent: Lightpanda/1.0 CustomSuffix/3.0", config.http_headers.user_agent_header);
}
test "HttpHeaders: fetch mode custom user agent" {
const allocator = std.testing.allocator;
const url = try allocator.dupeZ(u8, "https://example.com");
defer allocator.free(url);
const ua = try allocator.dupe(u8, "FetchBot/1.0");
var config = Config{
.mode = .{ .fetch = .{ .url = url, .common = .{ .user_agent = ua } } },
.exec_name = "test",
.http_headers = undefined,
};
config.http_headers = try HttpHeaders.init(allocator, &config);
defer config.http_headers.deinit(allocator);
defer allocator.free(ua);
try std.testing.expectEqualStrings("FetchBot/1.0", config.http_headers.user_agent);
try std.testing.expectEqualStrings("User-Agent: FetchBot/1.0", config.http_headers.user_agent_header);
test "Config: HttpHeaders - fetch mode default user agent" {
var config = try Config.init(testing.allocator, "", .{ .fetch = .{ .url = "https://example.com" } });
defer config.deinit(testing.allocator);
try testing.expectEqual("Lightpanda/1.0", config.http_headers.user_agent);
}
test "HttpHeaders: proxy bearer header" {
const allocator = std.testing.allocator;
const token: [:0]const u8 = try allocator.dupeZ(u8, "secret-token");
var config = Config{
.mode = .{ .serve = .{ .common = .{ .proxy_bearer_token = token } } },
.exec_name = "test",
.http_headers = undefined,
};
config.http_headers = try HttpHeaders.init(allocator, &config);
defer config.http_headers.deinit(allocator);
defer allocator.free(token);
try std.testing.expectEqualStrings("Lightpanda/1.0", config.http_headers.user_agent);
try std.testing.expectEqualStrings("Proxy-Authorization: Bearer secret-token", config.http_headers.proxy_bearer_header.?);
test "Config: HttpHeaders - fetch mode custom user agent" {
var config = try Config.init(testing.allocator, "", .{ .fetch = .{ .url = "https://example.com", .common = .{ .user_agent = "FetchBot/1.0" } } });
defer config.deinit(testing.allocator);
try testing.expectEqual("FetchBot/1.0", config.http_headers.user_agent);
try testing.expectEqual("User-Agent: FetchBot/1.0", config.http_headers.user_agent_header);
}
test "Config: HttpHeaders - proxy bearer header" {
var config = try Config.init(testing.allocator, "", .{ .serve = .{ .common = .{ .proxy_bearer_token = "secret-token" } } });
defer config.deinit(testing.allocator);
try testing.expectEqual("Lightpanda/1.0", config.http_headers.user_agent);
try testing.expectEqual("Proxy-Authorization: Bearer secret-token", config.http_headers.proxy_bearer_header.?);
}

View File

@@ -19,6 +19,7 @@
const std = @import("std");
const CDP = @import("../CDP.zig");
const log = @import("../../log.zig");
const Config = @import("../../Config.zig");
pub fn processMessage(cmd: *CDP.Command) !void {
const action = std.meta.stringToEnum(enum {
@@ -85,22 +86,13 @@ pub fn setUserAgentOverride(cmd: *CDP.Command) !void {
}
const ua = params.userAgent;
// Validate: all characters must be printable ASCII
for (ua) |c| {
if (!std.ascii.isPrint(c)) {
return cmd.sendError(-32602, "User agent contains non-printable characters", .{});
}
}
// Reject user agents containing "mozilla" (case-insensitive)
if (std.ascii.indexOfIgnoreCase(ua, "mozilla") != null) {
// go-rod client automatically set a Mozilla/ user agent.
// Since we don't want to stop this client to work, let's ignore the
// new user-agent and add a log instead.
log.warn(.not_implemented, "Emulation.setUserAgentOverride", .{ .param = "userAgent", .value = ua, .info = "User agent must not contain Mozilla" });
return cmd.sendResult(null, .{});
}
Config.validateUserAgent(ua) catch |err| switch (err) {
error.NonPrintable => return cmd.sendError(-32602, "User agent contains non-printable characters", .{}),
error.Reserved => {
log.warn(.not_implemented, "Emulation.setUserAgentOverride", .{ .param = "userAgent", .value = ua, .info = "User agent must not contain Mozilla" });
return cmd.sendResult(null, .{});
},
};
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const http_client = cmd.cdp.browser.http_client;
@@ -127,6 +119,9 @@ test "cdp.Emulation: setUserAgentOverride with valid user agent" {
}
test "cdp.Emulation: setUserAgentOverride ignores mozilla" {
const filter: testing.LogFilter = .init(&.{.not_implemented});
defer filter.deinit();
var ctx = try testing.context();
defer ctx.deinit();
_ = try ctx.loadBrowserContext(.{ .id = "BID-UA2" });
@@ -138,10 +133,13 @@ test "cdp.Emulation: setUserAgentOverride ignores mozilla" {
});
try ctx.expectSentResult(null, .{});
try testing.expect(ctx.cdp().browser_context.?.user_agent_changed == false);
try testing.expectEqual(false, ctx.cdp().browser_context.?.user_agent_changed);
}
test "cdp.Emulation: setUserAgentOverride ignores mozilla case insensitive" {
const filter: testing.LogFilter = .init(&.{.not_implemented});
defer filter.deinit();
var ctx = try testing.context();
defer ctx.deinit();
_ = try ctx.loadBrowserContext(.{ .id = "BID-UA3" });
@@ -153,10 +151,13 @@ test "cdp.Emulation: setUserAgentOverride ignores mozilla case insensitive" {
});
try ctx.expectSentResult(null, .{});
try testing.expect(ctx.cdp().browser_context.?.user_agent_changed == false);
try testing.expectEqual(false, ctx.cdp().browser_context.?.user_agent_changed);
}
test "cdp.Emulation: setUserAgentOverride rejects non-printable characters" {
const filter: testing.LogFilter = .init(&.{.not_implemented});
defer filter.deinit();
var ctx = try testing.context();
defer ctx.deinit();
_ = try ctx.loadBrowserContext(.{ .id = "BID-UA4" });
@@ -171,6 +172,9 @@ test "cdp.Emulation: setUserAgentOverride rejects non-printable characters" {
}
test "cdp.Emulation: setUserAgentOverride with optional params" {
const filter: testing.LogFilter = .init(&.{.not_implemented});
defer filter.deinit();
var ctx = try testing.context();
defer ctx.deinit();
_ = try ctx.loadBrowserContext(.{ .id = "BID-UA5" });

View File

@@ -32,6 +32,7 @@ pub const expectError = base.expectError;
pub const expectEqualSlices = base.expectEqualSlices;
pub const pageTest = base.pageTest;
pub const newString = base.newString;
pub const LogFilter = base.LogFilter;
const TestContext = struct {
read_at: usize = 0,