• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tamada / oinkie / 18366601428

09 Oct 2025 05:34AM UTC coverage: 20.36%. First build
18366601428

Pull #3

github

tamada
Refactor Windows installation step for LLVM and Clang to include environment variable setup and restore Ubuntu build step
Pull Request #3: introduce extraction mode

144 of 436 new or added lines in 6 files covered. (33.03%)

181 of 889 relevant lines covered (20.36%)

0.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/cli/src/main.rs
1
use std::path::PathBuf;
2

3
use clap::{Parser, Subcommand, ValueEnum};
4

5
use oinkie::birthmarks::{Birthmark, BirthmarkType};
6
use oinkie::{OinkieError, Result};
7
use oinkie::extractors::{self, Mode};
8
use oinkie::comparators::{Comparator, Similarity, Type as ComparatorType};
9

10
#[derive(Parser, Debug)]
11
#[command(name = "oinkie", about = "A tool for extracting and comparing birthmarks from LLVM IR or BC files")]
12
struct OinkieOpts {
13
    #[clap(subcommand)]
14
    command: OinkieCommand,
15
}
16

17
#[derive(Subcommand, Debug)]
18
enum OinkieCommand {
19
    #[command(name = "extract", about = "Extract birthmarks from LLVM IR or BC files")]
20
    Extract(ExtractOpts),
21

22
    #[command(name = "compare", about = "Compare two birthmarks")]
23
    Compare(CompareOpts),
24

25
    #[command(name = "run", about = "Extract birthmarks and compare them in one command")]
26
    Run(RunOpts),
27

28
    #[command(name = "execute", about = "Execute the given WASM script for analyzing birthmarks")]
29
    Execute(ExecuteOpts),
30

31
    #[command(name = "info", about = "Show information about the tool")]
32
    Info,
33
}
34

35
#[derive(Parser, Debug)]
36
struct ExtractSourceOpts {
37
    #[clap(short = 't', long = "type", value_name = "BIRTHMARK_TYPE", default_value = "op-seq", help = "Birthmark type for extraction")]
38
    btype: BirthmarkType,
39

40
    #[clap(short = 'm', long = "mode", value_name = "EXTRACTION_MODE", default_value = "file", help = "Extraction mode")]
41
    mode: Mode,
42

43
    #[clap(index = 1, value_name = "IR|BC", help = "Path to the LLVM IR or BC file")]
44
    inputs: Vec<PathBuf>,
45
}
46

47
#[derive(Parser, Debug)]
48
struct ExtractOpts {
49
    #[clap(short, long, default_value = "-", value_name = "DEST", help = "Output file path (default: stdout (\"-\"))")]
50
    dest: String,
51

52
    #[clap(flatten)]
53
    source: ExtractSourceOpts,
54
}
55

NEW
56
fn extract_birthmarks(inputs: Vec<PathBuf>, btype: BirthmarkType, mode: &Mode) -> Result<Vec<Birthmark>> {
×
57
    let result = inputs.iter()
×
NEW
58
        .map(|p| extractors::from_path(p, &btype, mode))
×
59
        .collect::<Vec<_>>();
×
60
    OinkieError::vec_result_to_result_vec(result)
×
NEW
61
        .map(|v| v.into_iter().flatten().collect())
×
62
}
×
63

64
fn extract(opts: ExtractOpts) -> oinkie::Result<()> {
×
65
    let mut errs = vec![];
×
NEW
66
    let (btype, dest, inputs, mode) = (opts.source.btype, opts.dest, opts.source.inputs, opts.source.mode);
×
67

NEW
68
    match extract_birthmarks(inputs, btype, &mode) {
×
69
        Ok(birthmarks) => {
×
70
            let json = serde_json::to_string_pretty(&birthmarks)
×
71
                .map_err(OinkieError::Json)?;
×
72
            if dest == "-" {
×
73
                println!("{}", json);
×
74
            } else {
×
75
                std::fs::write(dest, json)
×
76
                    .map_err(OinkieError::Io)?;
×
77
            }
78
        },
79
        Err(e) => errs.push(e),
×
80
    }
81
    OinkieError::error_or((), errs)
×
82
}
×
83

84
#[derive(Parser, Debug)]
85
struct CompareAlgorithmsOpts {
86
    #[clap(short, long, default_value = "jaccard", value_name = "COMPARATOR_TYPE", help = "Specifies the comparator")]
87
    comparator: ComparatorType,
88

89
    #[clap(short, long, default_value = "-", value_name = "DEST", help = "Output file path (default: stdout (\"-\"))")]
90
    dest: String,
91
}
92

93
#[derive(Parser, Debug)]
94
struct CompareOpts {
95
    #[clap(flatten)]
96
    algorithm: CompareAlgorithmsOpts,
97

98
    #[clap(index = 1, help = "Paths of the birthmark files to compare")]
99
    birthmarks: Vec<PathBuf>,
100
}
101

102
#[derive(Parser, Debug)]
103
struct RunOpts {
104
    #[clap(flatten)]
105
    extract_opts: ExtractSourceOpts,
106

107
    #[clap(flatten)]
108
    compare_opts: CompareAlgorithmsOpts,
109
}
110

111
#[derive(Parser, Debug)]
112
struct ExecuteOpts {
113
    #[clap(index = 1, default_value = "-", help = "the script file. If absent or '-', read from stdin.")]
114
    script: PathBuf,
115
}
116

117
fn read_birthmarks_from_json(paths: Vec<PathBuf>) -> Result<Vec<Birthmark>> {
×
118
    let result = paths.iter()
×
NEW
119
        .map(|p| oinkie::birthmarks::load(p))
×
120
        .collect::<Vec<_>>();
×
121
    OinkieError::vec_result_to_result_vec(result)
×
NEW
122
        .map(|v| v.into_iter().flatten().collect())
×
123
}
×
124

125
fn read_and_compare(opts: CompareOpts) -> oinkie::Result<()> {
×
126
    let (paths, algorithm) = (opts.birthmarks, opts.algorithm);
×
127
    let birthmarks = read_birthmarks_from_json(paths);
×
128
    compare(birthmarks, algorithm)
×
129
}
×
130

131
fn compare(birthmarks: Result<Vec<Birthmark>>, opts: CompareAlgorithmsOpts) -> oinkie::Result<()> {
×
132
    let comparator = oinkie::comparators::comparator(&opts.comparator);
×
133
    match birthmarks {
×
134
        Ok(birthmarks) => match calculate_similarities(birthmarks, comparator) {
×
135
            Ok(similarities) => {
×
136
                output_similarities(similarities, opts.dest)
×
137
            },
138
            Err(e) => Err(e),
×
139
        },
140
        Err(e) => Err(e),
×
141
    }
142
}
×
143

144
fn output_similarities(similarities: Vec<Similarity>, dest: String) -> oinkie::Result<()> {
×
145
    let json = serde_json::to_string_pretty(&similarities)
×
146
        .map_err(OinkieError::Json)?;
×
147
    if dest == "-" {
×
148
        println!("{}", json);
×
149
    } else {
×
150
        std::fs::write(dest, json)
×
151
            .map_err(OinkieError::Io)?;
×
152
    }
153
    Ok(())
×
154
}
×
155

156
fn calculate_similarities(birthmarks: Vec<Birthmark>, comparator: Box<dyn Comparator>) -> Result<Vec<Similarity>> {
×
157
    if birthmarks.len() < 2 {
×
158
        Err(OinkieError::Fatal("At least two birthmarks are required for comparison".to_string()))
×
159
    } else {
160
        let mut errs = vec![];
×
161
        let mut oks = vec![];
×
162
        for i in 0..birthmarks.len() {
×
163
            for j in (i+1)..birthmarks.len() {
×
164
                let a = &birthmarks[i];
×
165
                let b = &birthmarks[j];
×
166
                if !a.is_same_type(b) {
×
NEW
167
                    errs.push(OinkieError::Fatal(format!("Birthmark types do not match: {:?} vs {:?}", a.info, b.info)));
×
168
                }
×
169
                match comparator.compare(a, b) {
×
170
                    Ok(similarity) => oks.push(similarity),
×
171
                    Err(e) => errs.push(e),
×
172
                }
173
            }
174
        }
175
        OinkieError::error_or(oks, errs)
×
176
    }
177
}
×
178

179
fn run(opts: RunOpts) -> oinkie::Result<()> {
×
180
    let (eopts, copts) = (opts.extract_opts, opts.compare_opts);
×
NEW
181
    let birthmarks = extract_birthmarks(eopts.inputs, eopts.btype, &eopts.mode);
×
182
    let (ctype, dest) = (copts.comparator, copts.dest);
×
183
    let comparator = oinkie::comparators::comparator(&ctype);
×
184
    match birthmarks {
×
185
        Ok(birthmarks) => match calculate_similarities(birthmarks, comparator) {
×
186
            Ok(similarities) => output_similarities(similarities, dest),
×
187
            Err(e) => Err(e),
×
188
        },
189
        Err(e) => Err(e),
×
190
    }
191
}
×
192

193
fn execute(_opts: ExecuteOpts) -> oinkie::Result<()> {
×
194
    Ok(())
×
195
}
×
196

197
fn info() -> oinkie::Result<()> {
×
198
    println!("======== Oinkie Info ========");
×
199
    println!("Oinkie is a tool for detecting the code theft from LLVM IR/CB codes with birthmarks.
×
200
The birthmark is a unique characteristic of a program that can be used to identify it.
×
201
Oinkie extracts birthmarks from given IR/BC codes and compares them to calculate the similarities.");
×
202
    println!("======== Birthmarks  ========");
×
203
    BirthmarkType::value_variants().iter().for_each(|b| {
×
204
        println!("- {b:9}: {}", b.to_possible_value().unwrap().get_help().unwrap());
×
205
    });
×
206
    println!("======== Comparators ========");
×
207
    ComparatorType::value_variants().iter().for_each(|c| {
×
208
        println!("- {c:9}: {}", c.to_possible_value().unwrap().get_help().unwrap());
×
209
    });
×
210

211
    Ok(())
×
212
}
×
213

214
fn perform(opts: OinkieOpts) -> oinkie::Result<()> {
×
215
    use OinkieCommand::*;
216
    match opts.command {
×
217
        Extract(opts) => extract(opts),
×
218
        Compare(opts) => read_and_compare(opts),
×
219
        Run(opts) => run(opts),
×
220
        Execute(opts) => execute(opts),
×
221
        Info => info(),
×
222
    }
223
}
×
224

225
fn main() -> Result<()>{
×
226
    perform(OinkieOpts::parse())
×
227
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc