Skip to content

Commit 23d0265

Browse files
committed
harden isAllowedDir
1 parent b06c66b commit 23d0265

File tree

4 files changed

+49
-20
lines changed

4 files changed

+49
-20
lines changed

internal/config/config_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,8 +1037,8 @@ func agentConfig() *Config {
10371037
},
10381038
},
10391039
AllowedDirectories: []string{
1040-
"/etc/nginx/", "/etc/nginx-agent/", "/usr/local/etc/nginx/", "/var/run/nginx/", "/var/log/nginx/",
1041-
"/usr/share/nginx/modules/", "/etc/app_protect/",
1040+
"/etc/nginx/", "/etc/nginx-agent/", "/usr/local/etc/nginx", "/var/run/nginx", "/var/log/nginx",
1041+
"/usr/share/nginx/modules", "/etc/app_protect",
10421042
},
10431043
Collector: createDefaultCollectorConfig(),
10441044
Command: &Command{

internal/config/types.go

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import (
1010
"errors"
1111
"fmt"
1212
"log/slog"
13+
"os"
1314
"path/filepath"
14-
"regexp"
15+
"slices"
1516
"strings"
1617
"time"
1718

@@ -396,8 +397,9 @@ func (nr *NginxReceiver) Validate(allowedDirectories []string) error {
396397
return err
397398
}
398399

399-
func (c *Config) IsDirectoryAllowed(directory string) bool {
400-
allow, err := isAllowedDir(directory, c.AllowedDirectories)
400+
// IsAllowedDirectory checks if the given path is in the list of allowed directories.
401+
func (c *Config) IsDirectoryAllowed(path string) bool {
402+
allow, err := isAllowedDir(path, c.AllowedDirectories)
401403
if err != nil {
402404
slog.Warn("Unable to determine if directory is allowed", "error", err)
403405
return false
@@ -480,29 +482,40 @@ func (c *Config) IsCommandServerProxyConfigured() bool {
480482
}
481483

482484
// isAllowedDir checks if the given path is in the list of allowed directories.
483-
// It returns true if the path is allowed, false otherwise.
484-
// If the path is allowed but does not exist, it also logs a warning.
485-
// It also checks if the path is a file, in which case it checks the parent directory of the file.
485+
// It works on files and directories.
486+
// If the path is a file, it checks the directory of the file.
487+
// If the path is a directory, it checks the directory itself.
488+
// It also allows subdirectories within the allowed directories.
486489
func isAllowedDir(path string, allowedDirs []string) (bool, error) {
487490
if len(allowedDirs) == 0 {
488491
return false, errors.New("no allowed directories configured")
489492
}
490493

491-
directoryPath := path
492-
// Check if the path is a file, regex matches when end of string is /<filename>.<extension>
493-
isFilePath, err := regexp.MatchString(`/(\w+)\.(\w+)$`, directoryPath)
494-
if err != nil {
495-
return false, errors.New("error matching path" + directoryPath)
494+
// Check if the path is absolute
495+
if !filepath.IsAbs(path) {
496+
return false, fmt.Errorf("PathError: %s is not absolute", path)
496497
}
497498

498-
if isFilePath {
499-
directoryPath = filepath.Dir(directoryPath)
499+
// Does the path exist?
500+
info, err := os.Stat(path)
501+
if err == nil && !info.IsDir() {
502+
// Path exists and is not a directory
503+
// It's a file so get the parent directory
504+
path = filepath.Dir(path)
505+
}
506+
if err != nil {
507+
if os.IsNotExist(err) {
508+
// Path does not exist, we can still check if it's an allowed directory
509+
} else {
510+
// Some other error occurred
511+
return false, fmt.Errorf("PathError %s %w", path, err)
512+
}
500513
}
501514

502-
for _, allowedDirectory := range allowedDirs {
503-
// Check if the directoryPath starts with the allowedDirectory
504-
// This allows for subdirectories within the allowed directories.
505-
if strings.HasPrefix(directoryPath, allowedDirectory) {
515+
for _, dir := range allowedDirs {
516+
// Check if the path is a subdirectory of the allowed directory
517+
if slices.Contains(allowedDirs, path) || strings.HasPrefix(path, dir+"/") {
518+
slog.Info(fmt.Sprintf("Allowed directory %s is allowed", path))
506519
return true, nil
507520
}
508521
}

internal/config/types_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,22 @@ func TestTypes_isAllowedDir(t *testing.T) {
6666
},
6767
filePath: "/not-nginx-test/idontexist.conf",
6868
},
69+
{
70+
name: "Test 7: File is in allowed directory with trailing slash",
71+
allowed: true,
72+
allowedDirs: []string{
73+
"/etc/nginx/",
74+
},
75+
filePath: "/etc/nginx/nginx.conf",
76+
},
77+
{
78+
name: "Test 8: Prefix match",
79+
allowed: false,
80+
allowedDirs: []string{
81+
"/etc/nginx",
82+
},
83+
filePath: "/etc/nginx-test/nginx.conf",
84+
},
6985
}
7086

7187
for _, test := range tests {

test/types/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func AgentConfig() *config.Config {
6262
Multiplier: commonMultiplier,
6363
},
6464
},
65-
AllowedDirectories: []string{"/tmp/"},
65+
AllowedDirectories: []string{"/tmp"},
6666
Collector: &config.Collector{
6767
ConfigPath: "/etc/nginx-agent/nginx-agent-otelcol.yaml",
6868
Exporters: config.Exporters{

0 commit comments

Comments
 (0)