99 "strings"
1010 "text/template"
1111
12+ "github.com/grafana/synthetic-monitoring-agent/internal/prober/interpolation"
1213 sm "github.com/grafana/synthetic-monitoring-agent/pkg/pb/synthetic_monitoring"
1314)
1415
@@ -20,52 +21,7 @@ var templateFS embed.FS
2021var userVariables = regexp .MustCompile (`\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}` )
2122
2223func performVariableExpansion (in string ) string {
23- if len (in ) == 0 {
24- return `''`
25- }
26-
27- var s strings.Builder
28- buf := []byte (in )
29- locs := userVariables .FindAllSubmatchIndex (buf , - 1 )
30-
31- p := 0
32- for _ , loc := range locs {
33- if len (loc ) < 4 { // put the bounds checker at ease
34- panic ("unexpected result while building URL" )
35- }
36-
37- if s .Len () > 0 {
38- s .WriteRune ('+' )
39- }
40-
41- if pre := buf [p :loc [0 ]]; len (pre ) > 0 {
42- s .WriteRune ('\'' )
43- template .JSEscape (& s , pre )
44- s .WriteRune ('\'' )
45- s .WriteRune ('+' )
46- }
47-
48- s .WriteString (`vars['` )
49- // Because of the capture in the regular expression, the result
50- // has two indices that represent the matched substring, and
51- // two more indices that represent the capture group.
52- s .Write (buf [loc [2 ]:loc [3 ]])
53- s .WriteString (`']` )
54-
55- p = loc [1 ]
56- }
57-
58- if len (buf [p :]) > 0 {
59- if s .Len () > 0 {
60- s .WriteRune ('+' )
61- }
62-
63- s .WriteRune ('\'' )
64- template .JSEscape (& s , buf [p :])
65- s .WriteRune ('\'' )
66- }
67-
68- return s .String ()
24+ return interpolation .ToJavaScriptWithSecrets (in )
6925}
7026
7127// Query params must be appended to a URL that has already been created.
@@ -113,13 +69,19 @@ func interpolateBodyVariables(bodyVarName string, body *sm.HttpRequestBody) []st
11369 default :
11470 var buf strings.Builder
11571
116- matches := userVariables .FindAllString (string (body .Payload ), - 1 )
72+ // Find both regular and secret variables using submatch indices
73+ regularMatches := userVariables .FindAllSubmatchIndex (body .Payload , - 1 )
74+ secretMatches := interpolation .SecretRegex .FindAllSubmatchIndex (body .Payload , - 1 )
75+
11776 parsedMatches := make (map [string ]struct {})
118- out := make ([]string , 0 , len (matches ))
77+ out := make ([]string , 0 , len (regularMatches ) + len ( secretMatches ))
11978
120- // For every instance of ${variable} in the body,
121- // this block returns {bodyVarName} = {bodyVarName}.replaceAll('${variable}', vars['variable'])
122- for _ , m := range matches {
79+ // Handle regular variables
80+ for _ , match := range regularMatches {
81+ if len (match ) < 4 {
82+ continue
83+ }
84+ m := string (body .Payload [match [0 ]:match [1 ]])
12385 if _ , found := parsedMatches [m ]; found {
12486 continue
12587 }
@@ -132,15 +94,38 @@ func interpolateBodyVariables(bodyVarName string, body *sm.HttpRequestBody) []st
13294 buf .WriteString (m )
13395 buf .WriteString ("', vars['" )
13496 // writing the variable name from between ${ and }
135- for i := 2 ; i < len (m )- 1 ; i ++ {
136- buf .WriteByte (m [i ])
137- }
97+ buf .Write (body .Payload [match [2 ]:match [3 ]])
13898 buf .WriteString ("'])" )
13999 out = append (out , buf .String ())
140100
141101 parsedMatches [m ] = struct {}{}
142102 }
143103
104+ // Handle secret variables
105+ for _ , match := range secretMatches {
106+ if len (match ) < 4 {
107+ continue
108+ }
109+ m := string (body .Payload [match [0 ]:match [1 ]])
110+ if _ , found := parsedMatches [m ]; found {
111+ continue
112+ }
113+
114+ buf .Reset ()
115+ buf .WriteString (bodyVarName )
116+ buf .WriteString ("=" )
117+ buf .WriteString (bodyVarName )
118+ buf .WriteString (".replaceAll('" )
119+ buf .WriteString (m )
120+ buf .WriteString ("', await secrets.get('" )
121+ // writing the secret name from the capture group
122+ buf .Write (body .Payload [match [2 ]:match [3 ]])
123+ buf .WriteString ("'))" )
124+ out = append (out , buf .String ())
125+
126+ parsedMatches [m ] = struct {}{}
127+ }
128+
144129 return out
145130 }
146131}
@@ -422,6 +407,35 @@ func buildVars(variable *sm.MultiHttpEntryVariable) string {
422407 return b .String ()
423408}
424409
410+ func hasSecretVariables (settings * sm.MultiHttpSettings ) bool {
411+ for _ , entry := range settings .Entries {
412+ // Check URL
413+ if interpolation .SecretRegex .MatchString (entry .Request .Url ) {
414+ return true
415+ }
416+
417+ // Check headers
418+ for _ , header := range entry .Request .Headers {
419+ if interpolation .SecretRegex .MatchString (header .Value ) {
420+ return true
421+ }
422+ }
423+
424+ // Check query fields
425+ for _ , field := range entry .Request .QueryFields {
426+ if interpolation .SecretRegex .MatchString (field .Name ) || interpolation .SecretRegex .MatchString (field .Value ) {
427+ return true
428+ }
429+ }
430+
431+ // Check body
432+ if entry .Request .Body != nil && interpolation .SecretRegex .MatchString (string (entry .Request .Body .Payload )) {
433+ return true
434+ }
435+ }
436+ return false
437+ }
438+
425439func settingsToScript (settings * sm.MultiHttpSettings ) ([]byte , error ) {
426440 // Convert settings to script using a Go template
427441 tmpl , err := template .
@@ -442,9 +456,18 @@ func settingsToScript(settings *sm.MultiHttpSettings) ([]byte, error) {
442456
443457 var buf bytes.Buffer
444458
459+ // Create template data with secret variable detection
460+ templateData := struct {
461+ * sm.MultiHttpSettings
462+ HasSecretVariables bool
463+ }{
464+ MultiHttpSettings : settings ,
465+ HasSecretVariables : hasSecretVariables (settings ),
466+ }
467+
445468 // TODO(mem): figure out if we need to transform the data in some way
446469 // before executing the template
447- if err := tmpl .ExecuteTemplate (& buf , "script.tmpl" , settings ); err != nil {
470+ if err := tmpl .ExecuteTemplate (& buf , "script.tmpl" , templateData ); err != nil {
448471 return nil , fmt .Errorf ("executing script template: %w" , err )
449472 }
450473
0 commit comments