Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement telemetry features #217

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions apps/cargo-scout-audit/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions apps/cargo-scout-audit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ reqwest = { version = "=0.12.5", features = ["blocking", "json"] }
semver = "=1.0.23"
serde = { version = "=1.0.204", features = ["derive"] }
serde_json = "=1.0.120"
strum = "=0.26.3"
strum_macros = "=0.26.4"
strum = { version = "=0.26.3", features = ["derive"] }
tempfile = "=3.10.1"
tera = { version = "=1.20.0", features = ["builtins"] }
terminal_color_builder = "=0.1.1"
Expand Down
2 changes: 1 addition & 1 deletion apps/cargo-scout-audit/src/detectors/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::{
};
use crate::{
scout::blockchain::BlockChain,
utils::{print::print_info, telemetry::TracedError},
utils::{logger::TracedError, print::print_info},
};

#[derive(Error, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion apps/cargo-scout-audit/src/detectors/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::scout::blockchain::BlockChain;
use crate::utils::telemetry::TracedError;
use crate::utils::logger::TracedError;
use anyhow::{bail, Context, Ok, Result};
use cargo::core::{Dependency, GitReference, SourceId};
use git2::{RemoteCallbacks, Repository};
Expand Down
2 changes: 1 addition & 1 deletion apps/cargo-scout-audit/src/detectors/library.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
scout::blockchain::BlockChain,
utils::{cargo, env, telemetry::TracedError},
utils::{cargo, env, logger::TracedError},
};
use anyhow::{bail, Result};
use cargo_metadata::{Metadata, MetadataCommand, Package};
Expand Down
6 changes: 3 additions & 3 deletions apps/cargo-scout-audit/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use cargo_scout_audit::{
cli::{CargoSubCommand, Cli},
startup::run_scout,
utils::{print::print_error, telemetry},
utils::{logger, print::print_error},
};
use clap::Parser;
use tracing::level_filters::LevelFilter;

fn main() {
let subscriber = telemetry::get_subscriber("scout".into(), LevelFilter::OFF, std::io::stdout);
telemetry::init_subscriber(subscriber);
let subscriber = logger::get_subscriber("scout".into(), LevelFilter::OFF, std::io::stdout);
logger::init_subscriber(subscriber);

let cli = Cli::parse();

Expand Down
11 changes: 5 additions & 6 deletions apps/cargo-scout-audit/src/scout/blockchain.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use crate::build_config::TOOLCHAIN;
use anyhow::{bail, Result};
use cargo_metadata::Metadata;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter, EnumString};
use strum::{Display, EnumIter, EnumString, IntoEnumIterator};
use thiserror::Error;

#[derive(Debug, Copy, Clone, EnumIter, Display, EnumString)]
#[derive(Debug, Deserialize, Serialize, Copy, Clone, EnumIter, Display, EnumString)]
#[strum(serialize_all = "kebab-case")]
#[serde(rename_all = "kebab-case")]
pub enum BlockChain {
#[strum(serialize = "ink")]
Ink,
#[strum(serialize = "soroban")]
Soroban,
#[strum(serialize = "substrate-pallets")]
SubstratePallets,
}

Expand Down
1 change: 1 addition & 0 deletions apps/cargo-scout-audit/src/scout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pub mod findings;
pub mod nightly_runner;
pub mod post_processing;
pub mod project_info;
pub mod telemetry;
pub mod version_checker;
2 changes: 1 addition & 1 deletion apps/cargo-scout-audit/src/scout/project_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use regex::Regex;
use std::{fs, path::PathBuf};
use thiserror::Error;

use crate::{output::report::Package, utils::telemetry::TracedError};
use crate::{output::report::Package, utils::logger::TracedError};

#[derive(Debug)]
pub struct Project {
Expand Down
174 changes: 174 additions & 0 deletions apps/cargo-scout-audit/src/scout/telemetry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use anyhow::{Context, Result};
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
use std::{env, fs, path::PathBuf};
use strum::EnumIter;

use super::blockchain::BlockChain;

// TODO: Change to the new telemetry endpoint
// const SCOUT_TELEMETRY_URL: &str = "https://telemetry.coinfabrik.com/scout";
const SCOUT_TELEMETRY_URL: &str = "http://190.104.235.53:38522";

pub struct TelemetryClient {
report: ReportDto,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ReportDto {
pub user_id: String,
pub scout_version: String,
pub crate_type: BlockChain,
pub client_type: ClientType,
pub os: Os,
}

#[derive(Debug, Deserialize, Serialize, EnumIter, strum::Display, Clone)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum ClientType {
Vscode,
Cicd,
Cli,
}

#[derive(Debug, Deserialize, Serialize, EnumIter, strum::Display, Clone)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum Os {
Linux,
Macos,
Windows,
Other,
}

impl From<&str> for Os {
fn from(value: &str) -> Self {
match value {
"linux" => Os::Linux,
"macos" => Os::Macos,
"windows" => Os::Windows,
_ => Os::Other,
}
}
}

