Compare commits

...

10 Commits

Author SHA1 Message Date
Jia Chao
653c68b30f 增加一些文档说明,即注释
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-07-01 11:51:52 +08:00
Jia Chao
b0cadbfee8 release v0.1.0 pub use convert, sadb
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-07-01 10:57:16 +08:00
Jia Chao
2b8afdb2ea 子命令 db 功能初步实现
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-07-01 10:51:25 +08:00
Jia Chao
edb907941b 完成子命令 convert
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-06-28 10:41:16 +08:00
Jia Chao
a6cdd8e39b 更新命令行参数,更新 README
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-06-27 17:05:27 +08:00
Jia Chao
e29b653964 添加工具 help 说明
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-06-27 14:18:20 +08:00
Jia Chao
c88ee97f76 删除 Cargo.lock
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-06-27 11:30:13 +08:00
Jia Chao
d9c4504c61 更新 .gitignore
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-06-27 11:29:25 +08:00
Jia Chao
1a57382fc7 “完成命令行模式初始设置”
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-06-27 11:03:58 +08:00
Jia Chao
e340539e1a 添加依赖项
Signed-off-by: Jia Chao <jiac13@chinaunicom.cn>
2024-06-27 10:21:32 +08:00
6 changed files with 280 additions and 2 deletions

24
.gitignore vendored
View File

@ -1 +1,25 @@
# ---> Rust
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# Added by cargo
/target
# Test files
sainfos
cves

View File

@ -6,3 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.0", features = ["derive"] }
cvrf-xmlparser = { git = "https://git.zhgsun.com:8089/jiachao2130/cvrf-xmlparser.git", version = "0.1.0" }
serde_json = { version = "1.0" }

View File

@ -1 +1,17 @@
这个应用于 CULinux VAT 系统中,将 openEuler 的 cvrf 格式的安全公告转换为 cusa。
```
$ cvrf2cusa -h
cvrf2cusa 是一个用于将 CVRFCommon Vulnerability Reporting Framework格式的安全报告转换为 CUSACULinux Security Advisory的工具其输入格式为 Xml ,输出格式则为 Json。
Usage: cvrf2cusa <COMMAND>
Commands:
convert CVRF 转换输出子命令
db 创建并生成新的 CUSA 数据文件
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
```

69
src/cli.rs Normal file
View File

@ -0,0 +1,69 @@
use clap::{Parser, Subcommand};
// 构建命令行工具的结构体
/// cvrf2cusa 是一个用于将 CVRFCommon Vulnerability Reporting Framework格式的安全报告转换为
/// CUSACULinux Security Advisory的工具其输入格式为 Xml ,输出格式则为 Json。
#[derive(Clone, Debug, Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[clap(subcommand)]
pub subcommand: CliSub,
}
#[derive(Subcommand, Debug, Clone)]
pub enum CliSub {
/// CVRF 转换输出子命令
Convert(ConvertCli),
/// 创建并生成新的 CUSA 数据文件
Db(SaDbCli),
}
/// ConvertCli 用于将指定的 cvrf 格式 xml 文件转换为 cusa 格式
#[derive(Clone, Debug, Parser)]
#[command(author, version, about, long_about = None)]
pub struct ConvertCli {
/// 输入的文件,应为 cvrf 格式的 xml
#[arg(short, long)]
pub input: String,
/// 可选项,将 cvrf 转换为 cusa 后输出到对应的文件中,格式为 json
#[arg(short, long)]
pub output: Option<String>,
/// 是否在终端打印转换后的 cusa 内容,若不指定输出文件,则输出至终端,
/// 在指定 output 后同时使用该选项,亦可同时输出至文件和终端
#[arg(short, long, default_value_t = false)]
pub print: bool,
}
impl ConvertCli {
pub fn new(input: String, output: Option<String>, print: bool) -> Self {
ConvertCli {
input,
output,
print,
}
}
}
#[derive(Clone, Debug, Parser)]
#[command(author, version, about, long_about = None)]
pub struct SaDbCli {
/// 指定一个目录,递归读取其内部的 cvrf 格式的 xml 文件,并最终生成新的 SADB
#[arg(short, long)]
pub from: String,
/// 输出的 sa db 文件,默认为当前路径下的 sainfos
#[arg(long, default_value_t = String::from("sainfos"))]
pub sa: String,
/// 输出的 cve db 文件,默认为当前路径下的 cves
#[arg(long, default_value_t = String::from("cves"))]
pub cve: String,
}
// 从命令行环境变量读取并转换为 `Cli`
pub fn parse() -> Cli {
Cli::parse()
}

165
src/lib.rs Normal file
View File

