Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Make isInRange Operator Variadic for ipaddr Type

  • Reference Issues: N/A
  • Implementation PR(s):

Timeline

  • Started: 2025-06-26
  • Accepted: TBD
  • Stabilized: TBD

Summary

This RFC proposes modifying the existing .isInRange operator to Cedar's ipaddr extension type. The operator currently is unary, taking a single IP Address; but expanding it to be variadic will enable simplification of policies which check for containment in more than one IP Address block. This maintains backwards compatibility, while simplifying policies that must verify a request's source IP against an allowlist of allowed CIDR blocks, improving policy readability, scalability, and maintainability.

Basic example

A primary use case for the ipaddr type is to restrict access based on the source IP address of a request, which is often provided in the request context. When an application needs to check against a list of multiple allowed IP ranges (e.g., corporate networks, trusted partner networks), the current approach becomes verbose and difficult to manage.

// Policy to allow access only from corporate IP ranges (current method)
permit (
  principal,
  action == Action::"accessAdminDashboard",
  resource
)
when {
  context.network.source_ip.isInRange(ip("198.51.100.0/24")) ||
  context.network.source_ip.isInRange(ip("203.0.113.0/24")) ||
  context.network.source_ip.isInRange(ip("192.0.2.0/25")) ||
  context.network.source_ip.isInRange(ip("2001:db8:a001::/48"))
};

The proposed change simplifies this logic into a single function call making the policy's intent clearer.

// Policy to allow access only from corporate IP ranges (proposed method)
permit (
  principal,
  action == Action::"accessAdminDashboard",
  resource
)
when {
  context.source_ip.isInRange(
    ip("198.51.100.0/24"),
    ip("203.0.113.0/24"),
    ip("192.0.2.0/25"),
    ip("2001:db8:a001::/48")
  )
};

And we can represent the variadic expression as follows

"conditions": [
    {
        "kind": "when",
        "body": {
            "isInRange": [
                {
                    ".": {
                        "left": {
                            "Var": "context"
                        },
                        "attr": "source_ip"
                    }
                },
                {
                    "ip": [
                        {
                            "Value": "198.51.100.0/24"
                        }
                    ]
                },
                {
                    "ip": [
                        {
                            "Value": "203.0.113.0/24"
                        }
                    ]
                },
                 {
                    "ip": [
                        {
                            "Value": "192.0.2.0/25"
                        }
                    ]
                },
                {
                    "ip": [
                        {
                            "Value": "2001:db8:a001::/48"
                        }
                    ]
                }
            ]
        }
    }
]

Motivation

Making the .isInRange operator variadic is motivated by several key factors that align with Cedar's goals of expressiveness and readability.

Smaller JSON Formatted Policies

The current method of chaining .isInRange() calls with || operators creates a large and object tree that can quickly exceeded the stack depth and recursion limit of systems. The use of a variadic operator would allow for the reduction in not only the policy objet's size, but also the overall tree depth (the origin of this RFC).

Improved Policy Readability and Authoring

Cedar policies are designed to be easy to read and understand by both technical and non-technical stakeholders. Chaining .isInRange() calls with || operators creates syntactically complex expressions that obscure the simple intent of "is this IP in our allowlist?". As the list of IP ranges grows, the policy becomes increasingly unwieldy. The proposed operator replaces a complex boolean expression with a single, declarative function call, significantly improving the clarity and conciseness of the policy.

It should be explicitly documented that this operator performs an OR operation among the ranges provided, such that the provided IP address is in at least one of the listed ranges, not all of them.

Detailed design

Implementing this RFC requires extending the parser, evaluator, typechecker, and symbolic compiler to allow .isInRange to take one or more arguments. Since .isInRange is an extension function, there is no need to change the AST, since the AST nodes for extension functions already take as input a list of arguments.

The evaluator should be modified so that it no longer forces .isInRange to have exactly one argument. Instead, the evaluator checks that at least one argument is provided and that all provided arguments are of type ipaddr. If not, the evaluation errors. Otherwise, the evaluator checks if the receiver IP address is in the range of any argument IP addresses.

The typechecker and symbolic compiler should be modified similarly to the evaluator.

Similar to other functions, the arguments should be eagerly evaluated. In the event that an error occurs, or one of the arguments passed to the function is not an IP address, the function call should produce an error.

Drawbacks

  • Increased Language Surface Area: Any new feature adds to the complexity of the language. However, the proposed operator's logic is a minimal change, making it an intuitive and easily understood addition rather than a complex new concept.

  • Implementation Effort: This is a substantial feature that requires coordinated development effort across the cedar core repository (parser, validator, evaluator) and the cedar-spec repository for the formal model. This is a standard and expected cost for any significant language enhancement, similar to the effort required for features like the isEmpty operator.

Alternatives

The Status Quo (Chained ||)

This is the current method for checking against multiple IP ranges. As detailed extensively in the Motivation section, this approach is not scalable and suffers from poor readability and maintainability, especially as the number of IP ranges grows. It is the problem this RFC aims to solve.

Unresolved questions

Detailed Design requires input from someone more familiar with developing core Cedar functionality and SMT.