Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aeaconf2 integration #178

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
89 changes: 44 additions & 45 deletions checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,21 @@ import (
"reflect"
"regexp"
"strings"

"github.com/safinsingh/aeaconf2"
)

// check is the smallest unit that can show up on a scoring report. It holds all
// the conditions for a check, and its message and points (autogenerated or
// otherwise).
type check struct {
Message string
Hint string
Points int

Fail []cond
Pass []cond
PassOverride []cond
}
var funcRegistry map[string]reflect.Type
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can init this in var with type inference eg var funcRegistry = make(map[string]reflect.Type). Having a map to a reflect type smells really bad though LOL I'll have to understand the code


// cond, or condition, is the parameters for a given test within a check.
type cond struct {
Hint string
Type string
func init() {
funcRegistry = make(map[string]reflect.Type)

Path string
Cmd string
User string
Group string
Name string
Key string
Value string
After string
regex bool
funcRegistry["AutoCheckUpdatesEnabled"] = reflect.TypeOf(AutoCheckUpdatesEnabled{})
funcRegistry["DirContains"] = reflect.TypeOf(DirContains{})
funcRegistry["FileContains"] = reflect.TypeOf(FileContains{})
funcRegistry["PathExists"] = reflect.TypeOf(PathExists{})

aeaconf2.CheckFunctionRegistry(funcRegistry)
}

// requireArgs is a convenience function that prints a warning if any required
Expand Down Expand Up @@ -104,11 +90,11 @@ func handleReflectPanic(condFunc string) {
}

// runCheck executes a single condition check.
func runCheck(cond cond) bool {
if err := deobfuscateCond(&cond); err != nil {
func runCheck(cond aeaconf2.Condition) bool {
if err := deobfuscateCond(cond); err != nil {
fail(err.Error())
}
defer obfuscateCond(&cond)
defer obfuscateCond(cond)
debug("Running condition:\n", cond)

not := "Not"
Expand Down Expand Up @@ -178,11 +164,15 @@ func (c cond) CommandOutput() (bool, error) {
}

// DirContains returns true if any file in the directory contains the string value provided.
func (c cond) DirContains() (bool, error) {
c.requireArgs("Path", "Value")
result, err := cond{
Path: c.Path,
}.PathExists()
type DirContains struct {
aeaconf2.BaseCondition
Path string
Value string
Regex bool
}

func (d *DirContains) Score() (bool, error) {
result, err := (&PathExists{Path: d.Path}).Score()
if err != nil {
return false, err
}
Expand All @@ -191,7 +181,7 @@ func (c cond) DirContains() (bool, error) {
}

var files []string
err = filepath.Walk(c.Path, func(path string, info os.FileInfo, err error) error {
err = filepath.Walk(d.Path, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
files = append(files, path)
}
Expand All @@ -206,8 +196,7 @@ func (c cond) DirContains() (bool, error) {
}

for _, file := range files {
c.Path = file
result, err := c.FileContains()
result, err := (&FileContains{Path: file, Value: d.Value}).Score()
if os.IsPermission(err) {
return false, err
}
Expand All @@ -222,21 +211,27 @@ func (c cond) DirContains() (bool, error) {
//
// Newlines in regex may not work as expected, especially on Windows. It's
// best to not use these (ex. ^ and $).
func (c cond) FileContains() (bool, error) {
c.requireArgs("Path", "Value")
fileContent, err := readFile(c.Path)
type FileContains struct {
aeaconf2.BaseCondition
Path string
Value string
Regex bool
}

func (f *FileContains) Score() (bool, error) {
fileContent, err := readFile(f.Path)
if err != nil {
return false, err
}
found := false
for _, line := range strings.Split(fileContent, "\n") {
if c.regex {
found, err = regexp.Match(c.Value, []byte(line))
if f.Regex {
found, err = regexp.Match(f.Value, []byte(line))
if err != nil {
return false, err
}
} else {
found = strings.Contains(line, c.Value)
found = strings.Contains(line, f.Value)
}
if found {
break
Expand Down Expand Up @@ -264,9 +259,13 @@ func (c cond) FileEquals() (bool, error) {

// PathExists is a wrapper around os.Stat and os.IsNotExist, and determines
// whether a file or folder exists.
func (c cond) PathExists() (bool, error) {
c.requireArgs("Path")
_, err := os.Stat(c.Path)
type PathExists struct {
aeaconf2.BaseCondition
Path string
}

func (p *PathExists) Score() (bool, error) {
_, err := os.Stat(p.Path)
if err != nil && os.IsNotExist(err) {
return false, nil
} else if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions checks_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import (
"syscall"
)

func (c cond) AutoCheckUpdatesEnabled() (bool, error) {
result, err := cond{
type AutoCheckUpdatesEnabled struct{}

func (a *AutoCheckUpdatesEnabled) Score() (bool, error) {
result, err := DirContains{
Path: "/etc/apt/apt.conf.d/",
Value: `(?i)^\s*APT::Periodic::Update-Package-Lists\s+"1"\s*;\s*$`,
regex: true,
}.DirContains()
}.Score()
// If /etc/apt/ does not exist, try dnf (RHEL)
if err != nil {
autoConf, err := cond{
Expand Down
129 changes: 61 additions & 68 deletions configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"reflect"

"github.com/BurntSushi/toml"
"github.com/safinsingh/aeaconf2"
"github.com/safinsingh/aeaconf2/compat"
)

// parseConfig takes the config content as a string and attempts to parse it
Expand All @@ -17,17 +19,26 @@ func parseConfig(configContent string) {
fail("Configuration is empty!")
os.Exit(1)
}
md, err := toml.Decode(configContent, &conf)

headerRaw, checksRaw, err := compat.SeparateConfig([]byte(configContent))
if err != nil {
fail("Error decoding TOML: " + err.Error())
fail("error separating config file: " + err.Error())
os.Exit(1)
}
if verboseEnabled {
for _, undecoded := range md.Undecoded() {
warn("Undecoded scoring configuration key \"" + undecoded.String() + "\" will not be used.")
}

cfg := new(config)
err = toml.Unmarshal(headerRaw, cfg)
if err != nil {
fail("error parsing config file header: " + err.Error())
os.Exit(1)
}

ab := aeaconf2.DefaultAeaconfBuilder(checksRaw, funcRegistry).
SetLineOffset(countLines(headerRaw)).
SetMaxPoints(cfg.MaxPoints)

cfg.Checks = ab.GetChecks()

// If there's no remote, local must be enabled.
if conf.Remote == "" {
conf.Local = true
Expand Down Expand Up @@ -62,19 +73,6 @@ func parseConfig(configContent string) {
info("Consider updating your config to include:")
info(" version = '" + version + "'")
}

// Print warnings for impossible checks and undefined check types.
for i, check := range conf.Check {
if len(check.Pass) == 0 && len(check.PassOverride) == 0 {
warn("Check " + fmt.Sprintf("%d", i+1) + " does not define any possible ways to pass!")
}
allConditions := append(append(append([]cond{}, check.Pass[:]...), check.Fail[:]...), check.PassOverride[:]...)
for j, cond := range allConditions {
if cond.Type == "" {
warn("Check " + fmt.Sprintf("%d condition %d", i+1, j+1) + " does not have a check type!")
}
}
}
}

// writeConfig writes the in-memory config to disk as the an encrypted
Expand Down Expand Up @@ -157,21 +155,8 @@ func printConfig() {
if conf.EndDate != "" {
pass("End Date:", conf.EndDate)
}
for i, check := range conf.Check {
green("CHCK", fmt.Sprintf("Check %d (%d points):", i+1, check.Points))
fmt.Println("Message:", check.Message)
for _, c := range check.Pass {
fmt.Println("Pass Condition:")
fmt.Print(c)
}
for _, c := range check.PassOverride {
fmt.Println("PassOverride Condition:")
fmt.Print(c)
}
for _, c := range check.Fail {
fmt.Println("Fail Condition:")
fmt.Print(c)
}
for i, check := range conf.Checks {
green("CHCK", fmt.Sprintf("Check %d: %s", i+1, check.Debug()))
}
}

Expand All @@ -182,64 +167,72 @@ func obfuscateConfig() {
if err := obfuscateData(&conf.Password); err != nil {
fail(err.Error())
}
for i, check := range conf.Check {
if err := obfuscateData(&conf.Check[i].Message); err != nil {
for i := range conf.Checks {
if err := obfuscateData(&conf.Checks[i].Message); err != nil {
fail(err.Error())
}
if conf.Check[i].Hint != "" {
if err := obfuscateData(&conf.Check[i].Hint); err != nil {
fail(err.Error())
}
}
for j := range check.Pass {
if err := obfuscateCond(&conf.Check[i].Pass[j]); err != nil {
if conf.Checks[i].Hint != "" {
if err := obfuscateData(&conf.Checks[i].Hint); err != nil {
fail(err.Error())
}
}
for j := range check.PassOverride {
if err := obfuscateCond(&conf.Check[i].PassOverride[j]); err != nil {
fail(err.Error())
}
}
for j := range check.Fail {
if err := obfuscateCond(&conf.Check[i].Fail[j]); err != nil {
fail(err.Error())
}
if err := obfuscateCond(conf.Checks[i].Condition); err != nil {
fail(err.Error())
}
}
}

// obfuscateCond is a convenience function to obfuscate all string fields of a
// struct using reflection. It assumes all struct fields are strings.
func obfuscateCond(c *cond) error {

// ummmmmm
func obfuscateCond(c aeaconf2.Condition) error {
s := reflect.ValueOf(c).Elem()
t := s.Type()

for i := 0; i < s.NumField(); i++ {
if s.Type().Field(i).Name == "regex" {
continue
}
datum := s.Field(i).String()
if err := obfuscateData(&datum); err != nil {
return err
field := s.Field(i)
fieldType := t.Field(i).Type

if fieldType.Kind() == reflect.String {
datum := field.String()
if err := obfuscateData(&datum); err != nil {
return err
}
field.SetString(datum)
} else if fieldType.Implements(reflect.TypeOf((*aeaconf2.Condition)(nil)).Elem()) {
if err := obfuscateCond(field.Addr().Interface().(aeaconf2.Condition)); err != nil {
return err
}
}
s.Field(i).SetString(datum)
}

return nil
}

// deobfuscateCond is a convenience function to deobfuscate all string fields
// of a struct using reflection.
func deobfuscateCond(c *cond) error {
func deobfuscateCond(c aeaconf2.Condition) error {
s := reflect.ValueOf(c).Elem()
t := s.Type()

for i := 0; i < s.NumField(); i++ {
if s.Type().Field(i).Name == "regex" {
continue
}
datum := s.Field(i).String()
if err := deobfuscateData(&datum); err != nil {
return err
field := s.Field(i)
fieldType := t.Field(i).Type

if fieldType.Kind() == reflect.String {
datum := field.String()
if err := deobfuscateData(&datum); err != nil {
return err
}
field.SetString(datum)
} else if fieldType.Implements(reflect.TypeOf((*aeaconf2.Condition)(nil)).Elem()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This may look better as a function on the aeaconf struct. Maybe something like ModifyFields that takes a func(string) string as a parameter? Then we don't have to do the Implements stuff

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I could probably just do that on the aeaconf side

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added on v0.1.2 (compat.ModifyConditionStrings)

if err := deobfuscateCond(field.Addr().Interface().(aeaconf2.Condition)); err != nil {
return err
}
}
s.Field(i).SetString(datum)
}

return nil
}

Expand Down
Loading