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

Screenshots or screen recordings

Before After
COMING SOON image

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.

Edited by Dylan Bernardi

Merge request reports

Loading