Skip to content

feat(policy): add JSONPath predicates for REST request bodies #1848

@nic-nvidia

Description

@nic-nvidia

Problem Statement

OpenShell REST L7 policy can restrict by host, port, binary, HTTP method, path, and query parameters. Some REST APIs put the security-relevant authorization target inside the JSON request body, so path/query policy cannot express the intended least-privilege rule.

Example: Microsoft Graph sendMail is a REST call, not GraphQL:

POST https://graph.microsoft.com/v1.0/users/agent-mailbox@example.com/sendMail
Content-Type: application/json

The path identifies the sending mailbox, but the allowed recipient is only in the request body. With current REST L7 rules, policy can allow or deny the entire sendMail endpoint, but cannot express: allow this tool to send email only to allowed-recipient@example.com, with no cc/bcc recipients.

Proposed Design

Allow protocol: rest rules to include JSONPath predicates over the request body, evaluated fail-closed before forwarding upstream.

Example desired policy:

network_policies:
  outlook_send_self:
    name: outlook-send-self
    endpoints:
      - host: graph.microsoft.com
        port: 443
        protocol: rest
        enforcement: enforce
        rules:
          - allow:
              method: POST
              path: /v1.0/users/agent-mailbox@example.com/sendMail
              json_body:
                max_bytes: 262144
                assertions:
                  - selector: $.message.toRecipients[*].emailAddress.address
                    op: all_equal_ci
                    value: allowed-recipient@example.com
                  - selector: $.message.toRecipients
                    op: length_eq
                    value: 1
                  - selector: $.message.ccRecipients
                    op: empty_or_absent
                  - selector: $.message.bccRecipients
                    op: empty_or_absent
    binaries:
      - path: /usr/local/bin/outlook-send-self-email

Example Microsoft Graph payload:

{
  "message": {
    "subject": "Poem about DGX Spark",
    "body": {
      "contentType": "Text",
      "content": "DGX Spark wakes with silicon light,\nA desk-side star for models in flight.\nThrough tensors bright and memory wide,\nIt keeps tomorrow close beside."
    },
    "toRecipients": [
      {
        "emailAddress": {
          "address": "allowed-recipient@example.com"
        }
      }
    ],
    "ccRecipients": [],
    "bccRecipients": []
  },
  "saveToSentItems": true
}

Required semantics:

  • Apply only to JSON request bodies.
  • Fail closed if the body is missing, malformed, too large, or cannot be evaluated.
  • Do not log request body contents or selected values.
  • Deny rules should continue to take precedence over allow rules.
  • Deny responses should identify which assertion failed without leaking body data.

Alternatives Considered

This is related to #1272. The content_filters proposal there is more flexible and could solve this use case by running a supervisor-side filter script over the request body.

The request here is for a simpler declarative policy UX for common structured JSON API cases. JSONPath predicates would be enough for this example, but either approach would unblock the underlying need: allow or deny REST requests based on fields inside the JSON body.

One workaround we found is to put the recipient validation in a dedicated precompiled helper tool, then use OpenShell policy to restrict the sensitive endpoint so only that tool can call it.

Agent Investigation

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions