summaryrefslogtreecommitdiff
path: root/cli/args/flags_net.rs
diff options
context:
space:
mode:
authorAsher Gomez <ashersaupingomez@gmail.com>2023-08-03 21:19:19 +1000
committerGitHub <noreply@github.com>2023-08-03 13:19:19 +0200
commit6fb7e8d93bb9fd8cdd81130a394ae6061930c4f6 (patch)
tree2ec6dc2be234ef5a42023c1d75f1fc1316d80f06 /cli/args/flags_net.rs
parentdb287e216dd752bfcb3484cbfd93225e8463c363 (diff)
feat(permissions): add "--deny-*" flags (#19070)
This commit adds new "--deny-*" permission flags. These are complimentary to "--allow-*" flags. These flags can be used to restrict access to certain resources, even if they were granted using "--allow-*" flags or the "--allow-all" ("-A") flag. Eg. specifying "--allow-read --deny-read" will result in a permission error, while "--allow-read --deny-read=/etc" will allow read access to all FS but the "/etc" directory. Runtime permissions APIs ("Deno.permissions") were adjusted as well, mainly by adding, a new "PermissionStatus.partial" field. This field denotes that while permission might be granted to requested resource, it's only partial (ie. a "--deny-*" flag was specified that excludes some of the requested resources). Eg. specifying "--allow-read=foo/ --deny-read=foo/bar" and then querying for permissions like "Deno.permissions.query({ name: "read", path: "foo/" })" will return "PermissionStatus { state: "granted", onchange: null, partial: true }", denoting that some of the subpaths don't have read access. Closes #18804. --------- Co-authored-by: Bartek IwaƄczuk <biwanczuk@gmail.com> Co-authored-by: Nayeem Rahman <nayeemrmn99@gmail.com>
Diffstat (limited to 'cli/args/flags_net.rs')
-rw-r--r--cli/args/flags_net.rs202
1 files changed, 202 insertions, 0 deletions
diff --git a/cli/args/flags_net.rs b/cli/args/flags_net.rs
new file mode 100644
index 000000000..9f8a6b9f9
--- /dev/null
+++ b/cli/args/flags_net.rs
@@ -0,0 +1,202 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::url::Url;
+use std::net::IpAddr;
+use std::str::FromStr;
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct ParsePortError(String);
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct BarePort(u16);
+
+impl FromStr for BarePort {
+ type Err = ParsePortError;
+ fn from_str(s: &str) -> Result<BarePort, ParsePortError> {
+ if s.starts_with(':') {
+ match s.split_at(1).1.parse::<u16>() {
+ Ok(port) => Ok(BarePort(port)),
+ Err(e) => Err(ParsePortError(e.to_string())),
+ }
+ } else {
+ Err(ParsePortError(
+ "Bare Port doesn't start with ':'".to_string(),
+ ))
+ }
+ }
+}
+
+pub fn validator(host_and_port: &str) -> Result<String, String> {
+ if Url::parse(&format!("internal://{host_and_port}")).is_ok()
+ || host_and_port.parse::<IpAddr>().is_ok()
+ || host_and_port.parse::<BarePort>().is_ok()
+ {
+ Ok(host_and_port.to_string())
+ } else {
+ Err(format!("Bad host:port pair: {host_and_port}"))
+ }
+}
+
+/// Expands "bare port" paths (eg. ":8080") into full paths with hosts. It
+/// expands to such paths into 3 paths with following hosts: `0.0.0.0:port`,
+/// `127.0.0.1:port` and `localhost:port`.
+pub fn parse(paths: Vec<String>) -> clap::error::Result<Vec<String>> {
+ let mut out: Vec<String> = vec![];
+ for host_and_port in paths.iter() {
+ if Url::parse(&format!("internal://{host_and_port}")).is_ok()
+ || host_and_port.parse::<IpAddr>().is_ok()
+ {
+ out.push(host_and_port.to_owned())
+ } else if let Ok(port) = host_and_port.parse::<BarePort>() {
+ // we got bare port, let's add default hosts
+ for host in ["0.0.0.0", "127.0.0.1", "localhost"].iter() {
+ out.push(format!("{}:{}", host, port.0));
+ }
+ } else {
+ return Err(clap::Error::raw(
+ clap::error::ErrorKind::InvalidValue,
+ format!("Bad host:port pair: {host_and_port}"),
+ ));
+ }
+ }
+ Ok(out)
+}
+
+#[cfg(test)]
+mod bare_port_tests {
+ use super::BarePort;
+ use super::ParsePortError;
+
+ #[test]
+ fn bare_port_parsed() {
+ let expected = BarePort(8080);
+ let actual = ":8080".parse::<BarePort>();
+ assert_eq!(actual, Ok(expected));
+ }
+
+ #[test]
+ fn bare_port_parse_error1() {
+ let expected =
+ ParsePortError("Bare Port doesn't start with ':'".to_string());
+ let actual = "8080".parse::<BarePort>();
+ assert_eq!(actual, Err(expected));
+ }
+
+ #[test]
+ fn bare_port_parse_error2() {
+ let actual = ":65536".parse::<BarePort>();
+ assert!(actual.is_err());
+ }
+
+ #[test]
+ fn bare_port_parse_error3() {
+ let actual = ":14u16".parse::<BarePort>();
+ assert!(actual.is_err());
+ }
+
+ #[test]
+ fn bare_port_parse_error4() {
+ let actual = "Deno".parse::<BarePort>();
+ assert!(actual.is_err());
+ }
+
+ #[test]
+ fn bare_port_parse_error5() {
+ let actual = "deno.land:8080".parse::<BarePort>();
+ assert!(actual.is_err());
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::parse;
+
+ // Creates vector of strings, Vec<String>
+ macro_rules! svec {
+ ($($x:expr),*) => (vec![$($x.to_string()),*]);
+ }
+
+ #[test]
+ fn parse_net_args_() {
+ let entries = svec![
+ "deno.land",
+ "deno.land:80",
+ "::",
+ "::1",
+ "127.0.0.1",
+ "[::1]",
+ "1.2.3.4:5678",
+ "0.0.0.0:5678",
+ "127.0.0.1:5678",
+ "[::]:5678",
+ "[::1]:5678",
+ "localhost:5678",
+ "[::1]:8080",
+ "[::]:8000",
+ "[::1]:8000",
+ "localhost:8000",
+ "0.0.0.0:4545",
+ "127.0.0.1:4545",
+ "999.0.88.1:80"
+ ];
+ let expected = svec![
+ "deno.land",
+ "deno.land:80",
+ "::",
+ "::1",
+ "127.0.0.1",
+ "[::1]",
+ "1.2.3.4:5678",
+ "0.0.0.0:5678",
+ "127.0.0.1:5678",
+ "[::]:5678",
+ "[::1]:5678",
+ "localhost:5678",
+ "[::1]:8080",
+ "[::]:8000",
+ "[::1]:8000",
+ "localhost:8000",
+ "0.0.0.0:4545",
+ "127.0.0.1:4545",
+ "999.0.88.1:80"
+ ];
+ let actual = parse(entries).unwrap();
+ assert_eq!(actual, expected);
+ }
+
+ #[test]
+ fn parse_net_args_expansion() {
+ let entries = svec![":8080"];
+ let expected = svec!["0.0.0.0:8080", "127.0.0.1:8080", "localhost:8080"];
+ let actual = parse(entries).unwrap();
+ assert_eq!(actual, expected);
+ }
+
+ #[test]
+ fn parse_net_args_ipv6() {
+ let entries =
+ svec!["::", "::1", "[::1]", "[::]:5678", "[::1]:5678", "::cafe"];
+ let expected =
+ svec!["::", "::1", "[::1]", "[::]:5678", "[::1]:5678", "::cafe"];
+ let actual = parse(entries).unwrap();
+ assert_eq!(actual, expected);
+ }
+
+ #[test]
+ fn parse_net_args_ipv6_error1() {
+ let entries = svec![":::"];
+ assert!(parse(entries).is_err());
+ }
+
+ #[test]
+ fn parse_net_args_ipv6_error2() {
+ let entries = svec!["0123:4567:890a:bcde:fg::"];
+ assert!(parse(entries).is_err());
+ }
+
+ #[test]
+ fn parse_net_args_ipv6_error3() {
+ let entries = svec!["[::q]:8080"];
+ assert!(parse(entries).is_err());
+ }
+}