Skip to content

Commit 78b7893

Browse files
committed
Fix consistent passing of secureString and secureObject
1 parent 10ecdc8 commit 78b7893

File tree

8 files changed

+137
-21
lines changed

8 files changed

+137
-21
lines changed

dsc_lib/locales/en-us.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ parsingIndexAccessor = "Parsing index accessor '%{index}'"
511511
indexNotFound = "Index value not found"
512512
invalidAccessorKind = "Invalid accessor kind: '%{kind}'"
513513
functionResult = "Function results: %{results}"
514+
functionResultSecure = "Function result is secure"
514515
evalAccessors = "Evaluating accessors"
515516
memberNameNotFound = "Member '%{member}' not found"
516517
accessOnNonObject = "Member access on non-object value"

dsc_lib/src/configure/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::configure::config_doc::{ExecutionKind, Metadata, Resource};
55
use crate::configure::context::{Context, ProcessMode};
6+
use crate::configure::parameters::is_secure_value;
67
use crate::configure::{config_doc::RestartRequired, parameters::Input};
78
use crate::discovery::discovery_trait::DiscoveryFilter;
89
use crate::dscerror::DscError;
@@ -214,7 +215,19 @@ fn add_metadata(dsc_resource: &DscResource, mut properties: Option<Map<String, V
214215

215216
match properties {
216217
Some(properties) => {
217-
Ok(serde_json::to_string(&properties)?)
218+
let mut unsecure_properties = Map::new();
219+
for (key, value) in properties {
220+
if is_secure_value(&value) {
221+
if value.get("secureString").is_some() {
222+
unsecure_properties.insert(key.clone(), "<secureString>".into());
223+
} else if value.get("secureObject").is_some() {
224+
unsecure_properties.insert(key.clone(), "<secureObject>".into());
225+
}
226+
} else {
227+
unsecure_properties.insert(key, value);
228+
}
229+
}
230+
Ok(serde_json::to_string(&unsecure_properties)?)
218231
},
219232
_ => {
220233
Ok(String::new())

dsc_lib/src/configure/parameters.rs

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,53 @@ pub struct Input {
1212
}
1313

1414
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
15-
#[serde(deny_unknown_fields, untagged)]
16-
pub enum SecureKind {
15+
pub struct SecureString {
1716
#[serde(rename = "secureString")]
18-
SecureString(String),
17+
pub secure_string: String,
18+
}
19+
20+
impl Display for SecureString {
21+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22+
write!(f, "<secureString>")
23+
}
24+
}
25+
26+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
27+
pub struct SecureObject {
1928
#[serde(rename = "secureObject")]
20-
SecureObject(Value),
29+
pub secure_object: Value,
2130
}
2231

23-
impl Display for SecureKind {
32+
impl Display for SecureObject {
2433
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25-
match self {
26-
SecureKind::SecureString(_) => write!(f, "<secureString>"),
27-
SecureKind::SecureObject(_) => write!(f, "<secureObject>"),
34+
write!(f, "<secureObject>")
35+
}
36+
}
37+
38+
/// Check if a given JSON value is a secure value (either `SecureString` or `SecureObject`).
39+
///
40+
/// # Arguments
41+
///
42+
/// * `value` - The JSON value to check.
43+
///
44+
/// # Returns
45+
///
46+
/// `true` if the value is a secure value, `false` otherwise.
47+
#[must_use]
48+
pub fn is_secure_value(value: &Value) -> bool {
49+
if let Some(obj) = value.as_object() {
50+
if obj.len() == 1 && (obj.contains_key("secureString") || obj.contains_key("secureObject")) {
51+
return true;
2852
}
2953
}
54+
false
55+
}
56+
57+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
58+
#[serde(deny_unknown_fields, untagged)]
59+
pub enum SecureKind {
60+
#[serde(rename = "secureString")]
61+
SecureString(SecureString),
62+
#[serde(rename = "secureObject")]
63+
SecureObject(SecureObject),
3064
}

dsc_lib/src/dscresources/command_resource.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -717,9 +717,9 @@ async fn run_process_async(executable: &str, args: Option<Vec<String>>, input: O
717717
#[allow(clippy::implicit_hasher)]
718718
#[tokio::main]
719719
pub async fn invoke_command(executable: &str, args: Option<Vec<String>>, input: Option<&str>, cwd: Option<&str>, env: Option<HashMap<String, String>>, exit_codes: Option<&HashMap<i32, String>>) -> Result<(i32, String, String), DscError> {
720-
debug!("{}", t!("dscresources.commandResource.commandInvoke", executable = executable, args = args : {:?}));
720+
trace!("{}", t!("dscresources.commandResource.commandInvoke", executable = executable, args = args : {:?}));
721721
if let Some(cwd) = cwd {
722-
debug!("{}", t!("dscresources.commandResource.commandCwd", cwd = cwd));
722+
trace!("{}", t!("dscresources.commandResource.commandCwd", cwd = cwd));
723723
}
724724

725725
match run_process_async(executable, args, input, cwd, env, exit_codes).await {

dsc_lib/src/functions/parameters.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
use crate::configure::config_doc::DataType;
5-
use crate::configure::parameters::SecureKind;
5+
use crate::configure::parameters::{SecureObject, SecureString};
66
use crate::DscError;
77
use crate::configure::context::Context;
88
use crate::functions::{FunctionArgKind, Function, FunctionCategory, FunctionMetadata};
@@ -40,11 +40,15 @@ impl Function for Parameters {
4040
let Some(value) = value.as_str() else {
4141
return Err(DscError::Parser(t!("functions.parameters.keyNotString", key = key).to_string()));
4242
};
43-
let secure_string = SecureKind::SecureString(value.to_string());
43+
let secure_string = SecureString {
44+
secure_string: value.to_string(),
45+
};
4446
Ok(serde_json::to_value(secure_string)?)
4547
},
4648
DataType::SecureObject => {
47-
let secure_object = SecureKind::SecureObject(value.clone());
49+
let secure_object = SecureObject {
50+
secure_object: value.clone(),
51+
};
4852
Ok(serde_json::to_value(secure_object)?)
4953
},
5054
_ => {

dsc_lib/src/parser/expressions.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use tracing::{debug, trace};
77
use tree_sitter::Node;
88

99
use crate::configure::context::Context;
10+
use crate::configure::parameters::is_secure_value;
1011
use crate::dscerror::DscError;
1112
use crate::functions::FunctionDispatcher;
1213
use crate::parser::functions::Function;
@@ -113,17 +114,30 @@ impl Expression {
113114
/// This function will return an error if the expression fails to execute.
114115
pub fn invoke(&self, function_dispatcher: &FunctionDispatcher, context: &Context) -> Result<Value, DscError> {
115116
let result = self.function.invoke(function_dispatcher, context)?;
116-
// skip trace if function is 'secret()'
117-
if self.function.name() != "secret" {
117+
if self.function.name() != "secret" && !is_secure_value(&result) {
118118
let result_json = serde_json::to_string(&result)?;
119119
trace!("{}", t!("parser.expression.functionResult", results = result_json));
120+
} else {
121+
trace!("{}", t!("parser.expression.functionResultSecure"));
120122
}
121123
if self.accessors.is_empty() {
122124
Ok(result)
123125
}
124126
else {
125127
debug!("{}", t!("parser.expression.evalAccessors"));
126128
let mut value = result;
129+
let is_secure = is_secure_value(&value);
130+
if is_secure {
131+
// if a SecureString, extract the string value
132+
if let Some(string) = value.get("secureString") {
133+
if let Some(s) = string.as_str() {
134+
value = Value::String(s.to_string());
135+
}
136+
} else if let Some(obj) = value.get("secureObject") {
137+
// if a SecureObject, extract the object value
138+
value = obj.clone();
139+
}
140+
}
127141
for accessor in &self.accessors {
128142
let mut index = Value::Null;
129143
match accessor {
@@ -132,7 +146,12 @@ impl Expression {
132146
if !object.contains_key(member) {
133147
return Err(DscError::Parser(t!("parser.expression.memberNameNotFound", member = member).to_string()));
134148
}
135-
value = object[member].clone();
149+
if is_secure {
150+
// if the original value was a secure value, we need to convert the member value back to secure
151+
value = convert_to_secure(&object[member]);
152+
} else {
153+
value = object[member].clone();
154+
}
136155
} else {
137156
return Err(DscError::Parser(t!("parser.expression.accessOnNonObject").to_string()));
138157
}
@@ -169,3 +188,27 @@ impl Expression {
169188
}
170189
}
171190
}
191+
192+
fn convert_to_secure(value: &Value) -> Value {
193+
if let Some(string) = value.as_str() {
194+
let secure_string = crate::configure::parameters::SecureString {
195+
secure_string: string.to_string(),
196+
};
197+
return serde_json::to_value(secure_string).unwrap_or(value.clone());
198+
}
199+
200+
if let Some(obj) = value.as_object() {
201+
if obj.len() == 1 && obj.contains_key("secureObject") {
202+
let secure_object = crate::configure::parameters::SecureObject {
203+
secure_object: obj["secureObject"].clone(),
204+
};
205+
return serde_json::to_value(secure_object).unwrap_or(value.clone());
206+
}
207+
}
208+
209+
if let Some(array) = value.as_array() {
210+
let new_array: Vec<Value> = array.iter().map(convert_to_secure).collect();
211+
return Value::Array(new_array);
212+
}
213+
value.clone()
214+
}

dscecho/src/echo.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,25 @@ pub enum Output {
1717
#[serde(rename = "object")]
1818
Object(Value),
1919
#[serde(rename = "secureObject")]
20-
SecureObject(Value),
20+
SecureObject(SecureObject),
2121
#[serde(rename = "secureString")]
22-
SecureString(String),
22+
SecureString(SecureString),
2323
#[serde(rename = "string")]
2424
String(String),
2525
}
2626

27+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
28+
#[serde(deny_unknown_fields)]
29+
pub struct SecureString {
30+
pub secure_string: String,
31+
}
32+
33+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
34+
#[serde(deny_unknown_fields)]
35+
pub struct SecureObject {
36+
pub secure_object: Value,
37+
}
38+
2739
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
2840
#[serde(deny_unknown_fields)]
2941
pub struct Echo {

dscecho/src/main.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,30 @@ use args::Args;
88
use clap::Parser;
99
use rust_i18n::{i18n, t};
1010
use schemars::schema_for;
11-
use crate::echo::Echo;
11+
use crate::echo::{Echo, Output};
1212

1313
i18n!("locales", fallback = "en-us");
1414

1515
fn main() {
1616
let args = Args::parse();
1717
match args.input {
1818
Some(input) => {
19-
let echo = match serde_json::from_str::<Echo>(&input) {
19+
let mut echo = match serde_json::from_str::<Echo>(&input) {
2020
Ok(echo) => echo,
2121
Err(err) => {
2222
eprintln!("{}: {err}", t!("main.invalidJson"));
2323
std::process::exit(1);
2424
}
2525
};
26+
match echo.output {
27+
Output::SecureObject(_) => {
28+
echo.output = Output::String("<secureObject>".to_string());
29+
},
30+
Output::SecureString(_) => {
31+
echo.output = Output::String("<secureString>".to_string());
32+
},
33+
_ => {}
34+
}
2635
let json = serde_json::to_string(&echo).unwrap();
2736
println!("{json}");
2837
return;

0 commit comments

Comments
 (0)