Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/_docs/user-guide/eldritch.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,18 @@ The <b>file.find</b> method finds all files matching the used parameters. Return

The <b>http.download</b> method downloads a file at the URI specified in `uri` to the path specified in `dst`. If a file already exists at that location, it will be overwritten.

### http.get

`http.get(uri: str, query_params: Option<Dict<str, str>>, headers: Option<Dict<str, str>>) -> str`

The <b>http.get</b> method sends an HTTP GET request to the URI specified in `uri` with the optional query paramters specified in `query_params` and headers specified in `headers`, then return the response body as a string. Note: in order to conform with HTTP2+ all header names are transmuted to lowercase.

### http.post

`http.post(uri: str, body: Option<str>, form: Option<Dict<str, str>>, headers: Option<Dict<str, str>>) -> str`

The <b>http.post</b> method sends an HTTP POST request to the URI specified in `uri` with the optional request body specified by `body`, form paramters specified in `form`, and headers specified in `headers`, then return the response body as a string. Note: in order to conform with HTTP2+ all header names are transmuted to lowercase. Other Note: if a `body` and a `form` are supplied the value of `body` will be used.

---

## Pivot
Expand Down
212 changes: 212 additions & 0 deletions implants/lib/eldritch/src/http/get_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use anyhow::Result;
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use starlark::collections::SmallMap;
use std::collections::HashMap;

pub fn get(
uri: String,
query_params: Option<SmallMap<String, String>>,
headers: Option<SmallMap<String, String>>,
) -> Result<String> {
let mut query_map = HashMap::new();
let mut headers_map = HeaderMap::new();
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;

if let Some(q) = query_params {
for (k, v) in q {
query_map.insert(k, v);
}
}

if let Some(h) = headers {
for (k, v) in h {
let name = HeaderName::from_bytes(k.as_bytes())?;
let value = HeaderValue::from_bytes(v.as_bytes())?;
headers_map.append(name, value);
}
}

runtime.block_on(handle_get(uri, query_map, headers_map))
}

async fn handle_get(
uri: String,
query_params: HashMap<String, String>,
headers: HeaderMap,
) -> Result<String> {
#[cfg(debug_assertions)]
log::info!(
"eldritch sending HTTP GET request to '{}' with headers '{:#?}'",
uri,
headers
);

let client = reqwest::Client::new()
.get(uri)
.headers(headers)
.query(&query_params);
let resp = client.send().await?.text().await?;
Ok(resp)
}

