Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25f940788f | ||
|
|
c232b23b1e | ||
|
|
5b19c8c4b3 | ||
|
|
aeba3f4574 | ||
|
|
1f5ea402ea | ||
|
|
d54ffd1eba | ||
|
|
5bca5677b6 | ||
|
|
af021cb6be | ||
|
|
52891cf6b1 | ||
|
|
6e76c035df | ||
|
|
850d628a93 | ||
|
|
5bb03d39bc | ||
|
|
f5660570d2 | ||
|
|
f1b0608daa | ||
|
|
c775ae1dee | ||
|
|
36b5219008 | ||
|
|
79982fd5f0 | ||
|
|
5fe71feec3 | ||
|
|
b8d4eb0f51 | ||
|
|
e6444db009 | ||
|
|
7dcb6b81dc | ||
|
|
4fb56d91c2 |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
||||
github: [kpcyrd]
|
||||
patreon: kpcyrd
|
||||
|
||||
803
Cargo.lock
generated
803
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sn0int"
|
||||
version = "0.22.0"
|
||||
version = "0.24.0"
|
||||
description = "Semi-automatic OSINT framework and package manager"
|
||||
authors = ["kpcyrd <git@rxv.cc>"]
|
||||
license = "GPL-3.0"
|
||||
@@ -33,7 +33,7 @@ sqlite-bundled = ["libsqlite3-sys/bundled"]
|
||||
|
||||
[dependencies]
|
||||
sn0int-common = { version="0.13.0", path="sn0int-common" }
|
||||
sn0int-std = { version="=0.22.0", path="sn0int-std" }
|
||||
sn0int-std = { version="=0.24.0", path="sn0int-std" }
|
||||
rustyline = "9.0"
|
||||
log = "0.4"
|
||||
env_logger = "0.9"
|
||||
@@ -72,8 +72,8 @@ semver = "1"
|
||||
bytes = "0.4"
|
||||
bytesize = "1.0"
|
||||
ipnetwork = "0.18"
|
||||
strum = "0.21"
|
||||
strum_macros = "0.21"
|
||||
strum = "0.23"
|
||||
strum_macros = "0.23"
|
||||
embedded-triple = "0.1.0"
|
||||
humansize = "1.1.0"
|
||||
|
||||
@@ -93,7 +93,7 @@ os-version = "0.2"
|
||||
caps = "0.5"
|
||||
#syscallz = { path="../syscallz-rs" }
|
||||
syscallz = "0.16"
|
||||
nix = "0.22"
|
||||
nix = "0.23"
|
||||
|
||||
[target.'cfg(target_os="openbsd")'.dependencies]
|
||||
pledge = "0.4"
|
||||
|
||||
24
README.md
24
README.md
@@ -14,33 +14,34 @@
|
||||
[registry]: https://sn0int.com/
|
||||
|
||||
sn0int (pronounced [`/snoɪnt/`][ipa]) is a semi-automatic OSINT framework and
|
||||
package manager. It was built for IT security professionals and bug hunters to
|
||||
gather intelligence about a given target or about yourself. sn0int is
|
||||
enumerating attack surface by semi-automatically processing public information
|
||||
and mapping the results in a unified format for followup investigations.
|
||||
package manager. It's used by IT security professionals, bug bounty hunters,
|
||||
law enforcement agencies and in security awareness trainings to gather
|
||||
intelligence about a given target or about yourself. sn0int is enumerating
|
||||
attack surface by semi-automatically processing public information and mapping
|
||||
the results in a unified format for followup investigations.
|
||||
|
||||
[ipa]: http://ipa-reader.xyz/?text=sno%C9%AAnt
|
||||
|
||||
Among other things, sn0int is currently able to:
|
||||
|
||||
- Harvest subdomains from certificate transparency logs and passive dns
|
||||
- Mass resolve collected subdomains and scan for http or https services
|
||||
- Enrich ip addresses with asn and geoip info
|
||||
- Harvest emails from pgp keyservers and whois
|
||||
- Discover compromised logins in breaches
|
||||
- Find somebody's profiles across the internet
|
||||
- Enumerate local networks with unique techniques like passive arp
|
||||
- Gather information about phonenumbers
|
||||
- Attempt to bypass cloudflare with shodan
|
||||
- Harvest data and images from instagram profiles
|
||||
- Scan images for nudity
|
||||
- Harvest activity and images from social media profiles
|
||||
- Basic image processing
|
||||
|
||||
sn0int is heavily inspired by recon-ng and maltego, but remains more flexible
|
||||
and is fully opensource. None of the investigations listed above are hardcoded
|
||||
in the source, instead those are provided by modules that are executed in a
|
||||
in the source, instead they are provided by modules that are executed in a
|
||||
sandbox. You can easily extend sn0int by writing your own modules and share
|
||||
them with other users by publishing them to the sn0int registry. This allows
|
||||
you to ship updates for your modules on your own since you don't need to send a
|
||||
pull request.
|
||||
you to ship updates for your modules on your own instead of pull-requesting
|
||||
them into the sn0int codebase.
|
||||
|
||||
For questions and support join us on IRC: [irc.hackint.org:6697/#sn0int](https://webirc.hackint.org/#irc://irc.hackint.org/#sn0int)
|
||||
|
||||
@@ -226,6 +227,9 @@ For everything else please have a look at the [detailed list][1].
|
||||
- [http_fetch_json](https://sn0int.readthedocs.io/en/latest/reference.html#http-fetch-json)
|
||||
- [img_load](https://sn0int.readthedocs.io/en/latest/reference.html#img-load)
|
||||
- [img_exif](https://sn0int.readthedocs.io/en/latest/reference.html#img-exif)
|
||||
- [img_ahash](https://sn0int.readthedocs.io/en/latest/reference.html#img-ahash)
|
||||
- [img_dhash](https://sn0int.readthedocs.io/en/latest/reference.html#img-dhash)
|
||||
- [img_phash](https://sn0int.readthedocs.io/en/latest/reference.html#img-phash)
|
||||
- [img_nudity](https://sn0int.readthedocs.io/en/latest/reference.html#img-nudity)
|
||||
- [info](https://sn0int.readthedocs.io/en/latest/reference.html#info)
|
||||
- [intval](https://sn0int.readthedocs.io/en/latest/reference.html#intval)
|
||||
|
||||
@@ -504,6 +504,39 @@ Extract exif metadata from an image.
|
||||
if last_err() then return end
|
||||
debug(exif)
|
||||
|
||||
img_ahash
|
||||
---------
|
||||
|
||||
Calculate the Mean (aHash) perceptual hash.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
hash = img_ahash(blob)
|
||||
if last_err() then return end
|
||||
debug(hash)
|
||||
|
||||
img_dhash
|
||||
---------
|
||||
|
||||
Calculate the Gradient (dHash) perceptual hash.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
hash = img_dhash(blob)
|
||||
if last_err() then return end
|
||||
debug(hash)
|
||||
|
||||
img_phash
|
||||
---------
|
||||
|
||||
Calculate the DCT (pHash) perceptual hash.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
hash = img_phash(blob)
|
||||
if last_err() then return end
|
||||
debug(hash)
|
||||
|
||||
img_nudity
|
||||
----------
|
||||
|
||||
|
||||
11
modules/harness/img-hash.lua
Normal file
11
modules/harness/img-hash.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Description: TODO your description here
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
-- Source: images
|
||||
|
||||
function run(arg)
|
||||
debug(arg)
|
||||
info(img_ahash(arg['value']))
|
||||
info(img_dhash(arg['value']))
|
||||
info(img_phash(arg['value']))
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sn0int-std"
|
||||
version = "0.22.0"
|
||||
version = "0.24.0"
|
||||
description = "sn0int - stdlib"
|
||||
authors = ["kpcyrd <git@rxv.cc>"]
|
||||
repository = "https://github.com/kpcyrd/sn0int"
|
||||
@@ -27,13 +27,13 @@ ct-logs = "0.7"
|
||||
chrootable-https = "0.16"
|
||||
http = "0.2"
|
||||
bufstream = "0.1.4"
|
||||
pem = "0.8"
|
||||
pem = "1"
|
||||
url = "2.0"
|
||||
tungstenite = { version = "0.13", default-features = false }
|
||||
kuchiki = "0.8.0"
|
||||
maxminddb = "0.21"
|
||||
x509-parser = "0.10"
|
||||
der-parser = "5"
|
||||
x509-parser = "0.12"
|
||||
der-parser = "6"
|
||||
publicsuffix = { version="2", default-features=false }
|
||||
xml-rs = "0.8"
|
||||
geo = "0.18"
|
||||
@@ -45,6 +45,7 @@ sodiumoxide = { version="0.2.5", features=["use-pkg-config"] }
|
||||
|
||||
image = "0.23.0"
|
||||
kamadak-exif = "0.5.1"
|
||||
img_hash_median = "4.0.0"
|
||||
|
||||
bs58 = "0.4"
|
||||
digest = "0.9"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::errors::*;
|
||||
use image::{self, DynamicImage, GenericImageView};
|
||||
pub use img_hash_median::HashAlg;
|
||||
|
||||
pub mod exif;
|
||||
|
||||
@@ -79,6 +80,14 @@ impl Image {
|
||||
pub fn height(&self) -> u32 {
|
||||
self.image.height()
|
||||
}
|
||||
|
||||
pub fn perception_hash(&self, hash_alg: HashAlg) -> String {
|
||||
let hasher = img_hash_median::HasherConfig::new()
|
||||
.hash_alg(hash_alg)
|
||||
.to_hasher();
|
||||
let hash = hasher.hash_image(&self.image);
|
||||
hash.to_base64()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<DynamicImage> for Image {
|
||||
@@ -105,7 +114,6 @@ pub fn load(buf: &[u8]) -> Result<Image> {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -65,21 +65,21 @@ impl HttpSession {
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct RequestOptions {
|
||||
query: Option<HashMap<String, String>>,
|
||||
headers: Option<HashMap<String, String>>,
|
||||
basic_auth: Option<(String, String)>,
|
||||
user_agent: Option<String>,
|
||||
json: Option<serde_json::Value>,
|
||||
form: Option<serde_json::Value>,
|
||||
pub query: Option<HashMap<String, String>>,
|
||||
pub headers: Option<HashMap<String, String>>,
|
||||
pub basic_auth: Option<(String, String)>,
|
||||
pub user_agent: Option<String>,
|
||||
pub json: Option<serde_json::Value>,
|
||||
pub form: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
follow_redirects: usize,
|
||||
body: Option<String>,
|
||||
timeout: Option<u64>,
|
||||
pub follow_redirects: usize,
|
||||
pub body: Option<String>,
|
||||
pub timeout: Option<u64>,
|
||||
#[serde(default)]
|
||||
into_blob: bool,
|
||||
proxy: Option<SocketAddr>,
|
||||
pub into_blob: bool,
|
||||
pub proxy: Option<SocketAddr>,
|
||||
#[serde(default)]
|
||||
binary: bool,
|
||||
pub binary: bool,
|
||||
}
|
||||
|
||||
impl RequestOptions {
|
||||
@@ -110,11 +110,8 @@ pub struct HttpRequest {
|
||||
}
|
||||
|
||||
impl HttpRequest {
|
||||
pub fn new(session: &HttpSession, method: String, url: String, options: RequestOptions, default_agent: fn() -> String) -> HttpRequest {
|
||||
pub fn new(session: &HttpSession, method: String, url: String, user_agent: String, options: RequestOptions) -> HttpRequest {
|
||||
let cookies = session.cookies.clone();
|
||||
|
||||
let user_agent = options.user_agent.unwrap_or_else(default_agent);
|
||||
|
||||
let timeout = options.timeout.map(Duration::from_millis);
|
||||
|
||||
let mut request = HttpRequest {
|
||||
|
||||
19
src/args.rs
19
src/args.rs
@@ -66,6 +66,12 @@ pub enum SubCommand {
|
||||
/// Exclude entities from scope
|
||||
#[structopt(name="noscope")]
|
||||
Noscope(cmd::noscope_cmd::Args),
|
||||
/// Manage autoscope rules
|
||||
Autoscope(cmd::autoscope_cmd::Args),
|
||||
/// Manage autonoscope rules
|
||||
Autonoscope(cmd::autonoscope_cmd::Args),
|
||||
/// Rescope all entities based on autonoscope rules
|
||||
Rescope(cmd::rescope_cmd::Args),
|
||||
/// Manage workspaces
|
||||
#[structopt(name="workspace")]
|
||||
Workspace(cmd::workspace_cmd::Args),
|
||||
@@ -97,18 +103,11 @@ pub enum SubCommand {
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Run {
|
||||
/// Execute a module that has been installed
|
||||
pub module: String,
|
||||
#[structopt(flatten)]
|
||||
pub run: cmd::run_cmd::Args,
|
||||
/// Run a module from a path
|
||||
#[structopt(short="f", long="file")]
|
||||
pub file: bool,
|
||||
/// Run modules concurrently
|
||||
#[structopt(short="j", long="threads", default_value="1")]
|
||||
pub threads: usize,
|
||||
/// Verbose logging, once to print inserts even if they don't add new
|
||||
/// data, twice to activate the debug() function
|
||||
#[structopt(short="v", long="verbose", parse(from_occurrences))]
|
||||
pub verbose: u64,
|
||||
/// Expose stdin to modules
|
||||
#[structopt(long="stdin")]
|
||||
pub stdin: bool,
|
||||
@@ -138,7 +137,7 @@ pub struct Run {
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Sandbox {
|
||||
/// This value is only used for process listings
|
||||
label: String,
|
||||
_label: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
||||
@@ -41,6 +41,13 @@ impl TryFrom<Autonoscope> for DomainRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<Domain> for DomainRule {
|
||||
#[inline]
|
||||
fn matches(&self, domain: &Domain) -> Result<bool> {
|
||||
self.matches(domain.value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<NewDomain> for DomainRule {
|
||||
#[inline]
|
||||
fn matches(&self, domain: &NewDomain) -> Result<bool> {
|
||||
@@ -48,6 +55,13 @@ impl AutoRule<NewDomain> for DomainRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<Subdomain> for DomainRule {
|
||||
#[inline]
|
||||
fn matches(&self, domain: &Subdomain) -> Result<bool> {
|
||||
self.matches(domain.value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<NewSubdomain> for DomainRule {
|
||||
#[inline]
|
||||
fn matches(&self, domain: &NewSubdomain) -> Result<bool> {
|
||||
@@ -55,6 +69,18 @@ impl AutoRule<NewSubdomain> for DomainRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<Url> for DomainRule {
|
||||
#[inline]
|
||||
fn matches(&self, url: &Url) -> Result<bool> {
|
||||
let url = url.value.parse::<url::Url>()?;
|
||||
if let Some(domain) = url.domain() {
|
||||
self.matches(domain)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<NewUrl> for DomainRule {
|
||||
#[inline]
|
||||
fn matches(&self, url: &NewUrl) -> Result<bool> {
|
||||
|
||||
@@ -36,12 +36,25 @@ impl TryFrom<Autonoscope> for IpRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<IpAddr> for IpRule {
|
||||
fn matches(&self, ipaddr: &IpAddr) -> Result<bool> {
|
||||
self.matches(ipaddr.value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<NewIpAddr> for IpRule {
|
||||
fn matches(&self, ipaddr: &NewIpAddr) -> Result<bool> {
|
||||
self.matches(ipaddr.value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<Port> for IpRule {
|
||||
fn matches(&self, port: &Port) -> Result<bool> {
|
||||
let addr = port.value.parse::<net::SocketAddr>()?;
|
||||
self.matches(&addr.ip())
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<NewPort> for IpRule {
|
||||
fn matches(&self, port: &NewPort) -> Result<bool> {
|
||||
let addr = port.value.parse::<net::SocketAddr>()?;
|
||||
@@ -49,15 +62,25 @@ impl AutoRule<NewPort> for IpRule {
|
||||
}
|
||||
}
|
||||
|
||||
fn match_netblock_str(network: &IpNetwork, netblock: &str) -> Result<bool> {
|
||||
let range = netblock.parse::<ipnetwork::IpNetwork>()?;
|
||||
|
||||
if network.prefix() <= range.prefix() {
|
||||
Ok(network.contains(range.ip()))
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<Netblock> for IpRule {
|
||||
fn matches(&self, netblock: &Netblock) -> Result<bool> {
|
||||
match_netblock_str(&self.network, &netblock.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<NewNetblock> for IpRule {
|
||||
fn matches(&self, netblock: &NewNetblock) -> Result<bool> {
|
||||
let range = netblock.value.parse::<ipnetwork::IpNetwork>()?;
|
||||
|
||||
if self.network.prefix() <= range.prefix() {
|
||||
Ok(self.network.contains(range.ip()))
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
match_netblock_str(&self.network, &netblock.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,18 @@ fn sort_precision_desc<T: RulePrecision>(a: &T, b: &T) -> Ordering {
|
||||
}
|
||||
|
||||
impl RuleSet {
|
||||
pub fn domains(&self) -> &[Rule<DomainRule>] {
|
||||
&self.domains
|
||||
}
|
||||
|
||||
pub fn ips(&self) -> &[Rule<IpRule>] {
|
||||
&self.ips
|
||||
}
|
||||
|
||||
pub fn urls(&self) -> &[Rule<UrlRule>] {
|
||||
&self.urls
|
||||
}
|
||||
|
||||
pub fn load(db: &DatabaseSock) -> Result<Self> {
|
||||
use crate::schema::autonoscope::dsl::*;
|
||||
let rules = autonoscope.load::<Autonoscope>(db)?;
|
||||
@@ -143,6 +155,14 @@ impl RuleSet {
|
||||
rules
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.domains.is_empty() && self.ips.is_empty() && self.urls.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.domains.len() + self.ips.len() + self.urls.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push_rules_display<T: ToRule>(output: &mut Vec<(&'static str, String, bool)>, rules: &[Rule<T>]) {
|
||||
for rule in rules {
|
||||
@@ -238,7 +258,7 @@ pub trait RulePrecision {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Rule<T: ToRule> {
|
||||
rule: T,
|
||||
scoped: bool,
|
||||
pub scoped: bool,
|
||||
}
|
||||
|
||||
impl<T: ToRule> Rule<T> {
|
||||
|
||||
@@ -47,6 +47,12 @@ impl TryFrom<&str> for UrlRule {
|
||||
}
|
||||
|
||||
// TODO: there is no way to write a rule that matches all urls
|
||||
impl AutoRule<Url> for UrlRule {
|
||||
fn matches(&self, url: &Url) -> Result<bool> {
|
||||
self.matches(url.value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoRule<NewUrl> for UrlRule {
|
||||
fn matches(&self, url: &NewUrl) -> Result<bool> {
|
||||
self.matches(url.value.as_str())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::errors::*;
|
||||
|
||||
use crate::autonoscope;
|
||||
use crate::cmd::Cmd;
|
||||
use crate::fmt::colors::*;
|
||||
use crate::shell::Shell;
|
||||
use std::fmt::Write;
|
||||
@@ -65,7 +66,8 @@ pub fn run_with_scope_param(rl: &mut Shell, args: Args, scoped: bool) -> Result<
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
|
||||
let args = Args::from_iter_safe(args)?;
|
||||
run_with_scope_param(rl, args, false)
|
||||
impl Cmd for Args {
|
||||
fn run(self, rl: &mut Shell) -> Result<()> {
|
||||
run_with_scope_param(rl, self, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
use crate::errors::*;
|
||||
|
||||
use crate::cmd::Cmd;
|
||||
use crate::cmd::autonoscope_cmd;
|
||||
use crate::shell::Shell;
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
||||
pub type Args = autonoscope_cmd::Args;
|
||||
|
||||
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
|
||||
let args = Args::from_iter_safe(args)?;
|
||||
autonoscope_cmd::run_with_scope_param(rl, args, true)
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Args {
|
||||
#[structopt(flatten)]
|
||||
args: autonoscope_cmd::Args,
|
||||
}
|
||||
|
||||
impl Cmd for Args {
|
||||
fn run(self, rl: &mut Shell) -> Result<()> {
|
||||
autonoscope_cmd::run_with_scope_param(rl, self.args, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ pub mod keyring_cmd;
|
||||
pub mod noscope_cmd;
|
||||
pub mod notify_cmd;
|
||||
pub mod pkg_cmd;
|
||||
pub mod rescope_cmd;
|
||||
pub mod set_cmd;
|
||||
pub mod scope_cmd;
|
||||
pub mod stats_cmd;
|
||||
|
||||
266
src/cmd/rescope_cmd.rs
Normal file
266
src/cmd/rescope_cmd.rs
Normal file
@@ -0,0 +1,266 @@
|
||||
use crate::errors::*;
|
||||
|
||||
use crate::autonoscope::{AutoRule, ToRule};
|
||||
use crate::cmd::Cmd;
|
||||
use crate::db::Database;
|
||||
use crate::filters::{Filter, Target};
|
||||
use crate::shell::Shell;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use structopt::StructOpt;
|
||||
use structopt::clap::AppSettings;
|
||||
use crate::models::*;
|
||||
use crate::utils;
|
||||
use crate::term;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
|
||||
pub struct Args {
|
||||
/// Run rules interactively
|
||||
#[structopt(short, long)]
|
||||
interactive: bool,
|
||||
/// Automatically apply changes to database
|
||||
#[structopt(short="y", long)]
|
||||
auto_confirm: bool,
|
||||
/// Only show changes, do not apply them to the database
|
||||
#[structopt(short="n", long)]
|
||||
dry_run: bool,
|
||||
/// Only rescope entities matching specific filter
|
||||
#[structopt(subcommand)]
|
||||
target: Option<Target>,
|
||||
}
|
||||
|
||||
enum Entity {
|
||||
Domain(Domain),
|
||||
Subdomain(Subdomain),
|
||||
IpAddr(IpAddr),
|
||||
Url(Url),
|
||||
Port(Port),
|
||||
Netblock(Netblock),
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
fn set_scoped(&self, db: &Database, value: bool) -> Result<()> {
|
||||
match self {
|
||||
Entity::Domain(entity) => entity.set_scoped(db, value),
|
||||
Entity::Subdomain(entity) => entity.set_scoped(db, value),
|
||||
Entity::IpAddr(entity) => entity.set_scoped(db, value),
|
||||
Entity::Url(entity) => entity.set_scoped(db, value),
|
||||
Entity::Port(entity) => entity.set_scoped(db, value),
|
||||
Entity::Netblock(entity) => entity.set_scoped(db, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Input {
|
||||
Yes,
|
||||
No,
|
||||
Done,
|
||||
Always,
|
||||
Never,
|
||||
}
|
||||
|
||||
fn get_input() -> Result<Input> {
|
||||
loop {
|
||||
let input = utils::question_opt("Update this entity? [Y/n/d/a/x/?]")?;
|
||||
let input = input.map(|s| s.to_lowercase());
|
||||
match input.as_deref() {
|
||||
Some("y") | None => return Ok(Input::Yes),
|
||||
Some("n") => return Ok(Input::No),
|
||||
Some("d") => return Ok(Input::Done),
|
||||
Some("a") => return Ok(Input::Always),
|
||||
Some("x") => return Ok(Input::Never),
|
||||
Some("?") => {
|
||||
term::success("y -> yes, apply this change");
|
||||
term::success("n -> no, skip this change");
|
||||
term::success("d -> done, skip this and further changes");
|
||||
term::success("a -> always, apply every change caused by this specific rule");
|
||||
term::success("x -> never, skip every change caused by this specific rule");
|
||||
},
|
||||
Some(input) => term::error(&format!("Unrecognized input: {:?}", input)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Context {
|
||||
update_queue: Vec<(Entity, bool)>,
|
||||
always_rules: HashSet<(&'static str, String)>,
|
||||
never_rules: HashSet<(&'static str, String)>,
|
||||
done: bool,
|
||||
target: Option<Target>,
|
||||
}
|
||||
|
||||
fn rescope_to_queue<T, F1, F2, F3>(ctx: &mut Context, db: &Database, interactive: bool, get_filter: F1, matches_rule: F2, wrap: F3) -> Result<()>
|
||||
where
|
||||
T: Model + Scopable + fmt::Debug,
|
||||
F1: Fn(&Target) -> Option<&Filter>,
|
||||
F2: Fn(&T) -> Result<Option<((&'static str, String), bool)>>,
|
||||
F3: Fn(T) -> Entity,
|
||||
{
|
||||
// do nothing if we're already done
|
||||
if ctx.done {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// check if there are filters to be applied
|
||||
let filter = if let Some(target) = &ctx.target {
|
||||
if let Some(filter) = get_filter(&target) {
|
||||
// we've selected this specific entity type and there's a filter
|
||||
filter.parse_optional()
|
||||
.context("Filter is invalid")?
|
||||
} else {
|
||||
// we do not wish to process this entity type
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
// any entity type is fine
|
||||
Filter::any()
|
||||
};
|
||||
|
||||
let entities = db.filter::<T>(&filter)?;
|
||||
|
||||
for entity in entities {
|
||||
let currently_scoped = entity.scoped();
|
||||
debug!("rescoping entity: {:?}", entity);
|
||||
|
||||
if let Some((rule, should_be)) = matches_rule(&entity)? {
|
||||
// check if we're actively ignoring this rule
|
||||
if ctx.never_rules.contains(&rule) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if currently_scoped != should_be {
|
||||
let prefix = if should_be {
|
||||
"\x1b[1m[\x1b[32m+\x1b[0;1m]\x1b[0m"
|
||||
} else {
|
||||
"\x1b[1m[\x1b[31m-\x1b[0;1m]\x1b[0m"
|
||||
};
|
||||
|
||||
println!("{} Setting entity {:?} => {:?}: {:?}", prefix, currently_scoped, should_be, entity);
|
||||
|
||||
// check if we're auto-accepting this rule
|
||||
let input = if ctx.always_rules.contains(&rule) {
|
||||
Input::Yes
|
||||
} else if interactive {
|
||||
get_input()?
|
||||
} else {
|
||||
Input::Yes
|
||||
};
|
||||
|
||||
// process user input
|
||||
let input = match input {
|
||||
Input::Always => {
|
||||
ctx.always_rules.insert(rule);
|
||||
Input::Yes
|
||||
},
|
||||
Input::Never => {
|
||||
ctx.never_rules.insert(rule);
|
||||
Input::No
|
||||
},
|
||||
Input::Done => {
|
||||
ctx.done = true;
|
||||
break;
|
||||
},
|
||||
input => input,
|
||||
};
|
||||
|
||||
if let Input::Yes = input {
|
||||
ctx.update_queue.push((wrap(entity), should_be));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Cmd for Args {
|
||||
fn run(self, rl: &mut Shell) -> Result<()> {
|
||||
let rules = rl.db().autonoscope();
|
||||
term::success(&format!("Loaded {} rules", rules.len()));
|
||||
|
||||
let mut ctx = Context::default();
|
||||
ctx.target = self.target;
|
||||
|
||||
rescope_to_queue::<Domain, _, _, _>(&mut ctx, rl.db(), self.interactive, |t| t.domains(), |entity| {
|
||||
for rule in rules.domains() {
|
||||
if rule.matches(entity.value.as_str())? {
|
||||
return Ok(Some((rule.to_rule(), rule.scoped)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}, Entity::Domain)?;
|
||||
rescope_to_queue::<Subdomain, _, _, _>(&mut ctx, rl.db(), self.interactive, |t| t.subdomains(), |entity| {
|
||||
for rule in rules.domains() {
|
||||
if rule.matches(entity.value.as_str())? {
|
||||
return Ok(Some((rule.to_rule(), rule.scoped)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}, Entity::Subdomain)?;
|
||||
|
||||
rescope_to_queue::<IpAddr, _, _, _>(&mut ctx, rl.db(), self.interactive, |t| t.ipaddrs(), |entity| {
|
||||
for rule in rules.ips() {
|
||||
if rule.matches(entity)? {
|
||||
return Ok(Some((rule.to_rule(), rule.scoped)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}, Entity::IpAddr)?;
|
||||
|
||||
rescope_to_queue::<Url, _, _, _>(&mut ctx, rl.db(), self.interactive, |t| t.urls(), |entity| {
|
||||
for rule in rules.domains() {
|
||||
if rule.matches(entity)? {
|
||||
return Ok(Some((rule.to_rule(), rule.scoped)));
|
||||
}
|
||||
}
|
||||
for rule in rules.urls() {
|
||||
if rule.matches(entity)? {
|
||||
return Ok(Some((rule.to_rule(), rule.scoped)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}, Entity::Url)?;
|
||||
rescope_to_queue::<Port, _, _, _>(&mut ctx, rl.db(), self.interactive, |t| t.ports(), |entity| {
|
||||
for rule in rules.ips() {
|
||||
if rule.matches(entity)? {
|
||||
return Ok(Some((rule.to_rule(), rule.scoped)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}, Entity::Port)?;
|
||||
rescope_to_queue::<Netblock, _, _, _>(&mut ctx, rl.db(), self.interactive, |t| t.netblocks(), |entity| {
|
||||
for rule in rules.ips() {
|
||||
if rule.matches(entity)? {
|
||||
return Ok(Some((rule.to_rule(), rule.scoped)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}, Entity::Netblock)?;
|
||||
|
||||
if ctx.update_queue.is_empty() {
|
||||
term::success("Nothing has changed, not updating database");
|
||||
} else {
|
||||
let confirm = if self.dry_run {
|
||||
false
|
||||
} else if self.auto_confirm {
|
||||
true
|
||||
} else {
|
||||
utils::no_else_yes(&format!("Apply {} changes to scope now?", ctx.update_queue.len()))?
|
||||
};
|
||||
|
||||
if confirm {
|
||||
term::info(&format!("Applying {} changes to database", ctx.update_queue.len()));
|
||||
|
||||
for (update, value) in ctx.update_queue {
|
||||
update.set_scoped(rl.db(), value)?;
|
||||
}
|
||||
} else {
|
||||
term::info("Database not updated");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ use serde::Serialize;
|
||||
use sn0int_common::metadata::Source;
|
||||
use sn0int_std::ratelimits::Ratelimiter;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use structopt::StructOpt;
|
||||
use structopt::clap::AppSettings;
|
||||
|
||||
@@ -24,14 +25,26 @@ use structopt::clap::AppSettings;
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
|
||||
pub struct Args {
|
||||
#[structopt(short="j", long="threads", default_value="1")]
|
||||
threads: usize,
|
||||
#[structopt(short="v", long="verbose", parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
/// Execute a module that has been installed
|
||||
pub module: Option<String>,
|
||||
/// Run investigations concurrently
|
||||
#[structopt(short="j", default_value="1")]
|
||||
pub threads: usize,
|
||||
/// Verbose logging, once to print inserts even if they don't add new
|
||||
/// data, twice to activate the debug() function
|
||||
#[structopt(short="v", long, parse(from_occurrences))]
|
||||
pub verbose: u64,
|
||||
/// Set a specific socks5 proxy to use
|
||||
#[structopt(short="X", long)]
|
||||
pub proxy: Option<SocketAddr>,
|
||||
/// Set a different default user agent
|
||||
#[structopt(long)]
|
||||
pub user_agent: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Params<'a> {
|
||||
pub module: Option<&'a String>,
|
||||
pub threads: usize,
|
||||
pub verbose: u64,
|
||||
pub stdin: bool,
|
||||
@@ -39,25 +52,61 @@ pub struct Params<'a> {
|
||||
pub grant_full_keyring: bool,
|
||||
pub deny_keyring: bool,
|
||||
pub exit_on_error: bool,
|
||||
pub proxy: Option<SocketAddr>,
|
||||
pub user_agent: Option<&'a String>,
|
||||
}
|
||||
|
||||
impl<'a> Params<'a> {
|
||||
pub fn get_module(&self, rl: &Shell) -> Result<Module> {
|
||||
let module = if let Some(module) = self.module {
|
||||
rl.library().get(module)?
|
||||
.clone()
|
||||
} else {
|
||||
rl.module()
|
||||
.map(|m| m.to_owned())
|
||||
.ok_or_else(|| format_err!("No module selected"))?
|
||||
};
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
pub fn get_proxy(&self, rl: &Shell) -> Option<SocketAddr> {
|
||||
if self.proxy.is_some() {
|
||||
self.proxy
|
||||
} else {
|
||||
rl.config().network.proxy
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_user_agent(&self, rl: &Shell) -> Option<String> {
|
||||
if let Some(user_agent) = self.user_agent {
|
||||
Some(user_agent.to_string())
|
||||
} else {
|
||||
rl.config().network.user_agent.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a args::Run> for Params<'a> {
|
||||
fn from(args: &args::Run) -> Params {
|
||||
Params {
|
||||
threads: args.threads,
|
||||
verbose: args.verbose,
|
||||
module: args.run.module.as_ref(),
|
||||
threads: args.run.threads,
|
||||
verbose: args.run.verbose,
|
||||
stdin: args.stdin,
|
||||
grants: &args.grants,
|
||||
grant_full_keyring: args.grant_full_keyring,
|
||||
deny_keyring: args.deny_keyring,
|
||||
exit_on_error: args.exit_on_error,
|
||||
proxy: args.run.proxy,
|
||||
user_agent: args.run.user_agent.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Args> for Params<'static> {
|
||||
fn from(args: Args) -> Params<'static> {
|
||||
impl<'a> From<&'a Args> for Params<'a> {
|
||||
fn from(args: &Args) -> Params {
|
||||
Params {
|
||||
module: args.module.as_ref(),
|
||||
threads: args.threads,
|
||||
verbose: args.verbose,
|
||||
stdin: false,
|
||||
@@ -65,6 +114,8 @@ impl From<Args> for Params<'static> {
|
||||
grant_full_keyring: false,
|
||||
deny_keyring: false,
|
||||
exit_on_error: false,
|
||||
proxy: args.proxy,
|
||||
user_agent: args.user_agent.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,15 +204,14 @@ fn get_args(rl: &mut Shell, module: &Module) -> Result<Vec<(serde_json::Value, O
|
||||
}
|
||||
|
||||
pub fn dump_sandbox_init_msg(rl: &mut Shell, params: Params, options: HashMap<String, String>) -> Result<()> {
|
||||
let module = rl.module()
|
||||
.map(|m| m.to_owned())
|
||||
.ok_or_else(|| format_err!("No module selected"))?;
|
||||
let module = params.get_module(rl)?;
|
||||
let proxy = params.get_proxy(rl);
|
||||
let user_agent = params.get_user_agent(rl);
|
||||
|
||||
prepare_keyring(rl.keyring_mut(), &module, ¶ms)?;
|
||||
let keyring = rl.keyring().request_keys(&module);
|
||||
|
||||
let dns_config = Resolver::from_system_v4()?;
|
||||
let proxy = rl.config().network.proxy;
|
||||
|
||||
let args = get_args(rl, &module)?;
|
||||
for (arg, _pretty_arg, blobs) in args {
|
||||
@@ -169,6 +219,7 @@ pub fn dump_sandbox_init_msg(rl: &mut Shell, params: Params, options: HashMap<St
|
||||
keyring.clone(),
|
||||
dns_config.clone(),
|
||||
proxy,
|
||||
user_agent.clone(),
|
||||
options.clone(),
|
||||
module.clone(),
|
||||
arg,
|
||||
@@ -181,15 +232,15 @@ pub fn dump_sandbox_init_msg(rl: &mut Shell, params: Params, options: HashMap<St
|
||||
}
|
||||
|
||||
pub fn execute(rl: &mut Shell, params: Params, options: HashMap<String, String>) -> Result<()> {
|
||||
let module = rl.module()
|
||||
.map(|m| m.to_owned())
|
||||
.ok_or_else(|| format_err!("No module selected"))?;
|
||||
let module = params.get_module(rl)?;
|
||||
let proxy = params.get_proxy(rl);
|
||||
let user_agent = params.get_user_agent(rl);
|
||||
|
||||
prepare_keyring(rl.keyring_mut(), &module, ¶ms)?;
|
||||
let args = get_args(rl, &module)?;
|
||||
|
||||
rl.signal_register().catch_ctrl();
|
||||
let errors = worker::spawn(rl, &module, &mut Ratelimiter::new(), args, ¶ms, rl.config().network.proxy, options);
|
||||
let errors = worker::spawn(rl, &module, &mut Ratelimiter::new(), args, ¶ms, proxy, user_agent, options);
|
||||
rl.signal_register().reset_ctrlc();
|
||||
|
||||
if errors > 0 {
|
||||
@@ -212,6 +263,6 @@ impl Cmd for Args {
|
||||
Some(options) => options.clone(),
|
||||
_ => HashMap::new(),
|
||||
};
|
||||
execute(rl, self.into(), options)
|
||||
execute(rl, Params::from(&self), options)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,4 +75,5 @@ fn default_registry() -> String {
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct NetworkConfig {
|
||||
pub proxy: Option<SocketAddr>,
|
||||
pub user_agent: Option<String>,
|
||||
}
|
||||
|
||||
@@ -164,6 +164,11 @@ impl Database {
|
||||
self.autonoscope.rules()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn autonoscope(&self) -> &RuleSet {
|
||||
&self.autonoscope
|
||||
}
|
||||
|
||||
/// Returns true if we didn't have this value yet
|
||||
pub fn insert_generic(&self, object: Insert) -> Result<Option<(DbChange, i32)>> {
|
||||
let scoped = self.autonoscope.matches(&object)?;
|
||||
@@ -460,6 +465,11 @@ impl Filter {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn any() -> Filter {
|
||||
Filter::new("1")
|
||||
}
|
||||
|
||||
fn escape(value: &str) -> String {
|
||||
let mut out = String::from("'");
|
||||
for c in value.chars() {
|
||||
@@ -522,7 +532,7 @@ impl Filter {
|
||||
|
||||
if args.is_empty() {
|
||||
debug!("Using filter with no condition");
|
||||
return Ok(Filter::new("1"));
|
||||
return Ok(Filter::any());
|
||||
}
|
||||
|
||||
Self::parse(args)
|
||||
|
||||
@@ -215,6 +215,7 @@ pub struct LuaState {
|
||||
geoip: Option<Mutex<Lazy<MaxmindReader, Arc<GeoIP>>>>,
|
||||
asn: Option<Mutex<Lazy<MaxmindReader, Arc<AsnDB>>>>,
|
||||
proxy: Option<SocketAddr>,
|
||||
user_agent: Option<String>,
|
||||
options: HashMap<String, String>,
|
||||
}
|
||||
|
||||
@@ -372,7 +373,15 @@ impl State for LuaState {
|
||||
let mtx = self.http_sessions.lock().unwrap();
|
||||
let session = mtx.get(session_id).expect("Invalid session reference"); // TODO
|
||||
|
||||
HttpRequest::new(session, method, url, options, || format!("sn0int/{}", env!("CARGO_PKG_VERSION")))
|
||||
let user_agent = if let Some(user_agent) = &options.user_agent {
|
||||
user_agent.to_string()
|
||||
} else if let Some(user_agent) = &self.user_agent {
|
||||
user_agent.to_string()
|
||||
} else {
|
||||
format!("sn0int/{}", env!("CARGO_PKG_VERSION"))
|
||||
};
|
||||
|
||||
HttpRequest::new(session, method, url, user_agent, options)
|
||||
}
|
||||
|
||||
fn get_blob(&self, id: &str) -> Result<Arc<Blob>> {
|
||||
@@ -472,6 +481,7 @@ pub fn ctx<'a>(env: Environment, logger: Arc<Mutex<Box<dyn IpcChild>>>) -> (hlua
|
||||
geoip,
|
||||
asn,
|
||||
proxy: env.proxy,
|
||||
user_agent: env.user_agent,
|
||||
options: env.options,
|
||||
});
|
||||
|
||||
@@ -517,6 +527,9 @@ pub fn ctx<'a>(env: Environment, logger: Arc<Mutex<Box<dyn IpcChild>>>) -> (hlua
|
||||
runtime::http_fetch_json(&mut lua, state.clone());
|
||||
runtime::img_exif(&mut lua, state.clone());
|
||||
runtime::img_load(&mut lua, state.clone());
|
||||
runtime::img_ahash(&mut lua, state.clone());
|
||||
runtime::img_dhash(&mut lua, state.clone());
|
||||
runtime::img_phash(&mut lua, state.clone());
|
||||
runtime::img_nudity(&mut lua, state.clone());
|
||||
runtime::info(&mut lua, state.clone());
|
||||
runtime::intval(&mut lua, state.clone());
|
||||
@@ -662,6 +675,7 @@ impl Script {
|
||||
let keyring = Vec::new();
|
||||
let dns_config = Resolver::from_system_v4()?;
|
||||
let proxy = None;
|
||||
let user_agent = None;
|
||||
let psl = PslReader::String(r#"
|
||||
// ===BEGIN ICANN DOMAINS===
|
||||
com
|
||||
@@ -679,6 +693,7 @@ a.prod.fastly.net
|
||||
keyring,
|
||||
dns_config,
|
||||
proxy,
|
||||
user_agent,
|
||||
options: HashMap::new(),
|
||||
blobs: Vec::new(),
|
||||
psl,
|
||||
|
||||
@@ -34,6 +34,7 @@ pub struct Environment {
|
||||
pub keyring: Vec<KeyRingEntry>,
|
||||
pub dns_config: Resolver,
|
||||
pub proxy: Option<SocketAddr>,
|
||||
pub user_agent: Option<String>,
|
||||
pub options: HashMap<String, String>,
|
||||
pub blobs: Vec<Blob>,
|
||||
pub psl: PslReader,
|
||||
|
||||
@@ -50,12 +50,67 @@ pub enum Target {
|
||||
CryptoAddrs(Filter),
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn domains(&self) -> Option<&Filter> {
|
||||
if let Target::Domains(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subdomains(&self) -> Option<&Filter> {
|
||||
if let Target::Subdomains(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ipaddrs(&self) -> Option<&Filter> {
|
||||
if let Target::IpAddrs(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn urls(&self) -> Option<&Filter> {
|
||||
if let Target::Urls(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ports(&self) -> Option<&Filter> {
|
||||
if let Target::Ports(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn netblocks(&self) -> Option<&Filter> {
|
||||
if let Target::Netblocks(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Filter {
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
#[inline]
|
||||
pub fn any() -> db::Filter {
|
||||
db::Filter::any()
|
||||
}
|
||||
|
||||
pub fn parse_optional(&self) -> Result<db::Filter> {
|
||||
db::Filter::parse_optional(&self.args)
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ pub fn run(geoip: Option<MaxmindReader>, asn: Option<MaxmindReader>, psl: PslRea
|
||||
keyring: start.keyring,
|
||||
dns_config: start.dns_config,
|
||||
proxy: start.proxy,
|
||||
user_agent: start.user_agent,
|
||||
options: start.options,
|
||||
blobs: start.blobs,
|
||||
psl,
|
||||
|
||||
@@ -12,6 +12,7 @@ pub struct StartCommand {
|
||||
pub keyring: Vec<KeyRingEntry>,
|
||||
pub dns_config: Resolver,
|
||||
pub proxy: Option<SocketAddr>,
|
||||
pub user_agent: Option<String>,
|
||||
pub options: HashMap<String, String>,
|
||||
pub module: Module,
|
||||
pub arg: serde_json::Value,
|
||||
@@ -23,6 +24,7 @@ impl StartCommand {
|
||||
keyring: Vec<KeyRingEntry>,
|
||||
dns_config: Resolver,
|
||||
proxy: Option<SocketAddr>,
|
||||
user_agent: Option<String>,
|
||||
options: HashMap<String, String>,
|
||||
module: Module,
|
||||
arg: serde_json::Value,
|
||||
@@ -33,6 +35,7 @@ impl StartCommand {
|
||||
keyring,
|
||||
dns_config,
|
||||
proxy,
|
||||
user_agent,
|
||||
options,
|
||||
module,
|
||||
arg,
|
||||
|
||||
@@ -105,6 +105,7 @@ pub fn run(module: Module,
|
||||
verbose: u64,
|
||||
has_stdin: bool,
|
||||
proxy: Option<SocketAddr>,
|
||||
user_agent: Option<String>,
|
||||
options: HashMap<String, String>,
|
||||
blobs: Vec<Blob>,
|
||||
) -> Result<ExitEvent> {
|
||||
@@ -117,7 +118,7 @@ pub fn run(module: Module,
|
||||
};
|
||||
|
||||
let mut ipc_parent = IpcParent::setup(&module)?;
|
||||
ipc_parent.send_start(&StartCommand::new(verbose, keyring, dns_config, proxy, options, module, arg, blobs))?;
|
||||
ipc_parent.send_start(&StartCommand::new(verbose, keyring, dns_config, proxy, user_agent, options, module, arg, blobs))?;
|
||||
|
||||
let exit = loop {
|
||||
match ipc_parent.recv()? {
|
||||
|
||||
19
src/main.rs
19
src/main.rs
@@ -2,6 +2,7 @@ use env_logger::Env;
|
||||
use sn0int::args::{self, Args, SubCommand};
|
||||
use sn0int::auth;
|
||||
use sn0int::cmd::{self, LiteCmd};
|
||||
use sn0int::cmd::run_cmd::Params;
|
||||
use sn0int::config::Config;
|
||||
use sn0int::db;
|
||||
use sn0int::errors::*;
|
||||
@@ -23,8 +24,11 @@ use std::path::Path;
|
||||
fn run_run(gargs: &Args, args: &args::Run, config: &Config) -> Result<()> {
|
||||
let mut rl = shell::init(gargs, config, false)?;
|
||||
|
||||
let module = args.run.module.as_ref()
|
||||
.ok_or_else(|| format_err!("Module is required"))?;
|
||||
|
||||
let module = if args.file {
|
||||
let path = Path::new(&args.module);
|
||||
let path = Path::new(&module);
|
||||
|
||||
let filename = path.file_stem()
|
||||
.ok_or_else(|| format_err!("Failed to decode filename"))?
|
||||
@@ -34,7 +38,7 @@ fn run_run(gargs: &Args, args: &args::Run, config: &Config) -> Result<()> {
|
||||
Module::load(&path.to_path_buf(), "anonymous", filename, true)
|
||||
.context(format!("Failed to parse {:?}", path))?
|
||||
} else {
|
||||
rl.library().get(&args.module)?
|
||||
rl.library().get(module)?
|
||||
.clone()
|
||||
};
|
||||
|
||||
@@ -47,10 +51,14 @@ fn run_run(gargs: &Args, args: &args::Run, config: &Config) -> Result<()> {
|
||||
rl.set_target(Some(target));
|
||||
}
|
||||
|
||||
let mut params = Params::from(args);
|
||||
// The module was already set and loaded
|
||||
params.module = None;
|
||||
|
||||
if args.dump_sandbox_init_msg {
|
||||
cmd::run_cmd::dump_sandbox_init_msg(&mut rl, args.into(), Opt::collect(&args.options))
|
||||
cmd::run_cmd::dump_sandbox_init_msg(&mut rl, params, Opt::collect(&args.options))
|
||||
} else {
|
||||
cmd::run_cmd::execute(&mut rl, args.into(), Opt::collect(&args.options))
|
||||
cmd::run_cmd::execute(&mut rl, params, Opt::collect(&args.options))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +131,9 @@ fn run() -> Result<()> {
|
||||
Some(SubCommand::Activity(activity)) => run_cmd(&args, activity, &config),
|
||||
Some(SubCommand::Scope(scope)) => run_cmd(&args, scope, &config),
|
||||
Some(SubCommand::Noscope(noscope)) => run_cmd(&args, noscope, &config),
|
||||
Some(SubCommand::Autoscope(autoscope)) => run_cmd(&args, autoscope, &config),
|
||||
Some(SubCommand::Autonoscope(autonoscope)) => run_cmd(&args, autonoscope, &config),
|
||||
Some(SubCommand::Rescope(rescope)) => run_cmd(&args, rescope, &config),
|
||||
Some(SubCommand::Workspace(workspace)) => workspace.run(&config),
|
||||
Some(SubCommand::Fsck(fsck)) => run_cmd(&args, fsck, &config),
|
||||
Some(SubCommand::Export(export)) => run_cmd(&args, export, &config),
|
||||
|
||||
@@ -115,6 +115,14 @@ impl Scopable for Account {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::accounts::dsl::*;
|
||||
diesel::update(accounts.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::accounts::dsl::*;
|
||||
|
||||
|
||||
@@ -94,6 +94,14 @@ impl Scopable for Breach {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::breaches::dsl::*;
|
||||
diesel::update(breaches.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::breaches::dsl::*;
|
||||
|
||||
|
||||
@@ -113,6 +113,14 @@ impl Scopable for CryptoAddr {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::cryptoaddrs::dsl::*;
|
||||
diesel::update(cryptoaddrs.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::cryptoaddrs::dsl::*;
|
||||
|
||||
|
||||
@@ -99,6 +99,14 @@ impl Scopable for Device {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::devices::dsl::*;
|
||||
diesel::update(devices.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::devices::dsl::*;
|
||||
|
||||
|
||||
@@ -94,6 +94,14 @@ impl Scopable for Domain {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::domains::dsl::*;
|
||||
diesel::update(domains.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::domains::dsl::*;
|
||||
|
||||
|
||||
@@ -96,6 +96,14 @@ impl Scopable for Email {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::emails::dsl::*;
|
||||
diesel::update(emails.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::emails::dsl::*;
|
||||
|
||||
|
||||
@@ -116,6 +116,14 @@ impl Scopable for Image {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::images::dsl::*;
|
||||
diesel::update(images.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::images::dsl::*;
|
||||
|
||||
|
||||
@@ -109,6 +109,14 @@ impl Scopable for IpAddr {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::ipaddrs::dsl::*;
|
||||
diesel::update(ipaddrs.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::ipaddrs::dsl::*;
|
||||
|
||||
|
||||
@@ -243,6 +243,8 @@ pub trait Model: Sized {
|
||||
pub trait Scopable: Model {
|
||||
fn scoped(&self) -> bool;
|
||||
|
||||
fn set_scoped(&self, _db: &Database, _value: bool) -> Result<()>;
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize>;
|
||||
|
||||
fn noscope(db: &Database, filter: &Filter) -> Result<usize>;
|
||||
|
||||
@@ -98,6 +98,14 @@ impl Scopable for Netblock {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::netblocks::dsl::*;
|
||||
diesel::update(netblocks.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::netblocks::dsl::*;
|
||||
|
||||
|
||||
@@ -97,6 +97,14 @@ impl Scopable for Network {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::networks::dsl::*;
|
||||
diesel::update(networks.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::networks::dsl::*;
|
||||
|
||||
|
||||
@@ -105,6 +105,14 @@ impl Scopable for PhoneNumber {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::phonenumbers::dsl::*;
|
||||
diesel::update(phonenumbers.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::phonenumbers::dsl::*;
|
||||
|
||||
|
||||
@@ -106,6 +106,14 @@ impl Scopable for Port {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::ports::dsl::*;
|
||||
diesel::update(ports.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::ports::dsl::*;
|
||||
|
||||
|
||||
@@ -98,6 +98,14 @@ impl Scopable for Subdomain {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::subdomains::dsl::*;
|
||||
diesel::update(subdomains.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::subdomains::dsl::*;
|
||||
|
||||
|
||||
@@ -104,6 +104,14 @@ impl Scopable for Url {
|
||||
!self.unscoped
|
||||
}
|
||||
|
||||
fn set_scoped(&self, db: &Database, my_value: bool) -> Result<()> {
|
||||
use crate::schema::urls::dsl::*;
|
||||
diesel::update(urls.filter(id.eq(self.id)))
|
||||
.set(unscoped.eq(!my_value))
|
||||
.execute(db.db())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scope(db: &Database, filter: &Filter) -> Result<usize> {
|
||||
use crate::schema::urls::dsl::*;
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ pub fn exec(rl: &mut Shell, module: &Module, ratelimit: &mut Ratelimiter, option
|
||||
}
|
||||
|
||||
let params = Params {
|
||||
module: None,
|
||||
threads: 1,
|
||||
verbose,
|
||||
stdin: false,
|
||||
@@ -95,13 +96,15 @@ pub fn exec(rl: &mut Shell, module: &Module, ratelimit: &mut Ratelimiter, option
|
||||
grant_full_keyring: false,
|
||||
deny_keyring: false,
|
||||
exit_on_error: false,
|
||||
proxy: None,
|
||||
user_agent: None,
|
||||
};
|
||||
|
||||
prepare_keyring(rl.keyring_mut(), module, ¶ms)?;
|
||||
let args = vec![prepare_arg(notification)?];
|
||||
|
||||
debug!("Executing notification module {:?}", module_name);
|
||||
let errors = worker::spawn(rl, module, ratelimit, args, ¶ms, rl.config().network.proxy, options);
|
||||
let errors = worker::spawn(rl, module, ratelimit, args, ¶ms, rl.config().network.proxy, None, options);
|
||||
debug!("Notification module {:?} exited with {:?} errors", module_name, errors);
|
||||
|
||||
Ok(errors)
|
||||
|
||||
@@ -82,6 +82,7 @@ pub fn run(config: &Config) -> Result<()> {
|
||||
let keyring = Vec::new();
|
||||
let dns_config = Resolver::from_system_v4()?;
|
||||
let proxy = config.network.proxy;
|
||||
let user_agent = config.network.user_agent.clone();
|
||||
|
||||
let cache_dir = paths::cache_dir()?;
|
||||
let psl = PslReader::open(&cache_dir)?;
|
||||
@@ -93,6 +94,7 @@ pub fn run(config: &Config) -> Result<()> {
|
||||
keyring,
|
||||
dns_config,
|
||||
proxy,
|
||||
user_agent,
|
||||
options: HashMap::new(),
|
||||
blobs: Vec::new(),
|
||||
psl,
|
||||
|
||||
@@ -36,6 +36,48 @@ pub fn img_load(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn img_ahash(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("img_ahash", hlua::function1(move |blob: String| -> Result<String> {
|
||||
let img = state.get_blob(&blob)
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
|
||||
let img = gfx::load(&img.bytes)
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
|
||||
let hash = img.perception_hash(gfx::HashAlg::Mean);
|
||||
|
||||
Ok(hash)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn img_dhash(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("img_dhash", hlua::function1(move |blob: String| -> Result<String> {
|
||||
let img = state.get_blob(&blob)
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
|
||||
let img = gfx::load(&img.bytes)
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
|
||||
let hash = img.perception_hash(gfx::HashAlg::Gradient);
|
||||
|
||||
Ok(hash)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn img_phash(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("img_phash", hlua::function1(move |blob: String| -> Result<String> {
|
||||
let img = state.get_blob(&blob)
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
|
||||
let img = gfx::load(&img.bytes)
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
|
||||
let hash = img.perception_hash(gfx::HashAlg::Median);
|
||||
|
||||
Ok(hash)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Nudity {
|
||||
nude: bool,
|
||||
|
||||
@@ -70,6 +70,10 @@ pub fn init() -> Result<()> {
|
||||
ctx.allow_syscall(Syscall::getpeername)?;
|
||||
ctx.allow_syscall(Syscall::gettimeofday)?;
|
||||
ctx.allow_syscall(Syscall::membarrier)?;
|
||||
ctx.allow_syscall(Syscall::statx)?;
|
||||
ctx.allow_syscall(Syscall::lseek)?;
|
||||
#[cfg(target_arch = "arm")]
|
||||
ctx.allow_syscall(Syscall::_llseek)?;
|
||||
|
||||
ctx.set_action_for_syscall(Action::Errno(1), Syscall::openat)?;
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
|
||||
@@ -262,6 +262,7 @@ impl Completer for CmdCompleter {
|
||||
Ok((0, results))
|
||||
}
|
||||
},
|
||||
Command::Rescope => self.filter("rescope", &cmd),
|
||||
Command::Scope => self.filter("scope", &cmd),
|
||||
Command::Select => self.filter("select", &cmd),
|
||||
Command::Workspace => {
|
||||
|
||||
@@ -41,6 +41,7 @@ pub enum Command {
|
||||
Mod,
|
||||
Noscope,
|
||||
Pkg,
|
||||
Rescope,
|
||||
Run,
|
||||
Scope,
|
||||
Set,
|
||||
@@ -74,6 +75,7 @@ impl Command {
|
||||
Command::Mod => "mod",
|
||||
Command::Noscope => "noscope",
|
||||
Command::Pkg => "pkg",
|
||||
Command::Rescope => "rescope",
|
||||
Command::Run => "run",
|
||||
Command::Scope => "scope",
|
||||
Command::Set => "set",
|
||||
@@ -104,6 +106,7 @@ impl Command {
|
||||
Command::Keyring.as_str(),
|
||||
Command::Noscope.as_str(),
|
||||
Command::Pkg.as_str(),
|
||||
Command::Rescope.as_str(),
|
||||
Command::Run.as_str(),
|
||||
Command::Scope.as_str(),
|
||||
Command::Set.as_str(),
|
||||
@@ -137,15 +140,16 @@ impl FromStr for Command {
|
||||
"keyring" => Ok(Command::Keyring),
|
||||
"mod" => Ok(Command::Mod),
|
||||
"noscope" => Ok(Command::Noscope),
|
||||
"pkg" => Ok(Command::Pkg),
|
||||
"run" => Ok(Command::Run),
|
||||
"scope" => Ok(Command::Scope),
|
||||
"set" => Ok(Command::Set),
|
||||
"pkg" => Ok(Command::Pkg),
|
||||
"rescope" => Ok(Command::Rescope),
|
||||
"run" => Ok(Command::Run),
|
||||
"scope" => Ok(Command::Scope),
|
||||
"set" => Ok(Command::Set),
|
||||
"select" => Ok(Command::Select),
|
||||
"stats" => Ok(Command::Stats),
|
||||
"target" => Ok(Command::Target),
|
||||
"use" => Ok(Command::Use),
|
||||
"quickstart" => Ok(Command::Quickstart),
|
||||
"target" => Ok(Command::Target),
|
||||
"use" => Ok(Command::Use),
|
||||
"quickstart" => Ok(Command::Quickstart),
|
||||
"quit" => Ok(Command::Quit),
|
||||
"workspace" => Ok(Command::Workspace),
|
||||
"cal" => Ok(Command::Cal),
|
||||
@@ -496,8 +500,8 @@ pub fn run_once(rl: &mut Shell) -> Result<bool> {
|
||||
match line {
|
||||
Some((Command::Activity, args)) => cmd::<activity_cmd::Args>(rl, &args)?,
|
||||
Some((Command::Add, args)) => cmd::<add_cmd::Args>(rl, &args)?,
|
||||
Some((Command::Autonoscope, args)) => autonoscope_cmd::run(rl, &args)?,
|
||||
Some((Command::Autoscope, args)) => autoscope_cmd::run(rl, &args)?,
|
||||
Some((Command::Autonoscope, args)) => cmd::<autonoscope_cmd::Args>(rl, &args)?,
|
||||
Some((Command::Autoscope, args)) => cmd::<autoscope_cmd::Args>(rl, &args)?,
|
||||
Some((Command::Back, _)) => if rl.take_module().is_none() {
|
||||
return Ok(true);
|
||||
},
|
||||
@@ -510,6 +514,7 @@ pub fn run_once(rl: &mut Shell) -> Result<bool> {
|
||||
},
|
||||
Some((Command::Noscope, args)) => noscope_cmd::run(rl, &args)?,
|
||||
Some((Command::Pkg, args)) => cmd::<pkg_cmd::ArgsInteractive>(rl, &args)?,
|
||||
Some((Command::Rescope, args)) => cmd::<rescope_cmd::Args>(rl, &args)?,
|
||||
Some((Command::Run, args)) => cmd::<run_cmd::Args>(rl, &args)?,
|
||||
Some((Command::Scope, args)) => scope_cmd::run(rl, &args)?,
|
||||
Some((Command::Set, args)) => set_cmd::run(rl, &args)?,
|
||||
|
||||
@@ -437,7 +437,15 @@ impl RatelimitEvent {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn(rl: &mut Shell, module: &Module, ratelimit: &mut Ratelimiter, args: Vec<(serde_json::Value, Option<String>, Vec<Blob>)>, params: &Params, proxy: Option<SocketAddr>, options: HashMap<String, String>) -> usize {
|
||||
pub fn spawn(rl: &mut Shell,
|
||||
module: &Module,
|
||||
ratelimit: &mut Ratelimiter,
|
||||
args: Vec<(serde_json::Value, Option<String>, Vec<Blob>)>,
|
||||
params: &Params,
|
||||
proxy: Option<SocketAddr>,
|
||||
user_agent: Option<String>,
|
||||
options: HashMap<String, String>,
|
||||
) -> usize {
|
||||
// This function hangs if args is empty, so return early if that's the case
|
||||
if args.is_empty() {
|
||||
return 0;
|
||||
@@ -463,6 +471,7 @@ pub fn spawn(rl: &mut Shell, module: &Module, ratelimit: &mut Ratelimiter, args:
|
||||
let tx = tx.clone();
|
||||
let module = module.clone();
|
||||
let keyring = keyring.clone();
|
||||
let user_agent = user_agent.clone();
|
||||
let options = options.clone();
|
||||
let signal_register = rl.signal_register().clone();
|
||||
pool.execute(move || {
|
||||
@@ -476,7 +485,7 @@ pub fn spawn(rl: &mut Shell, module: &Module, ratelimit: &mut Ratelimiter, args:
|
||||
}
|
||||
|
||||
tx.send(Event2::Start);
|
||||
let event = match ipc::parent::run(module, &tx, arg, keyring, verbose, has_stdin, proxy, options, blobs) {
|
||||
let event = match ipc::parent::run(module, &tx, arg, keyring, verbose, has_stdin, proxy, user_agent, options, blobs) {
|
||||
Ok(exit) => exit,
|
||||
Err(err) => ExitEvent::SetupFailed(err.to_string()),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user