Module: Inspec::Schema::Primitives

Defined in:
lib/inspec/schema/primitives.rb

Defined Under Namespace

Classes: SchemaType

Constant Summary collapse

OBJECT =

Establish basic type shorthands for this schema ######################################## These three are essentially primitives, used as shorthand

{ "type" => "object", "additionalProperties" => true }.freeze
NUMBER =
{ "type" => "number" }.freeze
STRING =
{ "type" => "string" }.freeze
NULL =
{ "type" => "null" }.freeze
URL =

We might eventually enforce string format stuff on this

{ "type" => "string" }.freeze
TAGS =

A controls tags can have any number of properties, and any sorts of values

{
  "type" => "object",
  "additionalProperties" => true,
  "description" => "A set of any number of tags - they can have any sort of value and are usually metadata.  Example: 'nist' => ['AC-10'].",
}.freeze
INPUT =

Attributes/Inputs specify the inputs to a profile.

{
  "type" => "object",
  "additionalProperties" => true,
  "description" => "An input or attribute used in the run.",
}.freeze
IMPACT =
{
  "type" => "number",
  "minimum" => 0.0,
  "maximum" => 1.0,
  "description" => %q{
     Within a control file, impacts can be a decimal number in the range [0,1] or a string that is one of [none,low,medium,high,critical].
     However, the string will be automatically converted as follows:
     none      -> 0    to 0.01
     low       -> 0.01 to 0.4
     medium    -> 0.4  to 0.7
     high      -> 0.7  to 0.9
     critical  -> 0.9  to 1.0
  },
}.freeze
STATUS =

A status for a control

{
  "type" => "string",
  "enum" => %w{passed failed skipped error},
}.freeze
STATISTIC_ITEM =

We use “title” to name the type. and “description” (via the describe function) to describe its particular usage Summary item containing run statistics about a subset of the controls

SchemaType.new("Statistic Block", {
  "type" => "object",
  "additionalProperties" => true,
  "required" => ["total"],
  "properties" => {
    "total" => desc(NUMBER, "The total.  Example: the total number of controls in a given category for a run."),
  },
}, [], "Statistics for a given item, such as the total.")
STATISTIC_GROUPING =

Bundles several results statistics, representing distinct groups of controls

SchemaType.new("Statistic Hash", {
  "type" => "object",
  "additionalProperties" => true,
  "required" => [],
  "properties" => {
    "passed" => desc(STATISTIC_ITEM.ref, "Statistics for the controls that passed."),
    "skipped" => desc(STATISTIC_ITEM.ref, "Statistics for the controls that were skipped."),
    "failed" => desc(STATISTIC_ITEM.ref, "Statistics for the controls that failed."),
  },
}, [STATISTIC_ITEM], "Statistics for the control results.")
STATISTICS =

Contains statistics of an exec run.

SchemaType.new("Statistics", {
  "type" => "object",
  "additionalProperties" => true,
  "required" => ["duration"],
  "properties" => {
    "duration" => desc(NUMBER, "How long (in seconds) this run by the tool was."),
    "controls" => desc(STATISTIC_GROUPING.ref, "Breakdowns of control statistics by result"),
  },
}, [STATISTIC_GROUPING], "Statistics for the run(s) such as how long it took.")
DEPENDENCY =

Profile dependencies

SchemaType.new("Dependency", {
  "type" => "object",
  "additionalProperties" => true, # Weird stuff shows up here sometimes
  "required" => [], # Mysteriously even in a run profile we can have no status
  "properties" => {
    "name" => desc(STRING, "The name/assigned alias."),
    "url" => desc(URL, "The address of the dependency."),
    "branch" => desc(STRING, "The branch name for a git repo."),
    "path" => desc(STRING, "The relative path if the dependency is locally available."),
    "status_message" => desc(STRING, "The reason for the status if it is 'failed' or 'skipped'."),
    "status" => desc(STRING, "The status.  Should be: 'loaded', 'failed', or 'skipped'."), # NOTE: enum?
    "git" => desc(URL, "The location of the git repo.  Example: 'https://github.com/mitre/canonical-ubuntu-18.04-lts-stig-baseline.git'."),
    "supermarket" => desc(STRING, "The 'user/profilename' attribute for a Supermarket server."),
    "compliance" => desc(STRING, "The 'user/profilename' attribute for an Automate server."),
  },
}, [], "A dependency for a profile.  Can include relative paths or urls for where to find the dependency.")
PLATFORM =

Represents the platform the test was run on

SchemaType.new("Platform", {
  "type" => "object",
  "additionalProperties" => true,
  "required" => %w{name release},
  "properties" => {
    "name" => desc(STRING, "The name of the platform this was run on."),
    "release" => desc(STRING, "The version of the platform this was run on."),
    "target_id" => desc(STRING, "The id of the target.  Example: the name and version of the operating system were not sufficient to identify the platform so a release identifier can additionally be provided like '21H2' for the release version of MS Windows 10."),
  },
}, [], "Platform information such as its name.")
GENERATOR =

