18 Commits

Author SHA1 Message Date
dependabot[bot]
6de261bb14 Bump diesel from 1.4.8 to 2.2.3
Bumps [diesel](https://github.com/diesel-rs/diesel) from 1.4.8 to 2.2.3.
- [Release notes](https://github.com/diesel-rs/diesel/releases)
- [Changelog](https://github.com/diesel-rs/diesel/blob/v2.2.3/CHANGELOG.md)
- [Commits](https://github.com/diesel-rs/diesel/compare/v1.4.8...v2.2.3)

---
updated-dependencies:
- dependency-name: diesel
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-23 19:02:06 +00:00
kpcyrd
1f71836dbc Merge pull request #262 from kpcyrd/update
Update dependencies
2024-08-22 21:18:11 +02:00
kpcyrd
cfe35bb710 Update dependencies 2024-08-22 21:08:02 +02:00
kpcyrd
77c5e3e115 Merge pull request #256 from kpcyrd/dependabot/cargo/shlex-1.3.0
Bump shlex from 1.1.0 to 1.3.0
2024-01-25 01:50:12 +01:00
dependabot[bot]
44c708fe48 Bump shlex from 1.1.0 to 1.3.0
Bumps [shlex](https://github.com/comex/rust-shlex) from 1.1.0 to 1.3.0.
- [Changelog](https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/comex/rust-shlex/commits)

---
updated-dependencies:
- dependency-name: shlex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 21:42:37 +00:00
kpcyrd
dd52fafae0 Merge pull request #253 from kpcyrd/dependabot/cargo/openssl-0.10.60
Bump openssl from 0.10.57 to 0.10.60
2023-11-29 13:03:35 +01:00
dependabot[bot]
62cf78c34a Bump openssl from 0.10.57 to 0.10.60
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.57 to 0.10.60.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.57...openssl-v0.10.60)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 21:44:45 +00:00
kpcyrd
c1a2d411b3 Merge pull request #251 from sebdufourcq/main
Update docker command line in README.md
2023-09-17 05:16:48 +02:00
Sébastien DUFOURCQ
5554611f63 Update docker command line in README.md
https://github.com/kpcyrd/sn0int/issues/237
2023-09-16 23:11:53 +02:00
kpcyrd
b49aba5a8a Release v0.26.0 2023-09-09 13:15:17 +02:00
kpcyrd
f9abd33c87 Merge pull request #250 from kpcyrd/update
Update dependencies
2023-09-08 18:36:16 +02:00
kpcyrd
64d59c6eca Change target for runtime::http::tests::verify_request 2023-09-08 18:21:21 +02:00
kpcyrd
ea94650802 Update dependencies 2023-08-31 11:26:06 +02:00
kpcyrd
fe588f3b1d Merge pull request #247 from kpcyrd/clap-4
Port to clap 4
2023-08-31 02:07:22 +02:00
kpcyrd
88cb8b55f0 Port to clap 4
Fixes a bug in zsh completions
2023-07-08 15:01:41 +02:00
kpcyrd
1eeb1508e5 Merge pull request #242 from kpcyrd/dependabot/cargo/openssl-0.10.48
Bump openssl from 0.10.45 to 0.10.48
2023-03-25 13:30:38 +00:00
dependabot[bot]
c2f4edf475 Bump openssl from 0.10.45 to 0.10.48
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.45 to 0.10.48.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.45...openssl-v0.10.48)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-25 01:34:26 +00:00
kpcyrd
ed55944e6e Simplify install instructions for debian (reported by @einwickler) 2023-02-16 00:52:14 +01:00
72 changed files with 2409 additions and 1779 deletions

1872
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "sn0int"
version = "0.25.0"
version = "0.26.0"
description = "Semi-automatic OSINT framework and package manager"
authors = ["kpcyrd <git@rxv.cc>"]
license = "GPL-3.0"
@@ -32,19 +32,20 @@ assets = [
sqlite-bundled = ["libsqlite3-sys/bundled"]
[dependencies]
sn0int-common = { version="0.13.0", path="sn0int-common" }
sn0int-std = { version="=0.25.0", path="sn0int-std" }
sn0int-common = { version="0.14.0", path="sn0int-common" }
sn0int-std = { version="=0.26.0", path="sn0int-std" }
rustyline = "10.0"
log = "0.4"
env_logger = "0.9"
env_logger = "0.11"
hlua-badtouch = "0.4"
structopt = "0.3"
clap = { version = "4.3.11", features = ["derive", "env"] }
clap_complete = "4.3.2"
failure = "0.1"
rand = "0.8"
colored = "2"
lazy_static = "1.0"
shellwords = "1.0"
diesel = { version = "1.0.0", features = ["sqlite", "chrono"] }
diesel = { version = "2.2.3", features = ["sqlite", "chrono"] }
diesel_migrations = { version = "1.3.0", features = ["sqlite"] }
libsqlite3-sys = { version = "0.22.0", features = ["bundled-windows"] }
chrono = { version = "0.4", features = ["serde"] }
@@ -59,22 +60,21 @@ serde_urlencoded = "0.7"
serde_json = "1.0"
crossbeam-channel = "0.5"
ctrlc = "3.1"
opener = "0.5"
opener = "0.6"
separator = "0.4"
maplit = "1.0.1"
sloppy-rfc4880 = "0.2"
regex = "1.0"
toml = "0.5"
toml = "0.7"
threadpool = "1.7"
atty = "0.2"
semver = "1"
bytes = "0.4"
bytesize = "1.0"
ipnetwork = "0.18"
strum = "0.24"
strum_macros = "0.24"
ipnetwork = "0.20"
strum = "0.25"
strum_macros = "0.25"
embedded-triple = "0.1.0"
humansize = "1.1.0"
humansize = "2"
digest = "0.10"
md-5 = "0.10"
@@ -92,7 +92,7 @@ os-version = "0.2"
caps = "0.5"
#syscallz = { path="../syscallz-rs" }
syscallz = "0.16"
nix = "0.24"
nix = { version = "0.27", features = ["fs"] }
[target.'cfg(target_os="openbsd")'.dependencies]
pledge = "0.4"

View File

@@ -1,4 +1,4 @@
FROM rust:alpine3.15
FROM rust:alpine3.20
ENV RUSTFLAGS="-C target-feature=-crt-static"
RUN apk add --no-cache musl-dev sqlite-dev libseccomp-dev libsodium-dev
WORKDIR /usr/src/sn0int
@@ -10,7 +10,7 @@ RUN --mount=type=cache,target=/var/cache/buildkit \
cp -v /var/cache/buildkit/target/release/sn0int /
RUN strip /sn0int
FROM alpine:3.15
FROM alpine:3.20
RUN apk add --no-cache libgcc sqlite-libs libseccomp libsodium
COPY --from=0 /sn0int /usr/local/bin/sn0int
VOLUME ["/data", "/cache"]

View File

@@ -61,19 +61,16 @@ Mac OSX
Debian/Ubuntu/Kali
There are prebuilt packages signed by a debian maintainer. We can import the
key for this repository out of the debian keyring.
There are prebuilt packages signed by a debian maintainer:
apt install debian-keyring
gpg -a --export --keyring /usr/share/keyrings/debian-maintainers.gpg git@rxv.cc | apt-key add -
apt-key adv --keyserver keyserver.ubuntu.com --refresh-keys git@rxv.cc
echo deb http://apt.vulns.sexy stable main > /etc/apt/sources.list.d/apt-vulns-sexy.list
sudo apt install curl sq
curl -sSf https://apt.vulns.sexy/kpcyrd.pgp | sq dearmor | sudo tee /etc/apt/trusted.gpg.d/apt-vulns-sexy.gpg > /dev/null
echo deb http://apt.vulns.sexy stable main | sudo tee /etc/apt/sources.list.d/apt-vulns-sexy.list
apt update
apt install sn0int
Docker
docker run --rm --init -it -v "$PWD/.cache:/cache" -v "$PWD/.data:/data" kpcyrd/sn0int
docker run --rm --init -it -v "$PWD/.cache:/cache" -v "$PWD/.data:/data" ghcr.io/kpcyrd/sn0int
Alpine

View File

@@ -19,34 +19,31 @@ Mac OSX
$ brew install sn0int
Debian >= bullseye, Ubuntu >= 20.04, Kali
Debian >= bookwork, Ubuntu >= 22.10, Kali
-----------------------------------------
There are prebuilt packages signed by a debian maintainer. We can import the
key for this repository out of the debian keyring.
There are prebuilt packages signed by a debian maintainer:
.. code-block:: bash
$ sudo apt install debian-keyring
$ gpg -a --export --keyring /usr/share/keyrings/debian-maintainers.gpg kpcyrd@archlinux.org | sudo tee /etc/apt/trusted.gpg.d/apt-vulns-sexy.gpg
$ sudo apt install curl sq
$ curl -sSf https://apt.vulns.sexy/kpcyrd.pgp | sq keyring filter -B --handle 64B13F7117D6E07D661BBCE0FE763A64F5E54FD6 | sudo tee /etc/apt/trusted.gpg.d/apt-vulns-sexy.gpg > /dev/null
$ echo deb http://apt.vulns.sexy stable main | sudo tee /etc/apt/sources.list.d/apt-vulns-sexy.list
$ sudo apt update
$ sudo apt install sn0int
$ apt update
$ apt install sn0int
Debian <= buster, Ubuntu <= 19.10
---------------------------------
Debian <= bullseye, Ubuntu <= 22.04
-----------------------------------
There are prebuilt packages signed by a debian maintainer. We can import the
key for this repository out of the debian keyring.
There are prebuilt packages signed by a debian maintainer:
.. code-block:: bash
$ sudo apt install debian-keyring
$ gpg -a --export --keyring /usr/share/keyrings/debian-maintainers.gpg git@rxv.cc | sudo apt-key add -
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --refresh-keys git@rxv.cc
$ sudo apt install curl sq
$ curl -sSf https://apt.vulns.sexy/kpcyrd.pgp | sq dearmor | sudo tee /etc/apt/trusted.gpg.d/apt-vulns-sexy.gpg > /dev/null
$ echo deb http://apt.vulns.sexy stable main | sudo tee /etc/apt/sources.list.d/apt-vulns-sexy.list
$ sudo apt update
$ sudo apt install sn0int
$ apt update
$ apt install sn0int
Fedora/CentOS/Redhat
--------------------

View File

@@ -1,6 +1,7 @@
#[macro_use] extern crate boxxy;
extern crate sn0int;
#[macro_use]
extern crate boxxy;
extern crate env_logger;
extern crate sn0int;
fn stage1(sh: &mut boxxy::Shell, _args: Vec<String>) -> Result<(), boxxy::Error> {
shprintln!(sh, "[*] starting stage1");
@@ -14,8 +15,6 @@ fn main() {
println!("stage1 activate sandbox");
let toolbox = boxxy::Toolbox::new().with(vec![
("stage1", stage1),
]);
let toolbox = boxxy::Toolbox::new().with(vec![("stage1", stage1)]);
boxxy::Shell::new(toolbox).run()
}

View File

@@ -1,19 +1,19 @@
use clap::Parser;
use sn0int::errors::*;
use sn0int::geoip::{AsnDB, GeoIP, Maxmind};
use sn0int::paths;
use std::net::IpAddr;
use std::path::Path;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum Args {
#[structopt(name="asn")]
#[command(name = "asn")]
Asn(AsnArgs),
#[structopt(name="geoip")]
#[command(name = "geoip")]
GeoIP(GeoIPArgs),
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AsnArgs {
ip: IpAddr,
}
@@ -30,7 +30,7 @@ impl AsnArgs {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct GeoIPArgs {
ip: IpAddr,
}
@@ -47,9 +47,8 @@ impl GeoIPArgs {
}
}
fn run() -> Result<()> {
let args = Args::from_args();
let args = Args::parse();
debug!("{:?}", args);
let cache_dir = paths::cache_dir()?;
match args {

View File

@@ -1,21 +1,20 @@
use clap::Parser;
use sn0int::term::{Spinner, StackedSpinners, SPINNERS};
use std::thread;
use std::time::Duration;
use sn0int::term::{SPINNERS, Spinner, StackedSpinners};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum Args {
#[structopt(name="single")]
#[command(name = "single")]
Single(Single),
#[structopt(name="stacked")]
#[command(name = "stacked")]
Stacked(Stacked),
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Single {
idx: usize,
#[structopt(long="ticks", default_value="100")]
#[structopt(long = "ticks", default_value = "100")]
ticks: usize,
}
@@ -32,9 +31,8 @@ impl Single {
}
}
#[derive(Debug, StructOpt)]
pub struct Stacked {
}
#[derive(Debug, Parser)]
pub struct Stacked {}
impl Stacked {
fn run(&self) {
@@ -59,7 +57,7 @@ impl Stacked {
}
fn main() {
let args = Args::from_args();
let args = Args::parse();
match args {
Args::Single(args) => args.run(),
Args::Stacked(args) => args.run(),

View File

@@ -1,6 +1,6 @@
[package]
name = "sn0int-common"
version = "0.13.0"
version = "0.14.0"
description = "sn0int - common code"
authors = ["kpcyrd <git@rxv.cc>"]
license = "GPL-3.0"
@@ -13,3 +13,4 @@ serde = { version = "1.0", features=["derive"] }
rocket_failure_errors = "0.2"
anyhow = "1.0"
nom = "7.0"
clap = { version = "4.3.11", features = ["derive"] }

View File

@@ -18,15 +18,15 @@ pub fn valid_name(name: &str) -> Result<()> {
}
fn module(s: &str) -> nom::IResult<&str, ModuleID> {
let (input, (author, _, name)) = nom::sequence::tuple((
token,
nom::bytes::complete::tag("/"),
token,
))(s)?;
Ok((input, ModuleID {
author: author.to_string(),
name: name.to_string(),
}))
let (input, (author, _, name)) =
nom::sequence::tuple((token, nom::bytes::complete::tag("/"), token))(s)?;
Ok((
input,
ModuleID {
author: author.to_string(),
name: name.to_string(),
},
))
}
#[inline]
@@ -34,7 +34,7 @@ fn token(s: &str) -> nom::IResult<&str, &str> {
nom::bytes::complete::take_while1(valid_char)(s)
}
#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ModuleID {
pub author: String,
pub name: String,
@@ -50,8 +50,8 @@ impl FromStr for ModuleID {
type Err = Error;
fn from_str(s: &str) -> Result<ModuleID> {
let (trailing, module) = module(s)
.map_err(|err| anyhow!("Failed to parse module id: {:?}", err))?;
let (trailing, module) =
module(s).map_err(|err| anyhow!("Failed to parse module id: {:?}", err))?;
if !trailing.is_empty() {
bail!("Trailing data in module id");
}
@@ -85,10 +85,13 @@ mod tests {
#[test]
fn verify_valid() {
let result = ModuleID::from_str("kpcyrd/foo").expect("parse");
assert_eq!(result, ModuleID {
author: "kpcyrd".to_string(),
name: "foo".to_string(),
});
assert_eq!(
result,
ModuleID {
author: "kpcyrd".to_string(),
name: "foo".to_string(),
}
);
}
#[test]

View File

@@ -158,8 +158,7 @@ impl FromStr for Metadata {
type Err = Error;
fn from_str(code: &str) -> Result<Metadata> {
let (_, lines) = metalines(code)
.map_err(|_| format_err!("Failed to parse header"))?;
let (_, lines) = metalines(code).map_err(|_| format_err!("Failed to parse header"))?;
let mut data = NewMetadata::default();
@@ -194,24 +193,26 @@ pub struct NewMetadata<'a> {
impl<'a> NewMetadata<'a> {
fn try_from(self) -> Result<Metadata> {
let description = self.description.ok_or_else(|| format_err!("Description is required"))?;
let version = self.version.ok_or_else(|| format_err!("Version is required"))?;
let description = self
.description
.ok_or_else(|| format_err!("Description is required"))?;
let version = self
.version
.ok_or_else(|| format_err!("Version is required"))?;
let source = match self.source {
Some(x) => Some(x.parse()?),
_ => None,
};
let keyring_access = self.keyring_access.into_iter()
.map(String::from)
.collect();
let keyring_access = self.keyring_access.into_iter().map(String::from).collect();
let stealth = match self.stealth {
Some(x) => x.parse()?,
_ => Stealth::Normal,
};
let authors = self.authors.into_iter()
.map(String::from)
.collect();
let authors = self.authors.into_iter().map(String::from).collect();
let repository = self.repository.map(String::from);
let license = self.license.ok_or_else(|| format_err!("License is required"))?;
let license = self
.license
.ok_or_else(|| format_err!("License is required"))?;
let license = license.parse()?;
Ok(Metadata {
@@ -229,10 +230,7 @@ impl<'a> NewMetadata<'a> {
fn metaline(input: &str) -> IResult<&str, (EntryType, &str)> {
let (input, _) = tag("-- ")(input)?;
let (input, name) = map_res(
take_until(": "),
EntryType::from_str
)(input)?;
let (input, name) = map_res(take_until(": "), EntryType::from_str)(input)?;
let (input, _) = tag(": ")(input)?;
let (input, value) = take_until("\n")(input)?;
let (input, _) = tag("\n")(input)?;
@@ -241,14 +239,10 @@ fn metaline(input: &str) -> IResult<&str, (EntryType, &str)> {
}
fn metalines(input: &str) -> IResult<&str, Vec<(EntryType, &str)>> {
let (input, lines) = fold_many0(
metaline,
Vec::new,
|mut acc: Vec<_>, item| {
acc.push(item);
acc
}
)(input)?;
let (input, lines) = fold_many0(metaline, Vec::new, |mut acc: Vec<_>, item| {
acc.push(item);
acc
})(input)?;
let (input, _) = tag("\n")(input)?;
Ok((input, lines))
@@ -260,27 +254,34 @@ mod tests {
#[test]
fn verify_simple() {
let metadata = Metadata::from_str(r#"-- Description: Hello world, this is my description
let metadata = Metadata::from_str(
r#"-- Description: Hello world, this is my description
-- Version: 1.0.0
-- Source: domains
-- License: WTFPL
"#).expect("parse");
assert_eq!(metadata, Metadata {
description: "Hello world, this is my description".to_string(),
version: "1.0.0".to_string(),
license: License::WTFPL,
source: Some(Source::Domains),
stealth: Stealth::Normal,
authors: vec![],
repository: None,
keyring_access: Vec::new(),
});
"#,
)
.expect("parse");
assert_eq!(
metadata,
Metadata {
description: "Hello world, this is my description".to_string(),
version: "1.0.0".to_string(),
license: License::WTFPL,
source: Some(Source::Domains),
stealth: Stealth::Normal,
authors: vec![],
repository: None,
keyring_access: Vec::new(),
}
);
}
#[test]
fn verify_much_metadata() {
let metadata = Metadata::from_str(r#"-- Description: Hello world, this is my description
let metadata = Metadata::from_str(
r#"-- Description: Hello world, this is my description
-- Version: 1.0.0
-- Source: domains
-- Stealth: passive
@@ -289,59 +290,74 @@ mod tests {
-- Repository: https://github.com/kpcyrd/sn0int
-- License: WTFPL
"#).expect("parse");
assert_eq!(metadata, Metadata {
description: "Hello world, this is my description".to_string(),
version: "1.0.0".to_string(),
license: License::WTFPL,
source: Some(Source::Domains),
stealth: Stealth::Passive,
authors: vec![
"kpcyrd <git at rxv dot cc>".to_string(),
"kpcyrd's cat".to_string(),
],
repository: Some("https://github.com/kpcyrd/sn0int".to_string()),
keyring_access: Vec::new(),
});
"#,
)
.expect("parse");
assert_eq!(
metadata,
Metadata {
description: "Hello world, this is my description".to_string(),
version: "1.0.0".to_string(),
license: License::WTFPL,
source: Some(Source::Domains),
stealth: Stealth::Passive,
authors: vec![
"kpcyrd <git at rxv dot cc>".to_string(),
"kpcyrd's cat".to_string(),
],
repository: Some("https://github.com/kpcyrd/sn0int".to_string()),
keyring_access: Vec::new(),
}
);
}
#[test]
fn verify_no_source() {
let metadata = Metadata::from_str(r#"-- Description: Hello world, this is my description
let metadata = Metadata::from_str(
r#"-- Description: Hello world, this is my description
-- Version: 1.0.0
-- License: WTFPL
"#).expect("parse");
assert_eq!(metadata, Metadata {
description: "Hello world, this is my description".to_string(),
version: "1.0.0".to_string(),
license: License::WTFPL,
source: None,
stealth: Stealth::Normal,
authors: vec![],
repository: None,
keyring_access: Vec::new(),
});
"#,
)
.expect("parse");
assert_eq!(
metadata,
Metadata {
description: "Hello world, this is my description".to_string(),
version: "1.0.0".to_string(),
license: License::WTFPL,
source: None,
stealth: Stealth::Normal,
authors: vec![],
repository: None,
keyring_access: Vec::new(),
}
);
}
#[test]
fn verify_require_license() {
let metadata = Metadata::from_str(r#"-- Description: Hello world, this is my description
let metadata = Metadata::from_str(
r#"-- Description: Hello world, this is my description
-- Version: 1.0.0
-- Source: domains
"#);
"#,
);
assert!(metadata.is_err());
}
#[test]
fn verify_require_opensource_license() {
let metadata = Metadata::from_str(r#"-- Description: Hello world, this is my description
let metadata = Metadata::from_str(
r#"-- Description: Hello world, this is my description
-- Version: 1.0.0
-- Source: domains
-- License: Proprietary
"#);
"#,
);
assert!(metadata.is_err());
}

View File

@@ -1,8 +1,9 @@
use crate::errors::*;
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq, PartialOrd, Clone, Serialize, Deserialize)]
#[derive(Debug, Eq, PartialEq, PartialOrd, Clone, ValueEnum, Serialize, Deserialize)]
pub enum Stealth {
Loud,
Normal,
@@ -13,12 +14,7 @@ pub enum Stealth {
impl Stealth {
#[inline]
pub fn variants() -> &'static [&'static str] {
&[
"loud",
"normal",
"passive",
"offline",
]
&["loud", "normal", "passive", "offline"]
}
#[inline(always)]

View File

@@ -8,13 +8,13 @@ repository = "https://github.com/kpcyrd/sn0int"
edition = "2018"
[dependencies]
sn0int-common = { version="0.13.0", path="../sn0int-common" }
sn0int-common = { version="0.14.0", path="../sn0int-common" }
rocket = { version = "0.4", default-features=false }
#rocket_failure = { path = "../../rocket_failure" }
rocket_failure = { version = "0.2" }
rocket_contrib = { version = "0.4.1", features = ["handlebars_templates"] }
diesel = { version = "1.3", features = ["postgres", "r2d2"] }
diesel = { version = "2.2", features = ["postgres", "r2d2"] }
diesel_migrations = { version = "1.3.0", features = ["postgres"] }
diesel_full_text_search = "1.0.1"

View File

@@ -1,6 +1,6 @@
[package]
name = "sn0int-std"
version = "0.25.0"
version = "0.26.0"
description = "sn0int - stdlib"
authors = ["kpcyrd <git@rxv.cc>"]
repository = "https://github.com/kpcyrd/sn0int"
@@ -27,7 +27,7 @@ ct-logs = "0.7"
chrootable-https = "0.16"
http = "0.2"
bufstream = "0.1.4"
pem = "1"
pem = "3"
url = "2.0"
tungstenite = { version = "0.13", default-features = false }
kuchiki = "0.8.0"
@@ -36,7 +36,7 @@ x509-parser = "0.13"
der-parser = "8"
publicsuffix = { version="2", default-features=false }
xml-rs = "0.8"
geo = "0.23"
geo = "0.25"
bytes = "0.4"
chrono = { version = "0.4", features = ["serde"] }
mqtt-protocol = "0.11"
@@ -46,12 +46,12 @@ image = "0.23"
kamadak-exif = "0.5.1"
img_hash_median = "4.0.0"
bs58 = "0.4"
bs58 = "0.5"
digest = "0.10"
blake2 = "0.10"
data-encoding = "2.3.3"
thiserror = "1.0.38"
[dev-dependencies]
env_logger = "0.9"
env_logger = "0.10"
maplit = "1.0.1"

View File

@@ -15,10 +15,7 @@ pub struct Blob {
impl Blob {
pub fn create(bytes: Bytes) -> Blob {
let id = Self::hash(&bytes);
Blob {
id,
bytes,
}
Blob { id, bytes }
}
pub fn hash(bytes: &[u8]) -> String {
@@ -53,8 +50,7 @@ impl<'de> Deserialize<'de> for Blob {
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let bytes = BASE64.decode(s.as_bytes())
.map_err(de::Error::custom)?;
let bytes = BASE64.decode(s.as_bytes()).map_err(de::Error::custom)?;
Ok(Blob::create(Bytes::from(bytes)))
}
}
@@ -63,7 +59,6 @@ pub trait BlobState {
fn register_blob(&self, blob: Blob) -> String;
}
#[cfg(test)]
mod tests {
use super::*;
@@ -78,10 +73,13 @@ mod tests {
#[test]
fn verify_create_blob() {
let (bytes, blob) = blob();
assert_eq!(blob, Blob {
id: String::from("DTTV3EjpHBNJx3Zw7eJsVPm4bYXKmNkJQpVNkcvTtTSz"),
bytes,
});
assert_eq!(
blob,
Blob {
id: String::from("DTTV3EjpHBNJx3Zw7eJsVPm4bYXKmNkJQpVNkcvTtTSz"),
bytes,
}
);
}
#[test]

View File

@@ -1,11 +1,11 @@
use crate::errors::*;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::net::IpAddr;
use x509_parser::x509::X509Version;
use x509_parser::certificate::X509Certificate;
use x509_parser::extensions::{GeneralName, ParsedExtension};
use x509_parser::prelude::*;
use x509_parser::x509::X509Version;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Certificate {
@@ -25,7 +25,7 @@ impl Certificate {
bail!("input is not a certificate");
}
pem
},
}
Err(_) => bail!("Failed to parse pem"),
};
Certificate::from_bytes(&pem.contents)
@@ -41,7 +41,7 @@ impl Certificate {
bail!("unexpected certificate version");
}
der
},
}
Err(_) => bail!("Failed to parse der"),
};
@@ -63,28 +63,26 @@ impl Certificate {
match name {
GeneralName::DNSName(v) => {
valid_names.insert(v.to_string());
},
}
GeneralName::RFC822Name(v) => {
valid_emails.insert(v.to_string());
},
}
GeneralName::IPAddress(v) => {
let ip = match v.len() {
4 => Some(IpAddr::from([v[0], v[1], v[2], v[3]])),
16 => Some(IpAddr::from([
v[0], v[1], v[2], v[3],
v[4], v[5], v[6], v[7],
v[8], v[9], v[10], v[11],
v[12], v[13], v[14], v[15],
v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9],
v[10], v[11], v[12], v[13], v[14], v[15],
])),
_ => {
info!("Certificate is valid for invalid ip address: {:?}", v);
None
},
}
};
if let Some(ip) = ip {
valid_ipaddrs.insert(ip);
}
},
}
_ => (),
}
}
@@ -108,7 +106,8 @@ mod tests {
#[test]
fn test_parse_pem_github() {
let mut x = Certificate::parse_pem(r#"-----BEGIN CERTIFICATE-----
let mut x = Certificate::parse_pem(
r#"-----BEGIN CERTIFICATE-----
MIIHQjCCBiqgAwIBAgIQCgYwQn9bvO1pVzllk7ZFHzANBgkqhkiG9w0BAQsFADB1
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMTQwMgYDVQQDEytEaWdpQ2VydCBTSEEyIEV4dGVuZGVk
@@ -149,18 +148,24 @@ Kqg6LK0Hcq4K0sZnxE8HFxiZ92WpV2AVWjRMEc/2z2shNoDvxvFUYyY1Oe67xINk
myQKc+ygSBZzyLnXSFVWmHr3u5dcaaQGGAR42v6Ydr4iL38Hd4dOiBma+FXsXBIq
WUjbST4VXmdaol7uzFMojA4zkxQDZAvF5XgJlAFadfySna/teik=
-----END CERTIFICATE-----
"#).expect("Failed to parse cert");
"#,
)
.expect("Failed to parse cert");
x.valid_names.sort();
assert_eq!(x, Certificate {
valid_names: vec!["github.com".into(), "www.github.com".into()],
valid_emails: vec![],
valid_ipaddrs: vec![],
});
assert_eq!(
x,
Certificate {
valid_names: vec!["github.com".into(), "www.github.com".into()],
valid_emails: vec![],
valid_ipaddrs: vec![],
}
);
}
#[test]
fn test_parse_pem_1_1_1_1() {
let mut x = Certificate::parse_pem(r#"-----BEGIN CERTIFICATE-----
let mut x = Certificate::parse_pem(
r#"-----BEGIN CERTIFICATE-----
MIID9DCCA3qgAwIBAgIQBWzetBRl/ycHFsBukRYuGTAKBggqhkjOPQQDAjBMMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSYwJAYDVQQDEx1EaWdp
Q2VydCBFQ0MgU2VjdXJlIFNlcnZlciBDQTAeFw0xODAzMzAwMDAwMDBaFw0yMDAz
@@ -184,27 +189,30 @@ ADBlAjEAjoyy2Ogh1i1/Kh9+psMc1OChlQIvQF6AkojZS8yliar6m8q5nqC3qe0h
HR0fExwLAjAueWRnHX4QJ9loqMhsPk3NB0Cs0mStsNDNG6/DpCYw7XmjoG3y1LS7
ZkZZmqNn2Q8=
-----END CERTIFICATE-----
"#).expect("Failed to parse cert");
"#,
)
.expect("Failed to parse cert");
x.valid_names.sort();
x.valid_ipaddrs.sort();
assert_eq!(x, Certificate {
valid_names: vec![
"*.cloudflare-dns.com".into(),
"cloudflare-dns.com".into(),
],
valid_emails: vec![],
valid_ipaddrs: vec![
"1.0.0.1".parse().unwrap(),
"1.1.1.1".parse().unwrap(),
"2606:4700:4700::1001".parse().unwrap(),
"2606:4700:4700::1111".parse().unwrap(),
],
});
assert_eq!(
x,
Certificate {
valid_names: vec!["*.cloudflare-dns.com".into(), "cloudflare-dns.com".into(),],
valid_emails: vec![],
valid_ipaddrs: vec![
"1.0.0.1".parse().unwrap(),
"1.1.1.1".parse().unwrap(),
"2606:4700:4700::1001".parse().unwrap(),
"2606:4700:4700::1111".parse().unwrap(),
],
}
);
}
#[test]
fn test_long_san_extension() {
let mut x = Certificate::parse_pem(r#"-----BEGIN CERTIFICATE-----
let mut x = Certificate::parse_pem(
r#"-----BEGIN CERTIFICATE-----
MIII3jCCB8agAwIBAgIQAp1dOviF3mpYKKObx4fjxjANBgkqhkiG9w0BAQsFADBe
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMR0wGwYDVQQDExRHZW9UcnVzdCBSU0EgQ0EgMjAxODAe
@@ -254,56 +262,62 @@ mAlnYDoB0Mj2UIPvIeftkDfF6sURmmZb0/+AMbFDCQYHvZFPI8DFgcagy8og5XJZ
gQ+70UdJdM3RWyrd9R66aZwNGkcS6C2wtKCRhztWDMru/wNuyOsYS6JttoTYxRsh
z/6Vy8Ga9kigYVsa8ZFMR+Ex
-----END CERTIFICATE-----
"#).expect("Failed to parse cert");
"#,
)
.expect("Failed to parse cert");
x.valid_names.sort();
x.valid_ipaddrs.sort();
assert_eq!(x, Certificate {
valid_names: vec![
"aboutyou.de".into(),
"assets.aboutyou.de".into(),
"cdn.aboutstatic.com".into(),
"cdn.aboutyou-staging.de".into(),
"cdn.aboutyou.de".into(),
"cdn.edited.de".into(),
"cdn.mary-paul.de".into(),
"cdn.youandidol.de".into(),
"cdn1.aboutyou.de".into(),
"cdn2.aboutyou.de".into(),
"cdn3.aboutyou.de".into(),
"cdn4.aboutyou.de".into(),
"cdn5.aboutyou.de".into(),
"co-m.aboutyou.de".into(),
"co-mapp.aboutyou.de".into(),
"co-t.aboutyou.de".into(),
"co.aboutyou.de".into(),
"edited.de".into(),
"files.aboutstatic.com".into(),
"images.aboutstatic.com".into(),
"img.aboutstatic.com".into(),
"img.aboutyou.de".into(),
"m-assets.aboutyou.de".into(),
"m.aboutyou.de".into(),
"media.aboutyou.de".into(),
"static.aboutyou.de".into(),
"static1.aboutyou.de".into(),
"static2.aboutyou.de".into(),
"static3.aboutyou.de".into(),
"static4.aboutyou.de".into(),
"static5.aboutyou.de".into(),
"staticmail-cdn.aboutyou.de".into(),
"t.aboutyou.de".into(),
"witt-weiden.dam.acme.aboutyou.cloud".into(),
"witt-weiden.dam.staging.aboutyou.cloud".into(),
"www.aboutyou.de".into(),
],
valid_emails: vec![],
valid_ipaddrs: vec![],
});
assert_eq!(
x,
Certificate {
valid_names: vec![
"aboutyou.de".into(),
"assets.aboutyou.de".into(),
"cdn.aboutstatic.com".into(),
"cdn.aboutyou-staging.de".into(),
"cdn.aboutyou.de".into(),
"cdn.edited.de".into(),
"cdn.mary-paul.de".into(),
"cdn.youandidol.de".into(),
"cdn1.aboutyou.de".into(),
"cdn2.aboutyou.de".into(),
"cdn3.aboutyou.de".into(),
"cdn4.aboutyou.de".into(),
"cdn5.aboutyou.de".into(),
"co-m.aboutyou.de".into(),
"co-mapp.aboutyou.de".into(),
"co-t.aboutyou.de".into(),
"co.aboutyou.de".into(),
"edited.de".into(),
"files.aboutstatic.com".into(),
"images.aboutstatic.com".into(),
"img.aboutstatic.com".into(),
"img.aboutyou.de".into(),
"m-assets.aboutyou.de".into(),
"m.aboutyou.de".into(),
"media.aboutyou.de".into(),
"static.aboutyou.de".into(),
"static1.aboutyou.de".into(),
"static2.aboutyou.de".into(),
"static3.aboutyou.de".into(),
"static4.aboutyou.de".into(),
"static5.aboutyou.de".into(),
"staticmail-cdn.aboutyou.de".into(),
"t.aboutyou.de".into(),
"witt-weiden.dam.acme.aboutyou.cloud".into(),
"witt-weiden.dam.staging.aboutyou.cloud".into(),
"www.aboutyou.de".into(),
],
valid_emails: vec![],
valid_ipaddrs: vec![],
}
);
}
#[test]
fn test_san_email() {
let mut x = Certificate::parse_pem(r#"-----BEGIN CERTIFICATE-----
let mut x = Certificate::parse_pem(
r#"-----BEGIN CERTIFICATE-----
MIIE5zCCA8+gAwIBAgIQBvsKfZ5AGSW3Vc8Ldto1hTANBgkqhkiG9w0BAQUFADBp
MSQwIgYJKoZIhvcNAQkBFhVwa2lfYWRtaW5Ac3VuZ2FyZC5jb20xJjAkBgNVBAoT
HVN1bkdhcmQgQXZhaWxhYmlsaXR5IFNlcnZpY2VzMRkwFwYDVQQDExBTQVMgUHVi
@@ -332,18 +346,19 @@ K4f9GAOcawvNsI//mx99ol/ZGEamydeL9G0qiKrhqSxd2TGmFaIVJdu9fh59hos4
UT+11L6q7MSIXSIMV8kJSUUYE92P7bnAqViTIuu/hHnfmIhiy6t7AuT2QHEhqDab
EF4l5MwdUqs8FvM=
-----END CERTIFICATE-----
"#).expect("Failed to parse cert");
"#,
)
.expect("Failed to parse cert");
x.valid_names.sort();
x.valid_emails.sort();
x.valid_ipaddrs.sort();
assert_eq!(x, Certificate {
valid_names: vec![
"*.hosted.jivesoftware.com".into(),
],
valid_emails: vec![
"subjectname@example.com".into(),
],
valid_ipaddrs: vec![],
});
assert_eq!(
x,
Certificate {
valid_names: vec!["*.hosted.jivesoftware.com".into(),],
valid_emails: vec!["subjectname@example.com".into(),],
valid_ipaddrs: vec![],
}
);
}
}

View File

@@ -17,8 +17,7 @@ pub fn sodium_secretbox_open(encrypted: &[u8], key: &[u8]) -> Result<Vec<u8>> {
bail!("Encrypted message is too short");
}
let key = Key::from_slice(key)
.ok_or_else(|| format_err!("Key has wrong length"))?;
let key = Key::from_slice(key).ok_or_else(|| format_err!("Key has wrong length"))?;
let nonce = Nonce::from_slice(&encrypted[..secretbox::NONCEBYTES])
.ok_or_else(|| format_err!("Nonce has wrong length"))?;
let ciphertext = &encrypted[secretbox::NONCEBYTES..];

View File

@@ -1,13 +1,13 @@
use crate::errors::*;
use crate::hlua::{AnyHashableLuaValue, AnyLuaValue, AnyLuaString};
use std::collections::{self, HashMap};
use crate::hlua::{AnyHashableLuaValue, AnyLuaString, AnyLuaValue};
use crate::json::LuaJsonValue;
use std::collections::{self, HashMap};
pub fn from_lua<T>(x: LuaJsonValue) -> Result<T>
where for<'de> T: serde::Deserialize<'de>
where
for<'de> T: serde::Deserialize<'de>,
{
serde_json::from_value(x.into())
.map_err(Error::from)
serde_json::from_value(x.into()).map_err(Error::from)
}
#[derive(Debug, Default)]
@@ -26,22 +26,32 @@ impl LuaMap {
#[inline]
pub fn insert<K: Into<String>, V: Into<AnyLuaValue>>(&mut self, k: K, v: V) {
self.0.insert(AnyHashableLuaValue::LuaString(k.into()), v.into());
self.0
.insert(AnyHashableLuaValue::LuaString(k.into()), v.into());
}
#[inline]
pub fn insert_str<K: Into<String>, V: Into<String>>(&mut self, k: K, v: V) {
self.0.insert(AnyHashableLuaValue::LuaString(k.into()), AnyLuaValue::LuaString(v.into()));
self.0.insert(
AnyHashableLuaValue::LuaString(k.into()),
AnyLuaValue::LuaString(v.into()),
);
}
#[inline]
pub fn insert_num<K: Into<String>>(&mut self, k: K, v: f64) {
self.0.insert(AnyHashableLuaValue::LuaString(k.into()), AnyLuaValue::LuaNumber(v));
self.0.insert(
AnyHashableLuaValue::LuaString(k.into()),
AnyLuaValue::LuaNumber(v),
);
}
pub fn insert_serde<K: Into<String>, S: serde::Serialize>(&mut self, k: K, v: S) -> Result<()> {
let v = serde_json::to_value(v)?;
self.0.insert(AnyHashableLuaValue::LuaString(k.into()), LuaJsonValue::from(v).into());
self.0.insert(
AnyHashableLuaValue::LuaString(k.into()),
LuaJsonValue::from(v).into(),
);
Ok(())
}
}
@@ -102,7 +112,7 @@ impl From<LuaMap> for AnyLuaValue {
_ => None, // TODO: unknown types are discarded
}
})
.collect()
.collect(),
)
}
}
@@ -146,17 +156,20 @@ pub fn byte_array(bytes: AnyLuaValue) -> Result<Vec<u8>> {
match bytes {
AnyLuaValue::LuaAnyString(bytes) => Ok(bytes.0),
AnyLuaValue::LuaString(bytes) => Ok(bytes.into_bytes()),
AnyLuaValue::LuaArray(bytes) => {
Ok(bytes.into_iter()
.map(|num| match num.1 {
AnyLuaValue::LuaNumber(num) if (0.0..=255.0).contains(&num) && (num % 1.0 == 0.0) =>
Ok(num as u8),
AnyLuaValue::LuaNumber(num) =>
Err(format_err!("number is out of range: {:?}", num)),
_ => Err(format_err!("unexpected type: {:?}", num)),
})
.collect::<Result<_>>()?)
},
AnyLuaValue::LuaArray(bytes) => Ok(bytes
.into_iter()
.map(|num| match num.1 {
AnyLuaValue::LuaNumber(num)
if (0.0..=255.0).contains(&num) && (num % 1.0 == 0.0) =>
{
Ok(num as u8)
}
AnyLuaValue::LuaNumber(num) => {
Err(format_err!("number is out of range: {:?}", num))
}
_ => Err(format_err!("unexpected type: {:?}", num)),
})
.collect::<Result<_>>()?),
_ => Err(format_err!("invalid type: {:?}", bytes)),
}
}

View File

@@ -1,8 +1,8 @@
use crate::errors::*;
use crate::hlua::AnyLuaValue;
use crate::json::LuaJsonValue;
use geo::{LineString, Polygon, Coord};
use geo::prelude::*;
use geo::{Coord, LineString, Polygon};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
@@ -20,7 +20,8 @@ impl Point {
}
pub fn polygon_contains(ring: &[Point], p: &Point) -> bool {
let ring = ring.iter()
let ring = ring
.iter()
.map(|p| Coord { x: p.lon, y: p.lat })
.collect::<Vec<_>>();
@@ -36,75 +37,210 @@ mod tests {
fn hamburg_polygon() -> &'static [Point] {
&[
Point { lat: 53.63975308945899, lon: 9.764785766601562 },
Point { lat: 53.59494998253459, lon: 9.827270507812 },
Point { lat: 53.663153974456456, lon: 9.9151611328125 },
Point { lat: 53.65582987649682, lon: 9.976272583007812 },
Point { lat: 53.68613523817129, lon: 9.992752075195312 },
Point { lat: 53.68674518938816, lon: 10.051460266113281 },
Point { lat: 53.72495117617815, lon: 10.075492858886719 },
Point { lat: 53.71946627930625, lon: 10.118408203125 },
Point { lat: 53.743635083157756, lon: 10.164413452148438 },
Point { lat: 53.73104466704585, lon: 10.202865600585938 },
Point { lat: 53.676781546441546, lon: 10.16304016113281 },
Point { lat: 53.632832079199474, lon: 10.235824584960938 },
Point { lat: 53.608803292930894, lon: 10.2008056640625 },
Point { lat: 53.578646152866504, lon: 10.208358764648438 },
Point { lat: 53.57212285981298, lon: 10.163726806640625 },
Point { lat: 53.52071674896369, lon: 10.18707275390625 },
Point { lat: 53.52643162253097, lon: 10.224151611328125 },
Point { lat: 53.44062753992289, lon: 10.347747802734375 },
Point { lat: 53.38824275010831, lon: 10.248870849609375 },
Point { lat: 53.38824275010831, lon: 10.15960693359375 },
Point { lat: 53.44635321212876, lon: 10.064849853515625 },
Point { lat: 53.40595029739904, lon: 9.985198974609375 },
Point { lat: 53.42385506057106, lon: 9.951210021972656 },
Point { lat: 53.41843327091211, lon: 9.944171905517578 },
Point { lat: 53.41812635648326, lon: 9.927349090576172 },
Point { lat: 53.412294561442884, lon: 9.917736053466797 },
Point { lat: 53.41464783813818, lon: 9.901256561279297 },
Point { lat: 53.443490472483326, lon: 9.912586212158201 },
Point { lat: 53.45177144115704, lon: 9.897651672363281 },
Point { lat: 53.43633277935392, lon: 9.866924285888672 },
Point { lat: 53.427639673754776, lon: 9.866409301757812 },
Point { lat: 53.427639673754776, lon: 9.858856201171875 },
Point { lat: 53.46710230573499, lon: 9.795513153076172 },
Point { lat: 53.49039461941655, lon: 9.795341491699219 },
Point { lat: 53.49029248806277, lon: 9.77903366088867 },
Point { lat: 53.49856433088649, lon: 9.780235290527344 },
Point { lat: 53.5078554643033, lon: 9.758434295654297 },
Point { lat: 53.545407634092975, lon: 9.759807586669922 },
Point { lat: 53.568147234570084, lon: 9.633293151855469 },
Point { lat: 53.58802162343514, lon: 9.655780792236328 },
Point { lat: 53.568351121879815, lon: 9.727706909179688 },
Point { lat: 53.60921067445695, lon: 9.737663269042969 },
Point {
lat: 53.63975308945899,
lon: 9.764785766601562,
},
Point {
lat: 53.59494998253459,
lon: 9.827270507812,
},
Point {
lat: 53.663153974456456,
lon: 9.9151611328125,
},
Point {
lat: 53.65582987649682,
lon: 9.976272583007812,
},
Point {
lat: 53.68613523817129,
lon: 9.992752075195312,
},
Point {
lat: 53.68674518938816,
lon: 10.051460266113281,
},
Point {
lat: 53.72495117617815,
lon: 10.075492858886719,
},
Point {
lat: 53.71946627930625,
lon: 10.118408203125,
},
Point {
lat: 53.743635083157756,
lon: 10.164413452148438,
},
Point {
lat: 53.73104466704585,
lon: 10.202865600585938,
},
Point {
lat: 53.676781546441546,
lon: 10.16304016113281,
},
Point {
lat: 53.632832079199474,
lon: 10.235824584960938,
},
Point {
lat: 53.608803292930894,
lon: 10.2008056640625,
},
Point {
lat: 53.578646152866504,
lon: 10.208358764648438,
},
Point {
lat: 53.57212285981298,
lon: 10.163726806640625,
},
Point {
lat: 53.52071674896369,
lon: 10.18707275390625,
},
Point {
lat: 53.52643162253097,
lon: 10.224151611328125,
},
Point {
lat: 53.44062753992289,
lon: 10.347747802734375,
},
Point {
lat: 53.38824275010831,
lon: 10.248870849609375,
},
Point {
lat: 53.38824275010831,
lon: 10.15960693359375,
},
Point {
lat: 53.44635321212876,
lon: 10.064849853515625,
},
Point {
lat: 53.40595029739904,
lon: 9.985198974609375,
},
Point {
lat: 53.42385506057106,
lon: 9.951210021972656,
},
Point {
lat: 53.41843327091211,
lon: 9.944171905517578,
},
Point {
lat: 53.41812635648326,
lon: 9.927349090576172,
},
Point {
lat: 53.412294561442884,
lon: 9.917736053466797,
},
Point {
lat: 53.41464783813818,
lon: 9.901256561279297,
},
Point {
lat: 53.443490472483326,
lon: 9.912586212158201,
},
Point {
lat: 53.45177144115704,
lon: 9.897651672363281,
},
Point {
lat: 53.43633277935392,
lon: 9.866924285888672,
},
Point {
lat: 53.427639673754776,
lon: 9.866409301757812,
},
Point {
lat: 53.427639673754776,
lon: 9.858856201171875,
},
Point {
lat: 53.46710230573499,
lon: 9.795513153076172,
},
Point {
lat: 53.49039461941655,
lon: 9.795341491699219,
},
Point {
lat: 53.49029248806277,
lon: 9.77903366088867,
},
Point {
lat: 53.49856433088649,
lon: 9.780235290527344,
},
Point {
lat: 53.5078554643033,
lon: 9.758434295654297,
},
Point {
lat: 53.545407634092975,
lon: 9.759807586669922,
},
Point {
lat: 53.568147234570084,
lon: 9.633293151855469,
},
Point {
lat: 53.58802162343514,
lon: 9.655780792236328,
},
Point {
lat: 53.568351121879815,
lon: 9.727706909179688,
},
Point {
lat: 53.60921067445695,
lon: 9.737663269042969,
},
]
}
#[test]
fn test_polygon_hamburg_contains_hamburg() {
let contains = polygon_contains(hamburg_polygon(), &Point {
lat: 53.551085,
lon: 9.993682,
});
let contains = polygon_contains(
hamburg_polygon(),
&Point {
lat: 53.551085,
lon: 9.993682,
},
);
assert!(contains);
}
#[test]
fn test_polygon_hamburg_not_contains_berlin() {
let contains = polygon_contains(hamburg_polygon(), &Point {
lat: 52.52437,
lon: 13.41053,
});
let contains = polygon_contains(
hamburg_polygon(),
&Point {
lat: 52.52437,
lon: 13.41053,
},
);
assert!(!contains);
}
#[test]
fn test_polygon_hamburg_not_contains_ny() {
let contains = polygon_contains(hamburg_polygon(), &Point {
lat: 40.726662,
lon: -74.036677,
});
let contains = polygon_contains(
hamburg_polygon(),
&Point {
lat: 40.726662,
lon: -74.036677,
},
);
assert!(!contains);
}
}

View File

@@ -3,15 +3,14 @@ use crate::lazy::LazyInit;
use maxminddb::{self, geoip2};
use std::fmt;
use std::fs::{self, File};
use std::io::Read;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::io::Read;
use std::sync::Arc;
pub mod models;
use self::models::GeoLookup;
use self::models::AsnLookup;
use self::models::GeoLookup;
pub trait Maxmind: Sized {
fn filename() -> &'static str;
@@ -37,14 +36,13 @@ pub trait Maxmind: Sized {
}
// use cache path
let path = cache_dir
.join(Self::filename());
let path = cache_dir.join(Self::filename());
Ok(path)
}
fn from_buf(buf: Vec<u8>) -> Result<Self> {
let reader = maxminddb::Reader::from_source(buf)
.context("Failed to read geoip database")?;
let reader =
maxminddb::Reader::from_source(buf).context("Failed to read geoip database")?;
Ok(Self::new(reader))
}
@@ -72,9 +70,7 @@ pub struct MaxmindReader {
impl MaxmindReader {
fn open_path<P: AsRef<Path>>(path: P) -> Result<MaxmindReader> {
let reader = File::open(path)?;
Ok(MaxmindReader {
reader,
})
Ok(MaxmindReader { reader })
}
}
@@ -118,9 +114,7 @@ impl Maxmind for GeoIP {
#[inline]
fn new(reader: maxminddb::Reader<Vec<u8>>) -> Self {
GeoIP {
reader
}
GeoIP { reader }
}
}
@@ -150,9 +144,7 @@ impl Maxmind for AsnDB {
#[inline]
fn new(reader: maxminddb::Reader<Vec<u8>>) -> Self {
AsnDB {
reader
}
AsnDB { reader }
}
}

View File

@@ -134,9 +134,11 @@ pub struct AsnLookup {
impl AsnLookup {
pub fn try_from(lookup: geoip2::Isp) -> Result<AsnLookup> {
// parse maxminddb lookup
let asn = lookup.autonomous_system_number
let asn = lookup
.autonomous_system_number
.ok_or_else(|| format_err!("autonomous_system_number not set"))?;
let as_org = lookup.autonomous_system_organization
let as_org = lookup
.autonomous_system_organization
.ok_or_else(|| format_err!("autonomous_system_organization not set"))?;
Ok(AsnLookup {

View File

@@ -1,5 +1,5 @@
use crate::errors::*;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use std::io;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
@@ -9,7 +9,7 @@ pub struct Location {
}
impl Location {
fn try_from_iter<'a, I: IntoIterator<Item=&'a exif::Field>>(iter: I) -> Result<Self> {
fn try_from_iter<'a, I: IntoIterator<Item = &'a exif::Field>>(iter: I) -> Result<Self> {
let mut builder = LocationBuilder::default();
for f in iter {
debug!("Exif field: {:?}", f.display_value().to_string());
@@ -31,8 +31,12 @@ impl LocationBuilder {
fn add_one(&mut self, f: &exif::Field) -> Result<()> {
debug!("Exif tag: {:?}, {}", f.tag, f.value.display_as(f.tag));
match f.tag {
exif::Tag::GPSLatitudeRef => self.latitude_ref = Some(cardinal_direction_modifier(&f.value)?),
exif::Tag::GPSLongitudeRef => self.longitude_ref = Some(cardinal_direction_modifier(&f.value)?),
exif::Tag::GPSLatitudeRef => {
self.latitude_ref = Some(cardinal_direction_modifier(&f.value)?)
}
exif::Tag::GPSLongitudeRef => {
self.longitude_ref = Some(cardinal_direction_modifier(&f.value)?)
}
exif::Tag::GPSLatitude => self.latitude = Some(dms_to_float(&f.value)?),
exif::Tag::GPSLongitude => self.longitude = Some(dms_to_float(&f.value)?),
_ => (),
@@ -41,14 +45,18 @@ impl LocationBuilder {
}
fn build(self) -> Result<Location> {
let latitude = self.latitude
let latitude = self
.latitude
.ok_or_else(|| format_err!("Missing latitude field"))?;
let latitude_ref = self.latitude_ref
let latitude_ref = self
.latitude_ref
.ok_or_else(|| format_err!("Missing latitude field"))?;
let longitude = self.longitude
let longitude = self
.longitude
.ok_or_else(|| format_err!("Missing latitude field"))?;
let longitude_ref = self.longitude_ref
let longitude_ref = self
.longitude_ref
.ok_or_else(|| format_err!("Missing latitude field"))?;
Ok(Location {
@@ -60,8 +68,7 @@ impl LocationBuilder {
pub fn gps(img: &[u8]) -> Result<Option<Location>> {
let mut buf = io::Cursor::new(img);
let reader = exif::Reader::new()
.read_from_container(&mut buf)?;
let reader = exif::Reader::new().read_from_container(&mut buf)?;
let fields = reader.fields();
let location = Location::try_from_iter(fields).ok();
@@ -82,7 +89,7 @@ pub fn dms_to_float(dms: &exif::Value) -> Result<f64> {
let minutes = dms[1].to_f64();
let seconds = dms[2].to_f64();
let float = degrees + minutes/60.0 + seconds/3600.0;
let float = degrees + minutes / 60.0 + seconds / 3600.0;
let float = (float * 1000000.0).round() / 1000000.0;
Ok(float)
}
@@ -90,7 +97,8 @@ pub fn dms_to_float(dms: &exif::Value) -> Result<f64> {
pub fn cardinal_direction_modifier(value: &exif::Value) -> Result<f64> {
match value {
exif::Value::Ascii(s) => {
let s = s.get(0)
let s = s
.get(0)
.ok_or_else(|| format_err!("Cardinal direction value is empty"))?;
match s.first() {
@@ -100,7 +108,7 @@ pub fn cardinal_direction_modifier(value: &exif::Value) -> Result<f64> {
Some(b'W') => Ok(-1.0),
_ => bail!("Unexpected cardinal direction"),
}
},
}
_ => bail!("Unexpected exif value"),
}
}
@@ -119,60 +127,62 @@ mod tests {
tag: exif::Tag::GPSLatitudeRef,
ifd_num: exif::In::PRIMARY,
value: exif::Value::Ascii(vec![vec![b'N']]),
}, exif::Field {
},
exif::Field {
tag: exif::Tag::GPSLongitudeRef,
ifd_num: exif::In::PRIMARY,
value: exif::Value::Ascii(vec![vec![b'E']]),
}, exif::Field {
},
exif::Field {
tag: exif::Tag::GPSLatitude,
ifd_num: exif::In::PRIMARY,
value: exif::Value::Rational(vec![exif::Rational {
num: 43,
denom: 1,
}, exif::Rational {
num: 28,
denom: 1,
}, exif::Rational {
num: 176399999,
denom: 100000000,
}]),
}, exif::Field {
value: exif::Value::Rational(vec![
exif::Rational { num: 43, denom: 1 },
exif::Rational { num: 28, denom: 1 },
exif::Rational {
num: 176399999,
denom: 100000000,
},
]),
},
exif::Field {
tag: exif::Tag::GPSLongitude,
ifd_num: exif::In::PRIMARY,
value: exif::Value::Rational(vec![exif::Rational {
num: 11,
denom: 1,
}, exif::Rational {
num: 53,
denom: 1,
}, exif::Rational {
num: 742199999,
denom: 100000000,
}]),
value: exif::Value::Rational(vec![
exif::Rational { num: 11, denom: 1 },
exif::Rational { num: 53, denom: 1 },
exif::Rational {
num: 742199999,
denom: 100000000,
},
]),
},
]).unwrap();
])
.unwrap();
println!("{:?}", location);
assert_eq!(location, Location {
latitude: 43.467157,
longitude: 11.885395
});
assert_eq!(
location,
Location {
latitude: 43.467157,
longitude: 11.885395
}
);
}
#[test]
fn verify_dms() {
test_init();
let latitude = dms_to_float(&exif::Value::Rational(vec![exif::Rational {
num: 43,
denom: 1,
}, exif::Rational {
num: 28,
denom: 1,
}, exif::Rational {
num: 176399999,
denom: 100000000,
}])).unwrap();
let latitude = dms_to_float(&exif::Value::Rational(vec![
exif::Rational { num: 43, denom: 1 },
exif::Rational { num: 28, denom: 1 },
exif::Rational {
num: 176399999,
denom: 100000000,
},
]))
.unwrap();
assert_eq!(latitude, 43.467157);
}

View File

@@ -4,7 +4,6 @@ pub use img_hash_median::HashAlg;
pub mod exif;
#[derive(Debug)]
pub enum ImageFormat {
Png,
@@ -108,10 +107,7 @@ pub fn load(buf: &[u8]) -> Result<Image> {
let image = image::load_from_memory_with_format(buf, img_format)?;
Ok(Image {
image,
format,
})
Ok(Image { image, format })
}
#[cfg(test)]

View File

@@ -41,14 +41,10 @@ fn transform_element(entry: &kuchiki::NodeDataRef<kuchiki::ElementData>) -> Elem
Err(_) => {
debug!("html serialize failed");
String::new()
},
}
};
Element {
attrs,
text,
html,
}
Element { attrs, text, html }
}
pub fn html_select(html: &str, selector: &str) -> Result<Element> {
@@ -95,7 +91,6 @@ pub fn html_form(html: &str) -> Result<HashMap<String, String>> {
Ok(form)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -104,9 +99,10 @@ mod tests {
#[test]
fn test_html_select() {
let elems = html_select(r#"<html><div id="yey">content</div></html>"#, "#yey").unwrap();
assert_eq!(elems,
assert_eq!(
elems,
Element {
attrs: hashmap!{
attrs: hashmap! {
"id".into() => "yey".into(),
},
text: "content".into(),
@@ -117,15 +113,17 @@ mod tests {
#[test]
fn test_html_select_list() {
let elems = html_select_list(r#"<html><div id="yey">content</div></html>"#, "#yey").unwrap();
assert_eq!(elems, vec![
Element {
attrs: hashmap!{
let elems =
html_select_list(r#"<html><div id="yey">content</div></html>"#, "#yey").unwrap();
assert_eq!(
elems,
vec![Element {
attrs: hashmap! {
"id".into() => "yey".into(),
},
text: "content".into(),
html: r#"<div id="yey">content</div>"#.into(),
}
]);
}]
);
}
}

View File

@@ -1,13 +1,11 @@
use crate::errors::*;
use std::collections::HashMap;
use crate::hlua::AnyLuaValue;
use serde_json::{self, Deserializer, Value, Number, Map};
use serde_json::{self, Deserializer, Map, Number, Value};
use std::collections::HashMap;
pub fn decode<T: AsRef<[u8]>>(x: T) -> Result<AnyLuaValue> {
let v: Value = serde_json::from_slice(x.as_ref())
.context("deserialize failed")?;
let v: Value = serde_json::from_slice(x.as_ref()).context("deserialize failed")?;
let v: LuaJsonValue = v.into();
Ok(v.into())
}
@@ -15,8 +13,7 @@ pub fn decode<T: AsRef<[u8]>>(x: T) -> Result<AnyLuaValue> {
pub fn encode(v: AnyLuaValue) -> Result<String> {
let v: LuaJsonValue = v.into();
let v: Value = v.into();
let s = serde_json::to_string(&v)
.context("serialize failed")?;
let s = serde_json::to_string(&v).context("serialize failed")?;
Ok(s)
}
@@ -59,13 +56,16 @@ impl From<LuaJsonValue> for AnyLuaValue {
// TODO: not sure if this might fail
LuaJsonValue::Number(v) => AnyLuaValue::LuaNumber(v.as_f64().unwrap()),
LuaJsonValue::String(v) => AnyLuaValue::LuaString(v),
LuaJsonValue::Array(v) => AnyLuaValue::LuaArray(v.into_iter().enumerate()
.map(|(i, x)| (AnyLuaValue::LuaNumber((i+1) as f64), x.into()))
.collect()
LuaJsonValue::Array(v) => AnyLuaValue::LuaArray(
v.into_iter()
.enumerate()
.map(|(i, x)| (AnyLuaValue::LuaNumber((i + 1) as f64), x.into()))
.collect(),
),
LuaJsonValue::Object(v) => AnyLuaValue::LuaArray(v.into_iter()
.map(|(k, v)| (AnyLuaValue::LuaString(k), v.into()))
.collect()
LuaJsonValue::Object(v) => AnyLuaValue::LuaArray(
v.into_iter()
.map(|(k, v)| (AnyLuaValue::LuaString(k), v.into()))
.collect(),
),
}
}
@@ -77,9 +77,10 @@ impl From<AnyLuaValue> for LuaJsonValue {
AnyLuaValue::LuaNil => LuaJsonValue::Null,
AnyLuaValue::LuaBoolean(v) => LuaJsonValue::Bool(v),
AnyLuaValue::LuaString(v) => LuaJsonValue::String(v),
AnyLuaValue::LuaAnyString(v) => LuaJsonValue::Array(v.0.into_iter()
.map(|x| LuaJsonValue::Number(x.into()))
.collect()
AnyLuaValue::LuaAnyString(v) => LuaJsonValue::Array(
v.0.into_iter()
.map(|x| LuaJsonValue::Number(x.into()))
.collect(),
),
AnyLuaValue::LuaNumber(v) => {
// this is needed or every number is detected as float
@@ -88,23 +89,21 @@ impl From<AnyLuaValue> for LuaJsonValue {
} else {
Number::from_f64(v).expect("invalid LuaJson::Number")
})
},
}
AnyLuaValue::LuaArray(v) => {
if lua_array_is_list(&v) {
LuaJsonValue::Array(v.into_iter()
.map(|(_, v)| v.into())
.collect()
)
LuaJsonValue::Array(v.into_iter().map(|(_, v)| v.into()).collect())
} else {
LuaJsonValue::Object(v.into_iter()
.filter_map(|(k, v)| match k {
AnyLuaValue::LuaString(k) => Some((k, v.into())),
_ => None,
})
.collect()
LuaJsonValue::Object(
v.into_iter()
.filter_map(|(k, v)| match k {
AnyLuaValue::LuaString(k) => Some((k, v.into())),
_ => None,
})
.collect(),
)
}
},
}
AnyLuaValue::LuaOther => LuaJsonValue::Null,
}
}
@@ -117,13 +116,11 @@ impl From<LuaJsonValue> for serde_json::Value {
LuaJsonValue::Bool(v) => Value::Bool(v),
LuaJsonValue::Number(v) => Value::Number(v),
LuaJsonValue::String(v) => Value::String(v),
LuaJsonValue::Array(v) => Value::Array(v.into_iter()
.map(|x| x.into())
.collect()
),
LuaJsonValue::Object(v) => Value::Object(v.into_iter()
.map(|(k, v)| (k, v.into()))
.collect::<Map<_, _>>()
LuaJsonValue::Array(v) => Value::Array(v.into_iter().map(|x| x.into()).collect()),
LuaJsonValue::Object(v) => Value::Object(
v.into_iter()
.map(|(k, v)| (k, v.into()))
.collect::<Map<_, _>>(),
),
}
}
@@ -136,13 +133,11 @@ impl From<serde_json::Value> for LuaJsonValue {
Value::Bool(v) => LuaJsonValue::Bool(v),
Value::Number(v) => LuaJsonValue::Number(v),
Value::String(v) => LuaJsonValue::String(v),
Value::Array(v) => LuaJsonValue::Array(v.into_iter()
.map(|x| x.into())
.collect()
),
Value::Object(v) => LuaJsonValue::Object(v.into_iter()
.map(|(k, v)| (k, v.into()))
.collect::<HashMap<_, _>>()
Value::Array(v) => LuaJsonValue::Array(v.into_iter().map(|x| x.into()).collect()),
Value::Object(v) => LuaJsonValue::Object(
v.into_iter()
.map(|(k, v)| (k, v.into()))
.collect::<HashMap<_, _>>(),
),
}
}

View File

@@ -13,11 +13,12 @@ impl<T1: LazyInit<T2>, T2> Lazy<T1, T2> {
pub fn get(&mut self) -> Result<&mut T2> {
match self {
Lazy::Init(init) => {
let init = init.take()
let init = init
.take()
.ok_or_else(|| format_err!("Previous initialization failed"))?;
*self = Lazy::Active(init.initialize()?);
self.get()
},
}
Lazy::Active(active) => Ok(active),
}
}

View File

@@ -1,12 +1,12 @@
use chrootable_https::Client;
use crate::errors::*;
use crate::lazy::LazyInit;
use chrootable_https::Client;
use publicsuffix::{List, Psl as _};
use std::fs::{self, File};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::str::FromStr;
use std::sync::Arc;
#[derive(Debug, PartialEq)]
pub struct DnsName {
@@ -23,8 +23,8 @@ pub enum PslReader {
impl PslReader {
pub fn open_or_download<F>(cache_dir: &Path, indicator: F) -> Result<PslReader>
where
F: Fn(Box<dyn Fn() -> Result<PslReader>>) -> Result<PslReader>
where
F: Fn(Box<dyn Fn() -> Result<PslReader>>) -> Result<PslReader>,
{
let path = Self::path(cache_dir)?;
let reader = match Self::open_from(&path) {
@@ -55,14 +55,14 @@ impl PslReader {
}
// else, use local cache
let path = cache_dir
.join("public_suffix_list.dat");
let path = cache_dir.join("public_suffix_list.dat");
Ok(path)
}
pub fn download(path: &Path, url: &str) -> Result<()> {
let client = Client::with_system_resolver_v4()?;
let resp = client.get(url)
let resp = client
.get(url)
.wait_for_response()
.context("http request failed")?;
fs::write(path, &resp.body)?;
@@ -77,16 +77,14 @@ impl LazyInit<Arc<Psl>> for PslReader {
let mut buf = String::new();
file.read_to_string(&mut buf)?;
buf
},
}
PslReader::String(s) => s,
};
let list = List::from_str(&list)
.map_err(|e| format_err!("Failed to load public suffix list: {}", e))?;
Ok(Arc::new(Psl {
list,
}))
Ok(Arc::new(Psl { list }))
}
}
@@ -99,7 +97,9 @@ impl Psl {
pub fn parse_dns_name(&self, name: &str) -> Result<DnsName> {
let bytes = name.as_bytes();
let suffix = self.list.suffix(bytes)
let suffix = self
.list
.suffix(bytes)
.ok_or_else(|| format_err!("Failed to detect suffix"))?;
let suffix = String::from_utf8(suffix.as_bytes().to_vec())?;
@@ -124,60 +124,82 @@ impl Psl {
}
}
#[cfg(test)]
mod tests {
use super::*;
fn init() -> Arc<Psl> {
PslReader::String(r#"
PslReader::String(
r#"
// ===BEGIN ICANN DOMAINS===
com
// ===END ICANN DOMAINS===
// ===BEGIN PRIVATE DOMAINS===
a.prod.fastly.net
// ===END PRIVATE DOMAINS===
"#.into()).initialize().unwrap()
"#
.into(),
)
.initialize()
.unwrap()
}
#[test]
fn test_psl_example_com() {
let x = init().parse_dns_name("example.com").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: None,
root: "example.com".into(),
suffix: "com".into(),
});
let x = init()
.parse_dns_name("example.com")
.expect("parse_dns_name");
assert_eq!(
x,
DnsName {
fulldomain: None,
root: "example.com".into(),
suffix: "com".into(),
}
);
}
#[test]
fn test_psl_www_example_com() {
let x = init().parse_dns_name("www.example.com").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: Some("www.example.com".into()),
root: "example.com".into(),
suffix: "com".into(),
});
let x = init()
.parse_dns_name("www.example.com")
.expect("parse_dns_name");
assert_eq!(
x,
DnsName {
fulldomain: Some("www.example.com".into()),
root: "example.com".into(),
suffix: "com".into(),
}
);
}
#[test]
fn test_psl_com() {
let x = init().parse_dns_name("com").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: None,
root: "com".into(),
suffix: "com".into(),
});
assert_eq!(
x,
DnsName {
fulldomain: None,
root: "com".into(),
suffix: "com".into(),
}
);
}
#[test]
fn test_psl_a_b_c_d_e_f_g_com() {
let x = init().parse_dns_name("a.b.c.d.e.f.g.com").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: Some("a.b.c.d.e.f.g.com".into()),
root: "g.com".into(),
suffix: "com".into(),
});
let x = init()
.parse_dns_name("a.b.c.d.e.f.g.com")
.expect("parse_dns_name");
assert_eq!(
x,
DnsName {
fulldomain: Some("a.b.c.d.e.f.g.com".into()),
root: "g.com".into(),
suffix: "com".into(),
}
);
}
#[test]
@@ -188,41 +210,61 @@ a.prod.fastly.net
#[test]
fn test_psl_asdfinvalid() {
let x = init().parse_dns_name("asdfinvalid").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: None,
root: "asdfinvalid".into(),
suffix: "asdfinvalid".into(),
});
let x = init()
.parse_dns_name("asdfinvalid")
.expect("parse_dns_name");
assert_eq!(
x,
DnsName {
fulldomain: None,
root: "asdfinvalid".into(),
suffix: "asdfinvalid".into(),
}
);
}
#[test]
fn test_psl_www_example_asdfinvalid() {
let x = init().parse_dns_name("www.example.asdfinvalid").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: Some("www.example.asdfinvalid".into()),
root: "example.asdfinvalid".into(),
suffix: "asdfinvalid".into(),
});
let x = init()
.parse_dns_name("www.example.asdfinvalid")
.expect("parse_dns_name");
assert_eq!(
x,
DnsName {
fulldomain: Some("www.example.asdfinvalid".into()),
root: "example.asdfinvalid".into(),
suffix: "asdfinvalid".into(),
}
);
}
#[test]
fn test_psl_a_prod_fastly_net() {
let x = init().parse_dns_name("a.prod.fastly.net").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: None,
root: "a.prod.fastly.net".into(),
suffix: "a.prod.fastly.net".into(),
});
let x = init()
.parse_dns_name("a.prod.fastly.net")
.expect("parse_dns_name");
assert_eq!(
x,
DnsName {
fulldomain: None,
root: "a.prod.fastly.net".into(),
suffix: "a.prod.fastly.net".into(),
}
);
}
#[test]
fn test_psl_www_a_prod_fastly_net() {
let x = init().parse_dns_name("www.a.prod.fastly.net").expect("parse_dns_name");
assert_eq!(x, DnsName {
fulldomain: None,
root: "www.a.prod.fastly.net".into(),
suffix: "a.prod.fastly.net".into(),
});
let x = init()
.parse_dns_name("www.a.prod.fastly.net")
.expect("parse_dns_name");
assert_eq!(
x,
DnsName {
fulldomain: None,
root: "www.a.prod.fastly.net".into(),
suffix: "a.prod.fastly.net".into(),
}
);
}
}

View File

@@ -1,5 +1,5 @@
use chrono::prelude::*;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::result;
use std::sync::mpsc;
@@ -43,9 +43,7 @@ struct Bucket {
impl Bucket {
pub fn new() -> Bucket {
Bucket {
passes: Vec::new(),
}
Bucket { passes: Vec::new() }
}
pub fn pass(&mut self, passes: usize, time: u32) -> RatelimitResponse {

View File

@@ -1,8 +1,8 @@
use crate::errors::*;
use bufstream::BufStream;
use crate::hlua::AnyLuaValue;
use crate::json::LuaJsonValue;
use bufstream::BufStream;
use chrootable_https::dns::{DnsResolver, RecordType};
use chrootable_https::socks5::{self, ProxyDest};
use regex::Regex;
@@ -10,19 +10,18 @@ use serde::Deserialize;
use tokio::runtime::Runtime;
use std::fmt;
use std::str;
use std::io;
use std::io::prelude::*;
use std::io::BufRead;
use std::net::SocketAddr;
use std::net::TcpStream;
use std::net::{IpAddr, Ipv4Addr};
use std::str;
use std::time::Duration;
mod tls;
pub use self::tls::TlsData;
#[cfg(unix)]
fn unwrap_socket(socket: tokio::net::TcpStream) -> Result<TcpStream> {
use std::os::unix::io::AsRawFd;
@@ -49,7 +48,6 @@ pub struct SocketOptions {
// TODO: enable_sni (default to true)
// TODO: sni_name
// TODO: cacert
#[serde(default)]
pub connect_timeout: u64,
#[serde(default)]
@@ -96,10 +94,16 @@ pub enum Stream {
}
impl Stream {
pub fn connect_stream<R: DnsResolver>(resolver: &R, host: &str, port: u16, options: &SocketOptions) -> Result<Stream> {
pub fn connect_stream<R: DnsResolver>(
resolver: &R,
host: &str,
port: u16,
options: &SocketOptions,
) -> Result<Stream> {
let addrs = match host.parse::<IpAddr>() {
Ok(addr) => vec![addr],
Err(_) => resolver.resolve(host, RecordType::A)
Err(_) => resolver
.resolve(host, RecordType::A)
.wait_for_response()?
.success()?,
};
@@ -110,7 +114,7 @@ impl Stream {
match Stream::connect_addr(host, (addr, port).into(), options) {
Ok(socket) => {
return Ok(socket);
},
}
Err(err) => errors.push((addr, err)),
}
}
@@ -138,8 +142,16 @@ impl Stream {
tls::wrap_if_enabled(socket, host, options)
}
pub fn connect_socks5_stream(proxy: SocketAddr, host: &str, port: u16, options: &SocketOptions) -> Result<Stream> {
debug!("connecting to {:?}:{:?} with socks5 on {:?}", host, port, proxy);
pub fn connect_socks5_stream(
proxy: SocketAddr,
host: &str,
port: u16,
options: &SocketOptions,
) -> Result<Stream> {
debug!(
"connecting to {:?}:{:?} with socks5 on {:?}",
host, port, proxy
);
let addr = match host.parse::<Ipv4Addr>() {
Ok(ipaddr) => ProxyDest::Ipv4Addr(ipaddr),
@@ -206,12 +218,22 @@ impl Socket {
}
}
pub fn connect<R: DnsResolver>(resolver: &R, host: &str, port: u16, options: &SocketOptions) -> Result<Socket> {
pub fn connect<R: DnsResolver>(
resolver: &R,
host: &str,
port: u16,
options: &SocketOptions,
) -> Result<Socket> {
let stream = Stream::connect_stream(resolver, host, port, options)?;
Ok(Socket::new(stream))
}
pub fn connect_socks5(proxy: SocketAddr, host: &str, port: u16, options: &SocketOptions) -> Result<Socket> {
pub fn connect_socks5(
proxy: SocketAddr,
host: &str,
port: u16,
options: &SocketOptions,
) -> Result<Socket> {
let stream = Stream::connect_socks5_stream(proxy, host, port, options)?;
Ok(Socket::new(stream))
}
@@ -266,8 +288,7 @@ impl Socket {
pub fn recvline(&mut self) -> Result<String> {
let needle = self.newline.clone();
let buf = self.recvuntil(needle.as_bytes())?;
let line = String::from_utf8(buf)
.context("Failed to decode utf8")?;
let line = String::from_utf8(buf).context("Failed to decode utf8")?;
Ok(line)
}
@@ -321,10 +342,13 @@ impl Socket {
Ok(n) => n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(Vec::new()),
Err(e) => return Err(e.into())
Err(e) => return Err(e.into()),
};
match available.windows(delim_len).position(|window| window == delim) {
match available
.windows(delim_len)
.position(|window| window == delim)
{
Some(i) => {
buf.extend_from_slice(&available[..i + delim_len]);
(true, i + delim_len)
@@ -366,21 +390,34 @@ mod tests {
#[test]
fn verify_tls_good() {
let resolver = Resolver::from_system_v4().unwrap();
let _sock = Socket::connect(&resolver, "badssl.com", 443, &SocketOptions{
tls: true,
..Default::default()
}).unwrap();
let _sock = Socket::connect(
&resolver,
"badssl.com",
443,
&SocketOptions {
tls: true,
..Default::default()
},
)
.unwrap();
}
#[test]
#[ignore]
fn verify_tls_good_request() {
let resolver = Resolver::from_system_v4().unwrap();
let mut sock = Socket::connect(&resolver, "badssl.com", 443, &SocketOptions{
tls: true,
..Default::default()
}).unwrap();
sock.send(b"GET / HTTP/1.1\r\nHost: badssl.com\r\nConnection: close\r\n\r\n").unwrap();
let mut sock = Socket::connect(
&resolver,
"badssl.com",
443,
&SocketOptions {
tls: true,
..Default::default()
},
)
.unwrap();
sock.send(b"GET / HTTP/1.1\r\nHost: badssl.com\r\nConnection: close\r\n\r\n")
.unwrap();
let status = sock.recvline().unwrap();
assert_eq!(status, "HTTP/1.1 200 OK\r\n");
}
@@ -389,10 +426,15 @@ mod tests {
#[ignore]
fn verify_tls_expired() {
let resolver = Resolver::from_system_v4().unwrap();
let sock = Socket::connect(&resolver, "expired.badssl.com", 443, &SocketOptions{
tls: true,
..Default::default()
});
let sock = Socket::connect(
&resolver,
"expired.badssl.com",
443,
&SocketOptions {
tls: true,
..Default::default()
},
);
assert!(sock.is_err());
}

View File

@@ -1,13 +1,13 @@
use super::{SocketOptions, Stream};
use crate::errors::*;
use crate::hlua::AnyLuaValue;
use crate::json::LuaJsonValue;
use rustls::{self, ClientConfig, Session, ClientSession};
use rustls::{self, ClientConfig, ClientSession, Session};
use serde::Serialize;
use std::str;
use std::result;
use std::sync::Arc;
use std::net::TcpStream;
use super::{Stream, SocketOptions};
use std::result;
use std::str;
use std::sync::Arc;
#[derive(Debug, Serialize)]
pub struct TlsData {
@@ -42,7 +42,8 @@ pub fn wrap(stream: TcpStream, host: &str, options: &SocketOptions) -> Result<(S
if options.disable_tls_verify {
info!("tls verification has been disabled");
config.dangerous()
config
.dangerous()
.set_certificate_verifier(Arc::new(NoCertificateVerification {}));
}
@@ -73,12 +74,14 @@ fn get_dns_name(config: &mut ClientConfig, host: &str) -> webpki::DNSName {
fn setup(mut stream: TcpStream, mut session: ClientSession) -> Result<(Stream, TlsData)> {
info!("starting tls handshake");
if session.is_handshaking() {
session.complete_io(&mut stream)
session
.complete_io(&mut stream)
.context("Failed to read reply to tls client hello")?;
}
if session.wants_write() {
session.complete_io(&mut stream)
session
.complete_io(&mut stream)
.context("wants_write->complete_io failed")?;
}
@@ -88,19 +91,14 @@ fn setup(mut stream: TcpStream, mut session: ClientSession) -> Result<(Stream, T
};
if let Some(certs) = session.get_peer_certificates() {
tls.cert_chain = certs.into_iter()
tls.cert_chain = certs
.into_iter()
.rev()
.map(|c| {
pem::encode(&pem::Pem {
tag: String::from("CERTIFICATE"),
contents: c.0,
})
})
.map(|c| pem::encode(&pem::Pem::new("CERTIFICATE", c.0)))
.collect();
}
tls.cert = tls.cert_chain.last()
.map(|x| x.to_owned());
tls.cert = tls.cert_chain.last().map(|x| x.to_owned());
info!("successfully established tls connection");
let stream = rustls::StreamOwned::new(session, stream);
@@ -111,11 +109,13 @@ fn setup(mut stream: TcpStream, mut session: ClientSession) -> Result<(Stream, T
pub struct NoCertificateVerification {}
impl rustls::ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(&self,
fn verify_server_cert(
&self,
_roots: &rustls::RootCertStore,
_presented_certs: &[rustls::Certificate],
_dns_name: webpki::DNSNameRef<'_>,
_ocsp: &[u8]) -> result::Result<rustls::ServerCertVerified, rustls::TLSError> {
_ocsp: &[u8],
) -> result::Result<rustls::ServerCertVerified, rustls::TLSError> {
Ok(rustls::ServerCertVerified::assertion())
}
}

View File

@@ -1,17 +1,17 @@
use crate::blobs::{Blob, BlobState};
use crate::engine::structs::LuaMap;
use crate::errors::*;
use crate::json::LuaJsonValue;
use crate::hlua::AnyLuaValue;
use chrootable_https::{Request, Body, Uri};
pub use chrootable_https::{Client, HttpClient, Resolver, Response};
use chrootable_https::http::HttpTryFrom;
use chrootable_https::http::uri::Parts;
use crate::json::LuaJsonValue;
use chrootable_https::http::request::Builder;
use chrootable_https::http::uri::Parts;
use chrootable_https::http::HttpTryFrom;
use chrootable_https::{Body, Request, Uri};
pub use chrootable_https::{Client, HttpClient, Resolver, Response};
use data_encoding::BASE64;
use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
use serde::{Serialize, Deserialize};
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::fmt::Write;
@@ -27,15 +27,17 @@ pub fn url_set_qs<S: Serialize + fmt::Debug>(url: Uri, query: &S) -> Result<Uri>
let query = serde_urlencoded::to_string(query)?;
parts.path_and_query = Some(match parts.path_and_query {
Some(pq) => {
format!("{}?{}", pq.path(), query)
},
None => format!("/?{}", query),
}.parse()?);
parts.path_and_query = Some(
match parts.path_and_query {
Some(pq) => {
format!("{}?{}", pq.path(), query)
}
None => format!("/?{}", query),
}
.parse()?,
);
Uri::from_parts(parts)
.map_err(Error::from)
Uri::from_parts(parts).map_err(Error::from)
}
pub trait WebState {
@@ -58,10 +60,13 @@ impl HttpSession {
.map(char::from)
.take(16)
.collect();
(id.clone(), HttpSession {
id,
cookies: CookieJar::default(),
})
(
id.clone(),
HttpSession {
id,
cookies: CookieJar::default(),
},
)
}
}
@@ -112,7 +117,13 @@ pub struct HttpRequest {
}
impl HttpRequest {
pub fn new(session: &HttpSession, method: String, url: String, user_agent: String, options: RequestOptions) -> HttpRequest {
pub fn new(
session: &HttpSession,
method: String,
url: String,
user_agent: String,
options: RequestOptions,
) -> HttpRequest {
let cookies = session.cookies.clone();
let timeout = options.timeout.map(Duration::from_millis);
@@ -179,21 +190,21 @@ impl HttpRequest {
// finalize request
let body = match self.body {
Some(ReqBody::Raw(ref x)) => { Body::from(x.clone()) },
Some(ReqBody::Raw(ref x)) => Body::from(x.clone()),
Some(ReqBody::Form(ref x)) => {
// if Content-Type is not set, set header
if !observed_headers.contains("content-type") {
req.header("Content-Type", "application/x-www-form-urlencoded");
}
Body::from(serde_urlencoded::to_string(x)?)
},
}
Some(ReqBody::Json(ref x)) => {
// if Content-Type is not set, set header
if !observed_headers.contains("content-type") {
req.header("Content-Type", "application/json");
}
Body::from(serde_json::to_string(x)?)
},
}
None => Body::empty(),
};
let mut req = req.body(body)?;
@@ -204,7 +215,8 @@ impl HttpRequest {
let res = loop {
// send request
debug!("Sending http request: {:?}", req);
let res = client.request(req)
let res = client
.request(req)
.with_timeout(self.timeout)
.wait_for_response()?;
@@ -232,7 +244,8 @@ impl HttpRequest {
/// create a basic request, reusable when following redirects
fn mkrequest<T>(&self, method: &str, url: T) -> Builder
where Uri: HttpTryFrom<T>,
where
Uri: HttpTryFrom<T>,
{
let mut req = Request::builder();
req.method(method);
@@ -263,7 +276,8 @@ impl HttpRequest {
}
pub fn response_to_lua<S>(&self, state: &S, res: Response) -> Result<LuaMap>
where S: WebState + BlobState
where
S: WebState + BlobState,
{
// map result to LuaMap
let mut resp = LuaMap::new();

View File

@@ -1,13 +1,13 @@
use chrootable_https::DnsResolver;
use crate::errors::*;
use crate::hlua::AnyLuaValue;
use crate::json::LuaJsonValue;
use crate::sockets::{Stream, SocketOptions};
use crate::sockets::{SocketOptions, Stream};
use chrootable_https::DnsResolver;
use http::Request;
use serde::Deserialize;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::io;
use std::net::SocketAddr;
use tungstenite::protocol::{self, Message};
use url::Url;
@@ -44,7 +44,11 @@ pub struct WebSocket {
}
impl WebSocket {
pub fn negotiate(stream: Stream, url: Url, headers: Option<&HashMap<String, String>>) -> Result<WebSocket> {
pub fn negotiate(
stream: Stream,
url: Url,
headers: Option<&HashMap<String, String>>,
) -> Result<WebSocket> {
let mut req = Request::get(url.to_string()); // TODO: don't re-parse here
if let Some(headers) = headers {
@@ -56,19 +60,22 @@ impl WebSocket {
let req = req.body(()).unwrap();
let (sock, _resp) = tungstenite::client::client(req, stream)?;
Ok(WebSocket {
sock,
})
Ok(WebSocket { sock })
}
pub fn connect<R: DnsResolver>(resolver: &R, url: Url, options: &WebSocketOptions) -> Result<WebSocket> {
pub fn connect<R: DnsResolver>(
resolver: &R,
url: Url,
options: &WebSocketOptions,
) -> Result<WebSocket> {
let tls = match url.scheme() {
"ws" => false,
"wss" => true,
_ => bail!("Invalid websocket protocol"),
};
let host = url.host_str()
let host = url
.host_str()
.ok_or_else(|| format_err!("Missing host in url"))?;
let port = match (url.port(), tls) {
@@ -77,16 +84,21 @@ impl WebSocket {
(None, false) => 80,
};
let stream = Stream::connect_stream(resolver, host, port, &SocketOptions {
tls,
sni_value: None,
disable_tls_verify: false,
proxy: options.proxy,
let stream = Stream::connect_stream(
resolver,
host,
port,
&SocketOptions {
tls,
sni_value: None,
disable_tls_verify: false,
proxy: options.proxy,
connect_timeout: options.connect_timeout,
read_timeout: options.read_timeout,
write_timeout: options.write_timeout,
})?;
connect_timeout: options.connect_timeout,
read_timeout: options.read_timeout,
write_timeout: options.write_timeout,
},
)?;
Self::negotiate(stream, url, options.headers.as_ref())
}
@@ -107,12 +119,14 @@ impl WebSocket {
Ok(Message::Ping(ping)) => {
self.sock.write_message(Message::Pong(ping))?;
continue;
},
}
Ok(Message::Pong(_)) => continue, // this should never happen
Ok(Message::Close(_)) => Event::Close,
Err(tungstenite::Error::ConnectionClosed) => Event::Close,
Err(tungstenite::Error::AlreadyClosed) => Event::Close,
Err(tungstenite::Error::Io(err)) if err.kind() == io::ErrorKind::WouldBlock => Event::Timeout,
Err(tungstenite::Error::Io(err)) if err.kind() == io::ErrorKind::WouldBlock => {
Event::Timeout
}
Err(err) => return Err(err.into()),
};
return Ok(msg);

View File

@@ -1,14 +1,13 @@
use crate::errors::*;
use crate::json::LuaJsonValue;
use crate::hlua::AnyLuaValue;
use crate::json::LuaJsonValue;
use serde::Serialize;
use std::collections::HashMap;
use xml::attribute::OwnedAttribute;
use xml::name::OwnedName;
use xml::reader::{EventReader, ParserConfig, XmlEvent};
#[derive(Debug, PartialEq, Serialize)]
pub struct XmlDocument {
pub children: Vec<XmlElement>,
@@ -41,7 +40,8 @@ impl XmlElement {
#[inline]
fn from(name: OwnedName, attributes: Vec<OwnedAttribute>) -> XmlElement {
let name = name.local_name;
let attrs = attributes.into_iter()
let attrs = attributes
.into_iter()
.map(|attr| (attr.name.local_name, attr.value))
.collect();
XmlElement {
@@ -91,16 +91,13 @@ fn decode_raw(x: &str) -> Result<XmlDocument> {
match next {
XmlEvent::StartElement {
name,
attributes,
..
name, attributes, ..
} => {
stack.push(XmlElement::from(name, attributes));
},
XmlEvent::EndElement {
name,
} => {
let child = stack.pop()
}
XmlEvent::EndElement { name } => {
let child = stack
.pop()
.ok_or_else(|| format_err!("end element has no matching start element"))?;
let name = name.local_name;
@@ -113,7 +110,7 @@ fn decode_raw(x: &str) -> Result<XmlDocument> {
} else {
doc.children.push(child);
}
},
}
XmlEvent::CData(text) => append_text(&mut stack, text),
XmlEvent::Characters(text) => append_text(&mut stack, text),
_ => (),
@@ -142,63 +139,62 @@ mod tests {
#[test]
fn verify_xml_decode_empty_body() {
let doc = decode_raw("<body></body>").unwrap();
assert_eq!(doc, XmlDocument {
children: vec![
XmlElement {
assert_eq!(
doc,
XmlDocument {
children: vec![XmlElement {
name: String::from("body"),
attrs: HashMap::new(),
text: None,
children: vec![],
}
]
});
}]
}
);
}
#[test]
fn verify_xml_decode_single_tag() {
let doc = decode_raw("<body><foo x=\"1\" /></body>").unwrap();
assert_eq!(doc, XmlDocument {
children: vec![
XmlElement {
assert_eq!(
doc,
XmlDocument {
children: vec![XmlElement {
name: String::from("body"),
attrs: HashMap::new(),
text: None,
children: vec![
XmlElement {
name: String::from("foo"),
attrs: hashmap!{
String::from("x") => String::from("1"),
},
text: None,
children: vec![],
}
],
}
]
});
children: vec![XmlElement {
name: String::from("foo"),
attrs: hashmap! {
String::from("x") => String::from("1"),
},
text: None,
children: vec![],
}],
}]
}
);
}
#[test]
fn verify_xml_decode_single_tag_text() {
let doc = decode_raw("<body><foo x=\"1\">hello world</foo></body>").unwrap();
assert_eq!(doc, XmlDocument {
children: vec![
XmlElement {
assert_eq!(
doc,
XmlDocument {
children: vec![XmlElement {
name: String::from("body"),
attrs: HashMap::new(),
text: None,
children: vec![
XmlElement {
name: String::from("foo"),
attrs: hashmap!{
String::from("x") => String::from("1"),
},
text: Some(String::from("hello world")),
children: vec![],
}
],
}
]
});
children: vec![XmlElement {
name: String::from("foo"),
attrs: hashmap! {
String::from("x") => String::from("1"),
},
text: Some(String::from("hello world")),
children: vec![],
}],
}]
}
);
}
}

View File

@@ -1,18 +1,20 @@
use clap::{CommandFactory, Parser};
use clap_complete::Shell;
use crate::cmd;
use crate::errors::*;
use crate::options;
use crate::workspaces::Workspace;
use structopt::StructOpt;
use structopt::clap::{AppSettings, Shell};
use sn0int_common::ModuleID;
use std::io;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
#[command(version)]
pub struct Args {
/// Select a different workspace instead of the default
#[structopt(short="w", long="workspace", env="SN0INT_WORKSPACE")]
#[arg(short = 'w', long="workspace", env="SN0INT_WORKSPACE")]
pub workspace: Option<Workspace>,
#[structopt(subcommand)]
#[command(subcommand)]
pub subcommand: Option<SubCommand>,
}
@@ -22,49 +24,49 @@ impl Args {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum SubCommand {
/// Run a module directly
#[structopt(name="run")]
#[command(name="run")]
Run(Run),
/// For internal use
#[structopt(name="sandbox")]
#[command(name="sandbox")]
Sandbox(Sandbox),
/// Login to the registry for publishing
#[structopt(name="login")]
#[command(name="login")]
Login(Login),
/// Create a new module
#[structopt(name="new")]
#[command(name="new")]
New(New),
/// Publish a script to the registry
#[structopt(name="publish")]
#[command(name="publish")]
Publish(Publish),
/// Install a module from the registry
#[structopt(name="install")]
#[command(name="install")]
Install(Install),
/// Search in the registry
#[structopt(name="search")]
#[command(name="search")]
Search(Search),
/// The sn0int package manager
#[structopt(name="pkg")]
#[command(name="pkg")]
Pkg(cmd::pkg_cmd::Args),
/// Insert into the database
#[structopt(name="add")]
#[command(name="add")]
Add(cmd::add_cmd::Args),
/// Select from the database
#[structopt(name="select")]
#[command(name="select")]
Select(cmd::select_cmd::Args),
/// Delete from the database
#[structopt(name="delete")]
#[command(name="delete")]
Delete(cmd::delete_cmd::Args),
/// Query logged activity
#[structopt(name="activity")]
#[command(name="activity")]
Activity(cmd::activity_cmd::Args),
/// Include entities in the scope
#[structopt(name="scope")]
#[command(name="scope")]
Scope(cmd::scope_cmd::Args),
/// Exclude entities from scope
#[structopt(name="noscope")]
#[command(name="noscope")]
Noscope(cmd::noscope_cmd::Args),
/// Manage autoscope rules
Autoscope(cmd::autoscope_cmd::Args),
@@ -73,111 +75,123 @@ pub enum SubCommand {
/// Rescope all entities based on autonoscope rules
Rescope(cmd::rescope_cmd::Args),
/// Manage workspaces
#[structopt(name="workspace")]
#[command(name="workspace")]
Workspace(cmd::workspace_cmd::Args),
/// Calendar
#[structopt(name="cal")]
#[command(name="cal")]
Cal(cmd::cal_cmd::Args),
/// Notify
#[structopt(name="notify")]
#[command(name="notify")]
Notify(cmd::notify_cmd::Args),
/// Verify blob storage for corrupt and dangling blobs
#[structopt(name="fsck")]
#[command(name="fsck")]
Fsck(cmd::fsck_cmd::Args),
/// Export a workspace for external processing
#[structopt(name="export")]
#[command(name="export")]
Export(cmd::export_cmd::Args),
/// Show statistics about your current workspace
#[structopt(name="stats")]
#[command(name="stats")]
Stats(cmd::stats_cmd::Args),
/// Run a lua repl
#[structopt(name="repl")]
#[command(name="repl")]
Repl,
/// Show paths of various file system locations
#[structopt(name="paths")]
#[command(name="paths")]
Paths,
/// Generate shell completions
#[structopt(name="completions")]
#[command(name="completions")]
Completions(Completions),
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Run {
#[structopt(flatten)]
#[command(flatten)]
pub run: cmd::run_cmd::Args,
/// Run a module from a path
#[structopt(short="f", long="file")]
#[arg(short = 'f', long="file")]
pub file: bool,
/// Expose stdin to modules
#[structopt(long="stdin")]
#[arg(long="stdin")]
pub stdin: bool,
/// Automatically grant access to a keyring namespace
#[structopt(long="grant")]
#[arg(long="grant")]
pub grants: Vec<String>,
/// Automatically grant access to all requested keys
#[structopt(long="grant-full-keyring")]
#[arg(long="grant-full-keyring")]
pub grant_full_keyring: bool,
/// Automatically deny access to all requested keys
#[structopt(long="deny-keyring")]
#[arg(long="deny-keyring")]
pub deny_keyring: bool,
/// Exit on first error and set exit code
#[structopt(short="x", long="exit-on-error")]
#[arg(short = 'x', long="exit-on-error")]
pub exit_on_error: bool,
/// Set an option
#[structopt(short="o", long="option")]
#[arg(short = 'o', long="option")]
pub options: Vec<options::Opt>,
/// Narrow down targeted entities
#[structopt(short="t", long="target")]
#[arg(short = 't', long="target")]
pub target: Option<String>,
/// Dump the sandbox init message to stdout instead of running a child process
#[structopt(long="dump-sandbox-init-msg")]
#[arg(long="dump-sandbox-init-msg")]
pub dump_sandbox_init_msg: bool,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Sandbox {
/// This value is only used for process listings
_label: String,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Login {
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct New {
/// Path to the new file
pub path: String,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Publish {
/// The scripts to publish
#[structopt(required = true)]
#[arg(required = true)]
pub paths: Vec<String>,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Install {
/// The script to install
pub module: ModuleID,
/// Specify the version, defaults to the latest version
pub version: Option<String>,
#[structopt(short="f", long="force")]
#[arg(short = 'f', long="force")]
pub force: bool,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Search {
/// Only show modules that aren't installed yet
#[structopt(long="new")]
#[arg(long="new")]
pub new: bool,
/// The search query
pub query: String,
}
#[derive(Debug, StructOpt)]
/// Generate shell completions
#[derive(Debug, Parser)]
pub struct Completions {
#[structopt(possible_values=&Shell::variants())]
pub shell: Shell,
}
impl Completions {
pub fn generate(&self) -> Result<()> {
clap_complete::generate(
self.shell,
&mut Args::command(),
"sn0int",
&mut io::stdout(),
);
Ok(())
}
}

View File

@@ -205,7 +205,7 @@ impl RuleSet {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum RuleType {
Domain,
Ip,

View File

@@ -1,10 +1,10 @@
use crate::errors::*;
use std::str::FromStr;
pub mod date;
pub mod time;
#[derive(Debug)]
use crate::errors::*;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub enum DateArg {
Month(u32),
Num(i32),

View File

@@ -1,17 +1,15 @@
use crate::errors::*;
use crate::cmd::Cmd;
use crate::shell::Shell;
use crate::models::*;
use chrono::{Utc, NaiveDateTime, NaiveTime, Duration};
use clap::Parser;
use crate::cmd::Cmd;
use crate::errors::*;
use crate::models::*;
use crate::shell::Shell;
use regex::Regex;
use std::convert::TryFrom;
use std::io;
use std::str::FromStr;
use structopt::StructOpt;
use structopt::clap::AppSettings;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct TimeSpec {
datetime: NaiveDateTime,
}
@@ -64,23 +62,22 @@ impl FromStr for TimeSpec {
}
}
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
/// Only query events for a given topic
#[structopt(short="t", long="topic")]
#[arg(short = 't', long="topic")]
topic: Option<String>,
/// Only query events starting from that datetime
#[structopt(long="since")]
#[arg(long="since")]
since: Option<TimeSpec>,
/// Only query events until this datetime
#[structopt(long="until")]
#[arg(long="until")]
until: Option<TimeSpec>,
/// Try to select the previous event before --since as an initial state
#[structopt(short="i", long="initial")]
#[arg(short = 'i', long="initial")]
initial: bool,
/// Only query events that are tied to a location
#[structopt(short="l", long="location")]
#[arg(short = 'l', long="location")]
location: bool,
}

View File

@@ -1,13 +1,11 @@
use crate::errors::*;
use crate::blobs::Blob;
use crate::cmd::Cmd;
use crate::db::DbChange;
use crate::gfx;
use crate::models::*;
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
use crate::utils;
use crate::term;
use std::fmt::Debug;
@@ -18,62 +16,61 @@ use std::net::SocketAddr;
use std::path::Path;
use walkdir::WalkDir;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(subcommand)]
#[command(subcommand)]
subcommand: Target,
/// Do not actually insert into database
#[structopt(short="n", long="dry-run")]
#[arg(short = 'n', long="dry-run")]
dry_run: bool,
/// Stream structs from stdin line by line
#[structopt(long)]
#[arg(long)]
stdin: bool,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum Target {
/// Insert domain into the database
#[structopt(name="domain")]
#[command(name="domain")]
Domain(AddDomain),
/// Insert subdomain into the database
#[structopt(name="subdomain")]
#[command(name="subdomain")]
Subdomain(AddSubdomain),
/// Insert ip address into the database
#[structopt(name="ipaddr")]
#[command(name="ipaddr")]
IpAddr(AddIpAddr),
/// Insert url into the database
#[structopt(name="url")]
#[command(name="url")]
Url(AddUrl),
/// Insert email into the database
#[structopt(name="email")]
#[command(name="email")]
Email(AddEmail),
/// Insert phonenumber into the database
#[structopt(name="phonenumber")]
#[command(name="phonenumber")]
PhoneNumber(AddPhoneNumber),
/// Insert device into the database
#[structopt(name="device")]
#[command(name="device")]
Device(AddDevice),
/// Insert network into the database
#[structopt(name="network")]
#[command(name="network")]
Network(AddNetwork),
/// Insert account into the database
#[structopt(name="account")]
#[command(name="account")]
Account(AddAccount),
/// Insert breach into the database
#[structopt(name="breach")]
#[command(name="breach")]
Breach(AddBreach),
/// Insert images into the database
#[structopt(name="image")]
#[command(name="image")]
Image(AddImage),
/// Insert ip network into the database
#[structopt(name="netblock")]
#[command(name="netblock")]
Netblock(AddNetblock),
/// Insert port into the database
#[structopt(name="port")]
#[command(name="port")]
Port(AddPort),
/// Insert a crypto currency address into the database
#[structopt(name="cryptoaddr")]
#[command(name="cryptoaddr")]
CryptoAddr(AddCryptoAddr),
}
@@ -153,7 +150,7 @@ trait IntoInsert: Sized {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddDomain {
domain: Option<String>,
}
@@ -185,7 +182,7 @@ impl InsertFromString for AddDomain {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddSubdomain {
subdomain: Option<String>,
}
@@ -222,7 +219,7 @@ impl InsertFromString for AddSubdomain {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddIpAddr {
ipaddr: Option<net::IpAddr>,
}
@@ -269,7 +266,7 @@ impl InsertFromString for AddIpAddr {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddUrl {
url: Option<String>,
}
@@ -323,7 +320,7 @@ impl InsertFromString for AddUrl {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddEmail {
email: Option<String>,
}
@@ -350,7 +347,7 @@ impl InsertFromString for AddEmail {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddPhoneNumber {
phonenumber: Option<String>,
name: Option<String>,
@@ -405,7 +402,7 @@ impl InsertFromString for AddPhoneNumber {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddDevice {
mac: Option<String>,
name: Option<String>,
@@ -435,7 +432,7 @@ impl IntoInsert for AddDevice {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddNetwork {
network: Option<String>,
latitude: Option<f32>,
@@ -464,7 +461,7 @@ impl IntoInsert for AddNetwork {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddAccount {
service: Option<String>,
username: Option<String>,
@@ -506,7 +503,7 @@ impl IntoInsert for AddAccount {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddBreach {
name: Option<String>,
}
@@ -525,7 +522,7 @@ impl IntoInsert for AddBreach {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddImage {
paths: Vec<String>,
}
@@ -624,7 +621,7 @@ impl IntoInsert for AddImage {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddNetblock {
ipnet: Option<ipnetwork::IpNetwork>,
}
@@ -655,7 +652,7 @@ impl IntoInsert for AddNetblock {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddPort {
protocol: Option<String>,
addr: Option<SocketAddr>,
@@ -730,7 +727,7 @@ impl InsertFromString for AddPort {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct AddCryptoAddr {
address: Option<String>,
}

View File

@@ -1,37 +1,35 @@
use crate::errors::*;
use crate::autonoscope;
use crate::cmd::Cmd;
use crate::fmt::colors::*;
use crate::shell::Shell;
use std::fmt::Write;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
#[group(skip)]
pub struct Args {
#[structopt(subcommand)]
#[command(subcommand)]
subcommand: Subcommand,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum Subcommand {
#[structopt(name="add")]
#[command(name="add")]
Add(Add),
#[structopt(name="delete")]
#[command(name="delete")]
Delete(Delete),
#[structopt(name="list")]
#[command(name="list")]
List,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Add {
object: autonoscope::RuleType,
value: String,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Delete {
object: autonoscope::RuleType,
value: String,

View File

@@ -1,11 +1,10 @@
use crate::errors::*;
use clap::Parser;
use crate::cmd::Cmd;
use crate::cmd::autonoscope_cmd;
use crate::errors::*;
use crate::shell::Shell;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(flatten)]
args: autonoscope_cmd::Args,

View File

@@ -1,5 +1,4 @@
use crate::errors::*;
use chrono::Utc;
use crate::cal::DateArg;
use crate::cal::date::{DateContext, DateSpec};
@@ -7,21 +6,18 @@ use crate::cal::time::{DateTimeContext, DateTimeSpec};
use crate::cmd::Cmd;
use crate::models::*;
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
/// Show additional months for context
#[structopt(short="C", long)]
#[arg(short = 'C', long)]
context: Option<u32>,
/// Group events in 12 min slices
#[structopt(short="T", long, group = "view")]
#[arg(short = 'T', long, group = "view")]
time: bool,
/// Group events by hour
#[structopt(short="H", long, group = "view")]
#[arg(short = 'H', long, group = "view")]
hourly: bool,
args: Vec<DateArg>,
}

View File

@@ -1,16 +1,12 @@
use crate::errors::*;
use crate::cmd::Cmd;
use crate::filters::{Target, Filter};
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
use crate::models::*;
use crate::term;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(subcommand)]
subcommand: Target,

View File

@@ -1,21 +1,19 @@
use crate::errors::*;
use clap::Parser;
use clap::ValueEnum;
use crate::blobs::Blob;
use crate::cmd::Cmd;
use crate::db::ttl;
use crate::errors::*;
use crate::models::*;
use crate::shell::Shell;
use serde::{Serialize, Deserialize};
use std::io::{self, Write};
use structopt::StructOpt;
use structopt::clap::AppSettings;
use strum_macros::{EnumString, IntoStaticStr};
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
/// Specify the export format
#[structopt(short="f", long="format", possible_values=Format::variants())]
#[arg(short = 'f', long="format", value_enum)]
format: Format,
}
@@ -37,7 +35,7 @@ fn export<T: ExportFormat + Serialize>(rl: &mut Shell) -> Result<()> {
Ok(())
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, ValueEnum, Serialize, Deserialize)]
#[derive(EnumString, IntoStaticStr)]
#[strum(serialize_all = "kebab_case")]
pub enum Format {
@@ -45,17 +43,6 @@ pub enum Format {
JsonBlobs,
}
impl Format {
// TODO: this function should be generated by strum instead
#[inline]
fn variants() -> &'static [&'static str] {
&[
"json",
"json-blobs",
]
}
}
trait ExportFormat {
fn load(rl: &mut Shell) -> Result<Box<Self>>;
}

View File

@@ -1,25 +1,23 @@
use crate::errors::*;
use clap::{ArgAction, Parser};
use crate::blobs::Blob;
use crate::cmd::Cmd;
use crate::errors::*;
use crate::models::*;
use crate::shell::Shell;
use crate::term;
use crate::worker;
use crate::models::*;
use std::collections::HashSet;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Args {
/// Verbose output
#[structopt(short="v", long="verbose", parse(from_occurrences))]
verbose: u64,
#[arg(short = 'v', long="verbose", action(ArgAction::Count))]
verbose: u8,
/// Delete only dangling blobs
#[structopt(long="gc")]
#[arg(long="gc")]
gc: bool,
/// Delete dangling and corrupted blobs
#[structopt(long="gc-all")]
#[arg(long="gc-all")]
gc_all: bool,
}

View File

@@ -1,56 +1,47 @@
use clap::Parser;
use crate::errors::*;
use crate::keyring::{KeyName, KeyRing};
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use crate::utils;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub enum Args {
#[structopt(name="add")]
/// Add a new key to the keyring
Add(KeyRingAdd),
#[structopt(name="delete")]
/// Delete a key from the keyring
Delete(KeyRingDelete),
#[structopt(name="get")]
/// Get a key from the keyring
Get(KeyRingGet),
#[structopt(name="list")]
/// List keys in the keyring
List(KeyRingList),
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct KeyRingAdd {
key: KeyName,
secret: Option<String>,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct KeyRingDelete {
key: KeyName,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct KeyRingGet {
key: KeyName,
#[structopt(short="q",
long="quiet")]
#[arg(short = 'q', long="quiet")]
/// Only output secret key
quiet: bool,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct KeyRingList {
namespace: Option<String>,
}
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
let args = Args::from_iter_safe(args)?;
let args = Args::try_parse_from(args)?;
match args {
Args::Add(add) => keyring_add(rl, add),
Args::Delete(delete) => keyring_delete(rl, delete),

View File

@@ -2,17 +2,17 @@ use crate::errors::*;
use crate::shell::Shell;
use crate::config::Config;
pub trait Cmd: structopt::StructOpt + Sized {
pub trait Cmd: clap::Parser + Sized {
fn run(self, rl: &mut Shell) -> Result<()>;
#[inline]
fn run_str(rl: &mut Shell, args: &[String]) -> Result<()> {
let args = Self::from_iter_safe(args)?;
let args = Self::try_parse_from(args)?;
args.run(rl)
}
}
pub trait LiteCmd: structopt::StructOpt + Sized {
pub trait LiteCmd: clap::Parser + Sized {
fn run(self, config: &Config) -> Result<()>;
}

View File

@@ -1,18 +1,14 @@
use crate::errors::*;
use clap::Parser;
use crate::cmd::Cmd;
use crate::errors::*;
use crate::filters::{Target, Filter};
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use crate::models::*;
use crate::shell::Shell;
use crate::term;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(subcommand)]
#[command(subcommand)]
subcommand: Target,
}
@@ -40,7 +36,7 @@ impl Cmd for Args {
}
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
let args = Args::from_iter_safe(args)?;
let args = Args::try_parse_from(args)?;
args.run(rl)
}

View File

@@ -1,24 +1,21 @@
use crate::errors::*;
use clap::{ArgAction, Parser};
use crate::cmd::Cmd;
use crate::engine::Module;
use crate::errors::*;
use crate::notify::{self, Notification};
use crate::options::{self, Opt};
use crate::shell::Shell;
use crate::term;
use sn0int_std::ratelimits::Ratelimiter;
use std::fmt::Write;
use structopt::StructOpt;
use structopt::clap::AppSettings;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(subcommand)]
#[command(subcommand)]
subcommand: Subcommand,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum Subcommand {
/// Manually add a notification to the outbox
Send(SendArgs),
@@ -30,24 +27,24 @@ pub enum Subcommand {
Deliver,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct SendArgs {
/// Evaluate the routing rules, but do not actually send a notification
#[structopt(short="n", long)]
#[arg(short = 'n', long)]
pub dry_run: bool,
pub topic: String,
#[structopt(flatten)]
#[command(flatten)]
pub notification: Notification,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct ExecArgs {
pub module: String,
#[structopt(short="o", long="option")]
#[arg(short = 'o', long="option")]
pub options: Vec<options::Opt>,
#[structopt(short="v", long="verbose", parse(from_occurrences))]
verbose: u64,
#[structopt(flatten)]
#[arg(short = 'v', long="verbose", action(ArgAction::Count))]
verbose: u8,
#[command(flatten)]
pub notification: Notification,
}

View File

@@ -1,5 +1,4 @@
use crate::errors::*;
use crate::args::Install;
use crate::api::Client;
use crate::args;
@@ -16,80 +15,76 @@ use sn0int_common::metadata::Stealth;
use std::collections::HashSet;
use std::fmt::Write;
use std::sync::Arc;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(subcommand)]
#[command(subcommand)]
pub subcommand: SubCommand,
}
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct ArgsInteractive {
#[structopt(subcommand)]
#[command(subcommand)]
pub subcommand: SubCommandInteractive,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum SubCommand {
/// List installed modules
#[structopt(name="list")]
#[command(name="list")]
List(List),
/// Install module from registry
#[structopt(name="install")]
#[command(name="install")]
Install(args::Install),
/// Search modules in registry
#[structopt(name="search")]
#[command(name="search")]
Search(args::Search),
/// Update modules
#[structopt(name="update")]
#[command(name="update")]
Update(Update),
/// Uninstall a module
#[structopt(name="uninstall")]
#[command(name="uninstall")]
Uninstall(Uninstall),
/// Install all featured modules
#[structopt(name="quickstart")]
#[command(name="quickstart")]
Quickstart,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum SubCommandInteractive {
#[structopt(flatten)]
#[command(flatten)]
Base(SubCommand),
/// Reload modules
#[structopt(name="reload")]
#[command(name="reload")]
Reload(Reload),
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct List {
/// Only show modules with a specific input source
#[structopt(long="source")]
#[arg(long="source")]
pub source: Option<String>,
/// List outdated modules
#[structopt(long="outdated")]
#[arg(long="outdated")]
pub outdated_only: bool,
/// Only show modules with equal or better stealth level
#[structopt(long="stealth", possible_values=Stealth::variants())]
#[arg(long="stealth", value_enum)]
pub stealth: Option<Stealth>,
/// Filter by pattern
#[structopt(default_value="*")]
#[arg(default_value="*")]
pub pattern: String,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Reload {
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Update {
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Uninstall {
module: ModuleID,
}

View File

@@ -1,20 +1,16 @@
use crate::errors::*;
use crate::cmd::Cmd;
use crate::cmd::pkg_cmd::{ArgsInteractive as PkgArgs, SubCommand, SubCommandInteractive};
use crate::shell::Shell;
use crate::term;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
}
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
let _args = Args::from_iter_safe(args)?;
let _args = Args::try_parse_from(args)?;
term::warn("The \x1b[1mquickstart\x1b[0m command is deprecated, use \x1b[1mpkg quickstart\x1b[0m");

View File

@@ -7,26 +7,24 @@ use crate::filters::{Filter, Target};
use crate::shell::Shell;
use std::collections::HashSet;
use std::fmt;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
use crate::models::*;
use crate::utils;
use crate::term;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
/// Run rules interactively
#[structopt(short, long)]
#[arg(short, long)]
interactive: bool,
/// Automatically apply changes to database
#[structopt(short="y", long)]
#[arg(short='y', long)]
auto_confirm: bool,
/// Only show changes, do not apply them to the database
#[structopt(short="n", long)]
#[arg(short='n', long)]
dry_run: bool,
/// Only rescope entities matching specific filter
#[structopt(subcommand)]
#[command(subcommand)]
target: Option<Target>,
}

View File

@@ -1,15 +1,15 @@
use crate::errors::*;
use chrootable_https::dns::Resolver;
use clap::{ArgAction, Parser};
use crate::args;
use crate::blobs::{Blob, BlobStorage};
use crate::cmd::Cmd;
use crate::db::{ttl, Filter};
use crate::engine::Module;
use crate::errors::*;
use crate::ipc::common::StartCommand;
use crate::keyring::KeyRing;
use crate::models::*;
use crate::shell::Shell;
use crate::keyring::KeyRing;
use crate::term;
use crate::utils;
use crate::worker;
@@ -18,27 +18,23 @@ 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;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
/// Execute a module that has been installed
pub module: Option<String>,
/// Run investigations concurrently
#[structopt(short="j", default_value="1")]
#[arg(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,
#[arg(short = 'v', long, action(ArgAction::Count))]
pub verbose: u8,
/// Set a specific socks5 proxy to use
#[structopt(short="X", long)]
#[arg(short = 'X', long)]
pub proxy: Option<SocketAddr>,
/// Set a different default user agent
#[structopt(long)]
#[arg(long)]
pub user_agent: Option<String>,
}
@@ -46,7 +42,7 @@ pub struct Args {
pub struct Params<'a> {
pub module: Option<&'a String>,
pub threads: usize,
pub verbose: u64,
pub verbose: u8,
pub stdin: bool,
pub grants: &'a [String],
pub grant_full_keyring: bool,

View File

@@ -1,18 +1,14 @@
use crate::errors::*;
use crate::cmd::Cmd;
use crate::filters::{Target, Filter};
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
use crate::models::*;
use crate::term;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(subcommand)]
#[command(subcommand)]
subcommand: Target,
}
@@ -40,7 +36,7 @@ impl Cmd for Args {
}
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
let args = Args::from_iter_safe(args)?;
let args = Args::try_parse_from(args)?;
args.run(rl)
}

View File

@@ -1,17 +1,13 @@
use crate::errors::*;
use clap::Parser;
use crate::cmd::Cmd;
use crate::db::ttl;
use crate::errors::*;
use crate::filters::{Target, Filter};
use crate::models::*;
use crate::shell::Shell;
use serde::Serialize;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use crate::models::*;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
#[structopt(subcommand)]
subcommand: Target,
@@ -25,7 +21,7 @@ pub struct Args {
#[structopt(long, group="output")]
paths: bool,
/// Count rows returned
#[structopt(short="c", group="output")]
#[structopt(short = 'c', group="output")]
count: bool,
}

View File

@@ -1,12 +1,8 @@
use clap::Parser;
use crate::errors::*;
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
key: Option<String>,
value: Option<String>,
@@ -15,7 +11,7 @@ pub struct Args {
// TODO: maybe introduce global settings
// TODO: maybe allow setting jobs here as well in addition to -j
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
let args = Args::from_iter_safe(args)?;
let args = Args::try_parse_from(args)?;
let options = rl.options_mut()
.ok_or_else(|| format_err!("Module needs to be selected first"))?;

View File

@@ -7,26 +7,23 @@ use crate::errors::*;
use crate::models::*;
use crate::shell::{self, Shell};
use crate::workspaces;
use humansize::{FileSize, file_size_opts};
use separator::Separatable;
use serde::{Serialize, Deserialize};
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
#[derive(Debug, Clone, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Clone, Parser)]
pub struct Args {
/// Exclude blob storage
#[structopt(short, long)]
#[arg(short, long)]
short: bool,
/// Exclude categories that don't contain any structs
#[structopt(short, long)]
#[arg(short, long)]
quiet: bool,
/// Show workspace statistics in json
#[structopt(short, long)]
#[arg(short, long)]
json: bool,
/// Go through all workspaces
#[structopt(short, long)]
#[arg(short, long)]
all: bool,
}
@@ -116,8 +113,7 @@ impl Stats {
total_size += storage.stat(blob)?;
}
let total_human_size = total_size.file_size(file_size_opts::CONVENTIONAL)
.map_err(|e| format_err!("Failed to format size: {}", e))?;
let total_human_size = humansize::format_size(total_size, humansize::BINARY);
self.blobs = Some(BlobStats {
count: blobs.len(),

View File

@@ -3,14 +3,12 @@ use crate::errors::*;
use crate::db;
use crate::shell::Shell;
use sn0int_common::metadata::Source;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use clap::Parser;
use crate::term;
use crate::models::*;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
// TODO: target -p # print current filter
// TODO: target -c # clear filter
@@ -19,7 +17,7 @@ pub struct Args {
}
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
let args = Args::from_iter_safe(args)?;
let args = Args::try_parse_from(args)?;
let source = rl.module()
.ok_or_else(|| format_err!("No module selected"))

View File

@@ -1,18 +1,14 @@
use clap::Parser;
use crate::errors::*;
use crate::shell::Shell;
use structopt::StructOpt;
use structopt::clap::AppSettings;
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
module: String,
}
pub fn run(rl: &mut Shell, args: &[String]) -> Result<()> {
let args = Args::from_iter_safe(args)?;
let args = Args::try_parse_from(args)?;
let module = rl.library().get(&args.module)?.clone();
rl.set_module(module);

View File

@@ -1,28 +1,24 @@
use crate::errors::*;
use clap::Parser;
use crate::blobs::BlobStorage;
use crate::cmd::{Cmd, LiteCmd};
use crate::config::Config;
use crate::db::Database;
use crate::errors::*;
use crate::shell::Shell;
use crate::term;
use crate::utils;
use structopt::StructOpt;
use structopt::clap::AppSettings;
use crate::workspaces::{self, Workspace};
#[derive(Debug, StructOpt)]
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
#[derive(Debug, Parser)]
pub struct Args {
/// Delete a workspaceb
#[structopt(long = "delete", group = "action")]
#[arg(long = "delete", group = "action")]
delete: bool,
/// Show disk usage of workspace
#[structopt(long = "usage", group = "action")]
#[arg(long = "usage", group = "action")]
usage: bool,
/// Skip confirmation
#[structopt(short = "f", long = "force")]
#[arg(short = 'f', long = "force")]
force: bool,
workspaces: Vec<Workspace>,
}

View File

@@ -41,10 +41,10 @@ impl Config {
}
pub fn load_from<P: AsRef<Path>>(path: P) -> Result<Config> {
let config = fs::read(&path)
let config = fs::read_to_string(&path)
.context("Failed to read config file")?;
let config = toml::from_slice(&config)?;
let config = toml::from_str(&config)?;
Ok(config)
}

View File

@@ -38,7 +38,7 @@ pub trait State {
fn recv(&self) -> Result<serde_json::Value>;
fn verbose(&self) -> u64;
fn verbose(&self) -> u8;
#[inline]
fn info(&self, msg: String) {
@@ -208,7 +208,7 @@ pub struct LuaState {
http_sessions: Mutex<HashMap<String, HttpSession>>,
http_clients: Mutex<HashMap<String, Arc<chrootable_https::Client<Resolver>>>>,
verbose: u64,
verbose: u8,
keyring: Vec<KeyRingEntry>, // TODO: maybe hashmap
dns_config: Resolver,
psl: Mutex<Lazy<PslReader, Arc<Psl>>>,
@@ -247,7 +247,7 @@ impl State for LuaState {
tx.recv()
}
fn verbose(&self) -> u64 {
fn verbose(&self) -> u8 {
self.verbose
}

View File

@@ -30,7 +30,7 @@ pub use sn0int_std::engine::structs;
/// Data that is passed to every script
#[derive(Debug)]
pub struct Environment {
pub verbose: u64,
pub verbose: u8,
pub keyring: Vec<KeyRingEntry>,
pub dns_config: Resolver,
pub proxy: Option<SocketAddr>,

View File

@@ -1,52 +1,50 @@
use crate::errors::*;
use crate::db;
use structopt::StructOpt;
use clap::Parser;
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub enum Target {
/// On domains
#[structopt(name="domains")]
#[command(name="domains")]
Domains(Filter),
/// On subdomains
#[structopt(name="subdomains")]
#[command(name="subdomains")]
Subdomains(Filter),
/// On ipaddrs
#[structopt(name="ipaddrs")]
#[command(name="ipaddrs")]
IpAddrs(Filter),
/// On urls
#[structopt(name="urls")]
#[command(name="urls")]
Urls(Filter),
/// On emails
#[structopt(name="emails")]
#[command(name="emails")]
Emails(Filter),
/// On phone numbers
#[structopt(name="phonenumbers")]
#[command(name="phonenumbers")]
PhoneNumbers(Filter),
/// On devices
#[structopt(name="devices")]
#[command(name="devices")]
Devices(Filter),
/// On networks
#[structopt(name="networks")]
#[command(name="networks")]
Networks(Filter),
/// On accounts
#[structopt(name="accounts")]
#[command(name="accounts")]
Accounts(Filter),
/// On breaches
#[structopt(name="breaches")]
#[command(name="breaches")]
Breaches(Filter),
/// On images
#[structopt(name="images")]
#[command(name="images")]
Images(Filter),
/// On ports
#[structopt(name="ports")]
#[command(name="ports")]
Ports(Filter),
/// On ipnets
#[structopt(name="netblocks")]
#[command(name="netblocks")]
Netblocks(Filter),
/// On crypto currency addresses
#[structopt(name="cryptoaddrs")]
#[command(name="cryptoaddrs")]
CryptoAddrs(Filter),
}
@@ -100,7 +98,7 @@ impl Target {
}
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
pub struct Filter {
args: Vec<String>,
}

View File

@@ -8,7 +8,7 @@ use std::net::SocketAddr;
#[derive(Debug, Serialize, Deserialize)]
pub struct StartCommand {
pub verbose: u64,
pub verbose: u8,
pub keyring: Vec<KeyRingEntry>,
pub dns_config: Resolver,
pub proxy: Option<SocketAddr>,
@@ -20,7 +20,7 @@ pub struct StartCommand {
}
impl StartCommand {
pub fn new(verbose: u64,
pub fn new(verbose: u8,
keyring: Vec<KeyRingEntry>,
dns_config: Resolver,
proxy: Option<SocketAddr>,

View File

@@ -106,7 +106,7 @@ pub fn run(module: Module,
tx: &EventSender,
arg: serde_json::Value,
keyring: Vec<KeyRingEntry>,
verbose: u64,
verbose: u8,
has_stdin: bool,
proxy: Option<SocketAddr>,
user_agent: Option<String>,

View File

@@ -13,7 +13,7 @@ use std::path::{Path, PathBuf};
use sn0int_common::ModuleID;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyName {
pub namespace: String,
pub name: String,

View File

@@ -1,6 +1,7 @@
use env_logger::Env;
use sn0int::args::{self, Args, SubCommand};
use sn0int::auth;
use clap::Parser;
use sn0int::cmd::{self, LiteCmd};
use sn0int::cmd::run_cmd::Params;
use sn0int::config::Config;
@@ -15,8 +16,7 @@ use sn0int::psl::PslReader;
use sn0int::registry;
use sn0int::repl;
use sn0int::sandbox;
use sn0int::shell::{self, complete};
use structopt::StructOpt;
use sn0int::shell;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
@@ -99,7 +99,7 @@ end
}
fn run() -> Result<()> {
let mut args = Args::from_args();
let mut args = Args::parse();
if !args.is_sandbox() {
sandbox::fasten_seatbelt()?;
@@ -142,7 +142,7 @@ fn run() -> Result<()> {
Some(SubCommand::Stats(stats)) => run_cmd(&args, stats, &config),
Some(SubCommand::Repl) => repl::run(&config),
Some(SubCommand::Paths) => paths::run(&config),
Some(SubCommand::Completions(completions)) => complete::run_generate(&completions),
Some(SubCommand::Completions(completions)) => completions.generate(),
None => shell::run(&args, &config),
}
}

View File

@@ -1,22 +1,22 @@
mod rules;
use crate::errors::*;
use crate::cmd::run_cmd::prepare_keyring;
use clap::Parser;
use crate::cmd::run_cmd::Params;
use crate::cmd::run_cmd::prepare_keyring;
use crate::engine::Module;
use crate::errors::*;
use crate::options;
use crate::shell::Shell;
use crate::term::SpinLogger;
use crate::worker;
use self::rules::Glob;
use serde::{Serialize, Deserialize};
use structopt::StructOpt;
use sn0int_common::metadata::Source;
use sn0int_std::blobs::Blob;
use sn0int_std::ratelimits::Ratelimiter;
use std::collections::HashMap;
#[derive(Debug, StructOpt, Serialize)]
#[derive(Debug, Parser, Serialize)]
pub struct Notification {
pub subject: String,
pub body: Option<String>,
@@ -79,7 +79,7 @@ fn prepare_arg(notification: &Notification) -> Result<(serde_json::Value, Option
Ok((arg, None, vec![]))
}
pub fn exec(rl: &mut Shell, module: &Module, ratelimit: &mut Ratelimiter, options: HashMap<String, String>, verbose: u64, notification: &Notification) -> Result<usize> {
pub fn exec(rl: &mut Shell, module: &Module, ratelimit: &mut Ratelimiter, options: HashMap<String, String>, verbose: u8, notification: &Notification) -> Result<usize> {
let module_name = module.canonical();
debug!("Setting up notification execution with {:?}", module_name);

View File

@@ -97,7 +97,7 @@ mod tests {
let script = Script::load_unchecked(r#"
function run()
session = http_mksession()
req = http_request(session, "GET", "https://httpbin.org/anything", {})
req = http_request(session, "GET", "https://github.com", {})
x = http_send(req)
if last_err() then return end
print(x)

View File

@@ -1,18 +1,13 @@
use crate::args::{Args, Completions};
use crate::autonoscope::RuleType;
use crate::errors::*;
use rustyline::{self, Context};
use rustyline::completion::Completer;
use rustyline::highlight::Highlighter;
use rustyline::hint::Hinter;
use std::borrow::Cow::{self, Owned};
use std::str::FromStr;
use std::io::stdout;
use structopt::StructOpt;
use crate::shell::Command;
use crate::workspaces;
#[derive(Debug, Default)]
pub struct CmdCompleter {
pub modules: Vec<String>,
@@ -318,8 +313,3 @@ impl Highlighter for CmdCompleter {
impl rustyline::Helper for CmdCompleter {}
impl rustyline::validate::Validator for CmdCompleter {}
pub fn run_generate(args: &Completions) -> Result<()> {
Args::clap().gen_completions_to("sn0int", args.shell, &mut stdout());
Ok(())
}

View File

@@ -199,7 +199,7 @@ impl DatabaseEvent {
Self::notify(rl, spinner, ratelimit, &topic, subject);
}
fn on_activity<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, object: &NewActivity, verbose: u64) {
fn on_activity<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, object: &NewActivity, verbose: u8) {
Self::spinner_log_new_activity(spinner, object, verbose);
// TODO: we don't want to copy the match arms everywhere
@@ -211,7 +211,7 @@ impl DatabaseEvent {
Self::notify(rl, spinner, ratelimit, &topic, subject);
}
fn spinner_log_new_activity<T: SpinLogger>(spinner: &mut T, object: &NewActivity, verbose: u64) {
fn spinner_log_new_activity<T: SpinLogger>(spinner: &mut T, object: &NewActivity, verbose: u8) {
let mut log = format!("{:?} ", object.topic);
if let Some(uniq) = &object.uniq {
write!(log, "({:?}) ", uniq).expect("out of memory");
@@ -233,7 +233,7 @@ impl DatabaseEvent {
spinner.log(&log);
}
fn insert<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, object: Insert, ttl: Option<i32>, tx: DbSender, verbose: u64) {
fn insert<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, object: Insert, ttl: Option<i32>, tx: DbSender, verbose: u8) {
let db = rl.db();
if verbose >= 1 {
spinner.debug(&format!("Inserting: {:?}", object));
@@ -299,7 +299,7 @@ impl DatabaseEvent {
}
pub fn activity<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, object: NewActivity, tx: DbSender, verbose: u64) {
pub fn activity<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, object: NewActivity, tx: DbSender, verbose: u8) {
let db = rl.db();
let result = db.insert_activity(object.clone());
debug!("{:?} => {:?}", object, result);
@@ -320,7 +320,7 @@ impl DatabaseEvent {
tx.send(result).expect("Failed to send db result to channel");
}
pub fn update<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, family: &str, value: &str, update: &Update, tx: DbSender, verbose: u64) {
pub fn update<T: SpinLogger>(rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, family: &str, value: &str, update: &Update, tx: DbSender, verbose: u8) {
let db = rl.db();
if verbose >= 1 {
spinner.debug(&format!("Updating: {:?}", update));
@@ -344,7 +344,7 @@ impl DatabaseEvent {
tx.send(result).expect("Failed to send db result to channel");
}
pub fn apply<T: SpinLogger>(self, rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, tx: DbSender, verbose: u64) {
pub fn apply<T: SpinLogger>(self, rl: &mut Shell, spinner: &mut T, ratelimit: &mut Ratelimiter, tx: DbSender, verbose: u8) {
match self {
DatabaseEvent::Insert(object) => Self::insert(rl, spinner, ratelimit, object, None, tx, verbose),
DatabaseEvent::InsertTtl((object, ttl)) => Self::insert(rl, spinner, ratelimit, object, Some(ttl), tx, verbose),

View File

@@ -26,7 +26,7 @@ impl Workspace {
#[inline]
pub fn usage_human(&self) -> Result<String> {
let usage = self.usage()?;
Ok(bytesize::to_string(usage, false))
Ok(humansize::format_size(usage, humansize::BINARY))
}
pub fn usage(&self) -> Result<u64> {