#[derive(Debug, Deserialize)]
struct NewUserResponse {
user_id: String,
}

impl TelemetryClient {
pub fn new(blockchain: BlockChain, client_type: ClientType) -> Self {
let user_id = Self::get_user_id();

Self {
report: ReportDto {
user_id,
scout_version: env!("CARGO_PKG_VERSION").to_string(),
crate_type: blockchain,
client_type,
os: Os::from(env::consts::OS),
},
}
}

fn get_user_id() -> String {
let home_dir = get_home_directory();
let user_id_path = home_dir
.join(".scout-audit")
.join("telemetry")
.join("user_id.txt");

// Read user ID from file
if let Ok(user_id) = fs::read_to_string(&user_id_path) {
if !user_id.trim().is_empty() {
return user_id;
}
}

// Create parent directory if it doesn't exist
if let Some(parent) = user_id_path.parent() {
if let Err(e) = fs::create_dir_all(parent) {
tracing::error!("Failed to create telemetry directory: {}", e);
return "DONOTTRACK".to_string();
}
}

// Request new user ID from server
match Self::request_new_user_id() {
Ok(user_id) => {
if let Err(e) = fs::write(&user_id_path, &user_id) {
tracing::error!("Failed to cache user ID: {}", e);
}
user_id
}
Err(e) => {
tracing::warn!("Failed to get user ID: {}", e);
"DONOTTRACK".to_string()
}
}
}

fn request_new_user_id() -> Result<String> {
let client = Client::new();
let response = client
.post(format!("{}/user/new", SCOUT_TELEMETRY_URL))
.send()
.context("Failed to send request to telemetry server")?;

let new_user: NewUserResponse = response
.json()
.context("Failed to parse response from telemetry server")?;

Ok(new_user.user_id)
}

pub fn detect_client_type(args: &[String]) -> ClientType {
if args.contains(&"--message-format=json".to_string()) {
ClientType::Vscode
} else if args.contains(&"--cicd".to_string()) {
ClientType::Cicd
} else {
ClientType::Cli
}
}

pub fn send_report(&self) -> Result<()> {
if self.report.user_id.is_empty() || self.report.user_id.eq("DONOTTRACK") {
tracing::info!("Telemetry is disabled");
return Ok(());
}

let client = Client::new();
client
.post(format!("{}/report/new", SCOUT_TELEMETRY_URL))
.json(&self.report)
.send()
.context("Failed to send telemetry report")?;

Ok(())
}
}

#[cfg(windows)]
fn get_home_directory() -> PathBuf {
PathBuf::from(env::var("USERPROFILE").unwrap_or_else(|e| {
tracing::error!("Failed to get USERPROFILE: {}", e);
".".to_string()
}))
}

#[cfg(unix)]
fn get_home_directory() -> PathBuf {
PathBuf::from(env::var("HOME").unwrap_or_else(|e| {
tracing::error!("Failed to get HOME: {}", e);
".".to_string()
}))
}

#[cfg(not(any(windows, unix)))]
fn get_home_directory() -> PathBuf {
tracing::warn!("Unsupported OS for home directory detection");
PathBuf::from(".")
}
2 changes: 1 addition & 1 deletion apps/cargo-scout-audit/src/scout/version_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde_json::Value;
use std::env;
use thiserror::Error;

use crate::utils::telemetry::TracedError;
use crate::utils::logger::TracedError;

const CRATE_NAME: &str = env!("CARGO_PKG_NAME");
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down
9 changes: 8 additions & 1 deletion apps/cargo-scout-audit/src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use crate::{
findings::{get_crates, output_to_json, split_findings, temp_file_to_string},
nightly_runner::run_scout_in_nightly,
project_info::Project,
telemetry::TelemetryClient,
version_checker::VersionChecker,
},
utils::{
config::ProfileConfig,
detectors::{get_excluded_detectors, get_filtered_detectors, list_detectors},
detectors_info::get_detectors_info,
logger::TracedError,
print::{print_error, print_info},
telemetry::TracedError,
},
};
use anyhow::{Context, Ok, Result};
Expand Down Expand Up @@ -195,6 +196,7 @@ pub fn run_scout(mut opts: Scout) -> Result<Vec<Finding>> {
} else {
(successful_findings, raw_findings_string)
};

// Generate report
if inside_vscode {
std::io::stdout()
Expand All @@ -214,6 +216,11 @@ pub fn run_scout(mut opts: Scout) -> Result<Vec<Finding>> {
)?;
}

// Send telemetry data
let client_type = TelemetryClient::detect_client_type(&opts.args);
let telemetry_client = TelemetryClient::new(blockchain, client_type);
let _ = telemetry_client.send_report();

Ok(console_findings)
}

Expand Down
2 changes: 1 addition & 1 deletion apps/cargo-scout-audit/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ pub mod detectors;
pub mod detectors_info;
pub mod env;
pub mod json;
pub mod logger;
pub mod print;
pub mod telemetry;
Loading