Explains what software ran the inspec profile/made this file. Typically inspec but could in theory be a different software

SchemaType.new("Generator", {
  "type" => "object",
  "additionalProperties" => true,
  "required" => %w{name version},
  "properties" => {
    "name" => desc(STRING, "The name.  Example: Chef Inspec."),
    "version" => desc(STRING, "The version.  Example: 4.18.108."),
  },
}, [], "The tool that generated this file.  Example: Chef Inspec.")
SOURCE_LOCATION =

Occurs from “exec –reporter json” and “inspec json” Denotes what file this control comes from, and where within

SchemaType.new("Source Location", {
  "type" => "object",
  "additionalProperties" => true,
  "properties" => {
    "ref" => desc(STRING, "Path to the file that this control originates from."),
    "line" => desc(NUMBER, "The line on which this control is located."),
  },
  "required" => %w{ref line},
}, [], "The explicit location of the control.")
REFERENCE =

References an external document

SchemaType.new("Reference", {
  # Needs at least one of title (ref), url, or uri.
  "anyOf" => [
    {
      "type" => "object",
      "required" => ["ref"],
      "properties" => { "ref" => STRING },
      "description" => "A human readable/meaningful reference.  Example: a book title.",
    },
    {
      "type" => "object",
      "required" => ["url"],
      "properties" => { "url" => STRING }, # NOTE: should this be a URL?
      "description" => "A url pointing at the reference.",
    },
    {
      "type" => "object",
      "required" => ["uri"],
      "properties" => { "uri" => STRING }, # NOTE: should this be a URL?
      "description" => "A uri pointing at the reference.",
    },
    # I am of the opinion that this is just an error in the codebase itself. See profiles/wrapper-override to uncover new mysteries!
    {
      "type" => "object",
      "required" => ["ref"],
      "properties" => { "ref" => array(OBJECT) },
      "description" => "", # TODO: I'm not sure what goes here.  Maybe it's supposed to be objects similar to { "title" => "blah", "text" => "blah" }?

    },
  ],
}, [], "A reference to an external document.")
CONTROL_GROUP =

Represents a group of controls within a profile/.rb file

SchemaType.new("Control Group", {
  "type" => "object",
  "additionalProperties" => true,
  "required" => %w{id controls},
  "properties" => {
    "id" => desc(STRING, "The unique identifier for the group.  Example: the relative path to the file specifying the controls."),
    "title" => desc({ type: %w{string null} }, "The title of the group - should be human readable."), # NOTE: pretty unique type like this - wouldn't it be better to have it be a STRING and then continue to not make it required?
    "controls" => desc(array(STRING), "The set of controls as specified by their ids in this group.  Example: 'V-75443'."),
  },
}, [], "Descriptions for controls in a group, such as the list of all the controls.")
SUPPORT =

Occurs from “inspec exec –reporter json” and “inspec json” Represents a platfrom or group of platforms that this profile supports

SchemaType.new("Supported Platform", {
  "type" => "object",
  "additionalProperties" => true, # NOTE: This should really be false, and inspec should validate profiles to ensure people don't make dumb mistakes like platform_family
  "required" => [],
  "properties" => {
    "platform-family" => desc(STRING, "The platform family.  Example: 'redhat'."),
    "platform-name" => desc(STRING, "The platform name - can include wildcards.  Example: 'debian'."),
    "platform" => desc(STRING, "The location of the platform.  Can be: 'os', 'aws', 'azure', or 'gcp'."), # NOTE: enum?
    "release" => desc(STRING, "The release of the platform.  Example: '20.04' for 'ubuntu'."),
    # os-* supports are being deprecated
    "os-family" => desc(STRING, "Deprecated in favor of platform-family."),
    "os-name" => desc(STRING, "Deprecated in favor of platform-name."),
  },
}, [], "A supported platform target.  Example: the platform name being 'ubuntu'.")

Class Method Summary collapse

Class Method Details

.array(of_type) ⇒ Object

Use this function to easily make an array of a type



15
16
17
18
19
20
# File 'lib/inspec/schema/primitives.rb', line 15

def self.array(of_type)
  {
    "type" => "array",
    "items" => of_type,
  }
end

.desc(obj, description) ⇒ Object

Establish simple helpers for this schema ######################################## Use this function to easily make described types



10
11
12
# File 'lib/inspec/schema/primitives.rb', line 10

def self.desc(obj, description)
  obj.merge({ "description" => description })
end

.validate_schema(schema) ⇒ Object

This function performs some simple validation on schemas, to catch obvious missed requirements



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/inspec/schema/primitives.rb', line 23

def self.validate_schema(schema)
  return if schema["type"] != "object"
  raise "Objects in schema must have a \"required\" property, even if it is empty." unless schema.key?("required")

  return if schema["required"].empty?
  raise "An object with required properties must have a properties hash." unless schema.key?("properties")

  return if Set.new(schema["required"]) <= Set.new(schema["properties"].keys)

  raise "Not all required properties are present."
end