Add CLI flags for cookie URL, domain, path, httpOnly, secure, and expires (#266)
* Add CLI flags for cookie URL, domain, path, httpOnly, secure, and expires Extends the `cookies set` command to support setting cookies with additional parameters before loading a page, solving authentication workflows where cookies need to be set for different domains. **Key changes:** - Added CLI flags: `--url`, `--domain`, `--path`, `--httpOnly`, `--secure`, `--sameSite`, `--expires` - Added comprehensive test coverage for all new flags and combinations - Updated help documentation with usage examples - No daemon changes needed - it already supported these parameters **Example usage:** ```bash agent-browser cookies set session_id "abc123" --url https://app.example.com --httpOnly --secure ``` This allows setting cookies for a URL before opening the page, eliminating the need for workarounds in cross-domain authentication scenarios. Fixes #261 * Update lock * Fix compilation error
This commit is contained in:
Generated
+1
-1
@@ -4,7 +4,7 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "agent-browser"
|
||||
version = "0.6.0"
|
||||
version = "0.7.1"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"libc",
|
||||
|
||||
+191
-3
@@ -485,14 +485,106 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
||||
"set" => {
|
||||
let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||
context: "cookies set".to_string(),
|
||||
usage: "cookies set <name> <value>",
|
||||
usage: "cookies set <name> <value> [--url <url>] [--domain <domain>] [--path <path>] [--httpOnly] [--secure] [--sameSite <Strict|Lax|None>] [--expires <timestamp>]",
|
||||
})?;
|
||||
let value = rest.get(2).ok_or_else(|| ParseError::MissingArguments {
|
||||
context: "cookies set".to_string(),
|
||||
usage: "cookies set <name> <value>",
|
||||
usage: "cookies set <name> <value> [--url <url>] [--domain <domain>] [--path <path>] [--httpOnly] [--secure] [--sameSite <Strict|Lax|None>] [--expires <timestamp>]",
|
||||
})?;
|
||||
|
||||
let mut cookie = json!({ "name": name, "value": value });
|
||||
|
||||
// Parse optional flags
|
||||
let mut i = 3;
|
||||
while i < rest.len() {
|
||||
match rest[i] {
|
||||
"--url" => {
|
||||
if let Some(url) = rest.get(i + 1) {
|
||||
cookie["url"] = json!(url);
|
||||
i += 2;
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "cookies set --url".to_string(),
|
||||
usage: "--url <url>",
|
||||
});
|
||||
}
|
||||
}
|
||||
"--domain" => {
|
||||
if let Some(domain) = rest.get(i + 1) {
|
||||
cookie["domain"] = json!(domain);
|
||||
i += 2;
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "cookies set --domain".to_string(),
|
||||
usage: "--domain <domain>",
|
||||
});
|
||||
}
|
||||
}
|
||||
"--path" => {
|
||||
if let Some(path) = rest.get(i + 1) {
|
||||
cookie["path"] = json!(path);
|
||||
i += 2;
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "cookies set --path".to_string(),
|
||||
usage: "--path <path>",
|
||||
});
|
||||
}
|
||||
}
|
||||
"--httpOnly" => {
|
||||
cookie["httpOnly"] = json!(true);
|
||||
i += 1;
|
||||
}
|
||||
"--secure" => {
|
||||
cookie["secure"] = json!(true);
|
||||
i += 1;
|
||||
}
|
||||
"--sameSite" => {
|
||||
if let Some(same_site) = rest.get(i + 1) {
|
||||
// Validate sameSite value
|
||||
if *same_site == "Strict" || *same_site == "Lax" || *same_site == "None" {
|
||||
cookie["sameSite"] = json!(same_site);
|
||||
i += 2;
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "cookies set --sameSite".to_string(),
|
||||
usage: "--sameSite <Strict|Lax|None>",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "cookies set --sameSite".to_string(),
|
||||
usage: "--sameSite <Strict|Lax|None>",
|
||||
});
|
||||
}
|
||||
}
|
||||
"--expires" => {
|
||||
if let Some(expires_str) = rest.get(i + 1) {
|
||||
if let Ok(expires) = expires_str.parse::<i64>() {
|
||||
cookie["expires"] = json!(expires);
|
||||
i += 2;
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "cookies set --expires".to_string(),
|
||||
usage: "--expires <timestamp>",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "cookies set --expires".to_string(),
|
||||
usage: "--expires <timestamp>",
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Unknown flag, skip it (or could error)
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(
|
||||
json!({ "id": id, "action": "cookies_set", "cookies": [{ "name": name, "value": value }] }),
|
||||
json!({ "id": id, "action": "cookies_set", "cookies": [cookie] }),
|
||||
)
|
||||
}
|
||||
"clear" => Ok(json!({ "id": id, "action": "cookies_clear" })),
|
||||
@@ -1254,6 +1346,102 @@ mod tests {
|
||||
assert_eq!(cmd["action"], "cookies_clear");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_url() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --url https://example.com"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["url"], "https://example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_domain() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --domain example.com"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["domain"], "example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_path() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --path /api"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["path"], "/api");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_httponly() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --httpOnly"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["httpOnly"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_secure() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --secure"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["secure"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_samesite() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --sameSite Strict"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["sameSite"], "Strict");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_expires() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --expires 1234567890"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["expires"], 1234567890);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_multiple_flags() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --url https://example.com --httpOnly --secure --sameSite Lax"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["url"], "https://example.com");
|
||||
assert_eq!(cmd["cookies"][0]["httpOnly"], true);
|
||||
assert_eq!(cmd["cookies"][0]["secure"], true);
|
||||
assert_eq!(cmd["cookies"][0]["sameSite"], "Lax");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_with_all_flags() {
|
||||
let cmd = parse_command(&args("cookies set mycookie myvalue --url https://example.com --domain example.com --path /api --httpOnly --secure --sameSite None --expires 9999999999"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "cookies_set");
|
||||
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
|
||||
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
|
||||
assert_eq!(cmd["cookies"][0]["url"], "https://example.com");
|
||||
assert_eq!(cmd["cookies"][0]["domain"], "example.com");
|
||||
assert_eq!(cmd["cookies"][0]["path"], "/api");
|
||||
assert_eq!(cmd["cookies"][0]["httpOnly"], true);
|
||||
assert_eq!(cmd["cookies"][0]["secure"], true);
|
||||
assert_eq!(cmd["cookies"][0]["sameSite"], "None");
|
||||
assert_eq!(cmd["cookies"][0]["expires"], 9999999999i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookies_set_invalid_samesite() {
|
||||
let result = parse_command(&args("cookies set mycookie myvalue --sameSite Invalid"), &default_flags());
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// === Storage Tests ===
|
||||
|
||||
#[test]
|
||||
|
||||
+34
-6
@@ -1082,18 +1082,46 @@ Usage: agent-browser cookies [operation] [args]
|
||||
Manage browser cookies for the current context.
|
||||
|
||||
Operations:
|
||||
get Get all cookies (default)
|
||||
set <name> <value> Set a cookie
|
||||
clear Clear all cookies
|
||||
get Get all cookies (default)
|
||||
set <name> <value> [options] Set a cookie with optional properties
|
||||
clear Clear all cookies
|
||||
|
||||
Cookie Set Options:
|
||||
--url <url> URL for the cookie (allows setting before page load)
|
||||
--domain <domain> Cookie domain (e.g., ".example.com")
|
||||
--path <path> Cookie path (e.g., "/api")
|
||||
--httpOnly Set HttpOnly flag (prevents JavaScript access)
|
||||
--secure Set Secure flag (HTTPS only)
|
||||
--sameSite <Strict|Lax|None> SameSite policy
|
||||
--expires <timestamp> Expiration time (Unix timestamp in seconds)
|
||||
|
||||
Note: If --url, --domain, and --path are all omitted, the cookie will be set
|
||||
for the current page URL.
|
||||
|
||||
Global Options:
|
||||
--json Output as JSON
|
||||
--session <name> Use specific session
|
||||
|
||||
Examples:
|
||||
agent-browser cookies
|
||||
agent-browser cookies get
|
||||
# Simple cookie for current page
|
||||
agent-browser cookies set session_id "abc123"
|
||||
|
||||
# Set cookie for a URL before loading it (useful for authentication)
|
||||
agent-browser cookies set session_id "abc123" --url https://app.example.com
|
||||
|
||||
# Set secure, httpOnly cookie with domain and path
|
||||
agent-browser cookies set auth_token "xyz789" --domain example.com --path /api --httpOnly --secure
|
||||
|
||||
# Set cookie with SameSite policy
|
||||
agent-browser cookies set tracking_consent "yes" --sameSite Strict
|
||||
|
||||
# Set cookie with expiration (Unix timestamp)
|
||||
agent-browser cookies set temp_token "temp123" --expires 1735689600
|
||||
|
||||
# Get all cookies
|
||||
agent-browser cookies
|
||||
|
||||
# Clear all cookies
|
||||
agent-browser cookies clear
|
||||
"##
|
||||
}
|
||||
@@ -1494,7 +1522,7 @@ Network: agent-browser network <action>
|
||||
requests [--clear] [--filter <pattern>]
|
||||
|
||||
Storage:
|
||||
cookies [get|set|clear] Manage cookies
|
||||
cookies [get|set|clear] Manage cookies (set supports --url, --domain, --path, --httpOnly, --secure, --sameSite, --expires)
|
||||
storage <local|session> Manage web storage
|
||||
|
||||
Tabs:
|
||||
|
||||
Reference in New Issue
Block a user