feat(approval): Allow for pattern approvals with wildcards
What does this MR do and why?
Resolves: [DAP][Approvals] Allow monolith to handle patte... (#598446)
This change adds pattern-based approval functionality to GitLab's AI workflow tool call system. Previously, users could only approve specific tool calls by providing exact arguments. Now they can also create glob patterns (like "npm" or "git checkout *") that automatically approve any tool calls matching those patterns.
The main benefits are:
- Easier bulk approvals: Instead of approving each individual command, users can approve entire categories of commands with patterns
- More flexible security: For command-line tools, patterns match against the actual command text (like "git checkout feature-branch"), making it intuitive to write approval rules
- Backward compatibility: Existing exact-match approvals continue to work alongside the new pattern system
The implementation adds a new "patterns" field to store these glob patterns, updates the GraphQL API to accept either specific arguments or patterns (but not both), and includes logic to check if a tool call matches any approved pattern. Special handling ensures command tools are matched against the readable command text rather than raw JSON, making patterns more user-friendly.
References
- Main epic: Duo should remember command/tool approvals (gitlab-org#20519)
- Previous sub-epic: [ToolApproval] Create setting toggle for Approv... (gitlab-org#21593 - closed)
Screenshots or screen recordings
| Before | After |
|---|---|
| COMING SOON | ![]() |
How to set up and validate locally
Prerequisite: Tool approval for session must be enabled on the project/group. You need Maintainer access and an existing workflow. Replace <ID> below with your workflow's numeric ID.
Test via the GraphQL explorer (/-/graphql-explorer):
1. Approve a pattern
mutation {
updateDuoWorkflowToolCallApprovals(input: {
workflowId: "gid://gitlab/Ai::DuoWorkflows::Workflow/<ID>"
toolName: "run_command"
pattern: "git checkout *"
}) {
workflow { id toolCallApprovals }
errors
}
}Expected: success, toolCallApprovals contains {"run_command": {"call_args": [], "patterns": ["git checkout *"]}}.
2. Query a matching tool call
query {
duoWorkflowWorkflows(workflowId: "gid://gitlab/Ai::DuoWorkflows::Workflow/<ID>") {
nodes {
toolCallApproved(toolName: "run_command", toolCallArgs: "{\"command\":\"git checkout main\"}")
}
}
}Expected: toolCallApproved: true.
Additional test cases
3. Query a non-matching tool call
query {
duoWorkflowWorkflows(workflowId: "gid://gitlab/Ai::DuoWorkflows::Workflow/<ID>") {
nodes {
toolCallApproved(toolName: "run_command", toolCallArgs: "{\"command\":\"rm -rf /\"}")
}
}
}Expected: toolCallApproved: false.
4. Exact-match approval (backward compat)
mutation {
updateDuoWorkflowToolCallApprovals(input: {
workflowId: "gid://gitlab/Ai::DuoWorkflows::Workflow/<ID>"
toolName: "run_command"
toolCallArgs: "{\"command\":\"ls -la\"}"
}) {
workflow { id toolCallApprovals }
errors
}
}Then query with the same args — expected: toolCallApproved: true.
5. Validation errors
Both args and pattern:
mutation {
updateDuoWorkflowToolCallApprovals(input: {
workflowId: "gid://gitlab/Ai::DuoWorkflows::Workflow/<ID>"
toolName: "run_command"
toolCallArgs: "{\"command\":\"ls\"}"
pattern: "*"
}) {
errors
}
}Expected: "Exactly one of toolCallArgs or pattern must be provided".
Empty pattern:
mutation {
updateDuoWorkflowToolCallApprovals(input: {
workflowId: "gid://gitlab/Ai::DuoWorkflows::Workflow/<ID>"
toolName: "run_command"
pattern: ""
}) {
errors
}
}Expected: "Pattern must be a non-empty string".
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
