1

I am trying to write a schema to validate that an AWS IAM security group MUST NOT specify incoming IP addresses "0.0.0.0/0" can connect to port 22.

I am using oneOf operator and defining two set of properties and my intuition is that if both of the properties are satisfied, JSON schema should fail but it doesn't.

Sample JSON -

{
  "ipPermissions": [
    {
      "toPort": -1,
      "fromPort": -1,
      "ipRanges": [
        "10.0.0.0/16"
      ]
    },
    {
      "toPort": 22,
      "fromPort": 53,
      "ipRanges": [
        "0.0.0.0/0"
      ],
      "ipProtocol": "tcp"
    }
  ]
}

The above JSON should fail as ipPermission[1] object is-

{
      "toPort": 22,
      "fromPort": 53,
      "ipRanges": [
        "0.0.0.0/0"
      ],
      "ipProtocol": "tcp"
}

as ipRanges has value 0.0.0.0/0 when toPort is 22

Following JSON document should pass validation-

{
      "ipPermissions": [
        {
          "toPort": 22,
          "fromPort": -1,
          "ipRanges": [
            "10.0.0.0/16"
          ]
        },
        {
          "toPort": 22,
          "fromPort": 53,
          "ipRanges": [
            "somethingElse"
          ],
          "ipProtocol": "tcp"
        }
      ]
    }

because ipPermissions index[0] object has toPort value of 22 but ipRanges[0] has value 10.0.0.0/16 which is NOT 0.0.0.0/0

the following JSON should NOT pass the validation -

{
  "ipPermissions": [
    {
      "toPort": 22,
      "fromPort": -1,
      "ipRanges": [
        "10.0.0.0/16"
      ]
    },
    {
      "toPort": 22,
      "fromPort": 53,
      "ipRanges": [
        "somethingElse",
        "0.0.0.0/0"
      ],
      "ipProtocol": "tcp"
    }
  ]
}

as ipPermissions[1].ipRanges[1] value is 0.0.0.0/0

My JSON Schema-

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "required": [
    "ipPermissions"
  ],
  "properties": {
    "ipPermissions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "oneOf": {
            "ipRanges": {
              "type": "array",
              "items": {
                "type": "string",
                "value": "0.0.0.0/0"
              }
            },
            "toPort": {
              "type": "integer",
              "minimum": 23
            }
          }
        }
      }
    }
  }
}
2
  • I'm having trouble figuring out what you are trying to express. Can you expand your description and/or add some more examples of things that should pass and some that should fail? Commented Oct 7, 2016 at 6:55
  • @Json I've provided more examples. Commented Oct 7, 2016 at 17:36

2 Answers 2

4
+100

This one gets a little complicated, so I broke it up using definitions to hopefully make it easier to follow.

#/definitions/ipPermission

The general strategy is to define your properties first and then add the complex constraints separately using anyOf, oneOf, allOf, not, and/or dependencies. In this case the approach I took was to define a schema that matches the case you want to forbid and use not to fail validation if that schema matches.

#/definitions/port-22-and-0.0.0.0-0

This is the schema that validates if "toPort" is "22" and the "ipRanges" array contains "0.0.0.0/0". Unfortunately, JSON Schema doesn't have a contains keyword (yet), so we have to do some boolean logic gymnastics to express that constraint.

#/definitions/array-contains-0.0.0.0-0

We can't directly constrain an array to contain "0.0.0.0/0", but we can create a schema that forbids "0.0.0.0/0" to be in the array. Given such a schema that is valid if "0.0.0.0\0" is not in the array, then any JSON that doesn't validate must contain at least one instance of "0.0.0.0/0".

#/definitions/array-without-0.0.0.0-0

This describes the array that forbids "0.0.0.0/0" which is used to implement the contains constraint.

{
  "type": "object",
  "required": ["ipPermissions"],
  "properties": {
    "ipPermissions": {
      "type": "array",
      "items": { "$ref": "#/definitions/ipPermission" }
    }
  },
  "definitions": {
    "ipPermission": {
      "type": "object",
      "properties": {
        "toPort": { ... },
        "fromPort": { ... },
        "ipRanges": { ... },
        "ipProtocol": { ... }
      }
      "not": { "$ref": "#/definitions/port-22-and-0.0.0.0-0"}
    },
    "port-22-and-0.0.0.0-0": {
      "type": "object",
      "properties": {
        "toPort": { "enum": [22] },
        "ipRanges": { "$ref": "#/definitions/array-contains-0.0.0.0-0" }
      },
      "required": ["toPort", "ipRanges"]
    },
    "array-contains-0.0.0.0-0": {
      "not": { "$ref": "#/definitions/array-without-0.0.0.0-0" }
    },
    "array-without-0.0.0.0-0": {
      "type": "array",
      "items": {
        "not": { "enum": ["0.0.0.0/0"] }
      }
    }
  }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Actually I tried this schema jsoneditoronline.org/?id=a0c8f464f211fd1a4a3a98f59dc328f5 with document { "ipPermissions": [ { "toPort": 22, "fromPort": -1 }, { "fromPort": 53, "ipRanges": [ "0.0.0.0/0" ], "ipProtocol": "tcp" } ] } but it passed the validation
Which part of this is supposed to fail? The first item doesn't have an "ipRanges", so I expect it to pass. The second item doesn't have a "toPort" property, so I expect that to pass too. What am I missing?
my bad. I was using incorrect json for testing. I will do more testing and give you +100 bounty soon! thanks a lot for the help.
Great! Glad I could help.
0

You need to add a "required" node to your schema. However, in this case it also needs to specify one of N allowed nodes:

{
    "$schema":"http://json-schema.org/draft-04/schema#",
    "required":[
        "ipPermissions"
    ],
    "properties":{
        "ipPermissions":{
            "type":"array",
            "items":{
                "type":"object",
                "properties":{
                    "oneOf":{
                        "ipRanges":{
                            "type":"array",
                            "items":{
                                "type":"string",
                                "value":"0.0.0.0/0"
                            }
                        },
                        "toPort":{
                            "type":"integer",
                            "minimum":23
                        }
                    }
                },
                "oneOf":[
                    {
                        "required":[
                            "ipRanges"
                        ]
                    },
                    {
                        "required":[
                            "toPort"
                        ]
                    }
                ]
            }
        }
    }
}

This will validate instances with either an ipRanges node or a toPort node, but not both together, eg:

{
    "ipPermissions":[
        {
            "toPort":-1,
            "fromPort":-1
        },
        {
            "fromPort":53,
            "ipRanges":[
                "0.0.0.0/0"
            ],
            "ipProtocol":"tcp"
        }
    ]
}

3 Comments

Thanks for the help, I tried your JSON schema with following JSON { "ipPermissions": [ { "toPort": 22, "fromPort": -1 }, { "fromPort": 53, "ipRanges": [ "0.0.0.0/0" ], "ipProtocol": "tcp" } ] } but it passed the validation but it shouldn't have.
@user375868 Only one of toPort or ipProtocol is allowed. This was my understanding of your requirement. If this is not the case please clarify exactly what your requirement is. What is it about the JSON you supplied in your comment that should not validate?
please see updated question. I've provided more examples

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.