@ -0,0 +1,165 @@
use std::collections::HashMap;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use cvrf_xmlparser::{
CVRF,
// SaInfo 即为 CUSA
// SaInfo,
};
pub mod cli;
/// 定义 crate::Error
/// 大部分函数返回的错误
pub type Error = Box<dyn std::error::Error + Send + Sync>;
/// 定义 crate::Result
pub type Result<T> = std::result::Result<T, Error>;
pub fn cumain() -> Result<()> {
let cli = cli::parse();
match cli.subcommand {
cli::CliSub::Convert(cli) => covert(&cli),
cli::CliSub::Db(cli) => sadb(&cli),
}
}
/// 可使用 convert 函数将 cvrf 格式文件转换并输出至指定的 cusa 文件。
///
/// 例:
///
/// ```no_run
/// use cvrf2cusa::cli::ConvertCli
///
/// let cli = ConvertCli::new(
/// // input
/// "xxx-cvrf.xml".to_string,
/// // output
/// Some("pkg_version_said.json".to_string),
/// // print to screen
/// false,
/// );
///
/// cvrf2cusa::Convert(cli)?;
/// ```
pub fn covert(cli: &cli::ConvertCli) -> Result<()> {
// 检查 input此为必须项
let input = Path::new(&cli.input);
if !input.is_file() {
return Err("输入的文件不是有效的文件路径!".into());
}
// 从 input 读取 cvrf并转换为 cusa
let mut cvrf = CVRF::new();
cvrf.load_xml(&cli.input)?;
let cusa = cvrf.sainfo();
let data = serde_json::to_string_pretty(&cusa)?;
// 是否将 cusa 输出至指定文件
if let Some(output) = &cli.output {
let output = Path::new(output);
// output 所在父路径须提前创建
let parent = output.parent().unwrap();
if !parent.exists() {
return Err("无法访问输出文件所在路径,请确认!".into());
}
// 写入文件
let mut json_file = fs::OpenOptions::new().read(true).write(true).create(true).open(output)?;
json_file.write(data.as_bytes())?;
json_file.flush()?;
// 在输出到文件模式下,默认不再标准输出打印 CUSA
if !cli.print {
return Ok(())
}
}
// 在标准输出打印 CUSA
println!("{}", data);
Ok(())
}
pub fn sadb(cli: &cli::SaDbCli) -> Result<()> {
let source = &cli.from;
// 只要文件
let files = walk_dir(source, true);
let mut sadb = HashMap::new();
let mut cvedb = HashMap::new();
for _file in files {
match _file.extension() {
Some(tail) => {
if tail != "xml" {
continue;
}
// 从 xml 中读取 cvrf 并转换为 cusa
let file = if let Some(file) = _file.to_str() {
file
} else {
continue;
};
let mut cvrf = CVRF::new();
let _ = cvrf.load_xml(file);
let sa = cvrf.sainfo();
// cvedb
sa.cves.clone().into_iter().for_each(|cve| {
cvedb.insert(cve.id.clone(), cve);
});
// sadb
// 很烦,华为干的这事儿
// 2022/cvrf-openEuler-SA-2022-2063.xml 里的 ID 为 openEuler-SA-2022-2046
// 暂不处理
// TODO
if let Some(_) = sadb.insert(sa.id.clone(), sa) {
// unimplemented!();
}
}
_ => {}
}
}
// 写入 sadb 文件
let data = serde_json::to_string_pretty(&sadb)?;
let mut sadb_file = fs::OpenOptions::new().read(true).write(true).create(true).open(&cli.sa)?;
sadb_file.write(data.as_bytes())?;
sadb_file.flush()?;
// 写入 cvedb 文件
let data = serde_json::to_string_pretty(&cvedb)?;
let mut cvedb_file = fs::OpenOptions::new().read(true).write(true).create(true).open(&cli.cve)?;
cvedb_file.write(data.as_bytes())?;
cvedb_file.flush()?;
Ok(())
}
// 递归去读一个路径,返回一个文件列表
#[allow(dead_code)]
fn walk_dir<P: AsRef<Path>>(path: P, nodir: bool) -> Vec<PathBuf>
{
let mut res = vec![];
for entry in std::fs::read_dir(path).expect("read_dir call failed!") {
let entry = entry.unwrap().path();
if entry.is_dir() {
if !nodir {
res.push(entry.clone());
}
res.append(&mut walk_dir(entry, nodir));
} else {
// 暂不处理链接等情况
res.push(entry);
}
}
res
}

View File

@ -1,3 +1,4 @@
fn main() {
println!("Hello, world!");
use cvrf2cusa::{cumain, Result};
fn main() -> Result<()> {
cumain()
}