@@ -18,6 +18,12 @@ struct Cli {
1818enum Command {
1919 /// Read and print a Suricata configuration file.
2020 Print ( PrintArgs ) ,
21+
22+ /// Validate a Suricata configuration file against a JSON schema.
23+ Validate ( ValidateArgs ) ,
24+
25+ /// Print the embedded Suricata YAML JSON schema.
26+ PrintSchema ,
2127}
2228
2329#[ derive( Parser , Debug ) ]
@@ -30,9 +36,24 @@ struct PrintArgs {
3036 format : OutputFormat ,
3137}
3238
39+ #[ derive( Parser , Debug ) ]
40+ struct ValidateArgs {
41+ /// Path to the Suricata configuration file.
42+ path : PathBuf ,
43+
44+ /// Path to a JSON schema file. If omitted, the embedded schema is used.
45+ #[ arg( long) ]
46+ schema : Option < PathBuf > ,
47+
48+ /// Quiet mode. Print nothing when validation succeeds.
49+ #[ arg( short, long) ]
50+ quiet : bool ,
51+ }
52+
3353#[ derive( Clone , Copy , Debug , ValueEnum ) ]
3454enum OutputFormat {
3555 Yaml ,
56+ Json ,
3657 Debug ,
3758 Flat ,
3859}
@@ -43,6 +64,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
4364
4465 match cli. command {
4566 Command :: Print ( args) => print_config ( args) ,
67+ Command :: Validate ( args) => validate_config ( args) ,
68+ Command :: PrintSchema => print_schema ( ) ,
4669 }
4770}
4871
@@ -52,9 +75,58 @@ fn print_config(args: PrintArgs) -> Result<(), Box<dyn std::error::Error>> {
5275
5376 match args. format {
5477 OutputFormat :: Yaml => print ! ( "{}" , suricata_config:: print_yaml( & config) ?) ,
78+ OutputFormat :: Json => {
79+ println ! (
80+ "{}" ,
81+ serde_json:: to_string_pretty( & suricata_config:: config_to_json( & config) ) ?
82+ ) ;
83+ }
5584 OutputFormat :: Debug => println ! ( "{config:#?}" ) ,
5685 OutputFormat :: Flat => print ! ( "{}" , suricata_config:: print_flat_config( & config) ) ,
5786 }
5887
5988 Ok ( ( ) )
6089}
90+
91+ // Print the embedded Suricata YAML JSON schema.
92+ fn print_schema ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
93+ print ! ( "{}" , suricata_config:: SURICATA_YAML_SCHEMA ) ;
94+ Ok ( ( ) )
95+ }
96+
97+ // Load a configuration file, validate it against a schema, and report all issues.
98+ fn validate_config ( args : ValidateArgs ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
99+ let config = suricata_config:: load_file ( & args. path ) ?;
100+ let instance = suricata_config:: config_to_json ( & config) ;
101+
102+ let ( schema, schema_label) = if let Some ( schema_path) = args. schema {
103+ let schema_input = std:: fs:: read_to_string ( & schema_path) ?;
104+ let schema: serde_json:: Value = serde_json:: from_str ( & schema_input) ?;
105+ ( schema, schema_path. display ( ) . to_string ( ) )
106+ } else {
107+ (
108+ suricata_config:: embedded_schema ( ) ?,
109+ String :: from ( "embedded schema" ) ,
110+ )
111+ } ;
112+
113+ let errors = suricata_config:: validate_json_schema ( & instance, & schema) ;
114+ if errors. is_empty ( ) {
115+ if !args. quiet {
116+ println ! ( "OK: {}" , args. path. display( ) ) ;
117+ }
118+ return Ok ( ( ) ) ;
119+ }
120+
121+ eprintln ! (
122+ "Validation failed: {} issue(s) in {} against {}" ,
123+ errors. len( ) ,
124+ args. path. display( ) ,
125+ schema_label
126+ ) ;
127+ for error in errors {
128+ eprintln ! ( "{}" , error) ;
129+ }
130+
131+ Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "schema validation failed" ) . into ( ) )
132+ }
0 commit comments