From b7c4c74c46dbfa928f22ad14e5417ed7bc1ac63e Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 3 Mar 2023 14:07:14 -0800 Subject: [PATCH 1/3] add `schema` subcommand to get JSON schema --- ntreg/src/registry_key.rs | 2 +- registry/Cargo.toml | 1 + registry/src/args.rs | 11 ++++++++--- registry/src/config.rs | 9 ++++++--- registry/src/main.rs | 13 ++++++++++++- registry/src/regconfighelper.rs | 3 +++ 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/ntreg/src/registry_key.rs b/ntreg/src/registry_key.rs index a30748e0..b5dff904 100644 --- a/ntreg/src/registry_key.rs +++ b/ntreg/src/registry_key.rs @@ -435,7 +435,7 @@ impl Iterator for RegistrySubkeys { ) }; - if status == ERROR_NO_MORE_ITEMS as i32 { + if status == ERROR_NO_MORE_ITEMS { return None; } diff --git a/registry/Cargo.toml b/registry/Cargo.toml index bd100397..e268257b 100644 --- a/registry/Cargo.toml +++ b/registry/Cargo.toml @@ -19,6 +19,7 @@ clap = { version = "3.2", features = ["derive"] } crossterm = { version = "0.24.0" } ntreg = { path = "../ntreg" } ntstatuserror = { path = "../ntstatuserror" } +schemars = { version = "0.8.12" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/registry/src/args.rs b/registry/src/args.rs index f8fce553..2bf25158 100644 --- a/registry/src/args.rs +++ b/registry/src/args.rs @@ -54,15 +54,20 @@ pub enum SubCommand { #[clap(short, long, required = true, help = "The string to find.")] find: String, #[clap(short, long, help = "Recursively find.")] - recurse: Option, + recurse: bool, #[clap(short, long, help = "Only find keys.")] - keys_only: Option, + keys_only: bool, #[clap(short, long, help = "Only find values.")] - values_only: Option, + values_only: bool, }, #[clap(name = "config", about = "Manage registry configuration.", arg_required_else_help = true)] Config { #[clap(subcommand)] subcommand: ConfigSubCommand, }, + #[clap(name = "schema", about = "Retrieve JSON sceham.")] + Schema { + #[clap(short, long, help = "Pretty print JSON.")] + pretty: bool, + } } diff --git a/registry/src/config.rs b/registry/src/config.rs index d20a7993..4e3a03bb 100644 --- a/registry/src/config.rs +++ b/registry/src/config.rs @@ -1,12 +1,13 @@ +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] pub enum EnsureKind { Present, Absent, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] pub enum RegistryValueData { String(String), ExpandString(String), @@ -16,9 +17,11 @@ pub enum RegistryValueData { QWord(u64), } -#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] #[serde(rename = "Registry")] pub struct RegistryConfig { + #[serde(rename = "$id", skip_serializing_if = "Option::is_none")] + pub id: Option, #[serde(rename = "keyPath")] pub key_path: String, #[serde(rename = "valueName")] diff --git a/registry/src/main.rs b/registry/src/main.rs index df4cbded..2c3dbb36 100644 --- a/registry/src/main.rs +++ b/registry/src/main.rs @@ -6,6 +6,7 @@ use std::env; use args::Arguments; use atty::Stream; use clap::Parser; +use schemars::schema_for; use std::{io::{self, Read}, process::exit}; use crate::config::RegistryConfig; @@ -149,7 +150,17 @@ fn main() { if !in_desired_state { exit(EXIT_NOT_IN_DESIRED_STATE); } - } + }, + args::SubCommand::Schema { pretty } => { + let schema = schema_for!(RegistryConfig); + let json = if pretty { + serde_json::to_string_pretty(&schema).unwrap() + } + else { + serde_json::to_string(&schema).unwrap() + }; + println!("{}", json); + }, } exit(EXIT_SUCCESS); diff --git a/registry/src/regconfighelper.rs b/registry/src/regconfighelper.rs index 7a5dde81..8a423ae4 100644 --- a/registry/src/regconfighelper.rs +++ b/registry/src/regconfighelper.rs @@ -3,6 +3,8 @@ use ntreg::{registry_key::RegistryKey, registry_value::RegistryValueData as NtRe use ntstatuserror::{NtStatusError, NtStatusErrorKind}; use std::fmt::{Display, Formatter}; +const ID: &str = "https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json"; + #[derive(Debug, PartialEq, Eq)] pub enum RegistryError { NtStatus(NtStatusError), @@ -35,6 +37,7 @@ pub fn config_get(config: &RegistryConfig) -> Result { }; let mut reg_result = RegistryConfig { + id: Some(ID.to_string()), key_path: config.key_path.clone(), value_name: None, value_data: None, From ea07c8dda4e99990b2ac11db30ece492c6df7ae4 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 3 Mar 2023 14:21:31 -0800 Subject: [PATCH 2/3] fix tests due to new `$id` property --- registry/tests/registry.config.get.tests.ps1 | 4 ++-- registry/tests/registry.config.set.tests.ps1 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/tests/registry.config.get.tests.ps1 b/registry/tests/registry.config.get.tests.ps1 index 88cd7ea2..a8b5dcb2 100644 --- a/registry/tests/registry.config.get.tests.ps1 +++ b/registry/tests/registry.config.get.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Registry config get tests' { $LASTEXITCODE | Should -Be 0 $result = $out | ConvertFrom-Json $result.keyPath | Should -Be 'HKLM\Software\Microsoft\Windows\CurrentVersion' - ($result.psobject.properties | Measure-Object).Count | Should -Be 1 + ($result.psobject.properties | Measure-Object).Count | Should -Be 2 } It 'Can get a registry value' -Skip:(!$IsWindows) { @@ -25,6 +25,6 @@ Describe 'Registry config get tests' { $result.keyPath | Should -Be 'HKLM\Software\Microsoft\Windows\CurrentVersion' $result.valueName | Should -Be 'ProgramFilesPath' $result.valueData.ExpandString | Should -Be '%ProgramFiles%' - ($result.psobject.properties | Measure-Object).Count | Should -Be 3 + ($result.psobject.properties | Measure-Object).Count | Should -Be 4 } } diff --git a/registry/tests/registry.config.set.tests.ps1 b/registry/tests/registry.config.set.tests.ps1 index d677f340..a2a11641 100644 --- a/registry/tests/registry.config.set.tests.ps1 +++ b/registry/tests/registry.config.set.tests.ps1 @@ -23,7 +23,7 @@ Describe 'registry config set tests' { $result.keyPath | Should -Be 'HKCU\1\2\3' $result.valueName | Should -Be 'Hello' $result.valueData.String | Should -Be 'World' - ($result.psobject.properties | Measure-Object).Count | Should -Be 3 + ($result.psobject.properties | Measure-Object).Count | Should -Be 4 } It 'Can set a key to be absent' -Skip:(!$IsWindows) { From abefdde6f619c450f6918c53c12ebe359850678f Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 7 Mar 2023 12:10:01 -0800 Subject: [PATCH 3/3] Update registry/src/args.rs --- registry/src/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/src/args.rs b/registry/src/args.rs index 2bf25158..b1e2372d 100644 --- a/registry/src/args.rs +++ b/registry/src/args.rs @@ -65,7 +65,7 @@ pub enum SubCommand { #[clap(subcommand)] subcommand: ConfigSubCommand, }, - #[clap(name = "schema", about = "Retrieve JSON sceham.")] + #[clap(name = "schema", about = "Retrieve JSON schema.")] Schema { #[clap(short, long, help = "Pretty print JSON.")] pretty: bool,