Skip to content

The Echo resource has an incorrect JSON Schema and type definition #1202

@michaeltlombardi

Description

@michaeltlombardi

Prerequisites

  • Write a descriptive title.
  • Make sure you are able to repro it on the latest version
  • Search the existing issues.

Summary

I noticed initially that the JSON Schema returned for the Microsoft.DSC.Debug/Echo resource seemed strange, particularly the last entry in the anyOf keyword, which is just true.

Schema for the Echo resource
$schema: https://json-schema.org/draft/2020-12/schema                                                                             
title: Echo
type: object
properties:
  output:
    $ref: '#/$defs/Output'
  showSecrets:
    type:
    - boolean
    - 'null'
additionalProperties: false
required:
- output
$defs:
  Output:
    anyOf:
    - type: array
      items: true
    - type: boolean
    - type: integer
      format: int64
    - $ref: '#/$defs/SecureObject'
    - $ref: '#/$defs/SecureString'
    - type: string
    - true
  SecureObject:
    type: object
    properties:
      secureObject: true
    additionalProperties: false
    required:
    - secureObject
  SecureString:
    type: object
    properties:
      secureString:
        type: string
    additionalProperties: false
    required:
    - secureString

This relates back to how the type is defined:

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(untagged)]
pub enum Output {
#[serde(rename = "array")]
Array(Vec<Value>),
#[serde(rename = "bool")]
Bool(bool),
#[serde(rename = "number")]
Number(i64),
#[serde(rename = "secureObject")]
SecureObject(SecureObject),
#[serde(rename = "secureString")]
SecureString(SecureString),
#[serde(rename = "string")]
String(String),
// Object has to be last so it doesn't get matched first
#[serde(rename = "object")]
Object(Value),
}

Where line 25 defines the Object variant as taking a type of Value, which schemars generates as true. This effectively means that any value is always valid and negates the other entries in the anyOf keyword, but makes parsing the type more confusing.

Analysis

More correctly, Object should be of type Map<String, Value>, which is what the serde_json::Value::as_object() function returns when a Value instance is a JSON object.

Then the generated schema would be an object. This still has some wriggly complications for the actual schema, because validation more correctly only cares about when secureObject or secureString are defined at the top-level of the instance.

In other words, DSC today validates the following instance with the generated schema:

output:
  secureString:   'my secret'       # `secureString` is a string
  secureObject:                     # `secureObject` is an object
    count: 5
    name:  foo
  insecureObject:
    secureString: 5                 # `insecureObject.secureString` is an integer
    secureObject: foo               # `insecureObject.secureObject` is a string

As Object because it wasn't any of the prior items.

Fix proposal

I think the easiest fix is to make Object wrap Map<String, Value> instead of Value. However, it's not obvious based on the generated schema that secureString and secureObject as nested properties or array items are also subject to redaction.

To handle that, we probably need to do a more resilient schema definition which is unlikely to be readily generatable from schemars.

Steps to reproduce

dsc schema -r Microsoft.DSC.Debug/Echo

Expected behavior

Schema from dsc resource schema command
$schema: https://json-schema.org/draft/2020-12/schema                                                                             
title: Echo
type: object
properties:
  output:
    $ref: '#/$defs/Output'
  showSecrets:
    type:
    - boolean
    - 'null'
additionalProperties: false
required:
- output
$defs:
  Output:
    anyOf:
    - type: array
      items: true
    - type: boolean
    - type: integer
      format: int64
    - $ref: '#/$defs/SecureObject'
    - $ref: '#/$defs/SecureString'
    - type: string
    - type: object
      allOf:
      - not:
        required: [secureObject]
      - not:
        required: [secureString]
  SecureObject:
    type: object
    properties:
      secureObject: true
    additionalProperties: false
    required:
    - secureObject
  SecureString:
    type: object
    properties:
      secureString:
        type: string
    additionalProperties: false
    required:
    - secureString

Actual behavior

Schema from dsc resource schema command
$schema: https://json-schema.org/draft/2020-12/schema                                                                             
title: Echo
type: object
properties:
  output:
    $ref: '#/$defs/Output'
  showSecrets:
    type:
    - boolean
    - 'null'
additionalProperties: false
required:
- output
$defs:
  Output:
    anyOf:
    - type: array
      items: true
    - type: boolean
    - type: integer
      format: int64
    - $ref: '#/$defs/SecureObject'
    - $ref: '#/$defs/SecureString'
    - type: string
    - true
  SecureObject:
    type: object
    properties:
      secureObject: true
    additionalProperties: false
    required:
    - secureObject
  SecureString:
    type: object
    properties:
      secureString:
        type: string
    additionalProperties: false
    required:
    - secureString

Error details

N/A

Environment data

Name                           Value
----                           -----
PSVersion                      7.5.3
PSEdition                      Core
GitCommitId                    7.5.3
OS                             Microsoft Windows 10.0.26200
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Version

Latest main

Visuals

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions