@@ -5,12 +5,30 @@ import (
55 "fmt"
66 "io"
77 "io/fs"
8+ "path/filepath"
89 "strings"
910
1011 "github.com/open-policy-agent/opa/ast"
1112 "github.com/open-policy-agent/opa/bundle"
13+ "github.com/samber/lo"
1214)
1315
16+ var builtinNamespaces = map [string ]struct {}{
17+ "builtin" : {},
18+ "defsec" : {},
19+ "appshield" : {},
20+ }
21+
22+ func BuiltinNamespaces () []string {
23+ return lo .Keys (builtinNamespaces )
24+ }
25+
26+ func IsBuiltinNamespace (namespace string ) bool {
27+ return lo .ContainsBy (BuiltinNamespaces (), func (ns string ) bool {
28+ return strings .HasPrefix (namespace , ns + "." )
29+ })
30+ }
31+
1432func IsRegoFile (name string ) bool {
1533 return strings .HasSuffix (name , bundle .RegoExt ) && ! strings .HasSuffix (name , "_test" + bundle .RegoExt )
1634}
@@ -38,28 +56,20 @@ func (s *Scanner) loadPoliciesFromReaders(readers []io.Reader) (map[string]*ast.
3856 return modules , nil
3957}
4058
41- func (s * Scanner ) loadEmbedded (enableEmbeddedLibraries , enableEmbeddedPolicies bool ) error {
42- if enableEmbeddedLibraries {
43- loadedLibs , errLoad := LoadEmbeddedLibraries ()
44- if errLoad != nil {
45- return fmt .Errorf ("failed to load embedded rego libraries: %w" , errLoad )
46- }
47- for name , policy := range loadedLibs {
48- s .policies [name ] = policy
49- }
50- s .debug .Log ("Loaded %d embedded libraries." , len (loadedLibs ))
59+ func (s * Scanner ) loadEmbedded () error {
60+ loaded , err := LoadEmbeddedLibraries ()
61+ if err != nil {
62+ return fmt .Errorf ("failed to load embedded rego libraries: %w" , err )
5163 }
64+ s .embeddedLibs = loaded
65+ s .debug .Log ("Loaded %d embedded libraries." , len (loaded ))
5266
53- if enableEmbeddedPolicies {
54- loaded , err := LoadEmbeddedPolicies ()
55- if err != nil {
56- return fmt .Errorf ("failed to load embedded rego policies: %w" , err )
57- }
58- for name , policy := range loaded {
59- s .policies [name ] = policy
60- }
61- s .debug .Log ("Loaded %d embedded policies." , len (loaded ))
67+ loaded , err = LoadEmbeddedPolicies ()
68+ if err != nil {
69+ return fmt .Errorf ("failed to load embedded rego policies: %w" , err )
6270 }
71+ s .embeddedChecks = loaded
72+ s .debug .Log ("Loaded %d embedded policies." , len (loaded ))
6373
6474 return nil
6575}
@@ -75,7 +85,7 @@ func (s *Scanner) LoadPolicies(enableEmbeddedLibraries, enableEmbeddedPolicies b
7585 srcFS = s .policyFS
7686 }
7787
78- if err := s .loadEmbedded (enableEmbeddedLibraries , enableEmbeddedPolicies ); err != nil {
88+ if err := s .loadEmbedded (); err != nil {
7989 return err
8090 }
8191
@@ -124,9 +134,71 @@ func (s *Scanner) LoadPolicies(enableEmbeddedLibraries, enableEmbeddedPolicies b
124134 }
125135 s .store = store
126136
137+ if enableEmbeddedPolicies {
138+ s .policies = lo .Assign (s .policies , s .embeddedChecks )
139+ }
140+
141+ if enableEmbeddedLibraries {
142+ s .policies = lo .Assign (s .policies , s .embeddedLibs )
143+ }
144+
127145 return s .compilePolicies (srcFS , paths )
128146}
129147
148+ func (s * Scanner ) fallbackChecks (compiler * ast.Compiler ) {
149+
150+ var excludedFiles []string
151+
152+ for _ , e := range compiler .Errors {
153+ if _ , ok := e .Details .(* ast.RefErrInvalidDetail ); ! ok {
154+ continue
155+ }
156+
157+ loc := e .Location .File
158+
159+ if lo .Contains (excludedFiles , loc ) {
160+ continue
161+ }
162+
163+ badPolicy , exists := s .policies [loc ]
164+ if ! exists || badPolicy == nil {
165+ continue
166+ }
167+
168+ if ! IsBuiltinNamespace (getModuleNamespace (badPolicy )) {
169+ continue
170+ }
171+
172+ s .debug .Log ("Error occurred while parsing: %s, %s. \n Try loading embedded policy." , loc , e .Error ())
173+
174+ embedded := s .findMatchedEmbeddedCheck (badPolicy )
175+ if embedded == nil {
176+ s .debug .Log ("Failed to find embedded policy: %s" , loc )
177+ continue
178+ }
179+
180+ s .debug .Log ("Found embedded policy: %s" , embedded .Package .Location .File )
181+ delete (s .policies , loc ) // remove bad policy
182+ s .policies [embedded .Package .Location .File ] = embedded
183+ delete (s .embeddedChecks , embedded .Package .Location .File ) // avoid infinite loop if embedded check contains ref error
184+ excludedFiles = append (excludedFiles , e .Location .File )
185+ }
186+
187+ compiler .Errors = lo .Filter (compiler .Errors , func (e * ast.Error , _ int ) bool {
188+ return ! lo .Contains (excludedFiles , e .Location .File )
189+ })
190+ }
191+
192+ func (s * Scanner ) findMatchedEmbeddedCheck (module * ast.Module ) * ast.Module {
193+ for _ , policy := range s .embeddedChecks {
194+ if policy .Package .Path .String () == module .Package .Path .String () ||
195+ filepath .Base (policy .Package .Location .File ) == filepath .Base (module .Package .Location .File ) {
196+ return policy
197+ }
198+ }
199+ return nil
200+ }
201+
130202func (s * Scanner ) prunePoliciesWithError (compiler * ast.Compiler ) error {
131203 if len (compiler .Errors ) > s .regoErrorLimit {
132204 s .debug .Log ("Error(s) occurred while loading policies" )
@@ -157,6 +229,7 @@ func (s *Scanner) compilePolicies(srcFS fs.FS, paths []string) error {
157229
158230 compiler .Compile (s .policies )
159231 if compiler .Failed () {
232+ s .fallbackChecks (compiler )
160233 if err := s .prunePoliciesWithError (compiler ); err != nil {
161234 return err
162235 }
0 commit comments