@@ -3,6 +3,11 @@ package fuzz
33import (
44 "testing"
55
6+ "github.com/projectdiscovery/goflags"
7+ "github.com/projectdiscovery/nuclei/v3/pkg/protocols"
8+ "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/variables"
9+ "github.com/projectdiscovery/nuclei/v3/pkg/types"
10+ "github.com/projectdiscovery/nuclei/v3/pkg/utils"
611 "github.com/stretchr/testify/require"
712)
813
@@ -37,3 +42,219 @@ func TestRuleMatchKeyOrValue(t *testing.T) {
3742 require .False (t , result , "could not get correct result" )
3843 })
3944}
45+
46+ func TestEvaluateVariables (t * testing.T ) {
47+ t .Run ("keys" , func (t * testing.T ) {
48+ rule := & Rule {
49+ Keys : []string {"{{foo_var}}" },
50+ Part : "query" ,
51+ }
52+
53+ // mock
54+ templateVars := variables.Variable {
55+ InsertionOrderedStringMap : * utils .NewEmptyInsertionOrderedStringMap (1 ),
56+ }
57+ templateVars .Set ("foo_var" , "foo_var_value" )
58+
59+ constants := map [string ]interface {}{
60+ "const_key" : "const_value" ,
61+ }
62+
63+ options := & types.Options {}
64+
65+ // runtime vars (to simulate CLI)
66+ runtimeVars := goflags.RuntimeMap {}
67+ _ = runtimeVars .Set ("runtime_key=runtime_value" )
68+ options .Vars = runtimeVars
69+
70+ executorOpts := & protocols.ExecutorOptions {
71+ Variables : templateVars ,
72+ Constants : constants ,
73+ Options : options ,
74+ }
75+
76+ err := rule .Compile (nil , executorOpts )
77+ require .NoError (t , err , "could not compile rule" )
78+
79+ result := rule .matchKeyOrValue ("foo_var_value" , "test_value" )
80+ require .True (t , result , "should match evaluated variable key" )
81+
82+ result = rule .matchKeyOrValue ("{{foo_var}}" , "test_value" )
83+ require .False (t , result , "should not match unevaluated variable key" )
84+ })
85+
86+ t .Run ("keys-regex" , func (t * testing.T ) {
87+ rule := & Rule {
88+ KeysRegex : []string {"^{{foo_var}}" },
89+ Part : "query" ,
90+ }
91+
92+ templateVars := variables.Variable {
93+ InsertionOrderedStringMap : * utils .NewEmptyInsertionOrderedStringMap (1 ),
94+ }
95+ templateVars .Set ("foo_var" , "foo_var_value" )
96+
97+ executorOpts := & protocols.ExecutorOptions {
98+ Variables : templateVars ,
99+ Constants : map [string ]interface {}{},
100+ Options : & types.Options {},
101+ }
102+
103+ err := rule .Compile (nil , executorOpts )
104+ require .NoError (t , err , "could not compile rule" )
105+
106+ result := rule .matchKeyOrValue ("foo_var_value" , "test_value" )
107+ require .True (t , result , "should match evaluated variable in regex" )
108+
109+ result = rule .matchKeyOrValue ("other_key" , "test_value" )
110+ require .False (t , result , "should not match non-matching key" )
111+ })
112+
113+ t .Run ("values-regex" , func (t * testing.T ) {
114+ rule := & Rule {
115+ ValuesRegex : []string {"{{foo_var}}" },
116+ Part : "query" ,
117+ }
118+
119+ templateVars := variables.Variable {
120+ InsertionOrderedStringMap : * utils .NewEmptyInsertionOrderedStringMap (1 ),
121+ }
122+ templateVars .Set ("foo_var" , "test_pattern" )
123+
124+ executorOpts := & protocols.ExecutorOptions {
125+ Variables : templateVars ,
126+ Constants : map [string ]interface {}{},
127+ Options : & types.Options {},
128+ }
129+
130+ err := rule .Compile (nil , executorOpts )
131+ require .NoError (t , err , "could not compile rule" )
132+
133+ result := rule .matchKeyOrValue ("test_key" , "test_pattern" )
134+ require .True (t , result , "should match evaluated variable in values regex" )
135+
136+ result = rule .matchKeyOrValue ("test_key" , "other_value" )
137+ require .False (t , result , "should not match non-matching value" )
138+ })
139+
140+ // complex vars w/ consts and runtime vars
141+ t .Run ("complex-variables" , func (t * testing.T ) {
142+ rule := & Rule {
143+ Keys : []string {"{{template_var}}" , "{{const_key}}" , "{{runtime_key}}" },
144+ Part : "query" ,
145+ }
146+
147+ templateVars := variables.Variable {
148+ InsertionOrderedStringMap : * utils .NewEmptyInsertionOrderedStringMap (1 ),
149+ }
150+ templateVars .Set ("template_var" , "template_value" )
151+
152+ constants := map [string ]interface {}{
153+ "const_key" : "const_value" ,
154+ }
155+
156+ options := & types.Options {}
157+ runtimeVars := goflags.RuntimeMap {}
158+ _ = runtimeVars .Set ("runtime_key=runtime_value" )
159+ options .Vars = runtimeVars
160+
161+ executorOpts := & protocols.ExecutorOptions {
162+ Variables : templateVars ,
163+ Constants : constants ,
164+ Options : options ,
165+ }
166+
167+ err := rule .Compile (nil , executorOpts )
168+ require .NoError (t , err , "could not compile rule" )
169+
170+ result := rule .matchKeyOrValue ("template_value" , "test" )
171+ require .True (t , result , "should match template variable" )
172+
173+ result = rule .matchKeyOrValue ("const_value" , "test" )
174+ require .True (t , result , "should match constant" )
175+
176+ result = rule .matchKeyOrValue ("runtime_value" , "test" )
177+ require .True (t , result , "should match runtime variable" )
178+
179+ result = rule .matchKeyOrValue ("{{template_var}}" , "test" )
180+ require .False (t , result , "should not match unevaluated template variable" )
181+ })
182+
183+ t .Run ("invalid-variables" , func (t * testing.T ) {
184+ rule := & Rule {
185+ Keys : []string {"{{nonexistent_var}}" },
186+ Part : "query" ,
187+ }
188+
189+ executorOpts := & protocols.ExecutorOptions {
190+ Variables : variables.Variable {
191+ InsertionOrderedStringMap : * utils .NewEmptyInsertionOrderedStringMap (0 ),
192+ },
193+ Constants : map [string ]interface {}{},
194+ Options : & types.Options {},
195+ }
196+
197+ err := rule .Compile (nil , executorOpts )
198+ if err != nil {
199+ require .Contains (t , err .Error (), "unresolved" , "error should mention unresolved variables" )
200+ } else {
201+ result := rule .matchKeyOrValue ("some_key" , "some_value" )
202+ require .False (t , result , "should not match when variables are unresolved" )
203+ }
204+ })
205+
206+ t .Run ("evaluateVars-function" , func (t * testing.T ) {
207+ rule := & Rule {}
208+
209+ templateVars := variables.Variable {
210+ InsertionOrderedStringMap : * utils .NewEmptyInsertionOrderedStringMap (1 ),
211+ }
212+ templateVars .Set ("test_var" , "test_value" )
213+
214+ constants := map [string ]interface {}{
215+ "const_var" : "const_value" ,
216+ }
217+
218+ options := & types.Options {}
219+ runtimeVars := goflags.RuntimeMap {}
220+ _ = runtimeVars .Set ("runtime_var=runtime_value" )
221+ options .Vars = runtimeVars
222+
223+ executorOpts := & protocols.ExecutorOptions {
224+ Variables : templateVars ,
225+ Constants : constants ,
226+ Options : options ,
227+ }
228+
229+ rule .options = executorOpts
230+
231+ // Test simple var substitution
232+ result , err := rule .evaluateVars ("{{test_var}}" )
233+ require .NoError (t , err , "should evaluate template variable" )
234+ require .Equal (t , "test_value" , result , "should return evaluated value" )
235+
236+ // Test constant substitution
237+ result , err = rule .evaluateVars ("{{const_var}}" )
238+ require .NoError (t , err , "should evaluate constant" )
239+ require .Equal (t , "const_value" , result , "should return constant value" )
240+
241+ // Test runtime var substitution
242+ result , err = rule .evaluateVars ("{{runtime_var}}" )
243+ require .NoError (t , err , "should evaluate runtime variable" )
244+ require .Equal (t , "runtime_value" , result , "should return runtime value" )
245+
246+ // Test mixed content
247+ result , err = rule .evaluateVars ("prefix-{{test_var}}-suffix" )
248+ require .NoError (t , err , "should evaluate mixed content" )
249+ require .Equal (t , "prefix-test_value-suffix" , result , "should return mixed evaluated content" )
250+
251+ // Test unresolved var - should either fail during evaluation or return original string
252+ result2 , err := rule .evaluateVars ("{{nonexistent}}" )
253+ if err != nil {
254+ require .Contains (t , err .Error (), "unresolved" , "should fail for unresolved variable" )
255+ } else {
256+ // If no error, it should return the original unresolved variable
257+ require .Equal (t , "{{nonexistent}}" , result2 , "should return original string for unresolved variable" )
258+ }
259+ })
260+ }
0 commit comments