Exclude certain sections from var interpolation when running gitops cmd (#28625)
Fixes #27477 When running the gitops command do not perform variable interpolation inside the 'description' nor the 'resolution' sections.
This commit is contained in:
+42
-2
@@ -178,8 +178,11 @@ func expandEnv(s string, failOnSecret bool) (string, error) {
|
||||
}
|
||||
|
||||
s = escapeString(s, preventEscapingPrefix)
|
||||
exclusionZones := getExclusionZones(s)
|
||||
|
||||
var err *multierror.Error
|
||||
s = fleet.MaybeExpand(s, func(env string) (string, bool) {
|
||||
s = fleet.MaybeExpand(s, func(env string, startPos, endPos int) (string, bool) {
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(env, preventEscapingPrefix):
|
||||
return "$" + strings.TrimPrefix(env, preventEscapingPrefix), true
|
||||
@@ -193,6 +196,15 @@ func expandEnv(s string, failOnSecret bool) (string, error) {
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Don't expand fleet vars if they are inside an 'exclusion' zone,
|
||||
// i.e. 'description' or 'resolution'....
|
||||
for _, z := range exclusionZones {
|
||||
if startPos >= z[0] && endPos <= z[1] {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
v, ok := os.LookupEnv(env)
|
||||
if !ok {
|
||||
err = multierror.Append(err, fmt.Errorf("environment variable %q not set", env))
|
||||
@@ -230,7 +242,7 @@ func LookupEnvSecrets(s string, secretsMap map[string]string) error {
|
||||
return errors.New("secretsMap cannot be nil")
|
||||
}
|
||||
var err *multierror.Error
|
||||
_ = fleet.MaybeExpand(s, func(env string) (string, bool) {
|
||||
_ = fleet.MaybeExpand(s, func(env string, startPos, endPos int) (string, bool) {
|
||||
if strings.HasPrefix(env, fleet.ServerSecretPrefix) {
|
||||
// lookup the secret and save it, but don't replace
|
||||
v, ok := os.LookupEnv(env)
|
||||
@@ -258,3 +270,31 @@ func escapeString(s string, preventEscapingPrefix string) string {
|
||||
return strings.Repeat("\\", (len(match)/2)-1) + "$" + preventEscapingPrefix
|
||||
})
|
||||
}
|
||||
|
||||
// getExclusionZones returns which positions inside 's' should be
|
||||
// excluded from variable interpolation.
|
||||
func getExclusionZones(s string) [][2]int {
|
||||
// We need a different pattern per section because
|
||||
// the delimiting end pattern ((?:^\s+\w+:|\z)) includes the next
|
||||
// section token, meaning the matching logic won't work in case
|
||||
// we have a 'resolution:' followed by a 'description:' or
|
||||
// vice versa, and we try using something like (?:resolution:|description:)
|
||||
toExclude := []string{
|
||||
"resolution",
|
||||
"description",
|
||||
}
|
||||
patterns := make([]*regexp.Regexp, 0, len(toExclude))
|
||||
for _, e := range toExclude {
|
||||
pattern := fmt.Sprintf(`(?m)^\s*(?:%s:)(.|[\r\n])*?(?:^\s+\w+:|\z)`, e)
|
||||
patterns = append(patterns, regexp.MustCompile(pattern))
|
||||
}
|
||||
|
||||
var zones [][2]int
|
||||
for _, pattern := range patterns {
|
||||
result := pattern.FindAllStringIndex(s, -1)
|
||||
for _, r := range result {
|
||||
zones = append(zones, [2]int{r[0], r[1]})
|
||||
}
|
||||
}
|
||||
return zones
|
||||
}
|
||||
|
||||
@@ -223,3 +223,54 @@ func TestLookupEnvSecrets(t *testing.T) {
|
||||
require.Equal(t, tc.expResult, secretsMap)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExclusionZones(t *testing.T) {
|
||||
testCases := []struct {
|
||||
fixturePath []string
|
||||
expected map[[2]int]string
|
||||
}{
|
||||
{
|
||||
[]string{"testdata", "policies", "policies.yml"},
|
||||
map[[2]int]string{
|
||||
[2]int{46, 106}: " description: This policy should always fail.\n resolution:",
|
||||
[2]int{93, 155}: " resolution: There is no resolution for this policy.\n query:",
|
||||
[2]int{268, 328}: " description: This policy should always pass.\n resolution:",
|
||||
[2]int{315, 678}: " resolution: |\n Automated method:\n Ask your system administrator to deploy the following script which will ensure proper Security Auditing Retention:\n cp /etc/security/audit_control ./tmp.txt; origExpire=$(cat ./tmp.txt | grep expire-after); sed \"s/${origExpire}/expire-after:60d OR 5G/\" ./tmp.txt > /etc/security/audit_control; rm ./tmp.txt;\n query:",
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{"testdata", "global_config_no_paths.yml"},
|
||||
map[[2]int]string{
|
||||
[2]int{866, 949}: " description: Collect osquery performance stats directly from osquery\n query:", //
|
||||
[2]int{1754, 1818}: " description: This policy should always fail.\n resolution:", //
|
||||
[2]int{1803, 1869}: " resolution: There is no resolution for this policy.\n query:", //
|
||||
[2]int{1986, 2050}: " description: This policy should always pass.\n resolution:", //
|
||||
[2]int{2035, 2101}: " resolution: There is no resolution for this policy.\n query:", //
|
||||
[2]int{2394, 2458}: " description: This policy should always fail.\n resolution:", //
|
||||
[2]int{2443, 2509}: " resolution: There is no resolution for this policy.\n query:", //
|
||||
[2]int{2613, 2677}: " description: This policy should always fail.\n resolution:", //
|
||||
[2]int{2662, 3035}: " resolution: |\n Automated method:\n Ask your system administrator to deploy the following script which will ensure proper Security Auditing Retention:\n cp /etc/security/audit_control ./tmp.txt; origExpire=$(cat ./tmp.txt | grep expire-after); sed \"s/${origExpire}/expire-after:60d OR 5G/\" ./tmp.txt > /etc/security/audit_control; rm ./tmp.txt;\n query:",
|
||||
[2]int{6102, 6149}: " description: A cool global label\n query:", //
|
||||
[2]int{6246, 6292}: " description: A fly global label\n hosts:", //
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tC := range testCases {
|
||||
fPath := filepath.Join(tC.fixturePath...)
|
||||
|
||||
t.Run(fPath, func(t *testing.T) {
|
||||
fContents, err := os.ReadFile(fPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
contents := string(fContents)
|
||||
actual := getExclusionZones(contents)
|
||||
require.Equal(t, len(tC.expected), len(actual))
|
||||
|
||||
for pos, text := range tC.expected {
|
||||
require.Contains(t, actual, pos)
|
||||
require.Equal(t, contents[pos[0]:pos[1]], text, pos)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -84,8 +84,11 @@ policies:
|
||||
- name: 😊😊 Failing policy
|
||||
platform: linux
|
||||
description: This policy should always fail.
|
||||
resolution: There is no resolution for this policy.
|
||||
query: SELECT 1 FROM osquery_info WHERE start_time < 0;
|
||||
resolution: |
|
||||
Automated method:
|
||||
Ask your system administrator to deploy the following script which will ensure proper Security Auditing Retention:
|
||||
cp /etc/security/audit_control ./tmp.txt; origExpire=$(cat ./tmp.txt | grep expire-after); sed "s/${origExpire}/expire-after:60d OR 5G/" ./tmp.txt > /etc/security/audit_control; rm ./tmp.txt;
|
||||
query: SELECT 1;
|
||||
agent_options:
|
||||
command_line_flags:
|
||||
distributed_denylist_duration: 0
|
||||
|
||||
+5
-2
@@ -6,11 +6,14 @@
|
||||
- name: Passing policy
|
||||
platform: linux,windows,darwin,chrome
|
||||
description: This policy should always pass.
|
||||
resolution: There is no resolution for this policy.
|
||||
resolution: |
|
||||
Automated method:
|
||||
Ask your system administrator to deploy the following script which will ensure proper Security Auditing Retention:
|
||||
cp /etc/security/audit_control ./tmp.txt; origExpire=$(cat ./tmp.txt | grep expire-after); sed "s/${origExpire}/expire-after:60d OR 5G/" ./tmp.txt > /etc/security/audit_control; rm ./tmp.txt;
|
||||
query: SELECT 1;
|
||||
- name: No root logins (macOS, Linux)
|
||||
platform: linux,darwin
|
||||
query: SELECT 1 WHERE NOT EXISTS (SELECT * FROM last
|
||||
WHERE username = "root"
|
||||
AND time > (( SELECT unix_time FROM time ) - 3600 ))
|
||||
critical: true
|
||||
critical: true
|
||||
Reference in New Issue
Block a user