Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions add.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func AddResource(fileName string, gossConfig GossConfig, resourceName, key strin
res, err = gossConfig.Interfaces.AppendSysResource(key, sys, config)
case resource.HTTPResourceName:
res, err = gossConfig.HTTPs.AppendSysResource(key, sys, config)
case resource.RegistryResourceName:
res, err = gossConfig.Registries.AppendSysResource(key, sys, config)
default:
err = fmt.Errorf("undefined resource name: %s", resourceName)
}
Expand Down
8 changes: 8 additions & 0 deletions cmd/goss/goss.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,14 @@ func main() {
return goss.AddResources(c.GlobalString("gossfile"), resource.InterfaceResourceName, c.Args(), newRuntimeConfigFromCLI(c))
},
},
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an FYI: #1060 will break this if/when it's merged. The fix is easy though: it's just an indentation change and Action: func(c *cli.Context) error { needs to become Action: func(_ context.Context, c *cli.Context) error {.

Name: resource.RegistryResourceKey,
Usage: "add new registry key",
Action: func(c *cli.Context) error {
fatalAlphaIfNeeded(c)
return goss.AddResources(c.GlobalString("gossfile"), resource.RegistryResourceName, c.Args(), newRuntimeConfigFromCLI(c))
},
},
},
},
}
Expand Down
4 changes: 4 additions & 0 deletions docs/platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ This matrix attempts to track parity across platforms.
| | mtu | {{ fully_supported }} | {{ not_implemented }} | {{ not_implemented }} |
| **kernel-param** | | {{ fully_supported }} | {{ n_a }} | {{ n_a }} |
| | value | {{ fully_supported }} | {{ n_a }} | {{ n_a }} |
| **registry** | | {{ n_a }} | {{ n_a }} | {{community_supported}} |
| | exists | {{ n_a }} | {{ n_a }} | {{community_supported}} |
| | value | {{ n_a }} | {{ n_a }} | {{community_supported}} |
| | type | {{ n_a }} | {{ n_a }} | {{community_supported}} |
| **mount** | | {{ fully_supported }} | {{ not_implemented }} | {{ not_implemented }} |
| | exists | {{ fully_supported }} | {{ not_implemented }} | {{ not_implemented }} |
| | opts | {{ fully_supported }} | {{ not_implemented }} | {{ n_a }} |
Expand Down
36 changes: 36 additions & 0 deletions docs/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,36 @@ definitions:
value:
type: string
default: Linux
registryTest:
description: |
Validates the state of a Windows registry key value.
The key format is HIVE\SubKey\Path\ValueName.
When the value name contains backslashes, use "::" as separator:
HIVE\SubKey\Path::ValueName (e.g. HKLM\...\HardenedPaths::\\*\NETLOGON).
Supported hives: HKLM, HKCU, HKCR, HKU, HKCC.
required:
- exists
properties:
title: { "$ref":"#/definitions/title" }
meta: { "$ref":"#/definitions/meta" }
exists:
type: boolean
default: true
value:
anyOf:
- type: string
- type: integer
default: "Windows"
type:
type: string
default: REG_SZ
enum:
- REG_SZ
- REG_EXPAND_SZ
- REG_DWORD
- REG_QWORD
- REG_BINARY
- REG_MULTI_SZ
matchingTest:
properties:
title: { "$ref":"#/definitions/title" }
Expand Down Expand Up @@ -819,3 +849,9 @@ properties:
NOTE: This check is inspecting the contents of local passwd file /etc/passwd, this does not validate remote users (e.g. LDAP).
additionalProperties:
$ref: "#/definitions/userTest"

