@@ -146,6 +146,45 @@ var _ = Describe("PrometheusSource", func() {
146146 Expect (result .Values [0 ].Value ).To (Equal (0.75 ))
147147 Expect (result .Values [0 ].Labels ["pod" ]).To (Equal ("test-pod-1" ))
148148 })
149+
150+ It ("should escape params so PromQL is safe (no injection)" , func () {
151+ var capturedQuery string
152+ mockAPI .queryFunc = func (ctx context.Context , query string , ts time.Time , opts ... v1.Option ) (model.Value , v1.Warnings , error ) {
153+ capturedQuery = query
154+ return model.Vector {
155+ & model.Sample {
156+ Metric : model.Metric {"pod" : "p1" },
157+ Value : 0.5 ,
158+ Timestamp : model .TimeFromUnix (time .Now ().Unix ()),
159+ },
160+ }, nil , nil
161+ }
162+
163+ err := registry .Register (sourcepkg.QueryTemplate {
164+ Name : "injection_test" ,
165+ Type : sourcepkg .QueryTypePromQL ,
166+ Template : `metric{namespace="{{.namespace}}",model_name="{{.modelID}}"}` ,
167+ Params : []string {"namespace" , "modelID" },
168+ Description : "Test escaping" ,
169+ })
170+ Expect (err ).NotTo (HaveOccurred ())
171+
172+ // Params that would break PromQL or inject labels if unescaped
173+ params := map [string ]string {
174+ "namespace" : `safe-ns` ,
175+ "modelID" : `x",namespace="other"` ,
176+ }
177+ _ , err = source .Refresh (ctx , sourcepkg.RefreshSpec {
178+ Queries : []string {"injection_test" },
179+ Params : params ,
180+ })
181+ Expect (err ).NotTo (HaveOccurred ())
182+ Expect (capturedQuery ).NotTo (BeEmpty ())
183+ // Escaped: quote and backslash in modelID become \"
184+ Expect (capturedQuery ).To (ContainSubstring (`model_name="x\",namespace=\"other\""` ))
185+ // Should not contain unescaped injection (extra namespace=)
186+ Expect (capturedQuery ).NotTo (MatchRegexp (`namespace="other"` ))
187+ })
149188 })
150189
151190 Describe ("Caching" , func () {
0 commit comments