Repository: denoland/deno
Pull request: fix(cli): show clean error instead of panic for invalid --unsafely-ignore-certificate-errors value

## Summary

- Fixes a panic when a URL (e.g. `https://example.com/`) is passed to `--unsafely-ignore-certificate-errors`, which only accepts domains and IPs.
- Changed `unsafely_ignore_certificate_errors_parse` to return `clap::error::Result<()>` and propagate the validation error instead of `.unwrap()`-ing it.
- Updated all three call sites and `upgrade_parse` to propagate the error.

Fixes #30513

## Test plan

- [x] `./target/debug/deno run --unsafely-ignore-certificate-errors=https://example.com/` now prints `error: invalid value 'https://example.com/': URLs are not supported, only domains and ips` instead of panicking
- [x] `./target/debug/deno run --unsafely-ignore-certificate-errors=example.com` still works as before

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Changed files:
filename: cli/args/flags.rs
patch: @@ -1903,7 +1903,7 @@ pub fn flags_from_vec_with_initial_cwd(
         "types" => types_parse(&mut flags, &mut m),
         "uninstall" => uninstall_parse(&mut flags, &mut m),
         "update" => outdated_parse(&mut flags, &mut m, true)?,
-        "upgrade" => upgrade_parse(&mut flags, &mut m),
+        "upgrade" => upgrade_parse(&mut flags, &mut m)?,
         "vendor" => vendor_parse(&mut flags, &mut m),
         "publish" => publish_parse(&mut flags, &mut m)?,
         "x" => x_parse(&mut flags, &mut m)?,
@@ -6676,7 +6676,7 @@ fn info_parse(
   import_map_arg_parse(flags, matches);
   location_arg_parse(flags, matches);
   ca_file_arg_parse(flags, matches);
-  unsafely_ignore_certificate_errors_parse(flags, matches);
+  unsafely_ignore_certificate_errors_parse(flags, matches)?;
   node_modules_and_vendor_dir_arg_parse(flags, matches);
   lock_args_parse(flags, matches);
   no_remote_arg_parse(flags, matches);
@@ -7380,9 +7380,12 @@ fn types_parse(flags: &mut Flags, _matches: &mut ArgMatches) {
   flags.subcommand = DenoSubcommand::Types;
 }
 
-fn upgrade_parse(flags: &mut Flags, matches: &mut ArgMatches) {
+fn upgrade_parse(
+  flags: &mut Flags,
+  matches: &mut ArgMatches,
+) -> clap::error::Result<()> {
   ca_file_arg_parse(flags, matches);
-  unsafely_ignore_certificate_errors_parse(flags, matches);
+  unsafely_ignore_certificate_errors_parse(flags, matches)?;
 
   let dry_run = matches.get_flag("dry-run");
   let force = matches.get_flag("force");
@@ -7403,6 +7406,7 @@ fn upgrade_parse(flags: &mut Flags, matches: &mut ArgMatches) {
     version_or_hash_or_channel,
     checksum,
   });
+  Ok(())
 }
 
 fn vendor_parse(flags: &mut Flags, _matches: &mut ArgMatches) {
@@ -7454,7 +7458,7 @@ fn compile_args_without_check_parse(
   reload_arg_parse(flags, matches)?;
   lock_args_parse(flags, matches);
   ca_file_arg_parse(flags, matches);
-  unsafely_ignore_certificate_errors_parse(flags, matches);
+  unsafely_ignore_certificate_errors_parse(flags, matches)?;
   min_dep_age_arg_parse(flags, matches);
   Ok(())
 }
@@ -7685,13 +7689,14 @@ fn allow_and_deny_import_parse(
 fn unsafely_ignore_certificate_errors_parse(
   flags: &mut Flags,
   matches: &mut ArgMatches,
-) {
+) -> clap::error::Result<()> {
   if let Some(ic_wl) =
     matches.remove_many::<String>("unsafely-ignore-certificate-errors")
   {
-    let ic_allowlist = flags_net::parse(ic_wl.collect()).unwrap();
+    let ic_allowlist = flags_net::parse(ic_wl.collect())?;
     flags.unsafely_ignore_certificate_errors = Some(ic_allowlist);
   }
+  Ok(())
 }
 
 fn runtime_args_parse(

filename: tests/specs/cert/unsafely_ignore_certificate_errors_invalid/__test__.jsonc
patch: @@ -0,0 +1,14 @@
+{
+  "tests": {
+    "url_value_shows_error": {
+      "args": "eval --unsafely-ignore-certificate-errors=https://example.com/ console.log('hi')",
+      "output": "error: invalid value 'https://example.com/': URLs are not supported, only domains and ips\n",
+      "exitCode": 1
+    },
+    "valid_domain_works": {
+      "args": "eval --unsafely-ignore-certificate-errors=example.com console.log('hi')",
+      "output": "[WILDCARD]hi\n",
+      "exitCode": 0
+    }
+  }
+}