registry:
type: object
description: "Validates the state of a Windows registry key value."
additionalProperties:
$ref: "#/definitions/registryTest"
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require (
github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.17.1
github.com/urfave/cli v1.22.14
golang.org/x/sys v0.23.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.1
)
Expand Down Expand Up @@ -54,7 +55,6 @@ require (
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.23.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
Expand Down
6 changes: 6 additions & 0 deletions goss_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type GossConfig struct {
Interfaces resource.InterfaceMap `json:"interface,omitempty" yaml:"interface,omitempty"`
HTTPs resource.HTTPMap `json:"http,omitempty" yaml:"http,omitempty"`
Matchings resource.MatchingMap `json:"matching,omitempty" yaml:"matching,omitempty"`
Registries resource.RegistryMap `json:"registry,omitempty" yaml:"registry,omitempty"`
}

func NewGossConfig() *GossConfig {
Expand All @@ -44,6 +45,7 @@ func NewGossConfig() *GossConfig {
Interfaces: make(resource.InterfaceMap),
HTTPs: make(resource.HTTPMap),
Matchings: make(resource.MatchingMap),
Registries: make(resource.RegistryMap),
}
}

Expand Down Expand Up @@ -109,6 +111,9 @@ func (c *GossConfig) Merge(g2 GossConfig) {
for k, v := range g2.Matchings {
mergeType(c.Matchings, "matching", k, v)
}
for k, v := range g2.Registries {
mergeType(c.Registries, "registry", k, v)
}
}

func mergeType[V any](m map[string]V, t, k string, v V) {
Expand Down Expand Up @@ -136,6 +141,7 @@ func (c *GossConfig) Resources() []resource.Resource {
c.Mounts,
c.Interfaces,
c.Matchings,
c.Registries,
)

for _, m := range gm {
Expand Down
20 changes: 20 additions & 0 deletions integration-tests/goss/windows/tests/registry.goss.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
registry:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName:
exists: true
value:
match-regexp: "Windows.*"
type: REG_SZ

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentBuild:
exists: true
type: REG_SZ

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NonExistentValue12345:
exists: false

# Test explicit "::" separator for value names containing backslashes.
# HardenedPaths entries use UNC paths as value names (e.g. \\*\NETLOGON).
HKLM\SOFTWARE\Policies\Microsoft\Windows\NetworkProvider\HardenedPaths::\\*\NETLOGON:
exists: true
skip: true # only present on domain-joined machines with GPO applied
93 changes: 93 additions & 0 deletions resource/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package resource

import (
"context"
"fmt"

"github.com/goss-org/goss/system"
"github.com/goss-org/goss/util"
)

type Registry struct {
Title string `json:"title,omitempty" yaml:"title,omitempty"`
Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"`
id string `json:"-" yaml:"-"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Exists matcher `json:"exists" yaml:"exists"`
Value matcher `json:"value,omitempty" yaml:"value,omitempty"`
Type matcher `json:"type,omitempty" yaml:"type,omitempty"`
Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"`
}

const (
RegistryResourceKey = "registry"
RegistryResourceName = "Registry"
)

func init() {
registerResource(RegistryResourceKey, &Registry{})
}

func (r *Registry) ID() string {
if r.Name != "" && r.Name != r.id {
return fmt.Sprintf("%s: %s", r.id, r.Name)
}
return r.id
}

func (r *Registry) SetID(id string) { r.id = id }
func (r *Registry) SetSkip() { r.Skip = true }
func (r *Registry) TypeKey() string { return RegistryResourceKey }
func (r *Registry) TypeName() string { return RegistryResourceName }
func (r *Registry) GetTitle() string { return r.Title }
func (r *Registry) GetMeta() meta { return r.Meta }
func (r *Registry) GetName() string {
if r.Name != "" {
return r.Name
}
return r.id
}

func (r *Registry) Validate(sys *system.System) []TestResult {
ctx := context.WithValue(context.Background(), idKey{}, r.ID())
skip := r.Skip
sysRegistry := sys.NewRegistry(ctx, r.GetName(), sys, util.Config{})

var results []TestResult
results = append(results, ValidateValue(r, "exists", r.Exists, sysRegistry.Exists, skip))
if shouldSkip(results) {
skip = true
}
if r.Value != nil {
results = append(results, ValidateValue(r, "value", r.Value, sysRegistry.Value, skip))
}
if r.Type != nil {
results = append(results, ValidateValue(r, "type", r.Type, sysRegistry.Type, skip))
}
return results
}

func NewRegistry(sysRegistry system.Registry, config util.Config) (*Registry, error) {
key := sysRegistry.Key()
exists, _ := sysRegistry.Exists()
if !exists {
return &Registry{
id: key,
Exists: exists,
}, nil
}
value, err := sysRegistry.Value()
if err != nil {
return nil, err
}
regType, err := sysRegistry.Type()
if err != nil {
return nil, err
}
return &Registry{
id: key,
Exists: exists,
Value: value,
Type: regType,
}, nil
}
98 changes: 98 additions & 0 deletions resource/resource_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -1529,3 +1529,101 @@ func (ret *HTTPMap) UnmarshalYAML(unmarshal func(v interface{}) error) error {
*ret = tmp
return nil
}

type RegistryMap map[string]*Registry

func (r RegistryMap) AppendSysResource(sr string, sys *system.System, config util.Config) (*Registry, error) {
ctx := context.WithValue(context.Background(), idKey{}, sr)
sysres := sys.NewRegistry(ctx, sr, sys, config)
res, err := NewRegistry(sysres, config)
if err != nil {
return nil, err
}
if old_res, ok := r[res.ID()]; ok {
res.Title = old_res.Title
res.Meta = old_res.Meta
}
r[res.ID()] = res
return res, nil
}

func (r RegistryMap) AppendSysResourceIfExists(sr string, sys *system.System) (*Registry, system.Registry, bool, error) {
ctx := context.WithValue(context.Background(), idKey{}, sr)
sysres := sys.NewRegistry(ctx, sr, sys, util.Config{})
res, err := NewRegistry(sysres, util.Config{})
if err != nil {
return nil, nil, false, err
}
if e, _ := sysres.Exists(); !e {
return res, sysres, false, nil
}
if old_res, ok := r[res.ID()]; ok {
res.Title = old_res.Title
res.Meta = old_res.Meta
}
r[res.ID()] = res
return res, sysres, true, nil
}

func (ret *RegistryMap) UnmarshalJSON(data []byte) error {
unmarshal := func(i interface{}) error {
if err := json.Unmarshal(data, i); err != nil {
return err
}
return nil
}

zero := Registry{}
whitelist, err := util.WhitelistAttrs(zero, util.JSON)
if err != nil {
return err
}
if err := util.ValidateSections(unmarshal, zero, whitelist); err != nil {
return err
}

var tmp map[string]*Registry
if err := unmarshal(&tmp); err != nil {
return err
}

typ := reflect.TypeOf(zero)
typs := strings.Split(typ.String(), ".")[1]
for id, res := range tmp {
if res == nil {
return fmt.Errorf("Could not parse resource %s:%s", typs, id)
}
res.SetID(id)
}

*ret = tmp
return nil
}

func (ret *RegistryMap) UnmarshalYAML(unmarshal func(v interface{}) error) error {
zero := Registry{}
whitelist, err := util.WhitelistAttrs(zero, util.YAML)
if err != nil {
return err
}
if err := util.ValidateSections(unmarshal, zero, whitelist); err != nil {
return err
}

var tmp map[string]*Registry
if err := unmarshal(&tmp); err != nil {
return err
}

typ := reflect.TypeOf(zero)
typs := strings.Split(typ.String(), ".")[1]
for id, res := range tmp {
if res == nil {
return fmt.Errorf("Could not parse resource %s:%s", typs, id)
}
res.SetID(id)
}

*ret = tmp
return nil
}
2 changes: 1 addition & 1 deletion resource/resource_list_genny.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/goss-org/goss/util"
)

//go:generate genny -in=$GOFILE -out=resource_list.go gen "ResourceType=Addr,Command,DNS,File,Gossfile,Group,Package,Port,Process,Service,User,KernelParam,Mount,Interface,HTTP"
//go:generate genny -in=$GOFILE -out=resource_list.go gen "ResourceType=Addr,Command,DNS,File,Gossfile,Group,Package,Port,Process,Service,User,KernelParam,Mount,Interface,HTTP,Registry"
//go:generate sed -i -e "/^\\/\\/ +build genny/d" resource_list.go
//go:generate sed -i -e "/^\\/\\/go:.*/d" resource_list.go
//go:generate sed -i -e "s/aelsabbahy/goss-org/" resource_list.go
Expand Down
Loading