From 268b091fdcbd870811df2a552f3add60584be7ba Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 12 Aug 2023 18:30:59 -0400 Subject: [PATCH 01/15] Update starlark (#266) * eldritch update to starlark 1109aaf * Re-add extended dialect. * All tests pass. * Remove unused imports. * Surpress warnings starlark macros add. --- implants/Cargo.toml | 7 +- implants/golem/src/inter.rs | 2 +- implants/golem/src/inter/eval.rs | 125 +++++++++++------- implants/golem/tests/cli.rs | 2 +- implants/lib/eldritch/src/assets.rs | 8 +- implants/lib/eldritch/src/file.rs | 9 +- implants/lib/eldritch/src/file/list_impl.rs | 2 +- .../lib/eldritch/src/file/template_impl.rs | 9 +- implants/lib/eldritch/src/lib.rs | 26 +++- implants/lib/eldritch/src/pivot.rs | 8 +- .../lib/eldritch/src/pivot/port_scan_impl.rs | 6 +- .../lib/eldritch/src/pivot/ssh_exec_impl.rs | 6 +- implants/lib/eldritch/src/process.rs | 7 +- .../lib/eldritch/src/process/list_impl.rs | 8 +- implants/lib/eldritch/src/sys.rs | 6 +- implants/lib/eldritch/src/sys/exec_impl.rs | 4 +- implants/lib/eldritch/src/sys/shell_impl.rs | 8 +- 17 files changed, 145 insertions(+), 98 deletions(-) diff --git a/implants/Cargo.toml b/implants/Cargo.toml index 166fbe8f7..d111acd2a 100644 --- a/implants/Cargo.toml +++ b/implants/Cargo.toml @@ -7,8 +7,8 @@ members = [ ] [workspace.dependencies] -allocative = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "acf638430a00ca3855862e8c669670e1adaa42aa" } -allocative_derive = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "acf638430a00ca3855862e8c669670e1adaa42aa" } +allocative = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "1109aaf9b700d7cdd7ba48986aa650221b70a22f" } +allocative_derive = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "1109aaf9b700d7cdd7ba48986aa650221b70a22f" } anyhow = "1.0.65" assert_cmd = "2.0.6" async-recursion = "1.0.0" @@ -38,7 +38,8 @@ rust-embed = "6.6.0" serde = "1.0" serde_json = "1.0.87" sha256 = "1.0.3" -starlark = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "acf638430a00ca3855862e8c669670e1adaa42aa" } +starlark = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "1109aaf9b700d7cdd7ba48986aa650221b70a22f" } +starlark_derive = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "1109aaf9b700d7cdd7ba48986aa650221b70a22f" } structopt = "0.3.23" sys-info = "0.9.1" sysinfo = "0.28.4" diff --git a/implants/golem/src/inter.rs b/implants/golem/src/inter.rs index 6a40a6525..053f7c3c6 100644 --- a/implants/golem/src/inter.rs +++ b/implants/golem/src/inter.rs @@ -99,7 +99,7 @@ fn drain(xs: impl Iterator, json: bool, stats: &mut Stats) { } fn interactive(ctx: &Context) -> anyhow::Result<()> { - let mut rl = ReadLine::new("STARLARK_RUST_HISTFILE"); + let mut rl = ReadLine::new("STARLARK_RUST_HISTFILE")?; loop { match rl.read_line("$> ")? { Some(line) => { diff --git a/implants/golem/src/inter/eval.rs b/implants/golem/src/inter/eval.rs index 8c970db3a..faec8f93c 100644 --- a/implants/golem/src/inter/eval.rs +++ b/implants/golem/src/inter/eval.rs @@ -31,6 +31,7 @@ use starlark::docs::render_docs_as_code; use starlark::docs::Doc; use starlark::docs::DocItem; + use starlark::docs::DocModule; use starlark::environment::FrozenModule; use starlark::environment::Globals; use starlark::environment::Module; @@ -136,15 +137,17 @@ } fn url_for_doc(doc: &Doc) -> LspUrl { - let url = match &doc.item { - DocItem::Module(_) => Url::parse("starlark:/native/builtins.bzl").unwrap(), - DocItem::Object(_) => { - Url::parse(&format!("starlark:/native/builtins/{}.bzl", doc.id.name)).unwrap() - } - DocItem::Function(_) => Url::parse("starlark:/native/builtins.bzl").unwrap(), - }; - LspUrl::try_from(url).unwrap() - } + let url = match &doc.item { + DocItem::Module(_) => Url::parse("starlark:/native/builtins.bzl").unwrap(), + DocItem::Object(_) => { + Url::parse(&format!("starlark:/native/builtins/{}.bzl", doc.id.name)).unwrap() + } + DocItem::Function(_) | DocItem::Property(_) => { + Url::parse("starlark:/native/builtins.bzl").unwrap() + } + }; + LspUrl::try_from(url).unwrap() + } fn new_module(prelude: &[FrozenModule]) -> Module { let module = Module::new(); @@ -273,51 +276,58 @@ } } - impl LspContext for Context { - fn parse_file_with_contents(&self, uri: &LspUrl, content: String) -> LspEvalResult { - match uri { - LspUrl::File(uri) => { - let EvalResult { messages, ast } = - self.file_with_contents(&uri.to_string_lossy(), content); - LspEvalResult { - diagnostics: messages.map(Diagnostic::from).collect(), - ast, - } - } - _ => LspEvalResult::default(), - } - } +impl LspContext for Context { + fn parse_file_with_contents(&self, uri: &LspUrl, content: String) -> LspEvalResult { + match uri { + LspUrl::File(uri) => { + let EvalResult { messages, ast } = + self.file_with_contents(&uri.to_string_lossy(), content); + LspEvalResult { + diagnostics: messages.map(Diagnostic::from).collect(), + ast, + } + } + _ => LspEvalResult::default(), + } + } - fn resolve_load(&self, path: &str, current_file: &LspUrl) -> anyhow::Result { - let path = PathBuf::from(path); - match current_file { - LspUrl::File(current_file_path) => { - let current_file_dir = current_file_path.parent(); - let absolute_path = match (current_file_dir, path.is_absolute()) { - (_, true) => Ok(path), - (Some(current_file_dir), false) => Ok(current_file_dir.join(&path)), - (None, false) => Err(ResolveLoadError::MissingCurrentFilePath(path)), - }?; - Ok(Url::from_file_path(absolute_path).unwrap().try_into()?) - } - _ => Err( - ResolveLoadError::WrongScheme("file://".to_owned(), current_file.clone()).into(), - ), - } - } + fn resolve_load( + &self, + path: &str, + current_file: &LspUrl, + _workspace_root: Option<&Path>, + ) -> anyhow::Result { + let path = PathBuf::from(path); + match current_file { + LspUrl::File(current_file_path) => { + let current_file_dir = current_file_path.parent(); + let absolute_path = match (current_file_dir, path.is_absolute()) { + (_, true) => Ok(path), + (Some(current_file_dir), false) => Ok(current_file_dir.join(&path)), + (None, false) => Err(ResolveLoadError::MissingCurrentFilePath(path)), + }?; + Ok(Url::from_file_path(absolute_path).unwrap().try_into()?) + } + _ => Err( + ResolveLoadError::WrongScheme("file://".to_owned(), current_file.clone()).into(), + ), + } + } fn resolve_string_literal( - &self, - literal: &str, - current_file: &LspUrl, - ) -> anyhow::Result> { - self.resolve_load(literal, current_file).map(|url| { - Some(StringLiteralResult { - url, - location_finder: None, - }) - }) - } + &self, + literal: &str, + current_file: &LspUrl, + workspace_root: Option<&Path>, + ) -> anyhow::Result> { + self.resolve_load(literal, current_file, workspace_root) + .map(|url| { + Some(StringLiteralResult { + url, + location_finder: None, + }) + }) + } fn get_load_contents(&self, uri: &LspUrl) -> anyhow::Result> { match uri { @@ -341,6 +351,19 @@ ) -> anyhow::Result> { Ok(self.builtin_symbols.get(symbol).cloned()) } + + fn render_as_load( + &self, + _target: &LspUrl, + _current_file: &LspUrl, + _workspace_root: Option<&Path>, + ) -> anyhow::Result { + Err(anyhow::anyhow!("Not yet implemented, render_as_load")) + } + + fn get_environment(&self, _uri: &LspUrl) -> DocModule { + DocModule::default() + } } pub(crate) fn globals() -> Globals { diff --git a/implants/golem/tests/cli.rs b/implants/golem/tests/cli.rs index 3452be463..c4ee8c492 100644 --- a/implants/golem/tests/cli.rs +++ b/implants/golem/tests/cli.rs @@ -30,7 +30,7 @@ fn test_golem_main_syntax_fail() -> anyhow::Result<()> { cmd.arg("../../tests/golem_cli_test/syntax_fail.tome"); cmd.assert() .failure() - .stderr(predicate::str::contains("[TASK ERROR] ../../tests/golem_cli_test/syntax_fail.tome: [eldritch] Unable to parse eldritch tome: error: Parse error: unexpected string literal \'win\' here")); + .stderr(predicate::str::contains("[TASK ERROR] ../../tests/golem_cli_test/syntax_fail.tome: [eldritch] Unable to parse eldritch tome: error: Parse error: unexpected string literal \"win\" here")); Ok(()) } diff --git a/implants/lib/eldritch/src/assets.rs b/implants/lib/eldritch/src/assets.rs index 7ae52f730..26616c9e2 100644 --- a/implants/lib/eldritch/src/assets.rs +++ b/implants/lib/eldritch/src/assets.rs @@ -8,8 +8,8 @@ use derive_more::Display; use starlark::environment::{Methods, MethodsBuilder, MethodsStatic}; use starlark::values::none::NoneType; -use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType}; -use starlark::{starlark_type, starlark_simple_value, starlark_module}; +use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType, starlark_value}; +use starlark::{starlark_simple_value, starlark_module}; use serde::{Serialize,Serializer}; use rust_embed::RustEmbed; @@ -30,9 +30,9 @@ pub struct Asset; pub struct AssetsLibrary(); starlark_simple_value!(AssetsLibrary); +#[allow(non_upper_case_globals)] +#[starlark_value(type = "assets_library")] impl<'v> StarlarkValue<'v> for AssetsLibrary { - starlark_type!("assets_library"); - fn get_methods() -> Option<&'static Methods> { static RES: MethodsStatic = MethodsStatic::new(); RES.methods(methods) diff --git a/implants/lib/eldritch/src/file.rs b/implants/lib/eldritch/src/file.rs index cd91c72ff..69fbed898 100644 --- a/implants/lib/eldritch/src/file.rs +++ b/implants/lib/eldritch/src/file.rs @@ -17,8 +17,6 @@ mod template_impl; mod timestomp_impl; mod write_impl; -use std::fmt; - use allocative::Allocative; use derive_more::Display; @@ -26,8 +24,8 @@ use starlark::values::dict::Dict; use starlark::collections::SmallMap; use starlark::environment::{Methods, MethodsBuilder, MethodsStatic}; use starlark::values::none::NoneType; -use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType, Heap}; -use starlark::{starlark_type, starlark_simple_value, starlark_module}; +use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType, Heap, starlark_value}; +use starlark::{starlark_simple_value, starlark_module}; use serde::{Serialize,Serializer}; #[derive(Copy, Clone, Debug, PartialEq, Display, ProvidesStaticType, Allocative)] @@ -35,8 +33,9 @@ use serde::{Serialize,Serializer}; pub struct FileLibrary(); starlark_simple_value!(FileLibrary); +#[allow(non_upper_case_globals)] +#[starlark_value(type = "file_library")] impl<'v> StarlarkValue<'v> for FileLibrary { - starlark_type!("file_library"); fn get_methods() -> Option<&'static Methods> { static RES: MethodsStatic = MethodsStatic::new(); diff --git a/implants/lib/eldritch/src/file/list_impl.rs b/implants/lib/eldritch/src/file/list_impl.rs index 9620bb308..3693497a3 100644 --- a/implants/lib/eldritch/src/file/list_impl.rs +++ b/implants/lib/eldritch/src/file/list_impl.rs @@ -132,7 +132,7 @@ fn create_dict_from_file(starlark_heap: &Heap, file: File) -> Result{ tmp_res.insert_hashed(const_frozen_string!("file_name").to_value().get_hashed().unwrap(), tmp_value1.to_value()); let file_size = file.size as i32; - tmp_res.insert_hashed(const_frozen_string!("size").to_value().get_hashed().unwrap(), Value::new_int(file_size)); + tmp_res.insert_hashed(const_frozen_string!("size").to_value().get_hashed().unwrap(), starlark_heap.alloc(file_size)); let tmp_value2 = starlark_heap.alloc_str(&file.owner); tmp_res.insert_hashed(const_frozen_string!("owner").to_value().get_hashed().unwrap(), tmp_value2.to_value()); diff --git a/implants/lib/eldritch/src/file/template_impl.rs b/implants/lib/eldritch/src/file/template_impl.rs index 16ba43303..b8914f4e1 100644 --- a/implants/lib/eldritch/src/file/template_impl.rs +++ b/implants/lib/eldritch/src/file/template_impl.rs @@ -25,7 +25,7 @@ pub fn template(template_path: String, dst_path: String, args: SmallMap anyhow::Result<()>{ + let heap = Heap::new(); let mut map: SmallMap = SmallMap::new(); map.insert("name".to_string(), const_frozen_string!("greg").to_value()); - map.insert("age".to_string(), Value::new_int(29)); + map.insert("age".to_string(), heap.alloc(29)); map.insert("admin".to_string(), Value::new_bool(true)); let res = build_context(map)?; @@ -51,6 +52,8 @@ mod tests { let dst_path = String::from(tmp_file.path().to_str().unwrap()); dst_tmp_file.close()?; + let heap = Heap::new(); + // Write out template fs::write(path.clone(), r#"Hello {{ name }}, @@ -61,7 +64,7 @@ Congratulations on making it that far. // Setup our args let mut dict_data: SmallMap = SmallMap::new(); dict_data.insert("name".to_string(), const_frozen_string!("test123").to_value()); - dict_data.insert("age".to_string(), Value::new_int(22)); + dict_data.insert("age".to_string(), heap.alloc(22)); // Run our code template(path, dst_path.clone(), dict_data, true)?; diff --git a/implants/lib/eldritch/src/lib.rs b/implants/lib/eldritch/src/lib.rs index ffd8ff9c4..61705ef58 100644 --- a/implants/lib/eldritch/src/lib.rs +++ b/implants/lib/eldritch/src/lib.rs @@ -4,11 +4,11 @@ pub mod sys; pub mod pivot; pub mod assets; -use std::sync::mpsc::{Sender}; +use std::sync::mpsc::Sender; use serde_json::Map; use starlark::collections::SmallMap; use starlark::{starlark_module, PrintHandler}; -use starlark::environment::{GlobalsBuilder, Module, Globals}; +use starlark::environment::{GlobalsBuilder, Module, Globals, LibraryExtension}; use starlark::syntax::{AstModule, Dialect}; use starlark::eval::Evaluator; use starlark::values::dict::Dict; @@ -30,7 +30,23 @@ pub fn get_eldritch() -> anyhow::Result { const assets: AssetsLibrary = AssetsLibrary(); } - let globals = GlobalsBuilder::extended().with(eldritch).build(); + let globals = GlobalsBuilder::extended_by( + &[ + LibraryExtension::StructType, + LibraryExtension::RecordType, + LibraryExtension::EnumType, + LibraryExtension::Map, + LibraryExtension::Filter, + LibraryExtension::Partial, + LibraryExtension::ExperimentalRegex, + LibraryExtension::Debug, + LibraryExtension::Print, + LibraryExtension::Breakpoint, + LibraryExtension::Json, + LibraryExtension::Abs, + LibraryExtension::Typing, + ] + ).with(eldritch).build(); return Ok(globals); } @@ -64,7 +80,7 @@ pub fn eldritch_run(tome_filename: String, tome_contents: String, tome_parameter let ast = match AstModule::parse( &tome_filename, tome_contents.as_str().to_owned(), - &Dialect::Standard + &Dialect::Extended ) { Ok(res) => res, Err(err) => return Err(anyhow::anyhow!("[eldritch] Unable to parse eldritch tome: {}: {} {}", err.to_string(), tome_filename.as_str(), tome_contents.as_str())), @@ -132,7 +148,7 @@ pub fn eldritch_run(tome_filename: String, tome_contents: String, tome_parameter }, None => i32::MAX.into(), }; - new_value = Value::new_int(tmp_value); + new_value = module.heap().alloc(tmp_value); } let hashed_key = match new_key.to_value().get_hashed() { Ok(local_hashed_key) => local_hashed_key, diff --git a/implants/lib/eldritch/src/pivot.rs b/implants/lib/eldritch/src/pivot.rs index c516d2143..6101dd9b7 100644 --- a/implants/lib/eldritch/src/pivot.rs +++ b/implants/lib/eldritch/src/pivot.rs @@ -19,19 +19,19 @@ use russh_keys::{key, decode_secret_key}; use starlark::values::dict::Dict; use starlark::environment::{Methods, MethodsBuilder, MethodsStatic}; use starlark::values::none::NoneType; -use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType, Heap}; -use starlark::{starlark_type, starlark_simple_value, starlark_module}; +use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType, Heap, starlark_value}; +use starlark::{starlark_simple_value, starlark_module}; use serde::{Serialize,Serializer}; -use tokio::net::ToSocketAddrs; #[derive(Copy, Clone, Debug, PartialEq, Display, ProvidesStaticType, Allocative)] #[display(fmt = "PivotLibrary")] pub struct PivotLibrary(); starlark_simple_value!(PivotLibrary); +#[allow(non_upper_case_globals)] +#[starlark_value(type = "pivot_library")] impl<'v> StarlarkValue<'v> for PivotLibrary { - starlark_type!("pivot_library"); fn get_methods() -> Option<&'static Methods> { static RES: MethodsStatic = MethodsStatic::new(); diff --git a/implants/lib/eldritch/src/pivot/port_scan_impl.rs b/implants/lib/eldritch/src/pivot/port_scan_impl.rs index 2412a2921..493b4f41a 100644 --- a/implants/lib/eldritch/src/pivot/port_scan_impl.rs +++ b/implants/lib/eldritch/src/pivot/port_scan_impl.rs @@ -362,7 +362,7 @@ pub fn port_scan(starlark_heap: &Heap, target_cidrs: Vec, ports: Vec ast = res, Err(err) => return Err(err), @@ -623,7 +623,7 @@ res } } - let globals = GlobalsBuilder::extended().with(func_port_scan).build(); + let globals = GlobalsBuilder::standard().with(func_port_scan).build(); let module: Module = Module::new(); let mut eval: Evaluator = Evaluator::new(&module); diff --git a/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs b/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs index 6ef2f64e8..5ac3feaca 100644 --- a/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs +++ b/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use starlark::{values::{dict::Dict, Heap, Value}, collections::SmallMap, const_frozen_string}; +use starlark::{values::{dict::Dict, Heap}, collections::SmallMap, const_frozen_string}; use super::Session; @@ -47,7 +47,7 @@ pub fn ssh_exec(starlark_heap: &Heap, target: String, port: i32, command: String let stdout_value = starlark_heap.alloc_str(&cmd_res.stdout); dict_res.insert_hashed(const_frozen_string!("stdout").to_value().get_hashed().unwrap(), stdout_value.to_value()); - let status_value = Value::new_int(cmd_res.status); + let status_value = starlark_heap.alloc(cmd_res.status); dict_res.insert_hashed(const_frozen_string!("status").to_value().get_hashed().unwrap(), status_value); Ok(dict_res) @@ -185,7 +185,7 @@ mod tests { #[tokio::test] async fn test_pivot_ssh_exec() -> anyhow::Result<()> { - let ssh_port = allocate_localhost_unused_ports().await? as u16;; + let ssh_port = allocate_localhost_unused_ports().await? as u16; let ssh_host = "127.0.0.1".to_string(); let ssh_command = r#"echo "hello world""#.to_string(); let test_server_task = task::spawn( diff --git a/implants/lib/eldritch/src/process.rs b/implants/lib/eldritch/src/process.rs index f3d0c5afe..ee23e7d6b 100644 --- a/implants/lib/eldritch/src/process.rs +++ b/implants/lib/eldritch/src/process.rs @@ -8,8 +8,8 @@ use derive_more::Display; use starlark::environment::{Methods, MethodsBuilder, MethodsStatic}; use starlark::values::dict::Dict; use starlark::values::none::NoneType; -use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType, Heap}; -use starlark::{starlark_type, starlark_simple_value, starlark_module}; +use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, ProvidesStaticType, Heap, starlark_value}; +use starlark::{starlark_simple_value, starlark_module}; use serde::{Serialize,Serializer}; @@ -18,8 +18,9 @@ use serde::{Serialize,Serializer}; pub struct ProcessLibrary(); starlark_simple_value!(ProcessLibrary); +#[allow(non_upper_case_globals)] +#[starlark_value(type = "process_library")] impl<'v> StarlarkValue<'v> for ProcessLibrary { - starlark_type!("process_library"); fn get_methods() -> Option<&'static Methods> { static RES: MethodsStatic = MethodsStatic::new(); diff --git a/implants/lib/eldritch/src/process/list_impl.rs b/implants/lib/eldritch/src/process/list_impl.rs index 5d6cf9e41..85192ee55 100644 --- a/implants/lib/eldritch/src/process/list_impl.rs +++ b/implants/lib/eldritch/src/process/list_impl.rs @@ -34,11 +34,11 @@ pub fn list(starlark_heap: &Heap) -> Result> { // Create Dict type. let mut tmp_res = Dict::new(res); - tmp_res.insert_hashed(const_frozen_string!("pid").to_value().get_hashed().unwrap(), Value::new_int(match pid.as_u32().try_into() { + tmp_res.insert_hashed(const_frozen_string!("pid").to_value().get_hashed().unwrap(), starlark_heap.alloc(match pid.as_u32().try_into() { Ok(local_int) => local_int, Err(_) => -1, })); - tmp_res.insert_hashed(const_frozen_string!("ppid").to_value().get_hashed().unwrap(), Value::new_int(match tmp_ppid.try_into() { + tmp_res.insert_hashed(const_frozen_string!("ppid").to_value().get_hashed().unwrap(), starlark_heap.alloc(match tmp_ppid.try_into() { Ok(local_int) => local_int, Err(_) => -1, })); @@ -58,6 +58,8 @@ pub fn list(starlark_heap: &Heap) -> Result> { #[cfg(test)] mod tests { + use anyhow::Context; + use super::*; use std::process::Command; @@ -76,7 +78,7 @@ mod tests { let res = list(&binding)?; for proc in res{ let cur_pid = match proc.get(const_frozen_string!("pid").to_value())? { - Some(local_cur_pid) => local_cur_pid.to_int()?, + Some(local_cur_pid) => local_cur_pid.unpack_i32().context("Failed to unpack starlark int to i32")?, None => return Err(anyhow::anyhow!("pid couldn't be unwrapped")), }; if cur_pid as u32 == child.id() { diff --git a/implants/lib/eldritch/src/sys.rs b/implants/lib/eldritch/src/sys.rs index ecf819497..cb2630ce6 100644 --- a/implants/lib/eldritch/src/sys.rs +++ b/implants/lib/eldritch/src/sys.rs @@ -12,8 +12,9 @@ use derive_more::Display; use starlark::environment::{Methods, MethodsBuilder, MethodsStatic}; use starlark::values::none::NoneType; +use starlark::values::starlark_value; use starlark::values::{StarlarkValue, Value, Heap, dict::Dict, UnpackValue, ValueLike, ProvidesStaticType}; -use starlark::{starlark_type, starlark_simple_value, starlark_module}; +use starlark::{starlark_simple_value, starlark_module}; use serde::{Serialize,Serializer}; @@ -28,8 +29,9 @@ struct CommandOutput { pub struct SysLibrary(); starlark_simple_value!(SysLibrary); +#[allow(non_upper_case_globals)] +#[starlark_value(type = "sys_library")] impl<'v> StarlarkValue<'v> for SysLibrary { - starlark_type!("sys_library"); fn get_methods() -> Option<&'static Methods> { static RES: MethodsStatic = MethodsStatic::new(); diff --git a/implants/lib/eldritch/src/sys/exec_impl.rs b/implants/lib/eldritch/src/sys/exec_impl.rs index 5f7da2a98..dace385e4 100644 --- a/implants/lib/eldritch/src/sys/exec_impl.rs +++ b/implants/lib/eldritch/src/sys/exec_impl.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use starlark::{values::{Heap, dict::Dict, Value}, collections::SmallMap, const_frozen_string}; +use starlark::{values::{Heap, dict::Dict}, collections::SmallMap, const_frozen_string}; use std::process::Command; #[cfg(any(target_os = "linux", target_os = "macos"))] use nix::{sys::wait::waitpid, unistd::{fork, ForkResult}}; @@ -22,7 +22,7 @@ pub fn exec(starlark_heap: &Heap, path: String, args: Vec, disown: Optio let stderr_value = starlark_heap.alloc_str(cmd_res.stderr.as_str()); dict_res.insert_hashed(const_frozen_string!("stderr").to_value().get_hashed().unwrap(), stderr_value.to_value()); - let status_value = Value::new_int(cmd_res.status); + let status_value = starlark_heap.alloc(cmd_res.status); dict_res.insert_hashed(const_frozen_string!("status").to_value().get_hashed().unwrap(), status_value); Ok(dict_res) diff --git a/implants/lib/eldritch/src/sys/shell_impl.rs b/implants/lib/eldritch/src/sys/shell_impl.rs index 2dd8a2d42..0d68234b0 100644 --- a/implants/lib/eldritch/src/sys/shell_impl.rs +++ b/implants/lib/eldritch/src/sys/shell_impl.rs @@ -1,9 +1,9 @@ use anyhow::Result; use starlark::collections::SmallMap; use starlark::const_frozen_string; -use starlark::values::{Heap, Value}; +use starlark::values::Heap; use starlark::values::dict::Dict; -use std::process::{Command}; +use std::process::Command; use std::str; use super::CommandOutput; @@ -20,7 +20,7 @@ pub fn shell(starlark_heap: &Heap, cmd: String) -> Result { let stderr_value = starlark_heap.alloc_str(cmd_res.stderr.as_str()); dict_res.insert_hashed(const_frozen_string!("stderr").to_value().get_hashed().unwrap(), stderr_value.to_value()); - let status_value = Value::new_int(cmd_res.status); + let status_value = starlark_heap.alloc(cmd_res.status); dict_res.insert_hashed(const_frozen_string!("status").to_value().get_hashed().unwrap(), status_value); Ok(dict_res) @@ -117,7 +117,7 @@ func_shell("whoami") } } - let globals = GlobalsBuilder::extended().with(func_shell).build(); + let globals = GlobalsBuilder::standard().with(func_shell).build(); let module: Module = Module::new(); let mut eval: Evaluator = Evaluator::new(&module); From 9d2a7929a15894ef31b679e85f8b70f94052198e Mon Sep 17 00:00:00 2001 From: Joe Abbate <40615740+jabbate19@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:02:47 -0700 Subject: [PATCH 02/15] sys.get_user() (#257) * Linux-Side Get User * Fix Windows Implementation * Add Test * Fixup Windows Tests --------- Co-authored-by: Hulto <7121375+hulto@users.noreply.github.com> --- docs/_docs/user-guide/eldritch.md | 27 +++ implants/Cargo.toml | 2 +- implants/lib/eldritch/src/lib.rs | 2 +- implants/lib/eldritch/src/sys.rs | 10 + .../lib/eldritch/src/sys/get_user_impl.rs | 186 ++++++++++++++++++ 5 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 implants/lib/eldritch/src/sys/get_user_impl.rs diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 2e47bc264..7f78154be 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -403,6 +403,33 @@ An example is below: "distro": "Debian GNU/Linux 10 (buster)", "platform": "Linux" } +``` + +### sys.get_user +`sys.get_user() -> Dict` + +The sys.get_user method returns a dictionary that describes the current process's running user. +On *Nix, will return UID, EUID, GID, EGID, and detailed user info for the UID and EUID mappings. +For users, will return name and groups of user. + +```json +{ + "uid": { + "uid": 0, + "name": "root", + "gid": 0, + "groups": ["root"] + }, + "euid": { + "uid": 0, + "name": "root", + "gid": 0, + "groups": ["root"] + }, + "gid": 0, + "egid": 0 +} +``` ### sys.is_linux `sys.is_linux() -> bool` diff --git a/implants/Cargo.toml b/implants/Cargo.toml index d111acd2a..afad45bb7 100644 --- a/implants/Cargo.toml +++ b/implants/Cargo.toml @@ -42,7 +42,7 @@ starlark = { git = "https://github.com/facebookexperimental/starlark-rust", rev starlark_derive = { git = "https://github.com/facebookexperimental/starlark-rust", rev = "1109aaf9b700d7cdd7ba48986aa650221b70a22f" } structopt = "0.3.23" sys-info = "0.9.1" -sysinfo = "0.28.4" +sysinfo = "0.29.7" tar = "0.4.38" tavern = { path = "./lib/tavern" } tempfile = "3.3.0" diff --git a/implants/lib/eldritch/src/lib.rs b/implants/lib/eldritch/src/lib.rs index 61705ef58..97c912dc4 100644 --- a/implants/lib/eldritch/src/lib.rs +++ b/implants/lib/eldritch/src/lib.rs @@ -188,7 +188,7 @@ mod tests { r#" dir(file) == ["append", "compress", "copy", "download", "exists", "hash", "is_dir", "is_file", "list", "mkdir", "read", "remove", "rename", "replace", "replace_all", "template", "timestomp", "write"] dir(process) == ["kill", "list", "name"] -dir(sys) == ["dll_inject", "exec", "get_ip", "get_os", "is_linux", "is_macos", "is_windows", "shell"] +dir(sys) == ["dll_inject", "exec", "get_ip", "get_os", "get_user", "is_linux", "is_macos", "is_windows", "shell"] dir(pivot) == ["arp_scan", "bind_proxy", "ncat", "port_forward", "port_scan", "smb_exec", "ssh_exec", "ssh_password_spray"] dir(assets) == ["copy","list","read","read_binary"] "#, diff --git a/implants/lib/eldritch/src/sys.rs b/implants/lib/eldritch/src/sys.rs index cb2630ce6..a935f8063 100644 --- a/implants/lib/eldritch/src/sys.rs +++ b/implants/lib/eldritch/src/sys.rs @@ -1,6 +1,7 @@ mod exec_impl; mod get_ip_impl; mod get_os_impl; +mod get_user_impl; mod is_linux_impl; mod is_windows_impl; mod is_macos_impl; @@ -77,6 +78,15 @@ fn methods(builder: &mut MethodsBuilder) { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } get_ip_impl::get_ip(starlark_heap) } + fn get_user<'v>(this: SysLibrary, starlark_heap: &'v Heap) -> anyhow::Result> { + if false { + println!( + "Ignore unused this var. _this isn't allowed by starlark. {:?}", + this + ); + } + get_user_impl::get_user(starlark_heap) + } fn is_linux(this: SysLibrary) -> anyhow::Result { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } is_linux_impl::is_linux() diff --git a/implants/lib/eldritch/src/sys/get_user_impl.rs b/implants/lib/eldritch/src/sys/get_user_impl.rs new file mode 100644 index 000000000..90dfb40d7 --- /dev/null +++ b/implants/lib/eldritch/src/sys/get_user_impl.rs @@ -0,0 +1,186 @@ +use anyhow::Result; +use starlark::collections::SmallMap; +use starlark::values::dict::Dict; +use starlark::values::Heap; +use starlark::{const_frozen_string, values::ValueLike}; +use std::process; +use sysinfo::{Pid, ProcessExt, System, SystemExt, UserExt}; + +pub fn get_user(starlark_heap: &Heap) -> Result { + let res = SmallMap::new(); + let mut dict_res = Dict::new(res); + let user = SmallMap::new(); + let mut dict_user = Dict::new(user); + + + let sys = System::new_all(); + let pid = process::id() as usize; + if let Some(process) = sys.process(Pid::from(pid)) { + let uid = match process.user_id() { + Some(uid) => uid, + None => return Err(anyhow::anyhow!("Failed to get uid")), + }; + #[cfg(target_os="windows")] + let uid_value = starlark_heap.alloc(uid.to_string()); + #[cfg(not(target_os="windows"))] + let uid_value = starlark_heap.alloc(**uid); + dict_user.insert_hashed( + match const_frozen_string!("uid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc uid information: {}", e)), + }, + uid_value.to_value(), + ); + let user = match sys.get_user_by_id(uid) { + Some(user) => user, + None => return Err(anyhow::anyhow!("Failed to get user")), + }; + let user_name_value = starlark_heap.alloc_str(user.name()); + dict_user.insert_hashed( + match const_frozen_string!("name").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc name information: {}", e)), + }, + user_name_value.to_value(), + ); + let user_gid_value = starlark_heap.alloc(*user.group_id()); + dict_user.insert_hashed( + match const_frozen_string!("gid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc gid information: {}", e)), + }, + user_gid_value.to_value(), + ); + let user_groups_value = starlark_heap.alloc(Vec::from(user.groups())); + dict_user.insert_hashed( + match const_frozen_string!("groups").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc groups information: {}", e)), + }, + user_groups_value.to_value(), + ); + #[cfg(not(target_os="windows"))] + { + let euser = SmallMap::new(); + let mut dict_euser = Dict::new(euser); + let euid = match process.effective_user_id() { + Some(euid) => euid, + None => return Err(anyhow::anyhow!("Failed to get euid")), + }; + let euid_value = starlark_heap.alloc(**euid); + dict_euser.insert_hashed( + match const_frozen_string!("uid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc uid information: {}", e)), + }, + euid_value.to_value(), + ); + let euser = match sys.get_user_by_id(euid) { + Some(euser) => euser, + None => return Err(anyhow::anyhow!("Failed to get euser")), + }; + let euser_name_value = starlark_heap.alloc_str(euser.name()); + dict_euser.insert_hashed( + match const_frozen_string!("name").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc name information: {}", e)), + }, + euser_name_value.to_value(), + ); + let euser_gid_value = starlark_heap.alloc(*euser.group_id()); + dict_euser.insert_hashed( + match const_frozen_string!("gid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc gid information: {}", e)), + }, + euser_gid_value.to_value(), + ); + let euser_groups_value = starlark_heap.alloc(Vec::from(euser.groups())); + dict_euser.insert_hashed( + match const_frozen_string!("groups").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc groups information: {}", e)), + }, + euser_groups_value.to_value(), + ); + let dict_euser_value = starlark_heap.alloc(dict_euser); + dict_res.insert_hashed( + match const_frozen_string!("euid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc euid information: {}", e)), + }, + dict_euser_value, + ); + let gid = match process.group_id() { + Some(gid) => gid, + None => return Err(anyhow::anyhow!("Failed to get gid")), + }; + let gid_value = starlark_heap.alloc(*gid); + let egid = match process.effective_group_id() { + Some(egid) => egid, + None => return Err(anyhow::anyhow!("Failed to get egid")), + }; + let egid_value = starlark_heap.alloc(*egid); + dict_res.insert_hashed( + match const_frozen_string!("gid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc gid information: {}", e)), + }, + gid_value.to_value(), + ); + dict_res.insert_hashed( + match const_frozen_string!("egid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc egid information: {}", e)), + }, + egid_value.to_value(), + ); + } + let dict_user_value = starlark_heap.alloc(dict_user); + dict_res.insert_hashed( + match const_frozen_string!("uid").to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc uid information: {}", e)), + }, + dict_user_value, + ); + return Ok(dict_res); + } + Err(anyhow::anyhow!("Failed to obtain process information")) +} + +#[cfg(test)] +mod tests { + use starlark::values::{UnpackValue, Value}; + use super::*; + + #[test] + fn test_sys_get_user() -> anyhow::Result<()>{ + let test_heap = Heap::new(); + let res = get_user(&test_heap)?; + let keys: Vec<&str> = res.keys().map(|key| key.unpack_str().unwrap()).collect(); + assert!(keys.contains(&"uid")); + if !cfg!(target_os="windows") { + assert!(keys.contains(&"euid")); + assert!(keys.contains(&"egid")); + assert!(keys.contains(&"gid")); + } + let uid_data: Value<'_> = res.get(const_frozen_string!("uid").to_value())?.unwrap(); + let uid_data_map: SmallMap> = SmallMap::unpack_value(uid_data).unwrap(); + let uid_data_keys: Vec<&str> = uid_data_map.keys().map(|key| &key[..]).collect(); + assert!(uid_data_keys.contains(&"uid")); + assert!(uid_data_keys.contains(&"name")); + assert!(uid_data_keys.contains(&"gid")); + assert!(uid_data_keys.contains(&"groups")); + if !cfg!(target_os="windows") { + let euid_data: Value<'_> = res.get(const_frozen_string!("euid").to_value())?.unwrap(); + let euid_data_map: SmallMap> = SmallMap::unpack_value(euid_data).unwrap(); + let euid_data_keys: Vec<&str> = euid_data_map.keys().map(|key| &key[..]).collect(); + assert!(euid_data_keys.contains(&"uid")); + assert!(euid_data_keys.contains(&"name")); + assert!(euid_data_keys.contains(&"gid")); + assert!(euid_data_keys.contains(&"groups")); + } + Ok(()) + } +} \ No newline at end of file From 8c11caa7528c336d2096e50936dd2d4a17483f2c Mon Sep 17 00:00:00 2001 From: Joe Abbate <40615740+jabbate19@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:55:25 -0700 Subject: [PATCH 03/15] pivot.arp_scan() (#258) * ARP Scan * Fixup Error Messages * Remove all unwraps * Add Tests * Fix Windows Compile Error * Fix Windows Test * Change listener thread to return result * Update Dependency Location and Pivot.rs format * Fixup Thread Failure * Docs and Limiting Println to Debug --------- Co-authored-by: Hulto <7121375+hulto@users.noreply.github.com> --- docs/_docs/user-guide/eldritch.md | 17 + implants/Cargo.toml | 2 + implants/lib/eldritch/Cargo.toml | 2 + implants/lib/eldritch/src/pivot.rs | 8 +- .../lib/eldritch/src/pivot/arp_scan_impl.rs | 314 +++++++++++++++++- 5 files changed, 337 insertions(+), 6 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 7f78154be..c154e5123 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -230,6 +230,23 @@ If a file or directory already exists at this path, the method will fail. `pivot.arp_scan(target_cidrs: List) -> List` The pivot.arp_scan method is being proposed to allow users to enumerate hosts on their network without using TCP connect or ping. +- `target_cidrs` must be in a CIDR format eg. `127.0.0.1/32`. Domains and single IPs `example.com` / `127.0.0.1` cannot be passed. +- Must be running as `root` to use. +- Not supported on Windows + +Results will be in the format: + +```JSON +// Successful +$> pivot.arp_scan(["192.168.1.1/32"]) +[ + { "ip": "192.168.1.1", "mac": "ab:cd:ef:01:23:45", "interface": "eno0" } +] + +// Failure +$> pivot.arp_scan(["192.168.1.1/32"]) +[] +``` ### pivot.bind_proxy `pivot.bind_proxy(listen_address: str, listen_port: int, username: str, password: str ) -> None` diff --git a/implants/Cargo.toml b/implants/Cargo.toml index afad45bb7..1d3dd33dd 100644 --- a/implants/Cargo.toml +++ b/implants/Cargo.toml @@ -23,11 +23,13 @@ flate2 = "1.0.24" gazebo = "0.8.1" graphql_client = "0.12.0" httptest = "0.15.4" +ipnetwork = "0.20.0" itertools = "0.10" lsp-types = "0.93.0" network-interface = "1.0.1" nix = "0.26.1" openssl = "0.10" +pnet = "0.34.0" predicates = "2.1" rand = "0.8.5" regex = "1.5.5" diff --git a/implants/lib/eldritch/Cargo.toml b/implants/lib/eldritch/Cargo.toml index bb69e104c..786077418 100644 --- a/implants/lib/eldritch/Cargo.toml +++ b/implants/lib/eldritch/Cargo.toml @@ -14,8 +14,10 @@ derive_more = { workspace = true } eval = { workspace = true } flate2 = { workspace = true } gazebo = { workspace = true } +ipnetwork = { workspace = true } network-interface = { workspace = true } nix = { workspace = true } +pnet = { workspace = true } regex = { workspace = true } reqwest = { workspace = true , features = ["blocking", "stream"] } russh = { workspace = true } diff --git a/implants/lib/eldritch/src/pivot.rs b/implants/lib/eldritch/src/pivot.rs index 6101dd9b7..e135b48a6 100644 --- a/implants/lib/eldritch/src/pivot.rs +++ b/implants/lib/eldritch/src/pivot.rs @@ -78,9 +78,13 @@ fn methods(builder: &mut MethodsBuilder) { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } port_scan_impl::port_scan(starlark_heap, target_cidrs, ports, protocol, timeout) } - fn arp_scan(this: PivotLibrary, target_cidrs: Vec) -> anyhow::Result> { + fn arp_scan<'v>( + this: PivotLibrary, + starlark_heap: &'v Heap, + target_cidrs: Vec, + ) -> anyhow::Result>> { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } - arp_scan_impl::arp_scan(target_cidrs) + arp_scan_impl::arp_scan(starlark_heap, target_cidrs) } fn port_forward(this: PivotLibrary, listen_address: String, listen_port: i32, forward_address: String, forward_port: i32, protocol: String) -> anyhow::Result { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } diff --git a/implants/lib/eldritch/src/pivot/arp_scan_impl.rs b/implants/lib/eldritch/src/pivot/arp_scan_impl.rs index 740bbdf9e..9312d9724 100644 --- a/implants/lib/eldritch/src/pivot/arp_scan_impl.rs +++ b/implants/lib/eldritch/src/pivot/arp_scan_impl.rs @@ -1,5 +1,311 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; +use ipnetwork::{IpNetwork, Ipv4Network}; +#[cfg(not(target_os = "windows"))] +use pnet::{ + datalink::{self, channel, Channel::Ethernet, NetworkInterface}, + packet::{ + arp::{ArpOperations, ArpPacket, MutableArpPacket}, + ethernet::{EtherType, EthernetPacket, MutableEthernetPacket}, + Packet, + }, + util::MacAddr, +}; +use starlark::collections::SmallMap; +use starlark::const_frozen_string; +use starlark::values::{dict::Dict, Heap}; +use std::collections::HashMap; +use std::net::{IpAddr, Ipv4Addr}; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, SystemTime}; -pub fn arp_scan(_target_cidrs: Vec) -> Result> { - unimplemented!("Method unimplemented") -} \ No newline at end of file +#[cfg(not(target_os = "windows"))] +#[derive(Debug, Clone, PartialEq)] +pub struct ArpResponse { + _source_ip: Ipv4Addr, + source_mac: MacAddr, + interface: String, +} + +#[cfg(not(target_os = "windows"))] +fn start_listener( + interface: NetworkInterface, + data: Arc>>>, +) -> Result<()> { + if interface.ips.iter().filter(|ip| ip.is_ipv4()).count() == 0 { + return Err(anyhow!("Interface does not have a v4 address")); + } + let mac = interface.mac.ok_or(anyhow!("Could not obtain MAC of interface"))?; + let (mut tx, mut rx) = match channel(&interface, Default::default()) { + Ok(Ethernet(tx, rx)) => (tx, rx), + Ok(_) => return Err(anyhow!("Unhandled channel type")), + Err(e) => { + return Err(anyhow!("Error creating channel: {}", e)); + } + }; + let ips = match data.lock() { + Ok(lock) => lock.keys().cloned().collect::>(), + Err(err) => { + return Err(anyhow!("Failed to get lock on ips: {}", err)); + } + }; + for ip in ips { + let ip_to_use = match interface + .ips + .iter() + .find(|interface_ip| interface_ip.contains(IpAddr::V4(ip))) + { + Some(IpNetwork::V4(ipnet)) => ipnet, + Some(_) => continue, + None => continue, + }; + let mut arp_packet = [0u8; 28]; + let mut arp = match MutableArpPacket::new(&mut arp_packet) { + Some(arp) => arp, + None => { + return Err(anyhow!("Failed to create MutableArpPacket to send.")); + } + }; + arp.set_hardware_type(pnet::packet::arp::ArpHardwareType(1)); + arp.set_protocol_type(pnet::packet::ethernet::EtherType(0x0800)); + arp.set_hw_addr_len(6); + arp.set_proto_addr_len(4); + arp.set_operation(ArpOperations::Request); + arp.set_sender_hw_addr(mac); + arp.set_sender_proto_addr(ip_to_use.ip()); + arp.set_target_hw_addr(MacAddr::zero()); + arp.set_target_proto_addr(ip); + let mut eth_packet = [0u8; 60]; + let mut eth = match MutableEthernetPacket::new(&mut eth_packet) { + Some(eth) => eth, + None => { + return Err(anyhow!("Failed to create MutableEthernetPacket to send.")); + } + }; + eth.set_destination(MacAddr::broadcast()); + eth.set_source(mac); + eth.set_ethertype(EtherType(0x0806)); + eth.set_payload(arp.packet()); + match tx.send_to(eth.packet(), None) { + Some(Ok(_)) => {} + Some(Err(err)) => { + return Err(anyhow!("Failed to tx on {}: {}", interface.name, err)); + } + None => { + return Err(anyhow!("Failed to tx on {}: Returned None", interface.name)); + } + }; + let now = SystemTime::now(); + loop { + let elapsed = match now.elapsed() { + Ok(elapsed) => elapsed, + Err(err) => { + return Err(anyhow!("Failed to get elapsed time on {}: {}", interface.name, err)); + } + }; + if elapsed > Duration::from_secs(5) { + break; + } + match rx.next() { + Ok(packet) => { + let eth = match EthernetPacket::new(packet) { + Some(eth) => eth, + None => continue, + }; + let arp = match ArpPacket::new(eth.payload()) { + Some(arp) => arp, + None => continue, + }; + if arp.get_operation() != ArpOperations::Reply { + continue; + } + let source_ip = arp.get_sender_proto_addr(); + let source_mac = arp.get_sender_hw_addr(); + if source_ip == ip { + match data.lock() { + Ok(mut lock) => { + if let Some(target) = lock.get_mut(&ip) { + *target = Some(ArpResponse { + _source_ip: source_ip, + source_mac, + interface: interface.name.clone(), + }); + break; + } + return Err(anyhow!("Failed to find {} in HashMap", ip)); + } + Err(err) => { + return Err(anyhow!("Failed to get lock on data: {}", err)); + } + } + } + } + Err(e) => { + if e.kind() == std::io::ErrorKind::TimedOut { + continue; + } + return Err(anyhow!("Error receiving packet: {}", e)); + } + } + } + } + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn handle_arp_scan( + target_cidrs: Vec, +) -> Result>> { + let listener_out: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); + let target_cidrs = target_cidrs + .iter() + .map(|cidr| { + let (addr, prefix) = cidr.split_at( + cidr.find('/') + .ok_or(anyhow::anyhow!("Failed to find / in Network {}", cidr))?, + ); + let addr = match Ipv4Addr::from_str(addr) { + Ok(addr) => addr, + Err(_) => { + return Err(anyhow::anyhow!("Invalid IPv4 address: {}", addr)); + } + }; + let prefix: u8 = match prefix[1..].parse() { + Ok(prefix) => prefix, + Err(_) => { + return Err(anyhow::anyhow!("Invalid CIDR prefix: {}", prefix)); + } + }; + let network = match Ipv4Network::new(addr, prefix) { + Ok(network) => network, + Err(_) => { + return Err(anyhow::anyhow!("Invalid CIDR: {}", cidr)); + } + }; + Ok(network) + }) + .collect::>>()?; + for target_cidr in target_cidrs { + for ip in target_cidr.iter() { + match listener_out.lock() { + Ok(mut listener_lock) => { + listener_lock.insert(ip, None); + } + Err(err) => return Err(anyhow::anyhow!("Failed to get lock on IP List: {}", err)), + } + } + } + let interfaces = datalink::interfaces(); + for interface in interfaces { + let inner_out = listener_out.clone(); + let inner_interface = interface.clone(); + let thread = std::thread::spawn(move || { + match start_listener(inner_interface.clone(), inner_out) { + Ok(_) => {}, + Err(err) => { + #[cfg(debug_assertions)] + println!("Listener on {} failed: {}", inner_interface.name, err); + } + } + }); + thread.join().map_err(|err| { + anyhow::anyhow!( + "Failed to join thread for interface {}: {:?}", + interface.name, + err + ) + })? + } + let out = listener_out + .lock() + .map_err(|err| anyhow::anyhow!("Failed to get final lock when returning results: {}", err))? + .clone(); + Ok(out) +} + +#[cfg(not(target_os = "windows"))] +pub fn arp_scan(starlark_heap: &Heap, target_cidrs: Vec) -> Result> { + let mut out: Vec = Vec::new(); + let final_listener_output = handle_arp_scan(target_cidrs)?; + for (ipaddr, res) in final_listener_output { + if let Some(res) = res { + let hit_small_map = SmallMap::new(); + let mut hit_dict = Dict::new(hit_small_map); + let ipaddr_value = starlark_heap.alloc_str(&ipaddr.to_string()); + let source_mac_value = starlark_heap.alloc_str(&res.source_mac.to_string()); + let interface_value = starlark_heap.alloc_str(&res.interface.to_string()); + hit_dict.insert_hashed( + const_frozen_string!("ip").to_value().get_hashed()?, + ipaddr_value.to_value(), + ); + hit_dict.insert_hashed( + const_frozen_string!("mac").to_value().get_hashed()?, + source_mac_value.to_value(), + ); + hit_dict.insert_hashed( + const_frozen_string!("interface").to_value().get_hashed()?, + interface_value.to_value(), + ); + out.push(hit_dict); + } + } + Ok(out) +} + +#[cfg(target_os = "windows")] +pub fn arp_scan(starlark_heap: &Heap, target_cidrs: Vec) -> Result> { + Err(anyhow::anyhow!("ARP Scanning is not available on Windows.")) +} + +#[cfg(not(target_os = "windows"))] +#[cfg(test)] +mod tests { + use std::{collections::HashMap, sync::{Mutex, Arc}, net::Ipv4Addr, thread, time::Duration}; + use pnet::datalink::interfaces; + + use crate::pivot::arp_scan_impl::{handle_arp_scan, ArpResponse, start_listener}; + + #[test] + fn test_positive() { + assert_eq!( + handle_arp_scan(Vec::from(["127.0.0.1/32".to_string()])).unwrap(), + HashMap::from([("127.0.0.1".parse().unwrap(), None)]) + ); + } + + #[test] + fn test_no_slash() { + assert!(handle_arp_scan(Vec::from(["127.0.0.1".to_string()])).is_err()); + } + + #[test] + fn test_invalid_ipv4() { + assert!(handle_arp_scan(Vec::from(["127.0.0.256".to_string()])).is_err()); + } + + #[test] + fn test_invalid_cidr() { + assert!(handle_arp_scan(Vec::from(["127.0.0.1/33".to_string()])).is_err()); + } + + #[test] + fn test_lock_failure() { + let data = Arc::from(Mutex::from(HashMap::>::from([(Ipv4Addr::LOCALHOST, None)]))); + let data_clone = data.clone(); + thread::spawn(move || { + let _x = data_clone.lock().unwrap(); + panic!("Need to panic"); + }); + thread::sleep(Duration::from_secs(3)); + let loopback = { + let interfaces = interfaces(); + interfaces.iter().filter(|x| x.is_loopback()).next().unwrap().clone() + }; + assert!( + start_listener(loopback, data).is_err() + ); + } + + +} From e60410686d46ca9a361ce485918034d1d02cc5a6 Mon Sep 17 00:00:00 2001 From: Joe Abbate <40615740+jabbate19@users.noreply.github.com> Date: Mon, 14 Aug 2023 19:16:32 -0700 Subject: [PATCH 04/15] sys.get_pid() (#272) * Implement get_pid * Add newline * Add Sys Entry --- docs/_docs/user-guide/eldritch.md | 11 +++++++++++ implants/lib/eldritch/src/lib.rs | 2 +- implants/lib/eldritch/src/sys.rs | 5 +++++ implants/lib/eldritch/src/sys/get_pid_impl.rs | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 implants/lib/eldritch/src/sys/get_pid_impl.rs diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index c154e5123..0534552f8 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -422,6 +422,17 @@ An example is below: } ``` +### sys.get_pid +`sys.get_pid() -> int` + +The sys.get_pid method returns the process ID of the current process. +An example is below: + +```json +$> sys.get_pid() +123456 +``` + ### sys.get_user `sys.get_user() -> Dict` diff --git a/implants/lib/eldritch/src/lib.rs b/implants/lib/eldritch/src/lib.rs index 97c912dc4..2cb90f272 100644 --- a/implants/lib/eldritch/src/lib.rs +++ b/implants/lib/eldritch/src/lib.rs @@ -188,7 +188,7 @@ mod tests { r#" dir(file) == ["append", "compress", "copy", "download", "exists", "hash", "is_dir", "is_file", "list", "mkdir", "read", "remove", "rename", "replace", "replace_all", "template", "timestomp", "write"] dir(process) == ["kill", "list", "name"] -dir(sys) == ["dll_inject", "exec", "get_ip", "get_os", "get_user", "is_linux", "is_macos", "is_windows", "shell"] +dir(sys) == ["dll_inject", "exec", "get_ip", "get_os", "get_pid", "get_user", "is_linux", "is_macos", "is_windows", "shell"] dir(pivot) == ["arp_scan", "bind_proxy", "ncat", "port_forward", "port_scan", "smb_exec", "ssh_exec", "ssh_password_spray"] dir(assets) == ["copy","list","read","read_binary"] "#, diff --git a/implants/lib/eldritch/src/sys.rs b/implants/lib/eldritch/src/sys.rs index a935f8063..ead391a18 100644 --- a/implants/lib/eldritch/src/sys.rs +++ b/implants/lib/eldritch/src/sys.rs @@ -1,6 +1,7 @@ mod exec_impl; mod get_ip_impl; mod get_os_impl; +mod get_pid_impl; mod get_user_impl; mod is_linux_impl; mod is_windows_impl; @@ -78,6 +79,10 @@ fn methods(builder: &mut MethodsBuilder) { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } get_ip_impl::get_ip(starlark_heap) } + fn get_pid<'v>(this: SysLibrary, starlark_heap: &'v Heap) -> anyhow::Result { + if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } + get_pid_impl::get_pid(starlark_heap) + } fn get_user<'v>(this: SysLibrary, starlark_heap: &'v Heap) -> anyhow::Result> { if false { println!( diff --git a/implants/lib/eldritch/src/sys/get_pid_impl.rs b/implants/lib/eldritch/src/sys/get_pid_impl.rs new file mode 100644 index 000000000..0129c1261 --- /dev/null +++ b/implants/lib/eldritch/src/sys/get_pid_impl.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use std::process; +use starlark::values::Heap; + +pub fn get_pid(starlark_heap: &Heap) -> Result { + Ok(process::id()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sys_get_pid() { + let starlark_heap = Heap::new(); + let res = get_pid(&starlark_heap).unwrap(); + assert_eq!(res, process::id()); + } +} From cd8d9d2e66d77dd60e1f5f406c9ad67ad41bac2b Mon Sep 17 00:00:00 2001 From: Joe Abbate <40615740+jabbate19@users.noreply.github.com> Date: Mon, 14 Aug 2023 20:38:47 -0700 Subject: [PATCH 05/15] Differentiate IP Crate for Windows and Nix (#273) * Differentiate for Windows and Nix * Conditional Deps * Fix Cargo order --- implants/lib/eldritch/Cargo.toml | 8 +++-- implants/lib/eldritch/src/sys/get_ip_impl.rs | 34 +++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/implants/lib/eldritch/Cargo.toml b/implants/lib/eldritch/Cargo.toml index 786077418..3567aadc2 100644 --- a/implants/lib/eldritch/Cargo.toml +++ b/implants/lib/eldritch/Cargo.toml @@ -15,9 +15,7 @@ eval = { workspace = true } flate2 = { workspace = true } gazebo = { workspace = true } ipnetwork = { workspace = true } -network-interface = { workspace = true } nix = { workspace = true } -pnet = { workspace = true } regex = { workspace = true } reqwest = { workspace = true , features = ["blocking", "stream"] } russh = { workspace = true } @@ -43,5 +41,11 @@ windows-sys = { workspace = true, features = [ ]} whoami = { workspace = true } +[target.'cfg(windows)'.dependencies] +network-interface = { workspace = true } + +[target.'cfg(not(windows))'.dependencies] +pnet = { workspace = true } + [dev-dependencies] httptest = { workspace = true } diff --git a/implants/lib/eldritch/src/sys/get_ip_impl.rs b/implants/lib/eldritch/src/sys/get_ip_impl.rs index b1621270d..d054c4e06 100644 --- a/implants/lib/eldritch/src/sys/get_ip_impl.rs +++ b/implants/lib/eldritch/src/sys/get_ip_impl.rs @@ -1,16 +1,21 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; +#[cfg(target_os = "windows")] use network_interface::{NetworkInterfaceConfig, NetworkInterface}; +#[cfg(not(target_os = "windows"))] +use pnet::datalink::{interfaces, NetworkInterface}; use starlark::{values::{dict::Dict, Heap, Value}, collections::SmallMap, const_frozen_string}; const UNKNOWN: &str = "UNKNOWN"; #[derive(Debug)] +#[cfg(target_os = "windows")] struct NetInterface { name: String, ips: Vec, //IPv6 and IPv4 Addresses on the itnerface mac: String, } +#[cfg(target_os = "windows")] fn handle_get_ip() -> Result> { let mut res = Vec::new(); for network_interface in NetworkInterface::show()? { @@ -34,6 +39,12 @@ fn handle_get_ip() -> Result> { Ok(res) } +#[cfg(not(target_os = "windows"))] +fn handle_get_ip() -> Result> { + Ok(interfaces()) +} + +#[cfg(target_os = "windows")] fn create_dict_from_interface(starlark_heap: &Heap, interface: NetInterface) -> Result{ let res: SmallMap = SmallMap::new(); let mut tmp_res = Dict::new(res); @@ -55,6 +66,27 @@ fn create_dict_from_interface(starlark_heap: &Heap, interface: NetInterface) -> Ok(tmp_res) } +#[cfg(not(target_os = "windows"))] +fn create_dict_from_interface(starlark_heap: &Heap, interface: NetworkInterface) -> Result{ + let res: SmallMap = SmallMap::new(); + let mut tmp_res = Dict::new(res); + + let tmp_value1 = starlark_heap.alloc_str(&interface.name); + tmp_res.insert_hashed(const_frozen_string!("name").to_value().get_hashed().unwrap(), tmp_value1.to_value()); + + let mut tmp_value2_arr = Vec::::new(); + for ip in interface.ips { + tmp_value2_arr.push(starlark_heap.alloc_str(&ip.to_string()).to_value()); + } + let tmp_value2 = starlark_heap.alloc(tmp_value2_arr); + tmp_res.insert_hashed(const_frozen_string!("ips").to_value().get_hashed().unwrap(), tmp_value2); + + let tmp_value3 = starlark_heap.alloc_str(&interface.mac.map(|mac| mac.to_string()).unwrap_or(UNKNOWN.to_string())); + tmp_res.insert_hashed(const_frozen_string!("mac").to_value().get_hashed().unwrap(), tmp_value3.to_value()); + + + Ok(tmp_res) +} pub fn get_ip(starlark_heap: &Heap) -> Result> { let mut final_res: Vec = Vec::new(); From 1543bc6dae6e6c64402138abd42b506e2ec1b17a Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Tue, 15 Aug 2023 20:03:17 -0400 Subject: [PATCH 06/15] Updated reqwest. (#249) --- implants/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implants/Cargo.toml b/implants/Cargo.toml index 1d3dd33dd..86890cfd8 100644 --- a/implants/Cargo.toml +++ b/implants/Cargo.toml @@ -33,7 +33,7 @@ pnet = "0.34.0" predicates = "2.1" rand = "0.8.5" regex = "1.5.5" -reqwest = "0.11.4" +reqwest = "0.11.18" russh = "0.37.1" russh-keys = "0.37.1" rust-embed = "6.6.0" From 4cc8bf22b5af31d8ae1cb3fe2edd680f3728ec35 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Tue, 15 Aug 2023 21:09:27 -0400 Subject: [PATCH 07/15] Upgrade openssl (#248) * Upgrade openssl * Update Cargo.toml Ooop didn't mean to nuke that. --- implants/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implants/Cargo.toml b/implants/Cargo.toml index 86890cfd8..9717916f3 100644 --- a/implants/Cargo.toml +++ b/implants/Cargo.toml @@ -28,7 +28,7 @@ itertools = "0.10" lsp-types = "0.93.0" network-interface = "1.0.1" nix = "0.26.1" -openssl = "0.10" +openssl = "0.10.55" pnet = "0.34.0" predicates = "2.1" rand = "0.8.5" @@ -62,4 +62,4 @@ strip = true # Automatically strip symbols from the binary. opt-level = "z" # Optimize for size. lto = true codegen-units = 1 -panic = "abort" \ No newline at end of file +panic = "abort" From c682ff9ef5aac61f1ea104a442cdebf2f1b5f28e Mon Sep 17 00:00:00 2001 From: Joe Abbate <40615740+jabbate19@users.noreply.github.com> Date: Tue, 15 Aug 2023 20:45:25 -0700 Subject: [PATCH 08/15] sys.get_env() (#274) * Add sys.get_env() * I can't do alphabetical order --- docs/_docs/user-guide/eldritch.md | 13 ++++++ implants/lib/eldritch/src/lib.rs | 2 +- implants/lib/eldritch/src/sys.rs | 5 +++ implants/lib/eldritch/src/sys/get_env_impl.rs | 42 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 implants/lib/eldritch/src/sys/get_env_impl.rs diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 0534552f8..7e0c58268 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -383,6 +383,19 @@ sys.execute("/bin/bash",["-c", "ls /nofile"]) } ``` +### sys.get_env +`sys.get_env() -> Dict` + +The sys.get_env method returns a dictionary that describes the current process's environment variables. +An example is below: + +```json +{ + "FOO": "BAR", + "CWD": "/" +} +``` + ### sys.get_ip `sys.get_ip() -> List` diff --git a/implants/lib/eldritch/src/lib.rs b/implants/lib/eldritch/src/lib.rs index 2cb90f272..b268e8af9 100644 --- a/implants/lib/eldritch/src/lib.rs +++ b/implants/lib/eldritch/src/lib.rs @@ -188,7 +188,7 @@ mod tests { r#" dir(file) == ["append", "compress", "copy", "download", "exists", "hash", "is_dir", "is_file", "list", "mkdir", "read", "remove", "rename", "replace", "replace_all", "template", "timestomp", "write"] dir(process) == ["kill", "list", "name"] -dir(sys) == ["dll_inject", "exec", "get_ip", "get_os", "get_pid", "get_user", "is_linux", "is_macos", "is_windows", "shell"] +dir(sys) == ["dll_inject", "exec", "get_env", "get_ip", "get_os", "get_pid", "get_user", "is_linux", "is_macos", "is_windows", "shell"] dir(pivot) == ["arp_scan", "bind_proxy", "ncat", "port_forward", "port_scan", "smb_exec", "ssh_exec", "ssh_password_spray"] dir(assets) == ["copy","list","read","read_binary"] "#, diff --git a/implants/lib/eldritch/src/sys.rs b/implants/lib/eldritch/src/sys.rs index ead391a18..d28524ba5 100644 --- a/implants/lib/eldritch/src/sys.rs +++ b/implants/lib/eldritch/src/sys.rs @@ -1,4 +1,5 @@ mod exec_impl; +mod get_env_impl; mod get_ip_impl; mod get_os_impl; mod get_pid_impl; @@ -75,6 +76,10 @@ fn methods(builder: &mut MethodsBuilder) { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } dll_inject_impl::dll_inject(dll_path, pid) } + fn get_env<'v>(this: SysLibrary, starlark_heap: &'v Heap) -> anyhow::Result> { + if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } + get_env_impl::get_env(starlark_heap) + } fn get_ip<'v>(this: SysLibrary, starlark_heap: &'v Heap) -> anyhow::Result>> { if false { println!("Ignore unused this var. _this isn't allowed by starlark. {:?}", this); } get_ip_impl::get_ip(starlark_heap) diff --git a/implants/lib/eldritch/src/sys/get_env_impl.rs b/implants/lib/eldritch/src/sys/get_env_impl.rs new file mode 100644 index 000000000..295b509c2 --- /dev/null +++ b/implants/lib/eldritch/src/sys/get_env_impl.rs @@ -0,0 +1,42 @@ +use anyhow::Result; +use starlark::collections::SmallMap; +use starlark::values::dict::Dict; +use starlark::values::Heap; +use std::env; + +pub fn get_env(starlark_heap: &Heap) -> Result { + let res = SmallMap::new(); + let mut dict_res = Dict::new(res); + + for (key, val) in env::vars() { + let key_value = starlark_heap.alloc_str(&key); + let val_value = starlark_heap.alloc_str(&val); + dict_res.insert_hashed( + match key_value.to_value().get_hashed() { + Ok(val) => val, + Err(e) => return Err(anyhow::anyhow!("Failed to alloc name information: {}", e)), + }, + val_value.to_value(), + ); + } + + Ok(dict_res) +} + +#[cfg(test)] +mod tests { + use starlark::{values::Value, const_frozen_string}; + use anyhow::Result; + use std::env; + use super::*; + + #[test] + fn test_get_env() -> Result<()> { + env::set_var("FOO", "BAR"); + let test_heap = Heap::new(); + let res = get_env(&test_heap)?; + let val: Value<'_> = res.get(const_frozen_string!("FOO").to_value())?.unwrap(); + assert_eq!(val.unpack_str().unwrap(), "BAR"); + Ok(()) + } +} From da8db27c9e4840165c7934731b0cdf20c7ecca76 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Thu, 31 Aug 2023 00:26:40 -0400 Subject: [PATCH 09/15] I think this fixes json formatting. (#278) --- docs/_docs/user-guide/eldritch.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 7e0c58268..1d2e73f3e 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -143,7 +143,7 @@ The file.list method returns a list of files at the specified path. The p Each file is represented by a Dict type. Here is an example of the Dict layout: -```JSON +```json [ { "file_name": "implants", @@ -236,15 +236,20 @@ The pivot.arp_scan method is being proposed to allow users to enumerate h Results will be in the format: -```JSON -// Successful +```python $> pivot.arp_scan(["192.168.1.1/32"]) +``` +**Success** + +```json [ { "ip": "192.168.1.1", "mac": "ab:cd:ef:01:23:45", "interface": "eno0" } ] +``` -// Failure -$> pivot.arp_scan(["192.168.1.1/32"]) +**Failure** + +```json [] ``` @@ -272,7 +277,7 @@ Inputs: Results will be in the format: -```JSON +```json [ { "ip": "127.0.0.1", "port": 22, "protocol": "tcp", "status": "open"}, { "ip": "127.0.0.1", "port": 21, "protocol": "tcp", "status": "closed"}, @@ -401,7 +406,7 @@ An example is below: The sys.get_ip method returns a list of network interfaces as a dictionary. An example is available below: -```JSON +```json [ { "name": "eth0", @@ -441,7 +446,7 @@ An example is below: The sys.get_pid method returns the process ID of the current process. An example is below: -```json +```python $> sys.get_pid() 123456 ``` From 79dbc9b1dec3f73639d56d6bf625b22122edbb3e Mon Sep 17 00:00:00 2001 From: KCarretto Date: Tue, 5 Sep 2023 21:27:39 -0400 Subject: [PATCH 10/15] Embed UI & Login Redirects (#280) --- .gitignore | 1 - tavern/app.go | 73 +++++++-------- tavern/auth/middleware.go | 12 +++ tavern/internal/www/README.md | 4 + tavern/internal/www/build/asset-manifest.json | 16 ++++ tavern/internal/www/build/embed.go | 9 ++ tavern/internal/www/build/favicon.ico | Bin 0 -> 3870 bytes tavern/internal/www/build/index.html | 1 + tavern/internal/www/build/logo192.png | 3 + tavern/internal/www/build/logo512.png | 3 + tavern/internal/www/build/manifest.json | 25 ++++++ tavern/internal/www/build/robots.txt | 3 + .../www/build/static/css/main.ed062423.css | 4 + .../build/static/css/main.ed062423.css.map | 1 + .../www/build/static/js/787.4af0fb89.chunk.js | 2 + .../build/static/js/787.4af0fb89.chunk.js.map | 1 + .../www/build/static/js/main.1ffa92c8.js | 3 + .../static/js/main.1ffa92c8.js.LICENSE.txt | 83 ++++++++++++++++++ .../www/build/static/js/main.1ffa92c8.js.map | 1 + .../media/eldrich.a80c74e8249d2461e174.png | 3 + tavern/internal/www/http.go | 12 +++ 21 files changed, 224 insertions(+), 36 deletions(-) create mode 100644 tavern/internal/www/build/asset-manifest.json create mode 100644 tavern/internal/www/build/embed.go create mode 100644 tavern/internal/www/build/favicon.ico create mode 100644 tavern/internal/www/build/index.html create mode 100644 tavern/internal/www/build/logo192.png create mode 100644 tavern/internal/www/build/logo512.png create mode 100644 tavern/internal/www/build/manifest.json create mode 100644 tavern/internal/www/build/robots.txt create mode 100644 tavern/internal/www/build/static/css/main.ed062423.css create mode 100644 tavern/internal/www/build/static/css/main.ed062423.css.map create mode 100644 tavern/internal/www/build/static/js/787.4af0fb89.chunk.js create mode 100644 tavern/internal/www/build/static/js/787.4af0fb89.chunk.js.map create mode 100644 tavern/internal/www/build/static/js/main.1ffa92c8.js create mode 100644 tavern/internal/www/build/static/js/main.1ffa92c8.js.LICENSE.txt create mode 100644 tavern/internal/www/build/static/js/main.1ffa92c8.js.map create mode 100644 tavern/internal/www/build/static/media/eldrich.a80c74e8249d2461e174.png create mode 100644 tavern/internal/www/http.go diff --git a/.gitignore b/.gitignore index de3ebd698..102ebf495 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ cmd/implants/Cargo.lock # Build artifacts dist/** -build/** # Binaries for programs and plugins *.exe diff --git a/tavern/app.go b/tavern/app.go index 5114a2c1f..a3be659c9 100644 --- a/tavern/app.go +++ b/tavern/app.go @@ -10,17 +10,18 @@ import ( "net/http" "os" + "entgo.io/contrib/entgql" + "github.com/kcarretto/realm/tavern/graphql" "github.com/kcarretto/realm/tavern/tomes" - "entgo.io/contrib/entgql" gqlgraphql "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" "github.com/kcarretto/realm/tavern/auth" "github.com/kcarretto/realm/tavern/ent" "github.com/kcarretto/realm/tavern/ent/migrate" - "github.com/kcarretto/realm/tavern/graphql" "github.com/kcarretto/realm/tavern/internal/cdn" + "github.com/kcarretto/realm/tavern/internal/www" "github.com/urfave/cli" ) @@ -105,45 +106,16 @@ func NewServer(ctx context.Context, options ...func(*Config)) (*Server, error) { createTestData(ctx, client) } - // Create GraphQL Handler - srv := handler.NewDefaultServer(graphql.NewSchema(client)) - srv.Use(entgql.Transactioner{TxOpener: client}) - - // GraphQL Logging - gqlLogger := log.New(os.Stderr, "[GraphQL] ", log.Flags()) - srv.AroundOperations(func(ctx context.Context, next gqlgraphql.OperationHandler) gqlgraphql.ResponseHandler { - oc := gqlgraphql.GetOperationContext(ctx) - reqVars, err := json.Marshal(oc.Variables) - if err != nil { - gqlLogger.Printf("[ERROR] failed to marshal variables to JSON: %v", err) - return next(ctx) - } - - authName := "unknown" - id := auth.IdentityFromContext(ctx) - if id != nil { - authName = id.String() - } - - gqlLogger.Printf("%s (%s): %s", oc.OperationName, authName, string(reqVars)) - return next(ctx) - }) - - // Setup HTTP Handler + // Setup HTTP Handlers router := http.NewServeMux() router.Handle("/status", newStatusHandler()) - router.Handle("/", - playground.Handler("Tavern", "/graphql"), - ) - router.Handle("/graphql", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "*") - srv.ServeHTTP(w, req) - })) router.Handle("/oauth/login", auth.NewOAuthLoginHandler(cfg.oauth, privKey)) router.Handle("/oauth/authorize", auth.NewOAuthAuthorizationHandler(cfg.oauth, pubKey, client, "https://www.googleapis.com/oauth2/v3/userinfo")) + router.Handle("/graphql", newGraphQLHandler(client)) router.Handle("/cdn/", cdn.NewDownloadHandler(client)) router.Handle("/cdn/upload", cdn.NewUploadHandler(client)) + router.Handle("/", auth.WithLoginRedirect("/oauth/login", www.NewAppHandler())) + router.Handle("/playground", auth.WithLoginRedirect("/oauth/login", playground.Handler("Tavern", "/graphql"))) // Log Middleware httpLogger := log.New(os.Stderr, "[HTTP] ", log.Flags()) @@ -181,3 +153,34 @@ func NewServer(ctx context.Context, options ...func(*Config)) (*Server, error) { client: client, }, nil } + +func newGraphQLHandler(client *ent.Client) http.Handler { + srv := handler.NewDefaultServer(graphql.NewSchema(client)) + srv.Use(entgql.Transactioner{TxOpener: client}) + + // GraphQL Logging + gqlLogger := log.New(os.Stderr, "[GraphQL] ", log.Flags()) + srv.AroundOperations(func(ctx context.Context, next gqlgraphql.OperationHandler) gqlgraphql.ResponseHandler { + oc := gqlgraphql.GetOperationContext(ctx) + reqVars, err := json.Marshal(oc.Variables) + if err != nil { + gqlLogger.Printf("[ERROR] failed to marshal variables to JSON: %v", err) + return next(ctx) + } + + authName := "unknown" + id := auth.IdentityFromContext(ctx) + if id != nil { + authName = id.String() + } + + gqlLogger.Printf("%s (%s): %s", oc.OperationName, authName, string(reqVars)) + return next(ctx) + }) + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "*") + srv.ServeHTTP(w, req) + }) +} diff --git a/tavern/auth/middleware.go b/tavern/auth/middleware.go index 5080efc73..415c18bbe 100644 --- a/tavern/auth/middleware.go +++ b/tavern/auth/middleware.go @@ -82,3 +82,15 @@ func Middleware(handler http.Handler, graph *ent.Client) http.HandlerFunc { handler.ServeHTTP(w, r) }) } + +// WithLoginRedirect will redirect requests for the provided handler to the provided redirect URL if the request is unauthenticated. +func WithLoginRedirect(redirect string, handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if !IsAuthenticatedContext(req.Context()) { + http.Redirect(w, req, redirect, http.StatusFound) + return + } + handler.ServeHTTP(w, req) + }) + +} diff --git a/tavern/internal/www/README.md b/tavern/internal/www/README.md index c6617f437..f42ec4b56 100644 --- a/tavern/internal/www/README.md +++ b/tavern/internal/www/README.md @@ -16,3 +16,7 @@ _If this is your first time contributing WWW changes in this dev environment, re 2. Run `go run ./tavern` to start the teamserver (for the GraphQL API) run in the project root * Note: to run the teamserver with test data (useful for UI development), run `ENABLE_TEST_DATA=1 go run ./tavern` instead 3. In a separate terminal, navigate to the [UI Root /cmd/tavern/internal/www](https://github.com/KCarretto/realm/tree/main/cmd/tavern/internal/www) and run `npm start` + +## Building the Application + +When you're ready to include changes to the UI in the tavern binary, you'll need to run `npm build` to update files in the `build/` directory. These files will automatically be bundled into new compliations of the tavern binary. diff --git a/tavern/internal/www/build/asset-manifest.json b/tavern/internal/www/build/asset-manifest.json new file mode 100644 index 000000000..a1608f660 --- /dev/null +++ b/tavern/internal/www/build/asset-manifest.json @@ -0,0 +1,16 @@ +{ + "files": { + "main.css": "/static/css/main.ed062423.css", + "main.js": "/static/js/main.1ffa92c8.js", + "static/js/787.4af0fb89.chunk.js": "/static/js/787.4af0fb89.chunk.js", + "static/media/eldrich.png": "/static/media/eldrich.a80c74e8249d2461e174.png", + "index.html": "/index.html", + "main.ed062423.css.map": "/static/css/main.ed062423.css.map", + "main.1ffa92c8.js.map": "/static/js/main.1ffa92c8.js.map", + "787.4af0fb89.chunk.js.map": "/static/js/787.4af0fb89.chunk.js.map" + }, + "entrypoints": [ + "static/css/main.ed062423.css", + "static/js/main.1ffa92c8.js" + ] +} \ No newline at end of file diff --git a/tavern/internal/www/build/embed.go b/tavern/internal/www/build/embed.go new file mode 100644 index 000000000..d515f5c9c --- /dev/null +++ b/tavern/internal/www/build/embed.go @@ -0,0 +1,9 @@ +package build + +import "embed" + +// Content embedded from the application's build directory, includes the latest build of the UI. +// +//go:embed *.png *.html *.json *.txt *.ico +//go:embed static/* +var Content embed.FS diff --git a/tavern/internal/www/build/favicon.ico b/tavern/internal/www/build/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/tavern/internal/www/build/index.html b/tavern/internal/www/build/index.html new file mode 100644 index 000000000..e6958d3c7 --- /dev/null +++ b/tavern/internal/www/build/index.html @@ -0,0 +1 @@ +React App
\ No newline at end of file diff --git a/tavern/internal/www/build/logo192.png b/tavern/internal/www/build/logo192.png new file mode 100644 index 000000000..4652f3695 --- /dev/null +++ b/tavern/internal/www/build/logo192.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c386396ec70db3608075b5fbfaac4ab1ccaa86ba05a68ab393ec551eb66c3e00 +size 5347 diff --git a/tavern/internal/www/build/logo512.png b/tavern/internal/www/build/logo512.png new file mode 100644 index 000000000..ba6e512c9 --- /dev/null +++ b/tavern/internal/www/build/logo512.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ea4f4da7050c0cc408926f6a39c253624e9babb1d43c7977cd821445a60b461 +size 9664 diff --git a/tavern/internal/www/build/manifest.json b/tavern/internal/www/build/manifest.json new file mode 100644 index 000000000..080d6c77a --- /dev/null +++ b/tavern/internal/www/build/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/tavern/internal/www/build/robots.txt b/tavern/internal/www/build/robots.txt new file mode 100644 index 000000000..e9e57dc4d --- /dev/null +++ b/tavern/internal/www/build/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/tavern/internal/www/build/static/css/main.ed062423.css b/tavern/internal/www/build/static/css/main.ed062423.css new file mode 100644 index 000000000..a02ca098c --- /dev/null +++ b/tavern/internal/www/build/static/css/main.ed062423.css @@ -0,0 +1,4 @@ +/* +! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com +*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;-webkit-font-feature-settings:normal;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{-webkit-font-feature-settings:inherit;font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.btn-primary{--tw-bg-opacity:1;--tw-text-opacity:1;--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);align-items:center;background-color:rgb(126 34 206/var(--tw-bg-opacity));border-radius:.375rem;box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);color:rgb(255 255 255/var(--tw-text-opacity));display:inline-flex;font-size:.875rem;font-weight:600;line-height:1.25rem;padding:.75rem 1rem}.btn-primary:focus-visible{outline-color:#7e22ce;outline-offset:2px;outline-style:solid;outline-width:2px}.btn-primary:hover:enabled{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity))}.btn-primary:disabled{cursor:not-allowed;opacity:.5}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:-webkit-sticky;position:sticky}.inset-0{inset:0}.left-4{left:1rem}.left-full{left:100%}.top-0{top:0}.top-4{top:1rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.col-span-3{grid-column:span 3/span 3}.col-span-9{grid-column:span 9/span 9}.-m-2{margin:-.5rem}.-m-2\.5{margin:-.625rem}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.-ml-0{margin-left:0}.-ml-0\.5{margin-left:-.125rem}.-ml-px{margin-left:-1px}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-16{margin-right:4rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.w-0{width:0}.w-0\.5{width:.125rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-auto{width:auto}.w-full{width:100%}.min-w-0{min-width:0}.max-w-xs{max-width:20rem}.flex-1{flex:1 1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.translate-x-0{-webkit-transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px}.transform{-webkit-transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.gap-x-3{-webkit-column-gap:.75rem;column-gap:.75rem}.gap-x-6{-webkit-column-gap:1.5rem;column-gap:1.5rem}.gap-y-5{row-gap:1.25rem}.gap-y-7{row-gap:1.75rem}.-space-y-px>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(-1px*var(--tw-space-y-reverse));margin-top:calc(-1px*(1 - var(--tw-space-y-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.self-end{align-self:flex-end}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:9999px}.rounded-md{border-radius:.375rem}.rounded-bl-md{border-bottom-left-radius:.375rem}.rounded-br-md{border-bottom-right-radius:.375rem}.rounded-tl-md{border-top-left-radius:.375rem}.rounded-tr-md{border-top-right-radius:.375rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-purple-200{--tw-border-opacity:1;border-color:rgb(233 213 255/var(--tw-border-opacity))}.border-purple-700{--tw-border-opacity:1;border-color:rgb(126 34 206/var(--tw-border-opacity))}.border-transparent{border-color:transparent}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-gray-900\/80{background-color:rgba(17,24,39,.8)}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity))}.bg-purple-700{--tw-bg-opacity:1;background-color:rgb(126 34 206/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-4{padding:1rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-10{padding-bottom:2.5rem;padding-top:2.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.pb-10{padding-bottom:2.5rem}.pb-2{padding-bottom:.5rem}.pb-5{padding-bottom:1.25rem}.pb-6{padding-bottom:1.5rem}.pt-1{padding-top:.25rem}.pt-5{padding-top:1.25rem}.text-right{text-align:right}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-6{line-height:1.5rem}.tracking-wide{letter-spacing:.025em}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity))}.text-purple-900{--tw-text-opacity:1;color:rgb(88 28 135/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.placeholder-gray-500::-webkit-input-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity))}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-1,.ring-2{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 #0000;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-inset{--tw-ring-inset:inset}.ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity))}.ring-purple-700{--tw-ring-opacity:1;--tw-ring-color:rgb(126 34 206/var(--tw-ring-opacity))}.ring-white\/10{--tw-ring-color:hsla(0,0%,100%,.1)}.ring-offset-2{--tw-ring-offset-width:2px}.filter{-webkit-filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,-webkit-transform,-webkit-filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-transform,-webkit-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-linear{transition-timing-function:linear}.md-scroll-container,.option-container{background-color:#edf2f7;width:100%}.md-scroll-container{height:400px;overflow-y:scroll}.max-width{width:100%}.placeholder\:text-gray-400::-webkit-input-placeholder{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.placeholder\:text-gray-400::placeholder{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.hover\:bg-purple-100:hover{--tw-bg-opacity:1;background-color:rgb(243 232 255/var(--tw-bg-opacity))}.hover\:bg-purple-600:hover{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 #0000;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-inset:focus{--tw-ring-inset:inset}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.focus-visible\:outline:focus-visible{outline-style:solid}.focus-visible\:outline-2:focus-visible{outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-gray-300:focus-visible{outline-color:#d1d5db}.focus-visible\:outline-purple-700:focus-visible{outline-color:#7e22ce}.group:hover .group-hover\:bg-purple-800{--tw-bg-opacity:1;background-color:rgb(107 33 168/var(--tw-bg-opacity))}@media (min-width:640px){.sm\:ml-4{margin-left:1rem}.sm\:mt-0{margin-top:0}.sm\:flex{display:flex}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-1{padding-bottom:.25rem;padding-top:.25rem}.sm\:py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:leading-6{line-height:1.5rem}}@media (min-width:1024px){.lg\:fixed{position:fixed}.lg\:inset-y-0{bottom:0;top:0}.lg\:z-50{z-index:50}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-72{width:18rem}.lg\:flex-col{flex-direction:column}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:pl-72{padding-left:18rem}}.ReactVirtualized__Table__headerRow{font-weight:700;text-transform:uppercase}.ReactVirtualized__Table__headerRow,.ReactVirtualized__Table__row{align-items:center;display:flex;flex-direction:row}.ReactVirtualized__Table__headerTruncatedText{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ReactVirtualized__Table__headerColumn,.ReactVirtualized__Table__rowColumn{margin-right:10px;min-width:0}.ReactVirtualized__Table__rowColumn{text-overflow:ellipsis;white-space:nowrap}.ReactVirtualized__Table__headerColumn:first-of-type,.ReactVirtualized__Table__rowColumn:first-of-type{margin-left:10px}.ReactVirtualized__Table__sortableHeaderColumn{cursor:pointer}.ReactVirtualized__Table__sortableHeaderIconContainer{align-items:center;display:flex}.ReactVirtualized__Table__sortableHeaderIcon{fill:currentColor;flex:0 0 24px;height:1em;width:1em} +/*# sourceMappingURL=main.ed062423.css.map*/ \ No newline at end of file diff --git a/tavern/internal/www/build/static/css/main.ed062423.css.map b/tavern/internal/www/build/static/css/main.ed062423.css.map new file mode 100644 index 000000000..e24a812ec --- /dev/null +++ b/tavern/internal/www/build/static/css/main.ed062423.css.map @@ -0,0 +1 @@ +{"version":3,"file":"static/css/main.ed062423.css","mappings":"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","sources":["style.css","../node_modules/react-virtualized/source/styles.css"],"sourcesContent":["@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n.option-container{\n width: 100%;\n background-color: #EDF2F7;\n}\n.md-scroll-container{\n width: 100%;\n height: 400px;\n overflow-y: scroll;\n background-color: #EDF2F7;\n}\n.max-width{\n width: 100%;\n}\n\n@layer components {\n .btn-primary {\n @apply inline-flex items-center rounded-md bg-purple-700 px-4 py-3 text-sm font-semibold text-white shadow-sm enabled:hover:bg-purple-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-purple-700 disabled:opacity-50 disabled:cursor-not-allowed;\n }\n }","/* Collection default theme */\n\n.ReactVirtualized__Collection {\n}\n\n.ReactVirtualized__Collection__innerScrollContainer {\n}\n\n/* Grid default theme */\n\n.ReactVirtualized__Grid {\n}\n\n.ReactVirtualized__Grid__innerScrollContainer {\n}\n\n/* Table default theme */\n\n.ReactVirtualized__Table {\n}\n\n.ReactVirtualized__Table__Grid {\n}\n\n.ReactVirtualized__Table__headerRow {\n font-weight: 700;\n text-transform: uppercase;\n display: flex;\n flex-direction: row;\n align-items: center;\n}\n.ReactVirtualized__Table__row {\n display: flex;\n flex-direction: row;\n align-items: center;\n}\n\n.ReactVirtualized__Table__headerTruncatedText {\n display: inline-block;\n max-width: 100%;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n.ReactVirtualized__Table__headerColumn,\n.ReactVirtualized__Table__rowColumn {\n margin-right: 10px;\n min-width: 0px;\n}\n.ReactVirtualized__Table__rowColumn {\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.ReactVirtualized__Table__headerColumn:first-of-type,\n.ReactVirtualized__Table__rowColumn:first-of-type {\n margin-left: 10px;\n}\n.ReactVirtualized__Table__sortableHeaderColumn {\n cursor: pointer;\n}\n\n.ReactVirtualized__Table__sortableHeaderIconContainer {\n display: flex;\n align-items: center;\n}\n.ReactVirtualized__Table__sortableHeaderIcon {\n flex: 0 0 24px;\n height: 1em;\n width: 1em;\n fill: currentColor;\n}\n\n/* List default theme */\n\n.ReactVirtualized__List {\n}\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/tavern/internal/www/build/static/js/787.4af0fb89.chunk.js b/tavern/internal/www/build/static/js/787.4af0fb89.chunk.js new file mode 100644 index 000000000..ea101a24b --- /dev/null +++ b/tavern/internal/www/build/static/js/787.4af0fb89.chunk.js @@ -0,0 +1,2 @@ +"use strict";(self.webpackChunkwww=self.webpackChunkwww||[]).push([[787],{787:function(e,t,n){n.r(t),n.d(t,{getCLS:function(){return w},getFCP:function(){return g},getFID:function(){return C},getLCP:function(){return P},getTTFB:function(){return D}});var i,r,a,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v2-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},f=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},s=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},m=function(e,t,n){var i;return function(r){t.value>=0&&(r||n)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},v=-1,p=function(){return"hidden"===document.visibilityState?0:1/0},d=function(){f((function(e){var t=e.timeStamp;v=t}),!0)},l=function(){return v<0&&(v=p(),d(),s((function(){setTimeout((function(){v=p(),d()}),0)}))),{get firstHiddenTime(){return v}}},g=function(e,t){var n,i=l(),r=u("FCP"),a=function(e){"first-contentful-paint"===e.name&&(f&&f.disconnect(),e.startTime-1&&e(t)},r=u("CLS",0),a=0,o=[],v=function(e){if(!e.hadRecentInput){var t=o[0],i=o[o.length-1];a&&e.startTime-i.startTime<1e3&&e.startTime-t.startTime<5e3?(a+=e.value,o.push(e)):(a=e.value,o=[e]),a>r.value&&(r.value=a,r.entries=o,n())}},p=c("layout-shift",v);p&&(n=m(i,r,t),f((function(){p.takeRecords().map(v),n(!0)})),s((function(){a=0,T=-1,r=u("CLS",0),n=m(i,r,t)})))},y={passive:!0,capture:!0},E=new Date,L=function(e,t){i||(i=t,r=e,a=new Date,F(removeEventListener),S())},S=function(){if(r>=0&&r1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){L(e,t),r()},i=function(){r()},r=function(){removeEventListener("pointerup",n,y),removeEventListener("pointercancel",i,y)};addEventListener("pointerup",n,y),addEventListener("pointercancel",i,y)}(t,e):L(t,e)}},F=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,b,y)}))},C=function(e,t){var n,a=l(),v=u("FID"),p=function(e){e.startTimeperformance.now())return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("load",(function(){return setTimeout(t,0)}))}}}]); +//# sourceMappingURL=787.4af0fb89.chunk.js.map \ No newline at end of file diff --git a/tavern/internal/www/build/static/js/787.4af0fb89.chunk.js.map b/tavern/internal/www/build/static/js/787.4af0fb89.chunk.js.map new file mode 100644 index 000000000..51d51e083 --- /dev/null +++ b/tavern/internal/www/build/static/js/787.4af0fb89.chunk.js.map @@ -0,0 +1 @@ +{"version":3,"file":"static/js/787.4af0fb89.chunk.js","mappings":"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","sources":["../node_modules/web-vitals/dist/web-vitals.js"],"sourcesContent":["var e,t,n,i,r=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:\"v2-\".concat(Date.now(),\"-\").concat(Math.floor(8999999999999*Math.random())+1e12)}},a=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if(\"first-input\"===e&&!(\"PerformanceEventTiming\"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},o=function(e,t){var n=function n(i){\"pagehide\"!==i.type&&\"hidden\"!==document.visibilityState||(e(i),t&&(removeEventListener(\"visibilitychange\",n,!0),removeEventListener(\"pagehide\",n,!0)))};addEventListener(\"visibilitychange\",n,!0),addEventListener(\"pagehide\",n,!0)},u=function(e){addEventListener(\"pageshow\",(function(t){t.persisted&&e(t)}),!0)},c=function(e,t,n){var i;return function(r){t.value>=0&&(r||n)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},f=-1,s=function(){return\"hidden\"===document.visibilityState?0:1/0},m=function(){o((function(e){var t=e.timeStamp;f=t}),!0)},v=function(){return f<0&&(f=s(),m(),u((function(){setTimeout((function(){f=s(),m()}),0)}))),{get firstHiddenTime(){return f}}},d=function(e,t){var n,i=v(),o=r(\"FCP\"),f=function(e){\"first-contentful-paint\"===e.name&&(m&&m.disconnect(),e.startTime-1&&e(t)},f=r(\"CLS\",0),s=0,m=[],v=function(e){if(!e.hadRecentInput){var t=m[0],i=m[m.length-1];s&&e.startTime-i.startTime<1e3&&e.startTime-t.startTime<5e3?(s+=e.value,m.push(e)):(s=e.value,m=[e]),s>f.value&&(f.value=s,f.entries=m,n())}},h=a(\"layout-shift\",v);h&&(n=c(i,f,t),o((function(){h.takeRecords().map(v),n(!0)})),u((function(){s=0,l=-1,f=r(\"CLS\",0),n=c(i,f,t)})))},T={passive:!0,capture:!0},y=new Date,g=function(i,r){e||(e=r,t=i,n=new Date,w(removeEventListener),E())},E=function(){if(t>=0&&t1e12?new Date:performance.now())-e.timeStamp;\"pointerdown\"==e.type?function(e,t){var n=function(){g(e,t),r()},i=function(){r()},r=function(){removeEventListener(\"pointerup\",n,T),removeEventListener(\"pointercancel\",i,T)};addEventListener(\"pointerup\",n,T),addEventListener(\"pointercancel\",i,T)}(t,e):g(t,e)}},w=function(e){[\"mousedown\",\"keydown\",\"touchstart\",\"pointerdown\"].forEach((function(t){return e(t,S,T)}))},L=function(n,f){var s,m=v(),d=r(\"FID\"),p=function(e){e.startTimeperformance.now())return;n.entries=[t],e(n)}catch(e){}},\"complete\"===document.readyState?setTimeout(t,0):addEventListener(\"load\",(function(){return setTimeout(t,0)}))};export{h as getCLS,d as getFCP,L as getFID,F as getLCP,P as getTTFB};\n"],"names":["e","t","n","i","r","name","value","delta","entries","id","concat","Date","now","Math","floor","random","a","PerformanceObserver","supportedEntryTypes","includes","self","getEntries","map","observe","type","buffered","o","document","visibilityState","removeEventListener","addEventListener","u","persisted","c","f","s","m","timeStamp","v","setTimeout","firstHiddenTime","d","disconnect","startTime","push","window","performance","getEntriesByName","requestAnimationFrame","p","l","h","hadRecentInput","length","takeRecords","T","passive","capture","y","g","w","E","entryType","target","cancelable","processingStart","forEach","S","L","b","F","once","P","getEntriesByType","timing","max","navigationStart","responseStart","readyState"],"sourceRoot":""} \ No newline at end of file diff --git a/tavern/internal/www/build/static/js/main.1ffa92c8.js b/tavern/internal/www/build/static/js/main.1ffa92c8.js new file mode 100644 index 000000000..d529219d8 --- /dev/null +++ b/tavern/internal/www/build/static/js/main.1ffa92c8.js @@ -0,0 +1,3 @@ +/*! For license information please see main.1ffa92c8.js.LICENSE.txt */ +!function(){var e={457:function(e){"use strict";var t=Array.isArray,n=Object.keys,r=Object.prototype.hasOwnProperty,o="undefined"!==typeof Element;function i(e,a){if(e===a)return!0;if(e&&a&&"object"==typeof e&&"object"==typeof a){var l,s,u,c=t(e),d=t(a);if(c&&d){if((s=e.length)!=a.length)return!1;for(l=s;0!==l--;)if(!i(e[l],a[l]))return!1;return!0}if(c!=d)return!1;var f=e instanceof Date,p=a instanceof Date;if(f!=p)return!1;if(f&&p)return e.getTime()==a.getTime();var h=e instanceof RegExp,v=a instanceof RegExp;if(h!=v)return!1;if(h&&v)return e.toString()==a.toString();var m=n(e);if((s=m.length)!==n(a).length)return!1;for(l=s;0!==l--;)if(!r.call(a,m[l]))return!1;if(o&&e instanceof Element&&a instanceof Element)return e===a;for(l=s;0!==l--;)if(("_owner"!==(u=m[l])||!e.$$typeof)&&!i(e[u],a[u]))return!1;return!0}return e!==e&&a!==a}e.exports=function(e,t){try{return i(e,t)}catch(n){if(n.message&&n.message.match(/stack|recursion/i)||-2146828260===n.number)return console.warn("Warning: react-fast-compare does not handle circular references.",n.name,n.message),!1;throw n}}},110:function(e,t,n){"use strict";var r=n(441),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},i={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},a={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?a:l[e.$$typeof]||o}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=a;var u=Object.defineProperty,c=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(h){var o=p(n);o&&o!==h&&e(t,o,r)}var a=c(n);d&&(a=a.concat(d(n)));for(var l=s(t),v=s(n),m=0;m-1},ee.prototype.set=function(e,t){var n=this.__data__,r=ae(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},te.prototype.clear=function(){this.size=0,this.__data__={hash:new Z,map:new(K||ee),string:new Z}},te.prototype.delete=function(e){var t=ge(this,e).delete(e);return this.size-=t?1:0,t},te.prototype.get=function(e){return ge(this,e).get(e)},te.prototype.has=function(e){return ge(this,e).has(e)},te.prototype.set=function(e,t){var n=ge(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},ne.prototype.clear=function(){this.__data__=new ee,this.size=0},ne.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},ne.prototype.get=function(e){return this.__data__.get(e)},ne.prototype.has=function(e){return this.__data__.has(e)},ne.prototype.set=function(e,t){var n=this.__data__;if(n instanceof ee){var r=n.__data__;if(!K||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new te(r)}return n.set(e,t),this.size=n.size,this};var se,ue=function(e,t,n){for(var r=-1,o=Object(e),i=n(e),a=i.length;a--;){var l=i[se?a:++r];if(!1===t(o[l],l,o))break}return e};function ce(e){return null==e?void 0===e?f:u:q&&q in Object(e)?function(e){var t=A.call(e,q),n=e[q];try{e[q]=void 0;var r=!0}catch(i){}var o=D.call(e);r&&(t?e[q]=n:delete e[q]);return o}(e):function(e){return D.call(e)}(e)}function de(e){return Pe(e)&&ce(e)==i}function fe(e){return!(!Ie(e)||function(e){return!!M&&M in e}(e))&&(Oe(e)?L:p).test(function(e){if(null!=e){try{return P.call(e)}catch(t){}try{return e+""}catch(t){}}return""}(e))}function pe(e){if(!Ie(e))return function(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}(e);var t=we(e),n=[];for(var r in e)("constructor"!=r||!t&&A.call(e,r))&&n.push(r);return n}function he(e,t,n,r,o){e!==t&&ue(t,(function(i,a){if(o||(o=new ne),Ie(i))!function(e,t,n,r,o,i,a){var l=xe(e,n),s=xe(t,n),u=a.get(s);if(u)return void oe(e,n,u);var d=i?i(l,s,n+"",e,t,a):void 0,f=void 0===d;if(f){var p=Ce(s),h=!p&&Te(s),v=!p&&!h&&Ae(s);d=s,p||h||v?Ce(l)?d=l:Pe(m=l)&&Ee(m)?d=function(e,t){var n=-1,r=e.length;t||(t=Array(r));for(;++n-1&&e%1==0&&e0){if(++t>=800)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}(me);function _e(e,t){return e===t||e!==e&&t!==t}var ke=de(function(){return arguments}())?de:function(e){return Pe(e)&&A.call(e,"callee")&&!W.call(e,"callee")},Ce=Array.isArray;function Ee(e){return null!=e&&Re(e.length)&&!Oe(e)}var Te=Q||function(){return!1};function Oe(e){if(!Ie(e))return!1;var t=ce(e);return t==l||t==s||t==a||t==d}function Re(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=o}function Ie(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Pe(e){return null!=e&&"object"==typeof e}var Ae=k?function(e){return function(t){return e(t)}}(k):function(e){return Pe(e)&&Re(e.length)&&!!v[ce(e)]};function Me(e){return Ee(e)?re(e,!0):pe(e)}var De,je=(De=function(e,t,n,r){he(e,t,n,r)},ve((function(e,t){var n=-1,r=t.length,o=r>1?t[r-1]:void 0,i=r>2?t[2]:void 0;for(o=De.length>3&&"function"==typeof o?(r--,o):void 0,i&&function(e,t,n){if(!Ie(n))return!1;var r=typeof t;return!!("number"==r?Ee(n)&&be(t,n.length):"string"==r&&t in n)&&_e(n[t],e)}(t[0],t[1],i)&&(o=r<3?void 0:o,r=1),e=Object(e);++n