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

Implementation of additional proposed control operators and wasm bindings for validator #79

Merged
merged 49 commits into from
Sep 13, 2021
Merged
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
15d7b09
fix multiline byte string lexing
anweiss Nov 17, 2020
40bb651
fix error hoisting from parser
anweiss Nov 17, 2020
adfa227
.cat control operator
anweiss Dec 2, 2020
8490cce
lexer fixes and cleanup
anweiss Dec 3, 2020
678f405
lexer fix
anweiss Dec 3, 2020
dc8b115
concat updates
anweiss Dec 4, 2020
b8b898a
version bump
anweiss Dec 4, 2020
91c61e3
cat operator work
anweiss Dec 8, 2020
11d2e0e
byte string cat implementation
anweiss Dec 9, 2020
671fc05
cat implementation cleanup
anweiss Dec 9, 2020
d3c836f
fix issue parsing groupname entries with genericargs
anweiss Dec 9, 2020
f766a7c
text value type changes
anweiss Dec 11, 2020
955e3c9
refactor value validation for JSON and separate control implementation
anweiss Dec 11, 2020
3d04eff
fixes
anweiss Dec 11, 2020
7d0bff6
update deps and initial abnf support
anweiss Feb 1, 2021
728f295
clippy nits
anweiss Aug 23, 2021
54c498b
default control operator fixes
anweiss Aug 23, 2021
b1fe2c4
dedent support
anweiss Aug 26, 2021
1b0d583
feature control implementation
anweiss Aug 31, 2021
7ddd733
codeql scan tweak
anweiss Aug 31, 2021
8788a15
fix doc tests
anweiss Aug 31, 2021
31dd72c
fix codeql workflow
anweiss Aug 31, 2021
b55d50a
refactor some of the error handling
anweiss Sep 1, 2021
275358c
fix conditional deps
anweiss Sep 1, 2021
42a19d5
wasm fixes and dep updates
anweiss Sep 1, 2021
e187937
update lsp extension workflow
anweiss Sep 1, 2021
88c18f4
fix lsp deps
anweiss Sep 1, 2021
913824a
optional ast span fields
anweiss Sep 2, 2021
66b2a3a
fix failing checks
anweiss Sep 3, 2021
066250b
ast comment feature flag toggle
anweiss Sep 3, 2021
5bbfc01
wasm bindings for validator functions
anweiss Sep 3, 2021
cc60895
feature flag additional controls
anweiss Sep 7, 2021
0193722
remove nightly from ci workflows
anweiss Sep 7, 2021
9915dec
cbor and json feature flags
anweiss Sep 7, 2021
e599bab
abnfb implementation and cleanup
anweiss Sep 9, 2021
e8b24d8
abnf complex controller implementation
anweiss Sep 9, 2021
30d6643
address diffs
anweiss Sep 9, 2021
b4d9d86
feature control fixes
anweiss Sep 9, 2021
0309685
fixes and cleanup
anweiss Sep 10, 2021
8c2331e
fixes and readme updates
anweiss Sep 10, 2021
2b86d24
version bump
anweiss Sep 10, 2021
34adb51
feature control fixes
anweiss Sep 10, 2021
1146308
cli rework
anweiss Sep 10, 2021
34751af
fix style lint error
anweiss Sep 13, 2021
50fa767
CLI tweaks
anweiss Sep 13, 2021
8e085b8
re-add additional-controls feature flag
anweiss Sep 13, 2021
e25dfe1
change version to beta
anweiss Sep 13, 2021
6fbca2c
update readme
anweiss Sep 13, 2021
3a9d8d1
fix #82
anweiss Sep 13, 2021
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
Prev Previous commit
Next Next commit
cli rework
anweiss committed Sep 10, 2021

Verified

