From 9641ae0ce272208cb9c5644421bd6712a42be038 Mon Sep 17 00:00:00 2001 From: Jennifer Power Date: Thu, 5 Dec 2024 10:04:52 -0500 Subject: [PATCH] feat(rules): add rules.Store and in-memory implementation (#12) * feat: add Store interface and default memory-based implementation The Store interface allows rule information to be pulled from different types of sources while providing a default implementation for immediate use. Signed-off-by: Jennifer Power * chore(lint): setting download mode for modules to readonly Setting this allows validation that go.mod is up to date in CI Signed-off-by: Jennifer Power * chore: fixes comments on error types under rules Signed-off-by: Jennifer Power * chore: updates Memory with styling fixes Signed-off-by: Jennifer Power * chore: updates license header with copyright info Formats the license header to be compliant with Apache 2.0 short variant header Signed-off-by: Jennifer Power * fix: corrects the use of errors.Join Signed-off-by: Jennifer Power * chore: adds minor change to indexing component logic The ruleset does not need to be set in the map until it is populated Signed-off-by: Jennifer Power * chore: restore Makefile targets Signed-off-by: Jennifer Power * docs: refines Store interface comment for clarity Signed-off-by: Jennifer Power --------- Signed-off-by: Jennifer Power --- .gitignore | 3 - .golangci.yml | 3 + extensions/doc.go | 5 +- extensions/rules.go | 7 +- generators/doc.go | 5 +- generators/loader.go | 5 +- go.mod | 11 +- go.sum | 10 + rules/doc.go | 7 + rules/internal/set.go | 25 +++ rules/memory.go | 203 ++++++++++++++++++ rules/memory_test.go | 170 +++++++++++++++ rules/props.go | 41 ++++ rules/store.go | 30 +++ .../component-definition-no-components.json | 11 + .../component-definition-no-rules.json | 58 +++++ rules/testdata/component-definition-test.json | 139 ++++++++++++ 17 files changed, 724 insertions(+), 9 deletions(-) create mode 100644 rules/doc.go create mode 100644 rules/internal/set.go create mode 100644 rules/memory.go create mode 100644 rules/memory_test.go create mode 100644 rules/props.go create mode 100644 rules/store.go create mode 100644 rules/testdata/component-definition-no-components.json create mode 100644 rules/testdata/component-definition-no-rules.json create mode 100644 rules/testdata/component-definition-test.json diff --git a/.gitignore b/.gitignore index 718f1bd..50b6e96 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,3 @@ go.work.sum # IDE things .idea .vscode - -# OSCAL artifacts -oscal_complete_schema.json diff --git a/.golangci.yml b/.golangci.yml index 0690d82..3131327 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,6 @@ +run: + # disallows implicit automatic updating of go.mod and fails when any changes to go.mod are needed + modules-download-mode: readonly linters: enable: - errcheck # Checks for unchecked errors diff --git a/extensions/doc.go b/extensions/doc.go index 855a10d..4667162 100644 --- a/extensions/doc.go +++ b/extensions/doc.go @@ -1,4 +1,7 @@ -// SPDX-License-Identifier: Apache-2.0 +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ /* Package extensions defines types that represent OSCAL-based extensions defined by OSCAL Compass. diff --git a/extensions/rules.go b/extensions/rules.go index e9fe650..cfccc44 100644 --- a/extensions/rules.go +++ b/extensions/rules.go @@ -1,8 +1,11 @@ -// SPDX-License-Identifier: Apache-2.0 +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ package extensions -// Below are defined oscal.Property names for compass-based extensions +// Below are defined oscal.Property names for compass-based extensions. const ( RuleIdProp = "Rule_Id" RuleDescriptionProp = "Rule_Description" diff --git a/generators/doc.go b/generators/doc.go index 3ca9687..dbabaaf 100644 --- a/generators/doc.go +++ b/generators/doc.go @@ -1,4 +1,7 @@ -// SPDX-License-Identifier: Apache-2.0 +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ // Package generators generates OSCAL-based objects for use. This includes loading objects from existing content or // generating sample objects. diff --git a/generators/loader.go b/generators/loader.go index 77e06fa..75cd156 100644 --- a/generators/loader.go +++ b/generators/loader.go @@ -1,4 +1,7 @@ -// SPDX-License-Identifier: Apache-2.0 +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ package generators diff --git a/go.mod b/go.mod index 662e2ed..5c52a54 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,13 @@ module github.com/oscal-compass/oscal-sdk-go go 1.22.7 -require github.com/defenseunicorns/go-oscal v0.6.0 +require ( + github.com/defenseunicorns/go-oscal v0.6.0 + github.com/stretchr/testify v1.9.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index a50eb8b..27ee041 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/defenseunicorns/go-oscal v0.6.0 h1:eflEKfk7edu4L4kWf6aNQpS94ljfGP8lgWpsPYNtE1Q= github.com/defenseunicorns/go-oscal v0.6.0/go.mod h1:UHp2yK9ty2mYJDun7oNhbstCq6SAAwP4YGbw9n7uG6o= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/rules/doc.go b/rules/doc.go new file mode 100644 index 0000000..18a7986 --- /dev/null +++ b/rules/doc.go @@ -0,0 +1,7 @@ +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +// Package rules defines logic relating to the processing of compass defined Rules. +package rules diff --git a/rules/internal/set.go b/rules/internal/set.go new file mode 100644 index 0000000..6bf1b53 --- /dev/null +++ b/rules/internal/set.go @@ -0,0 +1,25 @@ +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package internal + +// Set represents a set data structure. +type Set[T comparable] map[T]struct{} + +// NewSet returns an initialized set. +func NewSet[T comparable]() Set[T] { + return make(Set[T]) +} + +// Add adds item into the set s. +func (s Set[T]) Add(item T) { + s[item] = struct{}{} +} + +// Has checks if the set contains an item. +func (s Set[T]) Has(item T) bool { + _, ok := s[item] + return ok +} diff --git a/rules/memory.go b/rules/memory.go new file mode 100644 index 0000000..953883d --- /dev/null +++ b/rules/memory.go @@ -0,0 +1,203 @@ +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package rules + +import ( + "context" + "errors" + "fmt" + + oscal112 "github.com/defenseunicorns/go-oscal/src/types/oscal-1-1-2" + + "github.com/oscal-compass/oscal-sdk-go/extensions" + . "github.com/oscal-compass/oscal-sdk-go/rules/internal" +) + +var ( + // Store interface check + _ Store = (*MemoryStore)(nil) + + // ErrRuleNotFound defines an error returned when rule queries fail. + ErrRuleNotFound = errors.New("associated rule object not found") + // ErrComponentsNotFound defines an error returned during MemoryStore creation when the input + // is invalid. + ErrComponentsNotFound = errors.New("no components not found") +) + +/* +MemoryStore provides implementation of a memory-based rule.Store. +WARNING: This implementation is not thread safe. +*/ +type MemoryStore struct { + // nodes saves the rule ID map keys, which are used with + // the other fields. + nodes map[string]extensions.RuleSet + // ByCheck store a mapping between the checkId and its parent + // ruleId + byCheck map[string]string + + // Below contains maps that store information by component and + // component types to form RuleSet with the correct context. + + // rulesByComponent stores the component title of any component + // mapped to any relevant rules. + rulesByComponent map[string]Set[string] + // checksByValidationComponent store checkId mapped to validation + // component title to filter check information on rules. + checksByValidationComponent map[string]Set[string] +} + +// NewMemoryStoreFromComponents creates a new memory-based rule finder. +func NewMemoryStoreFromComponents(components []oscal112.DefinedComponent) (*MemoryStore, error) { + if len(components) == 0 { + return nil, fmt.Errorf("failed to create memory store from components: %w", ErrComponentsNotFound) + } + store := &MemoryStore{ + nodes: make(map[string]extensions.RuleSet), + byCheck: make(map[string]string), + rulesByComponent: make(map[string]Set[string]), + checksByValidationComponent: make(map[string]Set[string]), + } + + for _, component := range components { + extractedRules := store.indexComponent(component) + if len(extractedRules) != 0 { + store.rulesByComponent[component.Title] = extractedRules + } + } + + return store, nil +} + +func (m *MemoryStore) indexComponent(component oscal112.DefinedComponent) Set[string] { + rules := NewSet[string]() + if component.Props == nil { + return rules + } + + // Catalog all registered check implementations by validation component for filtering in + // `rules.FindByComponent`. + checkIds := NewSet[string]() + + // Each rule set is linked by a group id in the property remarks + byRemarks := groupPropsByRemarks(*component.Props) + for _, propSet := range byRemarks { + ruleIdProp, ok := findProp(extensions.RuleIdProp, propSet) + if !ok { + continue + } + + ruleSet, ok := m.nodes[ruleIdProp.Value] + if !ok { + ruleSet = extensions.RuleSet{} + } + + // A check may or may not be registered. + placeholderCheck := extensions.Check{} + + for prop := range propSet { + switch prop.Name { + case extensions.RuleIdProp: + ruleSet.Rule.ID = prop.Value + case extensions.RuleDescriptionProp: + ruleSet.Rule.Description = prop.Value + case extensions.ParameterIdProp: + if ruleSet.Rule.Parameter == nil { + ruleSet.Rule.Parameter = &extensions.Parameter{} + } + ruleSet.Rule.Parameter.ID = prop.Value + case extensions.ParameterDescriptionProp: + if ruleSet.Rule.Parameter == nil { + ruleSet.Rule.Parameter = &extensions.Parameter{} + } + ruleSet.Rule.Parameter.Description = prop.Value + + case extensions.ParameterDefaultProp: + if ruleSet.Rule.Parameter == nil { + ruleSet.Rule.Parameter = &extensions.Parameter{} + } + ruleSet.Rule.Parameter.Value = prop.Value + case extensions.CheckIdProp: + placeholderCheck.ID = prop.Value + case extensions.CheckDescriptionProp: + placeholderCheck.Description = prop.Value + } + } + + if placeholderCheck.ID != "" { + ruleSet.Checks = append(ruleSet.Checks, placeholderCheck) + m.byCheck[placeholderCheck.ID] = ruleSet.Rule.ID + } + rules.Add(ruleSet.Rule.ID) + m.nodes[ruleSet.Rule.ID] = ruleSet + } + if len(checkIds) != 0 { + m.checksByValidationComponent[component.Title] = checkIds + } + + return rules +} + +func (m *MemoryStore) GetByRuleID(_ context.Context, ruleId string) (extensions.RuleSet, error) { + ruleSet, ok := m.nodes[ruleId] + if !ok { + return extensions.RuleSet{}, fmt.Errorf("rule %q: %w", ruleId, ErrRuleNotFound) + } + return ruleSet, nil +} + +func (m *MemoryStore) GetByCheckID(ctx context.Context, checkId string) (extensions.RuleSet, error) { + ruleId, ok := m.byCheck[checkId] + if !ok { + return extensions.RuleSet{}, fmt.Errorf("failed to find rule for check %q: %w", checkId, ErrRuleNotFound) + } + return m.GetByRuleID(ctx, ruleId) +} + +func (m *MemoryStore) FindByComponent(ctx context.Context, componentId string) ([]extensions.RuleSet, error) { + ruleIds, ok := m.rulesByComponent[componentId] + if !ok { + return nil, fmt.Errorf("failed to find rules for component %q", componentId) + } + + var ruleSets []extensions.RuleSet + var errs []error + for ruleId := range ruleIds { + ruleSet, err := m.GetByRuleID(ctx, ruleId) + if err != nil { + errs = append(errs, err) + } + + // Make sure we are only returning the relevant checks for this + // component. + if checkIds, ok := m.checksByValidationComponent[componentId]; ok { + filteredChecks := make([]extensions.Check, 0, len(ruleSet.Checks)) + for _, check := range ruleSet.Checks { + if checkIds.Has(check.ID) { + filteredChecks = append(filteredChecks, check) + } + } + ruleSet.Checks = filteredChecks + } + + ruleSets = append(ruleSets, ruleSet) + } + + if len(errs) > 0 { + joinedErr := errors.Join(errs...) + return ruleSets, fmt.Errorf("failed to find rules for component %q: %w", componentId, joinedErr) + } + + return ruleSets, nil +} + +func (m *MemoryStore) All(ctx context.Context) ([]extensions.RuleSet, error) { + var ruleSets []extensions.RuleSet + for _, rule := range m.nodes { + ruleSets = append(ruleSets, rule) + } + return ruleSets, nil +} diff --git a/rules/memory_test.go b/rules/memory_test.go new file mode 100644 index 0000000..3e8c9c0 --- /dev/null +++ b/rules/memory_test.go @@ -0,0 +1,170 @@ +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package rules + +import ( + "context" + "os" + "testing" + + oscaltypes112 "github.com/defenseunicorns/go-oscal/src/types/oscal-1-1-2" + "github.com/stretchr/testify/require" + + "github.com/oscal-compass/oscal-sdk-go/extensions" + "github.com/oscal-compass/oscal-sdk-go/generators" +) + +var ( + expectedCertFileRule = extensions.RuleSet{ + Rule: extensions.Rule{ + ID: "etcd_cert_file", + Description: "Ensure that the --cert-file argument is set as appropriate", + }, + Checks: []extensions.Check{ + { + ID: "etcd_cert_file", + Description: "Check that the --cert-file argument is set as appropriate", + }, + }, + } + expectedKeyFileRule = extensions.RuleSet{ + Rule: extensions.Rule{ + ID: "etcd_key_file", + Description: "Ensure that the --key-file argument is set as appropriate", + Parameter: &extensions.Parameter{ + ID: "file_name", + Description: "A parameter for a file name", + }, + }, + Checks: []extensions.Check{ + { + ID: "etcd_key_file", + Description: "Check that the --key-file argument is set as appropriate", + }, + }, + } +) + +func TestIndexComponents(t *testing.T) { + tests := []struct { + name string + testDataPath string + expError string + wantNodes map[string]extensions.RuleSet + }{ + { + name: "Valid/WithRules", + testDataPath: "testdata/component-definition-test.json", + wantNodes: map[string]extensions.RuleSet{ + "etcd_key_file": expectedKeyFileRule, + + "etcd_cert_file": expectedCertFileRule, + }, + }, + { + name: "Valid/NoRules", + testDataPath: "testdata/component-definition-no-rules.json", + wantNodes: map[string]extensions.RuleSet{}, + }, + { + name: "Failure/NoComponents", + testDataPath: "testdata/component-definition-no-components.json", + expError: "failed to create memory store from components: no components not found", + }, + } + + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + file, err := os.Open(c.testDataPath) + require.NoError(t, err) + definition, err := generators.NewComponentDefinition(file) + require.NoError(t, err) + + if definition.Components == nil { + definition.Components = &[]oscaltypes112.DefinedComponent{} + } + + testMemory, err := NewMemoryStoreFromComponents(*definition.Components) + + if c.expError != "" { + require.EqualError(t, err, c.expError) + } else { + require.NoError(t, err) + require.Equal(t, c.wantNodes, testMemory.nodes) + } + }) + } +} + +func TestMemoryStore_GetByRuleID(t *testing.T) { + testMemory := prepMemoryStore(t) + testCtx := context.Background() + + found, err := testMemory.GetByRuleID(testCtx, "etcd_cert_file") + require.NoError(t, err) + + expectedRule := extensions.RuleSet{ + Rule: extensions.Rule{ + ID: "etcd_cert_file", + Description: "Ensure that the --cert-file argument is set as appropriate", + }, + Checks: []extensions.Check{ + { + ID: "etcd_cert_file", + Description: "Check that the --cert-file argument is set as appropriate", + }, + }, + } + require.Equal(t, expectedRule, found) + + _, err = testMemory.GetByRuleID(testCtx, "not_present") + require.EqualError(t, err, "rule \"not_present\": associated rule object not found") + +} + +func TestMemoryStore_GetByCheckID(t *testing.T) { + testMemory := prepMemoryStore(t) + testCtx := context.Background() + + found, err := testMemory.GetByCheckID(testCtx, "etcd_key_file") + require.NoError(t, err) + require.Equal(t, expectedKeyFileRule, found) + + _, err = testMemory.GetByCheckID(testCtx, "not_present") + require.EqualError(t, err, "failed to find rule for check \"not_present\": associated rule object not found") + +} + +func TestMemoryStore_FindByComponent(t *testing.T) { + testMemory := prepMemoryStore(t) + testCtx := context.Background() + + validatorRuleSet, err := testMemory.FindByComponent(testCtx, "Validator") + require.NoError(t, err) + softwareRuleSet, err := testMemory.FindByComponent(testCtx, "Kubernetes") + require.NoError(t, err) + + require.Contains(t, validatorRuleSet, expectedCertFileRule) + require.Contains(t, validatorRuleSet, expectedKeyFileRule) + require.Contains(t, softwareRuleSet, expectedCertFileRule) + require.Contains(t, softwareRuleSet, expectedKeyFileRule) + + _, err = testMemory.FindByComponent(testCtx, "not_a_component") + require.EqualError(t, err, "failed to find rules for component \"not_a_component\"") +} + +func prepMemoryStore(t *testing.T) *MemoryStore { + testDataPath := "testdata/component-definition-test.json" + + file, err := os.Open(testDataPath) + require.NoError(t, err) + definition, err := generators.NewComponentDefinition(file) + require.NoError(t, err) + testMemory, err := NewMemoryStoreFromComponents(*definition.Components) + require.NoError(t, err) + + return testMemory +} diff --git a/rules/props.go b/rules/props.go new file mode 100644 index 0000000..f60b324 --- /dev/null +++ b/rules/props.go @@ -0,0 +1,41 @@ +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package rules + +import ( + oscal112 "github.com/defenseunicorns/go-oscal/src/types/oscal-1-1-2" + + . "github.com/oscal-compass/oscal-sdk-go/rules/internal" +) + +// groupPropsByRemarks will return the properties group by the same +// remark string. This is how properties are grouped to create rule sets. +func groupPropsByRemarks(props []oscal112.Property) map[string]Set[oscal112.Property] { + grouped := map[string]Set[oscal112.Property]{} + for _, prop := range props { + if prop.Remarks == "" { + continue + } + remarks := prop.Remarks + set, ok := grouped[remarks] + if !ok { + set = NewSet[oscal112.Property]() + } + set.Add(prop) + grouped[remarks] = set + } + return grouped +} + +// findProp finds a property in a set by the property name. +func findProp(name string, props Set[oscal112.Property]) (oscal112.Property, bool) { + for prop := range props { + if prop.Name == name { + return prop, true + } + } + return oscal112.Property{}, false +} diff --git a/rules/store.go b/rules/store.go new file mode 100644 index 0000000..f49bdf5 --- /dev/null +++ b/rules/store.go @@ -0,0 +1,30 @@ +/* + Copyright 2024 The OSCAL Compass Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package rules + +import ( + "context" + + "github.com/oscal-compass/oscal-sdk-go/extensions" +) + +// Store defines methods for filtering and searching for rule sets generated based on +// rule/check OSCAL extensions. +type Store interface { + // GetByRuleID return the rule object by the associated id or name. + GetByRuleID(ctx context.Context, ruleID string) (extensions.RuleSet, error) + // GetByCheckID returns rule object by the associated check id. + GetByCheckID(ctx context.Context, checkID string) (extensions.RuleSet, error) + + // FindByComponent find rule objects by the associated component title. + // Note that if a component is of type validation, this should only return + // checks relevant to that validation component. Target component types (non-validation) should + // return all information. + FindByComponent(ctx context.Context, componentID string) ([]extensions.RuleSet, error) + + // All lists all indexed rules. + All(ctx context.Context) ([]extensions.RuleSet, error) +} diff --git a/rules/testdata/component-definition-no-components.json b/rules/testdata/component-definition-no-components.json new file mode 100644 index 0000000..a34e599 --- /dev/null +++ b/rules/testdata/component-definition-no-components.json @@ -0,0 +1,11 @@ +{ + "component-definition": { + "uuid": "c14d8812-7098-4a9b-8f89-cba41b6ff0d8", + "metadata": { + "title": "Component definition", + "last-modified": "2023-02-21T06:53:42+00:00", + "version": "1.1", + "oscal-version": "1.0.2" + } + } +} diff --git a/rules/testdata/component-definition-no-rules.json b/rules/testdata/component-definition-no-rules.json new file mode 100644 index 0000000..9e97a28 --- /dev/null +++ b/rules/testdata/component-definition-no-rules.json @@ -0,0 +1,58 @@ +{ + "component-definition": { + "uuid": "c14d8812-7098-4a9b-8f89-cba41b6ff0d8", + "metadata": { + "title": "Component definition", + "last-modified": "2023-02-21T06:53:42+00:00", + "version": "1.1", + "oscal-version": "1.0.2" + }, + "components": [ + { + "uuid": "c8106bc8-5174-4e86-91a4-52f2fe0ed027", + "type": "service", + "title": "Kubernetes", + "description": "Kubernetes", + "control-implementations": [ + { + "uuid": "f79d6290-8efa-4ea7-b931-27b8435cf707", + "source": "profiles/cis/profile.json", + "description": "CIS Profile", + "implemented-requirements": [ + { + "uuid": "a1b5b713-52c7-46fb-ab57-ebac7f576b23", + "control-id": "CIS-2.1", + "description": "" + }, + { + "uuid": "bd737295-2dd9-436d-89b4-54a581afa154", + "control-id": "CIS-2.2", + "description": "" + }, + { + "uuid": "a463ae43-f562-434e-a3a4-2eaed2cbec59", + "control-id": "CIS-2.3", + "description": "" + }, + { + "uuid": "22d8d502-a9c2-444a-9a3e-e55c1405f42c", + "control-id": "CIS-2.4", + "description": "" + }, + { + "uuid": "e297ed7f-7a8f-44ed-af58-78b0693f3f4b", + "control-id": "CIS-2.5", + "description": "" + }, + { + "uuid": "13c6e9ee-1c85-440a-bbe9-6a54498fcde2", + "control-id": "CIS-2.6", + "description": "" + } + ] + } + ] + } + ] + } +} diff --git a/rules/testdata/component-definition-test.json b/rules/testdata/component-definition-test.json new file mode 100644 index 0000000..a571107 --- /dev/null +++ b/rules/testdata/component-definition-test.json @@ -0,0 +1,139 @@ +{ + "component-definition": { + "uuid": "c14d8812-7098-4a9b-8f89-cba41b6ff0d8", + "metadata": { + "title": "Component definition", + "last-modified": "2023-02-21T06:53:42+00:00", + "version": "1.1", + "oscal-version": "1.1.2" + }, + "components": [ + { + "uuid": "c8106bc8-5174-4e86-91a4-52f2fe0ed027", + "type": "service", + "title": "Kubernetes", + "description": "Kubernetes", + "props": [ + { + "name": "Rule_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_key_file", + "remarks": "rule_set_00" + }, + { + "name": "Rule_Description", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "Ensure that the --key-file argument is set as appropriate", + "remarks": "rule_set_00" + }, + { + "name": "Parameter_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "file_name", + "remarks": "rule_set_00" + }, + { + "name": "Parameter_Description", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "A parameter for a file name", + "remarks": "rule_set_00" + }, + { + "name": "Rule_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_cert_file", + "remarks": "rule_set_01" + }, + { + "name": "Rule_Description", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "Ensure that the --cert-file argument is set as appropriate", + "remarks": "rule_set_01" + } + ], + "control-implementations": [ + { + "uuid": "f79d6290-8efa-4ea7-b931-27b8435cf707", + "source": "profiles/cis/profile.json", + "description": "CIS Profile", + "implemented-requirements": [ + { + "uuid": "a1b5b713-52c7-46fb-ab57-ebac7f576b23", + "control-id": "CIS-2.1", + "description": "", + "props": [ + { + "name": "Rule_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_cert_file" + }, + { + "name": "Rule_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_key_file" + } + ] + } + ] + } + ] + }, + { + "uuid": "701c70f1-482b-42b0-a419-9870158cd9e2", + "type": "validation", + "title": "Validator", + "description": "An example validation component", + "props": [ + { + "name": "Rule_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_cert_file", + "remarks": "rule_set_08" + }, + { + "name": "Rule_Description", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "Ensure that the --cert-file argument is set as appropriate", + "remarks": "rule_set_08" + }, + { + "name": "Check_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_cert_file", + "remarks": "rule_set_08" + }, + { + "name": "Check_Description", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "Check that the --cert-file argument is set as appropriate", + "remarks": "rule_set_08" + }, + { + "name": "Rule_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_key_file", + "remarks": "rule_set_09" + }, + { + "name": "Rule_Description", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "Ensure that the --key-file argument is set as appropriate", + "remarks": "rule_set_10" + }, + { + "name": "Check_Id", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "etcd_key_file", + "remarks": "rule_set_09" + }, + { + "name": "Check_Description", + "ns": "http://oscal-compass.github.io/compliance-trestle/schemas/oscal/cd", + "value": "Check that the --key-file argument is set as appropriate", + "remarks": "rule_set_09" + } + ] + } + ] + } +}