scrub_minidump/
main.rs

1#![doc(
2    html_logo_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png",
3    html_favicon_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png"
4)]
5
6use std::fs;
7use std::path::PathBuf;
8
9use anyhow::{Context, Result, format_err};
10use clap::Parser;
11use relay_pii::{PiiAttachmentsProcessor, PiiConfig};
12
13/// Apply data scrubbing (PII) rules on a minidump file.
14///
15/// Remove all heap memory:
16///
17///     {"applications": {"$heap_memory": ["@anything:remove"]}}
18///
19/// Remove all memory regions:
20///
21///     {"applications": {"$stack_memory || $heap_memory": ["@anything:remove"]}}
22///
23/// Remove credit cards from heap memory:
24///
25///     {"applications": {"$heap_memory": ["@creditcard:remove"]}}
26///
27/// For more information on how to scrub IP addresses, user file paths and how to define custom
28/// regexes see <https://getsentry.github.io/relay/pii-config/>
29#[derive(Debug, Parser)]
30#[structopt(verbatim_doc_comment)]
31struct Cli {
32    /// Path to a PII config JSON file.
33    #[arg(short, long)]
34    config: PathBuf,
35
36    /// Path to the minidump to rewrite.
37    minidump: PathBuf,
38
39    /// Optional output path. By default, the minidump file is overwritten.
40    #[arg(short, long)]
41    output: Option<PathBuf>,
42}
43
44impl Cli {
45    fn load_pii_config(&self) -> Result<PiiConfig> {
46        let json = fs::read_to_string(&self.config).with_context(|| "failed to read PII config")?;
47        let config = serde_json::from_str(&json).with_context(|| "failed to parse PII config")?;
48        Ok(config)
49    }
50
51    fn load_minidump(&self) -> Result<Vec<u8>> {
52        let buf = fs::read(&self.minidump).with_context(|| "failed to open minidump")?;
53        Ok(buf)
54    }
55
56    fn minidump_name(&self) -> &str {
57        self.minidump
58            .file_name()
59            .and_then(|os_str| os_str.to_str())
60            .unwrap_or_default()
61    }
62
63    fn write_output(&self, data: &[u8]) -> Result<()> {
64        let path = match self.output {
65            Some(ref output) => output,
66            None => &self.minidump,
67        };
68
69        fs::write(path, data)
70            .with_context(|| format!("failed to write minidump to {}", path.display()))?;
71
72        println!("output written to {}", path.display());
73
74        Ok(())
75    }
76
77    pub fn run(self) -> Result<()> {
78        let config = self.load_pii_config()?;
79        let processor = PiiAttachmentsProcessor::new(config.compiled());
80
81        let mut data = self.load_minidump()?;
82        let changed = processor
83            .scrub_minidump(self.minidump_name(), &mut data)
84            .map_err(|e| format_err!("{e}"))?; // does not implement std::error::Error
85
86        if changed {
87            self.write_output(&data)?;
88        } else {
89            println!("nothing changed.");
90        }
91
92        Ok(())
93    }
94}
95
96fn print_error(error: &anyhow::Error) {
97    eprintln!("Error: {error}");
98
99    let mut cause = error.source();
100    while let Some(ref e) = cause {
101        eprintln!("  caused by: {e}");
102        cause = e.source();
103    }
104}
105
106fn main() {
107    let cli = Cli::parse();
108
109    match cli.run() {
110        Ok(()) => (),
111        Err(error) => {
112            print_error(&error);
113            std::process::exit(1);
114        }
115    }
116}