use crate::codegen;
use crate::errors::*;
use crate::keygen;
use crate::plot;
use crate::plot::{PkgFilter, PkgPatchValues};
use crate::sign::in_toto::VirtualEntry;
use clap::{ArgAction, CommandFactory, Parser, Subcommand};
use clap_complete::Shell;
use std::io::stdout;
use std::net::IpAddr;
use std::net::SocketAddr;
use std::path::PathBuf;
use url::Url;
#[derive(Debug, Parser)]
#[command(version)]
pub struct Args {
#[arg(short, long, global = true, action(ArgAction::Count))]
pub verbose: u8,
#[arg(short, long, global = true, action(ArgAction::Count))]
pub quiet: u8,
#[command(subcommand)]
pub subcommand: SubCommand,
}
#[derive(Debug, Subcommand)]
pub enum SubCommand {
Bait(Bait),
Front(Front),
#[command(subcommand)]
Infect(Infect),
#[command(subcommand)]
Tamper(Tamper),
#[command(subcommand)]
Keygen(Keygen),
#[command(subcommand)]
Sign(Sign),
#[command(subcommand)]
Hsm(Hsm),
Build(Build),
Check(Check),
Req(Req),
Completions(Completions),
}
#[derive(Debug, Clone, Parser)]
pub struct Plot {
#[arg(value_name = "PLOT-PATH")]
pub path: PathBuf,
#[arg(long = "cache-from")]
pub cache_from: Option<PathBuf>,
}
impl Plot {
pub async fn load_into_context(&self) -> Result<plot::Ctx> {
info!("Loading plot from {:?}...", self.path);
plot::Ctx::load_from_path(&self.path, self.cache_from.as_deref()).await
}
}
#[derive(Debug, Clone, Parser)]
pub struct Bind {
#[arg(
short = 'B',
long,
env = "SH4D0WUP_BIND",
default_value = "0.0.0.0:1337"
)]
pub addr: SocketAddr,
#[arg(short, long)]
pub no_bind: bool,
}
#[derive(Debug, Clone, Parser)]
pub struct Tls {
#[arg(long)]
pub cert: Option<PathBuf>,
#[arg(long)]
pub key: Option<PathBuf>,
}
#[derive(Debug, Clone, Parser)]
pub struct Bait {
#[clap(flatten)]
pub plot: Plot,
#[clap(flatten)]
pub bind: Bind,
#[clap(flatten)]
pub tls: Tls,
}
#[derive(Debug, Clone, Parser)]
pub struct Front {
#[arg(short = 'U', long)]
pub upstream: Url,
#[clap(flatten)]
pub bind: Bind,
#[clap(flatten)]
pub tls: Tls,
}
#[derive(Debug, Subcommand)]
pub enum Infect {
Pacman(InfectPacmanPkg),
Deb(InfectDebPkg),
Oci(InfectOci),
Apk(InfectApkPkg),
Elf(InfectElf),
ElfFwdStdin(InfectElfFwdStdin),
Sh(InfectSh),
}
#[derive(Debug, Clone, Parser)]
pub struct InfectPacmanPkg {
pub path: PathBuf,
pub out: PathBuf,
#[arg(long)]
pub set: Vec<String>,
#[arg(short = 'c', long)]
pub payload: Option<String>,
}
#[derive(Debug, Clone, Parser)]
pub struct InfectDebPkg {
pub path: PathBuf,
pub out: PathBuf,
#[arg(long)]
pub set: Vec<String>,
#[arg(short = 'c', long)]
pub payload: Option<String>,
}
#[derive(Debug, Clone, Parser)]
pub struct InfectOci {
pub path: PathBuf,
pub out: PathBuf,
#[arg(long, default_value = "14")]
pub entrypoint_hash_len: usize,
#[arg(long)]
pub entrypoint: Option<String>,
#[arg(short = 'c', long)]
pub payload: Option<String>,
#[arg(short = 't', long = "tag")]
pub tags: Vec<String>,
}
#[derive(Debug, Clone, Parser)]
pub struct InfectApkPkg {
pub path: PathBuf,
pub out: PathBuf,
#[arg(long)]
pub set: Vec<String>,
#[arg(short = 'S', long)]
pub signing_key: PathBuf,
#[arg(short = 'N', long)]
pub signing_key_name: String,
#[arg(short = 'c', long)]
pub payload: Option<String>,
}
#[derive(Debug, Clone, Parser)]
pub struct CompileElfConfig {
#[arg(short = 'B', long)]
pub backend: Option<codegen::Backend>,
#[arg(short, long)]
pub target: Option<String>,
}
#[derive(Debug, Clone, Parser)]
pub struct InfectElf {
pub path: PathBuf,
pub out: PathBuf,
#[clap(flatten)]
pub compile: CompileElfConfig,
#[arg(short = 'c', long)]
pub payload: Option<String>,
#[arg(short = 'e', long)]
pub elf: Option<String>,
#[arg(long)]
pub self_replace: bool,
#[arg(long)]
pub assume_path: Option<String>,
}
#[derive(Debug, Clone, Parser)]
pub struct InfectElfFwdStdin {
pub path: PathBuf,
pub out: PathBuf,
#[arg(long)]
pub exec: Option<String>,
#[arg(long = "arg")]
pub args: Vec<String>,
#[clap(flatten)]
pub compile: CompileElfConfig,
}
#[derive(Debug, Clone, Parser)]
pub struct InfectSh {
pub path: PathBuf,
pub out: PathBuf,
#[arg(short = 'c', long)]
pub payload: String,
#[arg(long = "hook")]
pub hooks: Vec<String>,
}
#[derive(Debug, Subcommand)]
pub enum Tamper {
PacmanDb(TamperPacman),
AptRelease(TamperAptRelease),
AptPackageList(TamperAptPackageList),
ApkIndex(TamperApkIndex),
GitCommit(TamperGitCommit),
}
#[derive(Debug, Clone, Parser)]
pub struct TamperPackageDatabaseConfig {
#[arg(long)]
pub filter: Vec<PkgFilter>,
#[arg(long)]
pub set: Vec<PkgPatchValues<Vec<String>>>,
#[arg(long)]
pub exclude: Vec<PkgFilter>,
}
#[derive(Debug, Clone, Parser)]
pub struct TamperPacman {
pub path: PathBuf,
pub out: PathBuf,
#[clap(flatten)]
pub config: TamperPackageDatabaseConfig,
}
#[derive(Debug, Clone, Parser)]
pub struct TamperAptRelease {
pub path: PathBuf,
pub out: PathBuf,
#[arg(long)]
pub release_set: Vec<String>,
#[arg(long)]
pub unsigned: bool,
#[arg(long)]
pub signing_key: Option<PathBuf>,
#[clap(flatten)]
pub config: TamperPackageDatabaseConfig,
}
#[derive(Debug, Clone, Parser)]
pub struct TamperAptPackageList {
pub path: PathBuf,
pub out: PathBuf,
#[clap(flatten)]
pub config: TamperPackageDatabaseConfig,
}
#[derive(Debug, Clone, Parser)]
pub struct TamperApkIndex {
pub path: PathBuf,
pub out: PathBuf,
#[clap(flatten)]
pub config: TamperPackageDatabaseConfig,
#[arg(short = 'S', long)]
pub signing_key: PathBuf,
#[arg(short = 'N', long)]
pub signing_key_name: String,
}
#[derive(Debug, Clone, Parser)]
pub struct TamperGitCommit {
#[arg(long)]
pub tree: Option<String>,
#[arg(long = "parent")]
pub parents: Vec<String>,
#[arg(long)]
pub no_parents: bool,
#[arg(long)]
pub author: Option<String>,
#[arg(long)]
pub committer: Option<String>,
#[arg(long)]
pub message: Option<String>,
#[arg(long)]
pub collision_prefix: Option<String>,
#[arg(long)]
pub nonce: Option<String>,
#[arg(long)]
pub stdin: bool,
#[arg(long, conflicts_with_all=&["stdin", "message"])]
pub message_stdin: bool,
#[arg(long)]
pub strip_header: bool,
}
#[derive(Debug, Subcommand)]
pub enum Keygen {
Tls(KeygenTls),
Pgp(KeygenPgp),
Ssh(KeygenSsh),
Openssl(KeygenOpenssl),
InToto(KeygenInToto),
}
#[derive(Debug, Clone, Parser)]
pub struct KeygenFlags {
#[arg(short = 'S', long)]
pub secret_key_only: bool,
#[arg(short = 'P', long)]
pub public_key_only: bool,
}
impl KeygenFlags {
pub fn all(&self) -> bool {
!self.secret_key_only && !self.public_key_only
}
pub fn secret_key(&self) -> bool {
self.all() || self.secret_key_only
}
pub fn public_key(&self) -> bool {
self.all() || self.public_key_only
}
}
#[derive(Debug, Clone, Parser)]
pub struct KeygenTls {
pub names: Vec<String>,
#[arg(long, group = "algo")]
pub ecdsa: bool,
#[arg(long, group = "algo")]
pub ed25519: bool,
#[arg(long, group = "algo")]
pub rsa: bool,
#[arg(long, group = "algo")]
pub rsa_sha256: bool,
#[arg(long, group = "algo")]
pub rsa_sha512: bool,
#[command(flatten)]
pub flags: KeygenFlags,
}
#[derive(Debug, Clone, Parser)]
pub struct KeygenPgp {
pub uids: Vec<String>,
#[command(flatten)]
pub flags: KeygenFlags,
}
#[derive(Debug, Clone, Parser)]
pub struct KeygenSsh {
#[arg(short = 't', long = "type", default_value = "ed25519")]
pub keytype: keygen::ssh::KeypairType,
#[arg(short, long)]
pub bits: Option<usize>,
#[command(flatten)]
pub flags: KeygenFlags,
}
#[derive(Debug, Clone, Parser)]
pub struct KeygenOpenssl {
#[arg(long, group = "keypair")]
pub rsa: bool,
#[arg(long, group = "keypair")]
pub secp256k1: bool,
#[arg(short, long)]
pub bits: Option<u32>,
#[command(flatten)]
pub flags: KeygenFlags,
}
#[derive(Debug, Clone, Parser)]
pub struct KeygenInToto {
#[command(flatten)]
pub flags: KeygenFlags,
}
#[derive(Debug, Subcommand)]
pub enum Sign {
PgpCleartext(SignPgp),
PgpDetached(SignPgp),
Openssl(SignOpenssl),
InToto(SignInToto),
}
#[derive(Debug, Clone, Parser)]
pub struct SignPgp {
#[arg(short, long)]
pub secret_key: PathBuf,
#[arg(short, long)]
pub binary: bool,
pub path: PathBuf,
}
#[derive(Debug, Clone, Parser)]
pub struct SignOpenssl {
#[arg(short, long)]
pub secret_key: PathBuf,
#[arg(short, long)]
pub binary: bool,
pub path: PathBuf,
#[arg(long, group = "hash")]
pub md5: bool,
#[arg(long, group = "hash")]
pub sha1: bool,
#[arg(long, group = "hash")]
pub sha256: bool,
#[arg(long, group = "hash")]
pub sha512: bool,
#[arg(long, group = "hash")]
pub sha3_256: bool,
#[arg(long, group = "hash")]
pub sha3_512: bool,
}
#[derive(Debug, Clone, Parser)]
pub struct SignInToto {
#[arg(short, long)]
pub secret_key: PathBuf,
pub path: PathBuf,
#[arg(long)]
pub name: String,
#[arg(long)]
pub material: Vec<VirtualEntry>,
#[arg(long)]
pub product: Vec<VirtualEntry>,
}
#[derive(Debug, Subcommand)]
pub enum Hsm {
#[clap(subcommand)]
Pgp(HsmPgp),
}
#[derive(Debug, Subcommand)]
pub enum HsmPgp {
Access(HsmAccess),
}
#[derive(Debug, Parser)]
pub struct HsmAccess {
#[clap(flatten)]
pub pin: HsmPin,
}
#[derive(Debug, Parser)]
pub struct HsmPin {
#[arg(short = 'p', long = "pin")]
pub value: Option<String>,
#[arg(long = "pin-file")]
pub file: Option<PathBuf>,
}
#[derive(Debug, Clone, Parser)]
pub struct Build {
#[clap(flatten)]
pub plot: Plot,
#[arg(short = 'C', long)]
pub context: Option<PathBuf>,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(short, long)]
pub no_build: bool,
}
#[derive(Debug, Clone, Parser)]
pub struct Check {
#[clap(flatten)]
pub plot: Plot,
#[arg(short = 'B', long, env = "SH4D0WUP_BIND")]
pub bind: Option<SocketAddr>,
#[arg(long)]
pub pull: bool,
#[arg(short, long, action(ArgAction::Count))]
pub no_exec: u8,
#[arg(short = 'K', long)]
pub keep: bool,
}
#[derive(Debug, Clone, Parser)]
pub struct Req {
#[clap(flatten)]
pub plot: Plot,
#[arg(value_name = "PATH")]
pub req_path: String,
#[arg(short = 'X', long, default_value = "GET")]
pub method: String,
#[arg(short = 'A', long)]
pub authority: Option<warp::host::Authority>,
#[arg(short = 'H', long = "header")]
pub headers: Vec<String>,
#[arg(long)]
pub addr: Option<IpAddr>,
#[arg(short = 'r', long = "response")]
pub show_response: bool,
#[arg(short = 's', long = "status")]
pub show_status: bool,
#[arg(long = "headers")]
pub show_headers: bool,
#[arg(short = 'c', long = "content")]
pub show_content: bool,
#[arg(short = 'C', long)]
pub hexdump: bool,
}
#[derive(Debug, Parser)]
pub struct Completions {
pub shell: Shell,
}
pub fn gen_completions(args: &Completions) -> Result<()> {
clap_complete::generate(args.shell, &mut Args::command(), "sh4d0wup", &mut stdout());
Ok(())
}