chore: Prevent users overriding configsource
This commit is contained in:
parent
8b8b422c19
commit
b000e82238
|
|
@ -40,7 +40,7 @@ type Action struct {
|
||||||
Groups []string `koanf:"groups"`
|
Groups []string `koanf:"groups"`
|
||||||
Justification string `koanf:"justification"`
|
Justification string `koanf:"justification"`
|
||||||
// SourceFile is set by OliveTin when loading config (not user YAML).
|
// SourceFile is set by OliveTin when loading config (not user YAML).
|
||||||
SourceFile string `koanf:"x-olivetin-source-file"`
|
SourceFile string `koanf:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (action *Action) RequiresJustification() bool {
|
func (action *Action) RequiresJustification() bool {
|
||||||
|
|
@ -113,7 +113,7 @@ type EntityFile struct {
|
||||||
Icon string `koanf:"icon"`
|
Icon string `koanf:"icon"`
|
||||||
Properties []EntityProperty `koanf:"properties"`
|
Properties []EntityProperty `koanf:"properties"`
|
||||||
// SourceFile is set by OliveTin when loading config (not user YAML).
|
// SourceFile is set by OliveTin when loading config (not user YAML).
|
||||||
SourceFile string `koanf:"x-olivetin-source-file"`
|
SourceFile string `koanf:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntityProperty defines a column shown when listing entity instances in the UI.
|
// EntityProperty defines a column shown when listing entity instances in the UI.
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ func AppendSource(cfg *Config, k *koanf.Koanf, configPath string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyStampedSourceFiles(k, cfg)
|
||||||
afterLoadFinalize(cfg, configPath)
|
afterLoadFinalize(cfg, configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import (
|
||||||
const sourceFileKey = "x-olivetin-source-file"
|
const sourceFileKey = "x-olivetin-source-file"
|
||||||
|
|
||||||
// stampSourceOnMaps sets the OliveTin source-file marker on each map in a
|
// stampSourceOnMaps sets the OliveTin source-file marker on each map in a
|
||||||
// koanf slice value (actions or entities).
|
// koanf slice value (actions or entities). Always overwrites so user-provided
|
||||||
|
// x-olivetin-source-file values cannot spoof the real config path.
|
||||||
func stampSourceOnMaps(raw any, sourceFile string) any {
|
func stampSourceOnMaps(raw any, sourceFile string) any {
|
||||||
if sourceFile == "" {
|
if sourceFile == "" {
|
||||||
return raw
|
return raw
|
||||||
|
|
@ -30,9 +31,6 @@ func stampSourceOnMap(item any, sourceFile string) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if existing, ok := m[sourceFileKey].(string); ok && existing != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
m[sourceFileKey] = sourceFile
|
m[sourceFileKey] = sourceFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,3 +47,50 @@ func stampConfigKey(k *koanf.Koanf, key, configPath string) {
|
||||||
}).Errorf("Failed to persist source stamps: %v", err)
|
}).Errorf("Failed to persist source stamps: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyStampedSourceFiles copies stamped source paths from koanf maps onto
|
||||||
|
// unmarshaled actions/entities. SourceFile uses koanf:"-" so YAML cannot set it.
|
||||||
|
func applyStampedSourceFiles(k *koanf.Koanf, cfg *Config) {
|
||||||
|
actionPaths := stampedSourcePaths(k.Get("actions"))
|
||||||
|
for i, action := range cfg.Actions {
|
||||||
|
if action != nil {
|
||||||
|
action.SourceFile = sourcePathAt(actionPaths, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entityPaths := stampedSourcePaths(k.Get("entities"))
|
||||||
|
for i, entity := range cfg.Entities {
|
||||||
|
if entity != nil {
|
||||||
|
entity.SourceFile = sourcePathAt(entityPaths, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sourcePathAt(paths []string, index int) string {
|
||||||
|
if index >= len(paths) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return paths[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func stampedSourcePaths(raw any) []string {
|
||||||
|
items, ok := raw.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := make([]string, len(items))
|
||||||
|
for i, item := range items {
|
||||||
|
paths[i] = stampedSourceFromMap(item)
|
||||||
|
}
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
func stampedSourceFromMap(item any) string {
|
||||||
|
m, ok := item.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
path, _ := m[sourceFileKey].(string)
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,3 +49,32 @@ entities:
|
||||||
require.Len(t, cfg.Entities, 1)
|
require.Len(t, cfg.Entities, 1)
|
||||||
assert.Equal(t, includePath, cfg.Entities[0].SourceFile)
|
assert.Equal(t, includePath, cfg.Entities[0].SourceFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAppendSourceIgnoresUserProvidedSourceFile(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
basePath := filepath.Join(dir, "config.yaml")
|
||||||
|
require.NoError(t, os.WriteFile(basePath, []byte(`
|
||||||
|
actions:
|
||||||
|
- title: Spoofed
|
||||||
|
shell: echo hi
|
||||||
|
x-olivetin-source-file: /tmp/fake-user-path.yaml
|
||||||
|
entities:
|
||||||
|
- name: host
|
||||||
|
file: hosts.yaml
|
||||||
|
x-olivetin-source-file: /tmp/fake-entity-path.yaml
|
||||||
|
`), 0o644))
|
||||||
|
|
||||||
|
k := koanf.New(".")
|
||||||
|
require.NoError(t, k.Load(file.Provider(basePath), yaml.Parser()))
|
||||||
|
|
||||||
|
cfg := config.DefaultConfig()
|
||||||
|
config.AppendSource(cfg, k, basePath)
|
||||||
|
|
||||||
|
require.Len(t, cfg.Actions, 1)
|
||||||
|
assert.Equal(t, basePath, cfg.Actions[0].SourceFile)
|
||||||
|
assert.NotEqual(t, "/tmp/fake-user-path.yaml", cfg.Actions[0].SourceFile)
|
||||||
|
|
||||||
|
require.Len(t, cfg.Entities, 1)
|
||||||
|
assert.Equal(t, basePath, cfg.Entities[0].SourceFile)
|
||||||
|
assert.NotEqual(t, "/tmp/fake-entity-path.yaml", cfg.Entities[0].SourceFile)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue