Skip to content

Commit 4b93037

Browse files
authored
PR #4: Add Positional Argument Matching Support (#387)
This extends argument matching to support positional arguments in addition to keyword arguments. Rules can now validate specific argument values by position index, enabling detection of security issues where dangerous values are passed as positional parameters. The implementation includes proper argument extraction from call sites and supports both numeric literal and complex expression values.
1 parent 4abbff5 commit 4b93037

File tree

3 files changed

+535
-6
lines changed

3 files changed

+535
-6
lines changed

sourcecode-parser/dsl/call_matcher.go

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,23 +200,89 @@ func (e *CallMatcherExecutor) parseKeywordArguments(args []core.Argument) map[st
200200
return kwargs
201201
}
202202

203-
// matchesArguments checks if CallSite arguments satisfy all constraints.
203+
// matchesArguments checks both positional and keyword argument constraints.
204204
//
205205
// Algorithm:
206206
// 1. If no constraints, return true (backward compatibility)
207-
// 2. Parse keyword arguments from CallSite
208-
// 3. Check each constraint against actual values
207+
// 2. Check positional arguments first
208+
// 3. Check keyword arguments
209209
// 4. Return true only if all constraints satisfied
210210
//
211-
// Performance: O(K) where K = number of keyword constraints (~1-3 typically).
211+
// Performance: O(P + K) where P=positional constraints, K=keyword constraints.
212212
func (e *CallMatcherExecutor) matchesArguments(cs *core.CallSite) bool {
213213
// No constraints = always match (backward compatibility)
214-
if len(e.IR.KeywordArgs) == 0 {
214+
if len(e.IR.PositionalArgs) == 0 && len(e.IR.KeywordArgs) == 0 {
215215
return true
216216
}
217217

218+
// Check positional arguments first
219+
if !e.matchesPositionalArguments(cs.Arguments) {
220+
return false
221+
}
222+
223+
// Check keyword arguments
224+
if !e.matchesKeywordArguments(cs.Arguments) {
225+
return false
226+
}
227+
228+
return true // All constraints satisfied!
229+
}
230+
231+
// matchesPositionalArguments checks positional argument constraints.
232+
//
233+
// Algorithm:
234+
// 1. If no positional constraints, return true
235+
// 2. For each position constraint:
236+
// a. Convert position string to int
237+
// b. Check if position exists in arguments
238+
// c. Extract and match argument value
239+
//
240+
// Performance: O(P) where P = number of positional constraints.
241+
func (e *CallMatcherExecutor) matchesPositionalArguments(args []core.Argument) bool {
242+
if len(e.IR.PositionalArgs) == 0 {
243+
return true // No positional constraints
244+
}
245+
246+
for posStr, constraint := range e.IR.PositionalArgs {
247+
// Convert position string to int
248+
pos, err := strconv.Atoi(posStr)
249+
if err != nil {
250+
// Invalid position string - should not happen with valid IR
251+
return false
252+
}
253+
254+
// Check if position exists in arguments
255+
if pos >= len(args) {
256+
return false // Argument at position doesn't exist
257+
}
258+
259+
// Get actual argument value
260+
actualValue := args[pos].Value
261+
262+
// Match against constraint
263+
if !e.matchesArgumentValue(actualValue, constraint) {
264+
return false
265+
}
266+
}
267+
268+
return true
269+
}
270+
271+
// matchesKeywordArguments checks keyword argument constraints (refactored from PR #2).
272+
//
273+
// Algorithm:
274+
// 1. If no keyword constraints, return true
275+
// 2. Parse keyword arguments from CallSite
276+
// 3. Check each constraint against actual values
277+
//
278+
// Performance: O(K) where K = number of keyword constraints.
279+
func (e *CallMatcherExecutor) matchesKeywordArguments(args []core.Argument) bool {
280+
if len(e.IR.KeywordArgs) == 0 {
281+
return true // No keyword constraints
282+
}
283+
218284
// Parse keyword arguments from CallSite
219-
keywordArgs := e.parseKeywordArguments(cs.Arguments)
285+
keywordArgs := e.parseKeywordArguments(args)
220286

221287
// Check each keyword argument constraint
222288
for name, constraint := range e.IR.KeywordArgs {

0 commit comments

Comments
 (0)