This commit was signed with the committer’s verified signature. The key has expired.
anweiss anweiss
commit 11463087cdf3694c63f854e7ef84d8e701e49aeb
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -30,10 +30,12 @@ serde_cbor = { version = "0.11.1", optional = true, default-features = false, fe
serde_json = { version = "1.0.66", optional = true, default-features = false }
uriparse = { version = "0.6.3", optional = true }
base64-url = { version = "1.4.10", optional = true }
abnf_to_pest = "0.5"
pest_meta = "2.1"
pest_vm = "2.1"
abnf_to_pest = "0.5.0"
pest_meta = "2.1.3"
pest_vm = "2.1.0"
displaydoc = { version = "0.2.3", default-features = false }
log = "0.4.14"
simplelog = "0.10.0"

[dev-dependencies]
indoc = "1.0.3"
265 changes: 138 additions & 127 deletions src/bin/cli.rs
Original file line number Diff line number Diff line change
@@ -4,65 +4,107 @@

#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;

use cddl::{cddl_from_str, lexer_from_str, validate_cbor_from_slice, validate_json_from_str};
use clap::{App, AppSettings, Arg, SubCommand};
use codespan_reporting::term::termcolor::{
BufferWriter, Color, ColorChoice, ColorSpec, WriteColor,
};

use simplelog::*;
use std::{
error::Error,
fs::{self, File},
io::{self, BufReader, Read, Write},
io::{self, BufReader, Read},
path::Path,
};

fn main() -> Result<(), Box<dyn Error>> {
TermLogger::init(
LevelFilter::Info,
ConfigBuilder::new()
.set_time_level(LevelFilter::Off)
.build(),
TerminalMode::Mixed,
ColorChoice::Auto,
)?;

#[cfg(feature = "additional-controls")]
let validate_subcommand = SubCommand::with_name("validate")
.about("Validate JSON and/or CBOR against a CDDL definition")
.usage("cddl validate --cddl <CDDL> --json <FILE>... --cbor <FILE>...\n cat hello.json | cddl validate --stdin")
.arg_from_usage("-c --cddl <CDDL> 'CDDL input file'")
.arg_from_usage("-f --features [FEATURE]... 'optional features to enable during validation'")
.arg(
Arg::with_name("json")
.long("json")
.takes_value(true)
.multiple(true)
.empty_values(false)
.help("JSON files to validate")
.required_unless_one(&["cbor", "stdin"])
)
.arg(
Arg::with_name("cbor")
.long("cbor")
.takes_value(true)
.multiple(true)
.empty_values(false)
.help("CBOR binary files to validate")
.required_unless_one(&["json", "stdin"]),
)
// .arg_from_usage("--json [FILE]... 'JSON files to validate'")
// .arg_from_usage("--cbor [FILE]... 'CBOR binary files to validate'")
.arg(
Arg::with_name("stdin")
.long("stdin")
.takes_value(false)
.help("JSON or CBOR input from stdin")
.long_help("files read from stdin which are encoded as valid utf-8\nwill be implied as JSON whereas invalid utf-8 encoded\ninput is implied as CBOR")

)
.setting(AppSettings::DeriveDisplayOrder);

#[cfg(not(feature = "additional-controls"))]
let validate_subcommand = SubCommand::with_name("validate")
.about("Validate JSON or CBOR against CDDL definition")
.arg_from_usage("-c --cddl <CDDL> 'CDDL input file'")
.arg_from_usage("--json [FILE]... 'JSON files to validate'")
.arg_from_usage("--cbor [FILE]... 'CBOR binary files to validate'")
.arg(
Arg::with_name("stdin")
.long("stdin")
.takes_value(false)
.help("JSON or CBOR input from stdin")
.conflicts_with_all(&["json", "cbor"]),
);

let app = App::new("cddl")
.version(crate_version!())
.author(crate_authors!())
.about("Tool for verifying conformance of CDDL definitions against RFC 8610 and for validating JSON documents")
.about("Tool for verifying conformance of CDDL definitions against RFC 8610 and for validating JSON documents and CBOR binary files")
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(SubCommand::with_name("compile-cddl")
.about("Compile CDDL against RFC 8610")
.arg_from_usage("-c --cddl=<FILE> 'CDDL input file'"))
.subcommand(SubCommand::with_name("compile-json")
.about("Compile JSON against RFC 8259")
.arg_from_usage("-j --json=<FILE> 'JSON input file'"))
.subcommand(SubCommand::with_name("validate")
.about("Validate JSON or CBOR against CDDL definition")
.arg_from_usage("-c --cddl=<CDDL> 'CDDL input file'")
.arg(Arg::with_name("stdin").long("stdin").takes_value(false).help("JSON or CBOR input from stdin"))
.arg(Arg::with_name("file").value_name("FILE").help("JSON or CBOR input file(s)").multiple(true).required(false)));
.subcommand(validate_subcommand);

let matches = app.get_matches();

let stdoutbuffwrtr = BufferWriter::stdout(ColorChoice::Auto);
let mut stdout = stdoutbuffwrtr.buffer();
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;

let stderrbuffwrtr = BufferWriter::stdout(ColorChoice::Auto);
let mut stderr = stderrbuffwrtr.buffer();
stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;

if let Some(matches) = matches.subcommand_matches("compile-cddl") {
if let Some(c) = matches.value_of("cddl") {
let p = Path::new(c);
if !p.exists() {
writeln!(&mut stderr, "CDDL document at path {:?} does not exist", p)?;
stderrbuffwrtr.print(&stderr)?;
error!("CDDL document {:?} does not exist", p);

return Ok(());
}

if let Some(e) = p.extension() {
if e.to_string_lossy() != "cddl" {
writeln!(
&mut stderr,
"File \"{}\" must have the \".cddl\" extension",
c
)?;
stderrbuffwrtr.print(&stderr)?;
error!("File \"{}\" must have the \".cddl\" extension", c);

return Ok(());
}
@@ -71,33 +113,22 @@ fn main() -> Result<(), Box<dyn Error>> {
let file_content = fs::read_to_string(c)?;
cddl_from_str(&mut lexer_from_str(&file_content), &file_content, true).map(|_| ())?;

writeln!(&mut stdout, "{} is conformant", c)?;
stdoutbuffwrtr.print(&stdout)?;
error!("{} is conformant", c);
}
}

if let Some(matches) = matches.subcommand_matches("compile-json") {
if let Some(j) = matches.value_of("json") {
let p = Path::new(j);
if !p.exists() {
writeln!(
&mut stderr,
"\nCDDL document at path {:?} does not exist",
p
)?;
stderrbuffwrtr.print(&stderr)?;
error!("CDDL document {:?} does not exist", p);

return Ok(());
}

if let Some(e) = p.extension() {
if e.to_string_lossy() != "json" {
writeln!(
&mut stderr,
"File \"{}\" must have the \".json\" extension",
j
)?;
stderrbuffwrtr.print(&stderr)?;
error!("File \"{}\" must have the \".json\" extension", j);

return Ok(());
}
@@ -113,103 +144,92 @@ fn main() -> Result<(), Box<dyn Error>> {

if let Some(matches) = matches.subcommand_matches("validate") {
if let Some(cddl) = matches.value_of("cddl") {
#[cfg(feature = "additional-controls")]
let enabled_features: Option<Vec<&str>> = matches.values_of("features").map(|f| f.collect());
#[cfg(feature = "additional-controls")]
if let Some(enabled_features) = &enabled_features {
let mut feature_str = String::from("enabled features: [");
for (idx, feature) in enabled_features.iter().enumerate() {
if idx == 0 {
feature_str.push_str(&format!("\"{}\"", feature));
} else {
feature_str.push_str(&format!(", \"{}\"", feature));
}
}
feature_str.push(']');

info!("{}", feature_str);
}

let p = Path::new(cddl);
if !p.exists() {
writeln!(
&mut stderr,
"\nCDDL document at path {:?} does not exist",
p
)?;
stderrbuffwrtr.print(&stderr)?;
error!("CDDL document {:?} does not exist", p);

return Ok(());
}

if let Some(e) = p.extension() {
if e.to_string_lossy() != "cddl" {
writeln!(
&mut stderr,
"File \"{}\" must have the \".cddl\" extension",
cddl
)?;
stderrbuffwrtr.print(&stderr)?;
error!("File \"{}\" must have the \".cddl\" extension", cddl);

return Ok(());
}
}

let cddl_str = fs::read_to_string(cddl)?;

writeln!(&mut stdout)?;
if let Some(files) = matches.values_of("json") {
for file in files {
let p = Path::new(file);
if !p.exists() {
error!("File {:?} does not exist", p);

continue;
}

#[cfg(feature = "additional-controls")]
let r = validate_json_from_str(
&cddl_str,
&fs::read_to_string(file)?,
enabled_features.as_deref(),
);
#[cfg(not(feature = "additional-controls"))]
let r = validate_json_from_str(&cddl_str, &fs::read_to_string(file)?);

if let Some(files) = matches.values_of("file") {
if matches.is_present("stdin") {
return Err(Box::from(clap::Error {
message: "cannot use --stdin flag with <FILE>... arg".to_string(),
kind: clap::ErrorKind::ArgumentConflict,
info: None,
}));
match r {
Ok(()) => {
info!("Validation of {:?} is successful", p);
}
Err(e) => {
error!("Validation of {:?} failed: {}", p, e);
}
}
}
}

if let Some(files) = matches.values_of("cbor") {
for file in files {
let p = Path::new(file);
if !p.exists() {
writeln!(&mut stderr, "\nFile at path {:?} does not exist", p)?;
stderrbuffwrtr.print(&stderr)?;
error!("File {:?} does not exist", p);

return Ok(());
continue;
}
let mut f = File::open(p)?;
let mut data = Vec::new();
f.read_to_end(&mut data)?;

#[cfg(feature = "additional-controls")]
let c = validate_cbor_from_slice(&cddl_str, &data, None);
#[cfg(not(feature = "additional-controls"))]
let c = validate_cbor_from_slice(&cddl_str, &data);

if let Some(ext) = p.extension() {
match ext.to_str() {
Some("json") => {
#[cfg(feature = "additional-controls")]
let r = validate_json_from_str(&cddl_str, &fs::read_to_string(file)?, None);
#[cfg(not(feature = "additional-controls"))]
let r = validate_json_from_str(&cddl_str, &fs::read_to_string(file)?);

match r {
Ok(()) => {
writeln!(&mut stdout, "Validation of {:?} is successful", p)?;
stdoutbuffwrtr.print(&stdout)?;
}
Err(e) => {
writeln!(&mut stderr, "Validation of {:?} failed", p)?;
writeln!(&mut stderr, "\n{}", e)?;
stderrbuffwrtr.print(&stderr)?;
}
}
}
Some("cbor") => {
let mut f = File::open(p)?;
let mut data = Vec::new();
f.read_to_end(&mut data)?;

#[cfg(feature = "additional-controls")]
let c = validate_cbor_from_slice(&cddl_str, &data, None);
#[cfg(not(feature = "additional-controls"))]
let c = validate_cbor_from_slice(&cddl_str, &data);

match c {
Ok(()) => {
writeln!(&mut stdout, "Validation of {:?} is successful", p)?;
stdoutbuffwrtr.print(&stdout)?;
}
Err(e) => {
writeln!(&mut stderr, "Validation of {:?} failed", p)?;
writeln!(&mut stderr, "\n{}", e)?;
stderrbuffwrtr.print(&stderr)?;
}
}
}
_ => {
writeln!(
&mut stderr,
"\nFile {:?} is an unsupported file type. Must be either .json or .cbor",
p
)?;
stderrbuffwrtr.print(&stderr)?;
}
match c {
Ok(()) => {
info!("Validation of {:?} is successful", p);
}
Err(e) => {
error!("Validation of {:?} failed: {}", p, e);
}
}
}
@@ -231,39 +251,30 @@ fn main() -> Result<(), Box<dyn Error>> {

match r {
Ok(()) => {
writeln!(&mut stdout, "Validation from stdin is successful")?;
stdoutbuffwrtr.print(&stdout)?;
info!("Validation from stdin is successful");
}
Err(e) => {
writeln!(&mut stderr, "Validation from stdin failed")?;
writeln!(&mut stderr, "\n{}", e)?;
stderrbuffwrtr.print(&stderr)?;
error!("Validation from stdin failed: {}", e);
}
}
} else {
#[cfg(feature = "additional-controls")]
let c = validate_cbor_from_slice(&cddl_str, &data, None);
let c = validate_cbor_from_slice(&cddl_str, &data, enabled_features.as_deref());
#[cfg(not(feature = "additional-controls"))]
let c = validate_cbor_from_slice(&cddl_str, &data);

match c {
Ok(()) => {
writeln!(&mut stdout, "Validation from stdin is successful")?;
stdoutbuffwrtr.print(&stdout)?;
info!("Validation from stdin is successful");
}
Err(e) => {
writeln!(&mut stderr, "Validation from stdin failed")?;
writeln!(&mut stderr, "\n{}", e)?;
stderrbuffwrtr.print(&stderr)?;
error!("Validation from stdin failed: {}", e);
}
}
}

return Ok(());
}

writeln!(&mut stderr, "\nMissing files to validate")?;
stderrbuffwrtr.print(&stderr)?;
}
}