1
1
package common
2
2
3
3
import (
4
- "crypto/sha256"
5
4
_ "embed"
6
- "encoding/hex"
7
5
"encoding/json"
8
6
"fmt"
9
- "net/url"
10
7
"os"
11
8
"path"
12
- "regexp"
13
- "sort"
14
9
"strings"
15
- "time"
10
+
11
+ "github.com/cortexapps/axon/server/snykbroker/acceptfile"
16
12
)
17
13
18
14
type Integration string
@@ -60,7 +56,7 @@ func ParseIntegration(s string) (Integration, error) {
60
56
}
61
57
62
58
func ValidIntegrations () []Integration {
63
- return []Integration {IntegrationGithub , IntegrationJira , IntegrationGitlab , IntegrationBitbucket , IntegrationSonarqube , IntegrationPrometheus }
59
+ return []Integration {IntegrationCustom , IntegrationGithub , IntegrationJira , IntegrationGitlab , IntegrationBitbucket , IntegrationSonarqube , IntegrationPrometheus }
64
60
}
65
61
66
62
type IntegrationInfo struct {
@@ -70,284 +66,26 @@ type IntegrationInfo struct {
70
66
AcceptFilePath string
71
67
}
72
68
73
- func (ii IntegrationInfo ) AcceptFile (localhostBase string ) (string , error ) {
74
-
75
- // load/locate the accept file then fix it up to have
76
- // an entry to talk to the axon HTTP server itself
77
- // which we use for status, etc
78
- alias := ii .Alias
79
- if len (alias ) == 0 {
80
- alias = "default"
81
- }
82
-
83
- content , err := ii .getAcceptFileContents ()
84
- if err != nil {
85
- return "" , err
86
- }
87
-
88
- rawFile := []byte (content )
89
- h := sha256 .New ()
90
- h .Write (rawFile )
91
- expectedPath := path .Join (
92
- os .TempDir (),
93
- "accept-files" ,
94
- ii .Integration .String (),
95
- alias ,
96
- hex .EncodeToString (h .Sum (nil )),
97
- "accept.json" ,
98
- )
99
-
100
- dict := map [string ]interface {}{}
101
- err = json .Unmarshal (rawFile , & dict )
102
- if err != nil {
103
- return "" , err
104
- }
105
-
106
- // we add a section like this to allow the server side
107
- // to hit the axon agent via an HTTP bridge
108
- // "private": [
109
- // {
110
- // "method": "any",
111
- // "path": "/__axon/*",
112
- // "origin": "http://localhost"
113
- // }
114
- // ]
115
-
116
- entries , ok := dict ["private" ].([]interface {})
117
- if ! ok {
118
- entries = []interface {}{}
119
- dict ["private" ] = entries
120
- }
121
-
122
- entry := map [string ]string {
123
- "method" : "any" ,
124
- "path" : "/__axon/*" ,
125
- "origin" : localhostBase ,
126
- }
127
- dict ["private" ] = append ([]interface {}{entry }, entries ... )
128
-
129
- if _ , ok := dict ["public" ]; ! ok {
130
- dict ["public" ] = []interface {}{}
131
- }
132
-
133
- json , err := json .Marshal (dict )
134
- if err != nil {
135
- return "" , err
136
- }
137
- err = os .MkdirAll (path .Dir (expectedPath ), os .ModeDir | os .ModePerm )
138
- if err != nil {
139
- return "" , err
140
- }
141
- err = os .WriteFile (expectedPath , json , os .ModePerm )
142
-
143
- return expectedPath , err
144
- }
145
-
146
- type ValueResolver func () string
69
+ func (ii IntegrationInfo ) ToAcceptFile () (* acceptfile.AcceptFile , error ) {
147
70
148
- func StringValueResolver (value string ) ValueResolver {
149
- return func () string {
150
- return value
151
- }
152
- }
153
-
154
- type ResolverMap map [string ]ValueResolver
155
-
156
- func NewResolverMapFromMap (m map [string ]string ) ResolverMap {
157
- rm := make (ResolverMap , len (m ))
158
- for key , value := range m {
159
- rm [key ] = StringValueResolver (value )
160
- }
161
- return rm
162
- }
163
-
164
- func (rm ResolverMap ) ToStringMap () map [string ]string {
165
- resolved := make (map [string ]string , len (rm ))
166
- for key , resolver := range rm {
167
- resolved [key ] = resolver ()
168
- }
169
- return resolved
170
- }
171
-
172
- func (rm ResolverMap ) Resolve (key string ) string {
173
- resolver , ok := rm [key ]
174
- if ! ok {
175
- return ""
176
- }
177
- return resolver ()
178
- }
179
-
180
- func EnvValueResolver (envVar string , defaultValue string , capture bool ) ValueResolver {
181
-
182
- captured := os .ExpandEnv (envVar )
183
- return func () string {
184
- if capture {
185
- return captured
186
- }
187
- if val := os .ExpandEnv (envVar ); val != "" {
188
- return val
189
- }
190
-
191
- return defaultValue
192
- }
193
- }
194
-
195
- func (ii IntegrationInfo ) RewriteOrigins (acceptFilePath string , writer func (string , ResolverMap ) string ) (* AcceptFileInfo , error ) {
196
-
197
- info , err := newAcceptFileInfo (acceptFilePath , writer )
198
- if err != nil {
71
+ if err := ii .Integration .Validate (); err != nil {
199
72
return nil , err
200
73
}
201
- _ , err = info .Rewrite ()
202
- if err != nil {
203
- return nil , err
204
- }
205
- return info , nil
206
- }
207
74
208
- type AcceptFileInfo struct {
209
- OriginalPath string
210
- RewrittenPath string
211
- Content string
212
- rawContent []byte
213
- Routes []AcceptFileRoute
214
- originRewriter func (uri string , headers ResolverMap ) string
215
- }
216
-
217
- var IgnoreHosts = []string {
218
- "localhost" ,
219
- "127.0.0.1" ,
220
- }
221
-
222
- func newAcceptFileInfo (acceptFilePath string , originRewriter func (string , ResolverMap ) string ) (* AcceptFileInfo , error ) {
223
- stat , err := os .Stat (acceptFilePath )
224
- if err != nil {
75
+ if _ , err := ii .ValidateSubtype (); err != nil {
225
76
return nil , err
226
77
}
227
78
228
- if stat .IsDir () {
229
- return nil , fmt .Errorf ("accept file path %q is a directory, expected a file" , acceptFilePath )
230
- }
231
-
232
- info := & AcceptFileInfo {
233
- OriginalPath : acceptFilePath ,
234
- originRewriter : originRewriter ,
235
- }
236
-
237
- info .rawContent , err = os .ReadFile (acceptFilePath )
79
+ content , err := ii .getAcceptFileContents ()
238
80
if err != nil {
239
81
return nil , err
240
82
}
83
+ file := acceptfile .NewAcceptFile ([]byte (content ))
241
84
242
- return info , nil
243
- }
244
-
245
- func (afi * AcceptFileInfo ) isIgnoredHost (host string ) bool {
246
- for _ , ignoreHost := range IgnoreHosts {
247
- if strings .HasPrefix (host , ignoreHost ) {
248
- return true
249
- }
250
- }
251
- return false
252
- }
253
-
254
- func (afi * AcceptFileInfo ) Rewrite () (string , error ) {
255
-
256
- if afi .Content == "" {
257
- dict := map [string ]interface {}{}
258
- err := json .Unmarshal (afi .rawContent , & dict )
259
- if err != nil {
260
- return "" , err
261
- }
262
-
263
- entries , ok := dict ["private" ].([]interface {})
264
- if ! ok {
265
- return "" , nil
266
- }
267
-
268
- for _ , entry := range entries {
269
- values := entry .(map [string ]any )
270
- rawOrigin , ok := values ["origin" ].(string )
271
- if ! ok {
272
- continue
273
- }
274
-
275
- origin := os .ExpandEnv (rawOrigin )
276
-
277
- parsed , err := url .Parse (origin )
278
- if err != nil {
279
- return "" , fmt .Errorf ("failed to parse origin %q: %w" , origin , err )
280
- }
281
-
282
- if afi .isIgnoredHost (parsed .Host ) {
283
- continue
284
- }
285
-
286
- if parsed .Scheme == "" {
287
- parsed .Scheme = "https"
288
- origin = parsed .String ()
289
- }
290
-
291
- // Extract headers if present
292
- var headers ResolverMap
293
- if headersInterface , hasHeaders := values ["headers" ]; hasHeaders {
294
- if headersMap , ok := headersInterface .(map [string ]interface {}); ok {
295
- headers = make (ResolverMap , len (headersMap ))
296
- for k , v := range headersMap {
297
- if strVal , ok := v .(string ); ok {
298
- // Resolve environment variables in header values
299
- headers [k ] = EnvValueResolver (strVal , "" , true )
300
- }
301
- }
302
- }
303
- }
304
-
305
- // rewrite the origin to use the writer function
306
- newOrigin := afi .originRewriter (origin , headers )
307
- if newOrigin != "" {
308
- values ["origin" ] = newOrigin
309
- }
310
- afi .Routes = append (afi .Routes , AcceptFileRoute {
311
- ResolvedOrigin : origin ,
312
- ProxyOrigin : newOrigin ,
313
- Headers : headers ,
314
- })
315
- }
316
-
317
- json , err := json .Marshal (dict )
318
- if err != nil {
319
- return "" , err
320
- }
321
- afi .Content = string (json )
322
-
323
- stat , err := os .Stat (afi .OriginalPath )
324
-
325
- if err != nil {
326
- return "" , err
327
- }
328
- newFilePath := path .Join (
329
- os .TempDir (),
330
- "accept-files-written" ,
331
- fmt .Sprintf ("rewrite.%v.%v" , time .Now ().UnixMilli (), stat .Name ()),
332
- )
333
- err = os .MkdirAll (path .Dir (newFilePath ), os .ModeDir | os .ModePerm )
334
- if err != nil {
335
- return "" , err
336
- }
337
-
338
- err = os .WriteFile (newFilePath , json , os .ModePerm )
339
- if err != nil {
340
- return "" , err
341
- }
342
- afi .RewrittenPath = newFilePath
85
+ if err := file .Validate (); err != nil {
86
+ return nil , err
343
87
}
344
- return afi .Content , nil
345
- }
346
-
347
- type AcceptFileRoute struct {
348
- ResolvedOrigin string
349
- ProxyOrigin string
350
- Headers ResolverMap
88
+ return file , nil
351
89
}
352
90
353
91
func (ii IntegrationInfo ) getAcceptFileContents () (string , error ) {
@@ -478,31 +216,5 @@ func (ii IntegrationInfo) getIntegrationAcceptFile() (string, error) {
478
216
if err != nil {
479
217
return "" , err
480
218
}
481
- if err := ii .ensureAcceptFileVars (strContent ); err != nil {
482
- return "" , err
483
- }
484
219
return strContent , nil
485
220
}
486
-
487
- var reContentVars = regexp .MustCompile (`\$\{(.*?)\}` )
488
-
489
- func (ii IntegrationInfo ) ensureAcceptFileVars (content string ) error {
490
- varMatch := reContentVars .FindAllStringSubmatch (content , - 1 )
491
-
492
- envVars := []string {}
493
-
494
- // sort these so they have a stable order
495
-
496
- for _ , match := range varMatch {
497
- envVars = append (envVars , match [1 ])
498
- }
499
-
500
- sort .Strings (envVars )
501
-
502
- for _ , envVar := range envVars {
503
- if os .Getenv (envVar ) == "" && os .Getenv (envVar + "_POOL" ) == "" {
504
- return fmt .Errorf ("missing required environment variable %q for integration %s" , envVar , ii .Integration .String ())
505
- }
506
- }
507
- return nil
508
- }
0 commit comments