Skip to content

Commit 0556135

Browse files
committed
feat: shallow dirs
1 parent 87a72ad commit 0556135

File tree

4 files changed

+73
-0
lines changed

4 files changed

+73
-0
lines changed

nix/packages/cis-audit/scanner/cmd/cis-generate-spec/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var (
2323
includeDynamic bool
2424
includePorts bool
2525
includeProcess bool
26+
shallowDirs []string
2627
strict bool
2728
verbose bool
2829
debug bool
@@ -50,6 +51,9 @@ Examples:
5051
5152
# Enable verbose logging to stderr
5253
cis-generate-spec --verbose --log-format json
54+
55+
# Scan directories without recursing into subdirectories
56+
cis-generate-spec --shallow-dirs /nix/store --shallow-dirs /data/pgdata
5357
`,
5458
Args: cobra.MaximumNArgs(1),
5559
Version: version,
@@ -62,6 +66,7 @@ func init() {
6266
rootCmd.Flags().BoolVar(&includeDynamic, "include-dynamic", false, "Include dynamic kernel parameters")
6367
rootCmd.Flags().BoolVar(&includePorts, "include-ports", false, "Include listening ports")
6468
rootCmd.Flags().BoolVar(&includeProcess, "include-processes", false, "Include running processes")
69+
rootCmd.Flags().StringArrayVar(&shallowDirs, "shallow-dirs", nil, "Directories to scan without recursion (can be specified multiple times)")
6570
rootCmd.Flags().BoolVar(&strict, "strict", false, "Fail on any access errors (default: skip and warn)")
6671
rootCmd.Flags().BoolVar(&verbose, "verbose", false, "Enable structured logging to stderr")
6772
rootCmd.Flags().BoolVar(&debug, "debug", false, "Enable debug logging (implies --verbose)")
@@ -86,6 +91,7 @@ func run(cmd *cobra.Command, args []string) error {
8691
IncludeDynamic: includeDynamic,
8792
IncludePorts: includePorts,
8893
IncludeProcesses: includeProcess,
94+
ShallowDirs: shallowDirs,
8995
}
9096
cfg, err := config.Load(configFile, cliOpts)
9197
if err != nil {

nix/packages/cis-audit/scanner/internal/config/defaults.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ var DefaultExclusions = Config{
2727
"/var/cache/apt/*",
2828
},
2929

30+
ShallowDirs: []string{
31+
// Nix store - contents change with deployments, only audit top-level structure
32+
"/nix/store",
33+
34+
// PostgreSQL data directory - contents are dynamic database state
35+
"/data/pgdata",
36+
},
37+
3038
KernelParams: []string{
3139
// Dynamic kernel parameters that change frequently and aren't security-relevant
3240
"fs.dentry-state", // Dentry cache statistics (dynamic)

nix/packages/cis-audit/scanner/internal/config/loader.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ type Config struct {
1818
// Paths to exclude from scanning (glob patterns supported)
1919
Paths []string `yaml:"paths,omitempty"`
2020

21+
// ShallowDirs are directories to scan at top level only (no recursion)
22+
// Files directly in these directories are scanned, but subdirectories are skipped
23+
ShallowDirs []string `yaml:"shallowDirs,omitempty"`
24+
2125
// Kernel parameters to exclude from scanning
2226
KernelParams []string `yaml:"kernelParams,omitempty"`
2327

@@ -42,6 +46,9 @@ type CLIOptions struct {
4246

4347
// IncludeProcesses enables process scanning (removes "process" from DisabledScanners)
4448
IncludeProcesses bool
49+
50+
// ShallowDirs adds directories to scan without recursion (from CLI)
51+
ShallowDirs []string
4552
}
4653

4754
// Load reads configuration from defaults, optional config file, and CLI options.
@@ -83,6 +90,11 @@ func Load(configPath string, opts CLIOptions) (*Config, error) {
8390
cfg.DisabledScanners = removeItems(cfg.DisabledScanners, []string{"process"})
8491
}
8592

93+
// Add CLI shallow dirs to config
94+
if len(opts.ShallowDirs) > 0 {
95+
cfg.ShallowDirs = append(cfg.ShallowDirs, opts.ShallowDirs...)
96+
}
97+
8698
return &cfg, nil
8799
}
88100

@@ -108,6 +120,7 @@ func merge(base, file Config) Config {
108120

109121
// Append file exclusions to base exclusions (additive)
110122
result.Paths = append(result.Paths, file.Paths...)
123+
result.ShallowDirs = append(result.ShallowDirs, file.ShallowDirs...)
111124
result.KernelParams = append(result.KernelParams, file.KernelParams...)
112125
result.DisabledScanners = append(result.DisabledScanners, file.DisabledScanners...)
113126

@@ -188,3 +201,39 @@ func (c *Config) IsScannerDisabled(scannerType string) bool {
188201
}
189202
return false
190203
}
204+
205+
// IsShallowDir checks if the given path is a shallow directory (should not recurse into).
206+
// Returns true if path exactly matches a shallow dir or is a subdirectory of one.
207+
func (c *Config) IsShallowDir(path string) bool {
208+
for _, shallow := range c.ShallowDirs {
209+
// Normalize paths (remove trailing slashes)
210+
shallow = strings.TrimSuffix(shallow, "/")
211+
path = strings.TrimSuffix(path, "/")
212+
213+
// Check if this is exactly the shallow dir or a subdirectory
214+
if path == shallow || strings.HasPrefix(path, shallow+"/") {
215+
return true
216+
}
217+
}
218+
return false
219+
}
220+
221+
// GetShallowDirDepth returns the depth of the shallow directory that contains this path.
222+
// Returns -1 if path is not in any shallow directory.
223+
// Depth 0 = the shallow dir itself, depth 1 = immediate child, etc.
224+
func (c *Config) GetShallowDirDepth(path string) int {
225+
for _, shallow := range c.ShallowDirs {
226+
shallow = strings.TrimSuffix(shallow, "/")
227+
path = strings.TrimSuffix(path, "/")
228+
229+
if path == shallow {
230+
return 0
231+
}
232+
if strings.HasPrefix(path, shallow+"/") {
233+
// Count the depth relative to shallow dir
234+
relPath := strings.TrimPrefix(path, shallow+"/")
235+
return strings.Count(relPath, "/") + 1
236+
}
237+
}
238+
return -1
239+
}

nix/packages/cis-audit/scanner/internal/scanners/files.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ func (s *FileScanner) Scan(ctx context.Context, opts ScanOptions) (ScanStats, er
7373
return nil
7474
}
7575

76+
// Handle shallow directories - scan top level only, skip subdirectories
77+
if d != nil && d.IsDir() {
78+
depth := cfg.GetShallowDirDepth(path)
79+
if depth > 1 {
80+
// This is a subdirectory inside a shallow dir - skip it
81+
opts.Logger.Debug("Skipping subdirectory in shallow dir", "path", path, "depth", depth)
82+
return filepath.SkipDir
83+
}
84+
}
85+
7686
// Handle walk errors (permission denied, etc.)
7787
if err != nil {
7888
return s.handleError(err, path, opts)

0 commit comments

Comments
 (0)