11package resource
22
33import (
4+ "context"
45 "encoding/json"
56 "fmt"
67 "runtime"
@@ -65,16 +66,25 @@ func ResourceList(cmd *cobra.Command, args []string) error {
6566 return err
6667 }
6768
68- // Check if we need to decrypt secrets (expensive RSA operation )
69+ // Check if we need to fetch secrets (expensive server join + RSA decryption )
6970 // For v5 resources, metadata (name, username, uri) can be decrypted without secrets
70- needSecretDecryption := false
71+ needSecrets := false
7172 for _ , col := range config .columns {
7273 switch strings .ToLower (col ) {
7374 case "password" , "description" :
74- needSecretDecryption = true
75+ needSecrets = true
7576 }
7677 }
7778
79+ // Check if CEL filter references Password or Description
80+ if ! needSecrets && config .celFilter != "" {
81+ refsSecrets , err := util .CELExpressionReferencesFields (config .celFilter , []string {"Password" , "Description" }, CelEnvOptions ... )
82+ if err != nil {
83+ return fmt .Errorf ("Parsing filter: %w" , err )
84+ }
85+ needSecrets = refsSecrets
86+ }
87+
7888 ctx := util .GetContext ()
7989
8090 client , err := util .GetClient (ctx )
@@ -89,22 +99,24 @@ func ResourceList(cmd *cobra.Command, args []string) error {
8999 FilterIsOwnedByMe : config .own ,
90100 FilterIsSharedWithGroup : config .group ,
91101 FilterHasParent : config .folderParents ,
92- ContainSecret : true ,
93- ContainResourceType : true ,
102+ ContainSecret : needSecrets ,
94103 })
95104 if err != nil {
96105 return fmt .Errorf ("Listing Resource: %w" , err )
97106 }
98107
99- resources , err = filterResources (& resources , config .celFilter , ctx , client )
108+ // Decrypt all resources in parallel
109+ decrypted , err := decryptResourcesParallel (ctx , client , resources , needSecrets )
100110 if err != nil {
101111 return err
102112 }
103113
104- // Decrypt all resources in parallel
105- decrypted , err := decryptResourcesParallel (client , resources , needSecretDecryption )
106- if err != nil {
107- return err
114+ // Apply CEL filter on already-decrypted data
115+ if config .celFilter != "" {
116+ decrypted , err = filterDecryptedResources (decrypted , config .celFilter , ctx )
117+ if err != nil {
118+ return err
119+ }
108120 }
109121
110122 if config .jsonOutput {
@@ -114,28 +126,29 @@ func ResourceList(cmd *cobra.Command, args []string) error {
114126 return printTableResources (decrypted , config .columns )
115127}
116128
117- func decryptResourcesParallel (client * api.Client , resources []api.Resource , needSecretDecryption bool ) ([]decryptedResource , error ) {
118- // Filter resources with secrets
129+ func decryptResourcesParallel (ctx context.Context , client * api.Client , resources []api.Resource , needSecrets bool ) ([]decryptedResource , error ) {
130+ // Use parallel decryption with worker pool
131+ numWorkers := runtime .NumCPU ()
132+ if numWorkers > 16 {
133+ numWorkers = 16 // Cap at 16 workers
134+ }
135+ if len (resources ) < numWorkers {
136+ numWorkers = len (resources )
137+ }
138+
139+ // Filter resources - only require secrets if we're fetching them
119140 var validResources []api.Resource
120141 for i := range resources {
121- if len (resources [i ].Secrets ) > 0 {
122- validResources = append ( validResources , resources [ i ])
142+ if needSecrets && len (resources [i ].Secrets ) == 0 {
143+ continue
123144 }
145+ validResources = append (validResources , resources [i ])
124146 }
125147
126148 if len (validResources ) == 0 {
127149 return []decryptedResource {}, nil
128150 }
129151
130- // Use parallel decryption with worker pool
131- numWorkers := runtime .NumCPU ()
132- if numWorkers > 16 {
133- numWorkers = 16 // Cap at 16 workers
134- }
135- if len (validResources ) < numWorkers {
136- numWorkers = len (validResources )
137- }
138-
139152 // Channel for work items and results
140153 // Note: Session keys are pre-fetched during Login() when the server supports v5 metadata,
141154 // so no additional prefetching is needed here.
@@ -150,12 +163,43 @@ func decryptResourcesParallel(client *api.Client, resources []api.Resource, need
150163 defer wg .Done ()
151164 for idx := range jobs {
152165 resource := validResources [idx ]
166+
167+ // Lookup resource type from cache (single API call for all types)
168+ rType , err := client .GetResourceTypeCached (ctx , resource .ResourceTypeID )
169+ if err != nil {
170+ results <- decryptedResource {index : idx , err : fmt .Errorf ("Get ResourceType: %w" , err )}
171+ continue
172+ }
173+
174+ // For v4 resources without secret decryption, use plaintext fields directly
175+ // This avoids unnecessary function calls for 10k+ resources
176+ isV5 := strings .HasPrefix (rType .Slug , "v5-" )
177+ if ! needSecrets && ! isV5 {
178+ // V4 resource - metadata is plaintext, no decryption needed
179+ results <- decryptedResource {
180+ index : idx ,
181+ resource : resource ,
182+ name : resource .Name ,
183+ username : resource .Username ,
184+ uri : resource .URI ,
185+ password : "" ,
186+ description : resource .Description ,
187+ }
188+ continue
189+ }
190+
191+ // Handle case where secrets weren't fetched
192+ var secret api.Secret
193+ if len (resource .Secrets ) > 0 {
194+ secret = resource .Secrets [0 ]
195+ }
196+
153197 _ , name , username , uri , pass , desc , err := helper .GetResourceFromDataWithOptions (
154198 client ,
155199 resource ,
156- resource . Secrets [ 0 ] ,
157- resource . ResourceType ,
158- needSecretDecryption ,
200+ secret ,
201+ * rType ,
202+ needSecrets ,
159203 )
160204 results <- decryptedResource {
161205 index : idx ,
0 commit comments