diff --git a/changes/22555-gitops-paths b/changes/22555-gitops-paths new file mode 100644 index 0000000000..f03618e303 --- /dev/null +++ b/changes/22555-gitops-paths @@ -0,0 +1 @@ +* GitOps script and software installer relative paths are now always relative to the file they're in. This change breaks existing YAML files that had to account for previous inconsistent behavior (e.g. script paths declared in no-team.yml being relative to default.yaml one directory up). \ No newline at end of file diff --git a/pkg/spec/gitops.go b/pkg/spec/gitops.go index bc52b9fa85..e124f7efa7 100644 --- a/pkg/spec/gitops.go +++ b/pkg/spec/gitops.go @@ -428,9 +428,11 @@ func parseControls(top map[string]json.RawMessage, result *GitOps, baseDir strin } controlsTop.Defined = true if controlsTop.Path == nil { + controlsTop.Scripts = resolvePaths(controlsTop.Scripts, baseDir) result.Controls = controlsTop } else { - fileBytes, err := os.ReadFile(resolveApplyRelativePath(baseDir, *controlsTop.Path)) + controlsFilePath := resolveApplyRelativePath(baseDir, *controlsTop.Path) + fileBytes, err := os.ReadFile(controlsFilePath) if err != nil { return multierror.Append(multiError, fmt.Errorf("failed to read controls file %s: %v", *controlsTop.Path, err)) } @@ -451,12 +453,27 @@ func parseControls(top map[string]json.RawMessage, result *GitOps, baseDir strin fmt.Errorf("nested paths are not supported: %s in %s", *pathControls.Path, *controlsTop.Path), ) } + + pathControls.Scripts = resolvePaths(pathControls.Scripts, filepath.Dir(controlsFilePath)) result.Controls = pathControls } } return multiError } +func resolvePaths(input []BaseItem, baseDir string) []BaseItem { + var resolved []BaseItem + for _, item := range input { + if item.Path != nil { + resolvedPath := resolveApplyRelativePath(baseDir, *item.Path) + item.Path = &resolvedPath + } + resolved = append(resolved, item) + } + + return resolved +} + func parsePolicies(top map[string]json.RawMessage, result *GitOps, baseDir string, multiError *multierror.Error) *multierror.Error { policiesRaw, ok := top["policies"] if !ok { @@ -479,7 +496,8 @@ func parsePolicies(top map[string]json.RawMessage, result *GitOps, baseDir strin } result.Policies = append(result.Policies, &item.GitOpsPolicySpec) } else { - fileBytes, err := os.ReadFile(resolveApplyRelativePath(baseDir, *item.Path)) + filePath := resolveApplyRelativePath(baseDir, *item.Path) + fileBytes, err := os.ReadFile(filePath) if err != nil { multiError = multierror.Append(multiError, fmt.Errorf("failed to read policies file %s: %v", *item.Path, err)) continue @@ -504,11 +522,11 @@ func parsePolicies(top map[string]json.RawMessage, result *GitOps, baseDir strin multiError, fmt.Errorf("nested paths are not supported: %s in %s", *pp.Path, *item.Path), ) } else { - if err := parsePolicyInstallSoftware(baseDir, result.TeamName, pp, result.Software.Packages); err != nil { + if err := parsePolicyInstallSoftware(filepath.Dir(filePath), result.TeamName, pp, result.Software.Packages); err != nil { multiError = multierror.Append(multiError, fmt.Errorf("failed to parse policy install_software %q: %v", pp.Name, err)) continue } - if err := parsePolicyRunScript(baseDir, result.TeamName, pp, result.Controls.Scripts); err != nil { + if err := parsePolicyRunScript(filepath.Dir(filePath), result.TeamName, pp, result.Controls.Scripts); err != nil { multiError = multierror.Append(multiError, fmt.Errorf("failed to parse policy run_script %q: %v", pp.Name, err)) continue } @@ -562,20 +580,23 @@ func parsePolicyRunScript(baseDir string, teamName *string, policy *Policy, scri return errors.New("empty run_script path") } - _, err := os.Stat(resolveApplyRelativePath(baseDir, policy.RunScript.Path)) + scriptPath := resolveApplyRelativePath(baseDir, policy.RunScript.Path) + _, err := os.Stat(scriptPath) if err != nil { return fmt.Errorf("script file does not exist %q: %v", policy.RunScript.Path, err) } scriptOnTeamFound := false + var foundScriptPaths []string for _, script := range scripts { - if policy.RunScript.Path == *script.Path { + foundScriptPaths = append(foundScriptPaths, *script.Path) + if scriptPath == *script.Path { scriptOnTeamFound = true break } } if !scriptOnTeamFound { - return fmt.Errorf("policy script not found on team: %s", policy.RunScript.Path) + return fmt.Errorf("policy script not found on team: %v vs. %v", foundScriptPaths, scriptPath) } scriptName := filepath.Base(policy.RunScript.Path) diff --git a/pkg/spec/gitops_test.go b/pkg/spec/gitops_test.go index 3e655ccf44..067e6c0bf5 100644 --- a/pkg/spec/gitops_test.go +++ b/pkg/spec/gitops_test.go @@ -262,7 +262,8 @@ func TestValidGitOpsYaml(t *testing.T) { assert.Equal(t, "🔥 Failing policy with script", gitops.Policies[7].Name) assert.NotNil(t, gitops.Policies[7].RunScript) - assert.Equal(t, "./lib/collect-fleetd-logs.sh", gitops.Policies[7].RunScript.Path) + // . or .. depending on whether with paths or without + assert.Contains(t, gitops.Policies[7].RunScript.Path, "./lib/collect-fleetd-logs.sh") } }, ) @@ -989,17 +990,17 @@ policies: config = getTeamConfig([]string{"policies"}) config += ` policies: - - path: ./script-policy.yml + - path: ./policies/script-policy.yml software: controls: scripts: - - path: ./top.policies2.yml + - path: ./policies/policies2.yml ` path, basePath := createTempFile(t, "", config) err = file.Copy( - filepath.Join("testdata", "script-policy.yml"), - filepath.Join(basePath, "script-policy.yml"), + filepath.Join("testdata", "policies", "script-policy.yml"), + filepath.Join(basePath, "policies", "script-policy.yml"), 0o755, ) require.NoError(t, err) diff --git a/pkg/spec/testdata/global_config.yml b/pkg/spec/testdata/global_config.yml index 0f9f52486f..255308ad21 100644 --- a/pkg/spec/testdata/global_config.yml +++ b/pkg/spec/testdata/global_config.yml @@ -13,8 +13,8 @@ queries: automations_enabled: true logging: snapshot policies: - - path: ./top.policies.yml - - path: ./top.policies2.yml + - path: ./policies/policies.yml + - path: ./policies/policies2.yml - path: ./empty.yml - name: 😊😊 Failing policy platform: linux diff --git a/pkg/spec/testdata/top.policies.yml b/pkg/spec/testdata/policies/policies.yml similarity index 100% rename from pkg/spec/testdata/top.policies.yml rename to pkg/spec/testdata/policies/policies.yml diff --git a/pkg/spec/testdata/top.policies2.yml b/pkg/spec/testdata/policies/policies2.yml similarity index 100% rename from pkg/spec/testdata/top.policies2.yml rename to pkg/spec/testdata/policies/policies2.yml diff --git a/pkg/spec/testdata/script-policy.yml b/pkg/spec/testdata/policies/script-policy.yml similarity index 85% rename from pkg/spec/testdata/script-policy.yml rename to pkg/spec/testdata/policies/script-policy.yml index 7bc4dca526..ac8108309f 100644 --- a/pkg/spec/testdata/script-policy.yml +++ b/pkg/spec/testdata/policies/script-policy.yml @@ -4,4 +4,4 @@ resolution: There is no resolution for this policy. query: SELECT 1 FROM osquery_info WHERE start_time < 0; run_script: - path: ./lib/collect-fleetd-logs.sh \ No newline at end of file + path: ../lib/collect-fleetd-logs.sh \ No newline at end of file diff --git a/pkg/spec/testdata/team_config.yml b/pkg/spec/testdata/team_config.yml index 2d088fd7a3..52a8b8d2df 100644 --- a/pkg/spec/testdata/team_config.yml +++ b/pkg/spec/testdata/team_config.yml @@ -17,8 +17,8 @@ queries: automations_enabled: true logging: snapshot policies: - - path: ./top.policies.yml - - path: ./top.policies2.yml + - path: ./policies/policies.yml + - path: ./policies/policies2.yml - path: ./empty.yml - name: 😊😊 Failing $POLICY platform: linux @@ -32,7 +32,7 @@ policies: query: SELECT * from osquery_info; run_script: path: ./lib/collect-fleetd-logs.sh - - path: ./script-policy.yml + - path: ./policies/script-policy.yml software: packages: - path: ./microsoft-teams.pkg.software.yml diff --git a/server/service/client.go b/server/service/client.go index d39bfe868b..0d568ba270 100644 --- a/server/service/client.go +++ b/server/service/client.go @@ -498,9 +498,8 @@ func (c *Client) ApplyGroup( } } if scripts := extractAppCfgScripts(specs.AppConfig); scripts != nil { - files := resolveApplyRelativePaths(baseDir, scripts) - scriptPayloads := make([]fleet.ScriptPayload, len(files)) - for i, f := range files { + scriptPayloads := make([]fleet.ScriptPayload, len(scripts)) + for i, f := range scripts { b, err := os.ReadFile(f) if err != nil { return nil, nil, nil, fmt.Errorf("applying fleet config: %w", err) @@ -575,9 +574,8 @@ func (c *Client) ApplyGroup( tmScripts := extractTmSpecsScripts(specs.Teams) tmScriptsPayloads := make(map[string][]fleet.ScriptPayload, len(tmScripts)) for k, paths := range tmScripts { - files := resolveApplyRelativePaths(baseDir, paths) - scriptPayloads := make([]fleet.ScriptPayload, len(files)) - for i, f := range files { + scriptPayloads := make([]fleet.ScriptPayload, len(paths)) + for i, f := range paths { b, err := os.ReadFile(f) if err != nil { return nil, nil, nil, fmt.Errorf("applying fleet config: %w", err)