6
6
"os"
7
7
"os/exec"
8
8
"path/filepath"
9
+ "sync"
9
10
)
10
11
11
12
// ProcessAssureType handles the assurance process for policies of type "assure"
@@ -23,7 +24,7 @@ func ProcessAssureType(policy Policy, rgPath string, targetDir string, filePaths
23
24
24
25
func executeAssure (policy Policy , rgPath string , targetDir string , filesToAssure []string ) error {
25
26
// Create a temporary file to store the search patterns
26
- searchPatternFile , err := createSearchPatternFile (policy .Regex )
27
+ searchPatternFile , err := createSearchPatternFile (policy .Regex , NormalizeFilename ( policy . ID ) )
27
28
if err != nil {
28
29
log .Error ().Err (err ).Msg ("error creating search pattern file" )
29
30
return fmt .Errorf ("error creating search pattern file: %w" , err )
@@ -49,6 +50,8 @@ func executeAssure(policy Policy, rgPath string, targetDir string, filesToAssure
49
50
writer := bufio .NewWriter (jsonoutfile )
50
51
defer writer .Flush ()
51
52
53
+ processedIgnorePatterns := processIgnorePatterns (policyData .Config .Flags .Ignore )
54
+
52
55
codePatternAssureJSON := []string {
53
56
"--pcre2" ,
54
57
"--no-heading" ,
@@ -64,39 +67,24 @@ func executeAssure(policy Policy, rgPath string, targetDir string, filesToAssure
64
67
return fmt .Errorf ("no target directory defined" )
65
68
}
66
69
67
- // Append the file targets
68
- if len (filesToAssure ) > 0 {
69
- codePatternAssureJSON = append (codePatternAssureJSON , filesToAssure ... )
70
- } else if policy .FilePattern == "" {
71
- codePatternAssureJSON = append (codePatternAssureJSON , targetDir )
70
+ codePatternAssureJSON = append (codePatternAssureJSON , processedIgnorePatterns ... )
71
+
72
+ matchesFound := true
73
+
74
+ // Parallel execution for large file sets
75
+ if policy .FilePattern == "" {
76
+ log .Warn ().Str ("policy" , policy .ID ).Msg ("ASSURE Policy without a filepattern is suboptimal" )
77
+ }
78
+ if len (filesToAssure ) > 25 {
79
+ matchesFound , err = executeParallelAssure (rgPath , codePatternAssureJSON , filesToAssure , writer )
72
80
} else {
73
- return fmt . Errorf ( "no files matched policy pattern" )
81
+ matchesFound , err = executeSingleAssure ( rgPath , codePatternAssureJSON , filesToAssure , targetDir , policy , writer )
74
82
}
75
83
76
- // Execute the ripgrep command for JSON output
77
- cmdJSON := exec .Command (rgPath , codePatternAssureJSON ... )
78
- cmdJSON .Stdout = writer
79
- cmdJSON .Stderr = os .Stderr
84
+ if err != nil {
80
85
81
- log .Debug ().Msgf ("Creating JSON output for assure policy %s... " , policy .ID )
82
- err = cmdJSON .Run ()
86
+ log .Error ().Err (err ).Msg ("error executing ripgrep batch" )
83
87
84
- // Check if ripgrep found any matches
85
- matchesFound := true
86
- if err != nil {
87
- if exitError , ok := err .(* exec.ExitError ); ok {
88
- // Exit code 1 in ripgrep means "no matches found"
89
- if exitError .ExitCode () == 1 {
90
- matchesFound = false
91
- err = nil // Reset error as this is the expected outcome for assure
92
- } else {
93
- log .Error ().Err (err ).Msg ("error executing ripgrep for JSON output" )
94
- return fmt .Errorf ("error executing ripgrep for JSON output: %w" , err )
95
- }
96
- } else {
97
- log .Error ().Err (err ).Msg ("error executing ripgrep for JSON output" )
98
- return fmt .Errorf ("error executing ripgrep for JSON output: %w" , err )
99
- }
100
88
}
101
89
102
90
// Patch the JSON output file
@@ -107,6 +95,7 @@ func executeAssure(policy Policy, rgPath string, targetDir string, filesToAssure
107
95
}
108
96
109
97
log .Debug ().Msgf ("JSON output for assure policy %s written to: %s " , policy .ID , jsonOutputFile )
98
+ log .Debug ().Msgf ("Scanned ~%d files for policy %s" , len (filesToAssure ), policy .ID )
110
99
111
100
// Determine the status based on whether matches were found
112
101
status := "NOT FOUND"
@@ -143,3 +132,115 @@ func executeAssure(policy Policy, rgPath string, targetDir string, filesToAssure
143
132
144
133
return nil
145
134
}
135
+
136
+ func executeParallelAssure (rgPath string , baseArgs []string , filesToScan []string , writer * bufio.Writer ) (bool , error ) {
137
+
138
+ const batchSize = 25
139
+ matched := true
140
+ var wg sync.WaitGroup
141
+ errChan := make (chan error , len (filesToScan )/ batchSize + 1 )
142
+ var mu sync.Mutex
143
+
144
+ for i := 0 ; i < len (filesToScan ); i += batchSize {
145
+ end := i + batchSize
146
+ if end > len (filesToScan ) {
147
+ end = len (filesToScan )
148
+ }
149
+ batch := filesToScan [i :end ]
150
+
151
+ // log.Debug().Msgf("RGM: %v", batch)
152
+
153
+ wg .Add (1 )
154
+ go func (batch []string ) {
155
+ defer wg .Done ()
156
+ args := append (baseArgs , batch ... )
157
+ cmd := exec .Command (rgPath , args ... )
158
+ output , err := cmd .Output ()
159
+
160
+ if err != nil {
161
+
162
+ if exitError , ok := err .(* exec.ExitError ); ok {
163
+ // Exit code 1 in ripgrep means "no matches found"
164
+ if exitError .ExitCode () == 1 {
165
+ matched = false
166
+ err = nil // Reset error as this is the expected outcome for assure
167
+ }
168
+ }
169
+
170
+ if exitError , ok := err .(* exec.ExitError ); ok && exitError .ExitCode () != 1 {
171
+ errChan <- fmt .Errorf ("error executing ripgrep: %w" , err )
172
+ return
173
+ }
174
+ }
175
+
176
+ mu .Lock ()
177
+ _ , writeErr := writer .Write (output )
178
+ if writeErr == nil {
179
+ writeErr = writer .Flush ()
180
+ }
181
+ mu .Unlock ()
182
+
183
+ if writeErr != nil {
184
+ errChan <- fmt .Errorf ("error writing output: %w" , writeErr )
185
+ }
186
+ }(batch )
187
+ }
188
+
189
+ wg .Wait ()
190
+ close (errChan )
191
+
192
+ for err := range errChan {
193
+ if err != nil {
194
+ return matched , err
195
+ }
196
+ }
197
+
198
+ return matched , nil
199
+ }
200
+
201
+ func executeSingleAssure (rgPath string , baseArgs []string , filesToScan []string , targetDir string , policy Policy , writer * bufio.Writer ) (bool , error ) {
202
+
203
+ if len (filesToScan ) > 0 {
204
+ baseArgs = append (baseArgs , filesToScan ... )
205
+ } else {
206
+ log .Error ().Str ("policy" , policy .ID ).Msgf ("no files matched policy pattern on target : %s" , targetDir )
207
+ }
208
+
209
+ matched := true
210
+
211
+ // log.Debug().Msgf("RGS: %v", baseArgs)
212
+
213
+ cmdJSON := exec .Command (rgPath , baseArgs ... )
214
+ cmdJSON .Stdout = writer
215
+ cmdJSON .Stderr = os .Stderr
216
+
217
+ log .Debug ().Msgf ("Creating JSON output for policy %s... " , policy .ID )
218
+ err := cmdJSON .Run ()
219
+ if err != nil {
220
+ if exitError , ok := err .(* exec.ExitError ); ok {
221
+
222
+ if exitError .ExitCode () == 1 {
223
+ matched = false
224
+ err = nil // Reset error as this is the expected outcome for assure
225
+ }
226
+
227
+ if exitError .ExitCode () == 2 {
228
+ log .Warn ().Msgf ("RG exited with code 2" )
229
+ log .Debug ().Msgf ("RG Error Args: %v" , baseArgs )
230
+ if len (exitError .Stderr ) > 0 {
231
+ log .Debug ().Msgf ("RG exited with code 2 stderr: %s" , string (exitError .Stderr ))
232
+ }
233
+ }
234
+ if exitError .ExitCode () != 1 {
235
+ log .Error ().Err (err ).Msg ("error executing ripgrep for JSON output" )
236
+ return matched , fmt .Errorf ("error executing ripgrep for JSON output: %w" , err )
237
+ }
238
+
239
+ } else {
240
+ log .Error ().Err (err ).Msg ("error executing ripgrep for JSON output" )
241
+ return matched , fmt .Errorf ("error executing ripgrep for JSON output: %w" , err )
242
+ }
243
+ }
244
+
245
+ return matched , nil
246
+ }
0 commit comments