#[cfg(test)]
mod tests {

use super::*;
use httptest::{matchers::*, responders::*, Expectation, Server};
use starlark::collections::SmallMap;

#[test]
fn test_get_no_params_or_headers() -> anyhow::Result<()> {
// running test http server
let server = Server::run();
server.expect(
Expectation::matching(request::method_path("GET", "/foo"))
.respond_with(status_code(200).body("test body")),
);

// reference test server uri
let url = server.url("/foo").to_string();

// run our code
let contents = get(url, None, None)?;

// check request returned correctly
assert_eq!(contents, "test body");

Ok(())
}

#[test]
fn test_get_empty_params_and_headers() -> anyhow::Result<()> {
// running test http server
let server = Server::run();
server.expect(
Expectation::matching(request::method_path("GET", "/foo"))
.respond_with(status_code(200).body("test body")),
);

// reference test server uri
let url = server.url("/foo").to_string();

// run our code
let contents = get(url, Some(SmallMap::new()), Some(SmallMap::new()))?;

// check request returned correctly
assert_eq!(contents, "test body");

Ok(())
}

#[test]
fn test_get_with_params() -> anyhow::Result<()> {
// running test http server
let server = Server::run();
let m = all_of![
request::method_path("GET", "/foo"),
request::query(url_decoded(contains(("a", "true")))),
request::query(url_decoded(contains(("b", "bar")))),
request::query(url_decoded(contains(("c", "3")))),
];
server.expect(Expectation::matching(m).respond_with(status_code(200).body("test body")));

// reference test server uri
let url = server.url("/foo").to_string();

// run our code
let mut params = SmallMap::new();
params.insert("a".to_string(), "true".to_string());
params.insert("b".to_string(), "bar".to_string());
params.insert("c".to_string(), "3".to_string());
let contents = get(url, Some(params), None)?;

// check request returned correctly
assert_eq!(contents, "test body");

Ok(())
}

#[test]
fn test_get_with_hybrid_params() -> anyhow::Result<()> {
// running test http server
let server = Server::run();
let m = all_of![
request::method_path("GET", "/foo"),
request::query(url_decoded(contains(("a", "true")))),
request::query(url_decoded(contains(("b", "bar")))),
request::query(url_decoded(contains(("c", "3")))),
];
server.expect(Expectation::matching(m).respond_with(status_code(200).body("test body")));

// reference test server uri
let url = server.url("/foo?a=true").to_string();

// run our code
let mut params = SmallMap::new();
params.insert("b".to_string(), "bar".to_string());
params.insert("c".to_string(), "3".to_string());
let contents = get(url, Some(params), None)?;

// check request returned correctly
assert_eq!(contents, "test body");

Ok(())
}

#[test]
fn test_get_with_headers() -> anyhow::Result<()> {
// running test http server
let server = Server::run();
let m = all_of![
request::method_path("GET", "/foo"),
request::headers(contains(("a", "TRUE"))),
request::headers(contains(("b", "bar"))),
];
server.expect(Expectation::matching(m).respond_with(status_code(200).body("test body")));

// reference test server uri
let url = server.url("/foo").to_string();

// run our code
let mut headers = SmallMap::new();
headers.insert("A".to_string(), "TRUE".to_string());
headers.insert("b".to_string(), "bar".to_string());
let contents = get(url, None, Some(headers))?;

// check request returned correctly
assert_eq!(contents, "test body");

Ok(())
}

#[test]
fn test_get_with_params_and_headers() -> anyhow::Result<()> {
// running test http server
let server = Server::run();
let m = all_of![
request::method_path("GET", "/foo"),
request::headers(contains(("a", "TRUE"))),
request::headers(contains(("b", "bar"))),
request::query(url_decoded(contains(("c", "3")))),
];
server.expect(Expectation::matching(m).respond_with(status_code(200).body("test body")));

// reference test server uri
let url = server.url("/foo").to_string();

// run our code
let mut headers = SmallMap::new();
headers.insert("A".to_string(), "TRUE".to_string());
headers.insert("b".to_string(), "bar".to_string());
let mut params = SmallMap::new();
params.insert("c".to_string(), "3".to_string());
let contents = get(url, Some(params), Some(headers))?;

// check request returned correctly
assert_eq!(contents, "test body");

Ok(())
}
}
13 changes: 13 additions & 0 deletions implants/lib/eldritch/src/http/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod download_impl;
mod get_impl;
mod post_impl;

use starlark::{
collections::SmallMap,
environment::MethodsBuilder,
starlark_module,
values::{none::NoneType, starlark_value},
Expand All @@ -24,4 +27,14 @@ fn methods(builder: &mut MethodsBuilder) {
download_impl::download(uri, dst)?;
Ok(NoneType{})
}

#[allow(unused_variables)]
fn get(this: &HTTPLibrary, uri: String, query_params: Option<SmallMap<String, String>>, headers: Option<SmallMap<String, String>>) -> anyhow::Result<String> {
get_impl::get(uri, query_params, headers)
}

#[allow(unused_variables)]
fn post(this: &HTTPLibrary, uri: String, body: Option<String>, form: Option<SmallMap<String, String>>, headers: Option<SmallMap<String, String>>) -> anyhow::Result<String> {
post_impl::post(uri, body, form, headers)
}
}
Loading