Skip to content

feat(core): Add search term information to protos.#3547

Merged
c-r33d merged 1 commit into
mainfrom
dspx-2716-protos
Jun 12, 2026
Merged

feat(core): Add search term information to protos.#3547
c-r33d merged 1 commit into
mainfrom
dspx-2716-protos

Conversation

@c-r33d

@c-r33d c-r33d commented May 29, 2026

Copy link
Copy Markdown
Contributor

1.) Add common search object that will be used for substring matching in Postgres.

Summary by CodeRabbit

  • New Features

    • Added optional search capability to list operations across multiple services, including attributes, key access servers, keys, namespaces, obligations, registered resources, and subject mappings.
  • Documentation

    • Enhanced API validation descriptions with improved constraint documentation and clearer examples for URL and identifier fields.
    • Updated error response structure and schema organization for improved consistency across services.

@github-actions github-actions Bot added comp:policy Policy Configuration ( attributes, subject mappings, resource mappings, kas registry) docs Documentation size/s labels May 29, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a standardized search capability across the core policy services. By defining a common Search message and incorporating it into existing list request structures, the change enables consistent support for substring-based filtering, which will be utilized for backend database queries.

Highlights

  • New Search Protocol: Introduced a new Search message in service/policy/selectors.proto containing a term field to facilitate substring matching.
  • Request Integration: Integrated the policy.Search object as an optional field into various list request messages across the policy service.
  • Documentation Update: Updated the generated gRPC documentation to include the new Search message and its usage in request definitions.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: docs/openapi/**/* (7)
    • docs/openapi/policy/attributes/attributes.openapi.yaml
    • docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml
    • docs/openapi/policy/namespaces/namespaces.openapi.yaml
    • docs/openapi/policy/obligations/obligations.openapi.yaml
    • docs/openapi/policy/registeredresources/registered_resources.openapi.yaml
    • docs/openapi/policy/selectors.openapi.yaml
    • docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
  • Ignored by pattern: protocol/**/* (7)
    • protocol/go/policy/attributes/attributes.pb.go
    • protocol/go/policy/kasregistry/key_access_server_registry.pb.go
    • protocol/go/policy/namespaces/namespaces.pb.go
    • protocol/go/policy/obligations/obligations.pb.go
    • protocol/go/policy/registeredresources/registered_resources.pb.go
    • protocol/go/policy/selectors.pb.go
    • protocol/go/policy/subjectmapping/subject_mapping.pb.go
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


A query string for search we find, / To leave the simple lists behind. / With protos updated, clean and bright, / The data flows with newfound light.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Search message and adds an optional search field to various list request messages across multiple protobuf files to support search functionality. A review comment identifies an inconsistency where the protobuf definition in selectors.proto uses the field name term, while the generated documentation in docs/grpc/index.html refers to it as query. It is recommended to update the protobuf field name to query to maintain consistency.

Comment thread service/policy/selectors.proto
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@c-r33d, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 38 minutes and 51 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1898a2d1-71d1-4bf4-bb5e-3365560a5736

📥 Commits

Reviewing files that changed from the base of the PR and between 6fca8b9 and acac21f.

⛔ Files ignored due to path filters (7)
  • protocol/go/policy/attributes/attributes.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/kasregistry/key_access_server_registry.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/namespaces/namespaces.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/obligations/obligations.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/registeredresources/registered_resources.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/selectors.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/subjectmapping/subject_mapping.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (16)
  • docs/grpc/index.html
  • docs/openapi/policy/attributes/attributes.openapi.yaml
  • docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml
  • docs/openapi/policy/namespaces/namespaces.openapi.yaml
  • docs/openapi/policy/obligations/obligations.openapi.yaml
  • docs/openapi/policy/registeredresources/registered_resources.openapi.yaml
  • docs/openapi/policy/selectors.openapi.yaml
  • docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
  • service/policy/attributes/attributes.proto
  • service/policy/kasregistry/key_access_server_registry.proto
  • service/policy/namespaces/namespaces.proto
  • service/policy/obligations/obligations.proto
  • service/policy/registeredresources/registered_resources.proto
  • service/policy/selectors.proto
  • service/policy/selectors_test.go
  • service/policy/subjectmapping/subject_mapping.proto
📝 Walkthrough

Walkthrough

The PR adds a reusable policy.Search selector and threads it into several list-request contracts. It also rewires and reshapes many OpenAPI schemas and RPC operation bindings, including entity/resource compositions, Connect error payloads, KAS endpoints, and multiple request validation blocks.

Changes

Search filter contract

Layer / File(s) Summary
Search type and validation
service/policy/selectors.proto, service/policy/selectors_test.go, docs/grpc/index.html, docs/openapi/policy/selectors.openapi.yaml
Search is added with a bounded term field, tested, and documented in gRPC/OpenAPI schema docs.
Search fields in list requests
service/policy/attributes/attributes.proto, service/policy/kasregistry/key_access_server_registry.proto, service/policy/namespaces/namespaces.proto, service/policy/obligations/obligations.proto, service/policy/registeredresources/registered_resources.proto, service/policy/subjectmapping/subject_mapping.proto, docs/grpc/index.html, docs/openapi/policy/*
List requests across attributes, KAS registry, namespaces, obligations, registered resources, and subject mapping gain optional search fields and matching documentation/schema references.

OpenAPI path and schema contract updates

Layer / File(s) Summary
Operation request/response rewiring
docs/openapi/authorization/*, docs/openapi/entityresolution/*, docs/openapi/kas/kas.openapi.yaml, docs/openapi/policy/actions/*, docs/openapi/policy/attributes/*, docs/openapi/policy/kasregistry/*, docs/openapi/policy/keymanagement/*, docs/openapi/policy/namespaces/*, docs/openapi/policy/obligations/*, docs/openapi/policy/registeredresources/*, docs/openapi/policy/resourcemapping/*, docs/openapi/policy/subjectmapping/*, docs/openapi/policy/unsafe/*
Many REST operations are realigned to the matching request and response schema references, including deprecated and renamed paths.
Schema composition and validation
docs/openapi/authorization/authorization.openapi.yaml, docs/openapi/authorization/v2/authorization.openapi.yaml, docs/openapi/common/common.openapi.yaml, docs/openapi/entity/entity.openapi.yaml, docs/openapi/entityresolution/entity_resolution.openapi.yaml, docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml, docs/openapi/kas/kas.openapi.yaml, docs/openapi/policy/actions/actions.openapi.yaml, docs/openapi/policy/attributes/attributes.openapi.yaml, docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml, docs/openapi/policy/keymanagement/key_management.openapi.yaml, docs/openapi/policy/namespaces/namespaces.openapi.yaml, docs/openapi/policy/objects.openapi.yaml, docs/openapi/policy/obligations/obligations.openapi.yaml, docs/openapi/policy/registeredresources/registered_resources.openapi.yaml, docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml, docs/openapi/policy/selectors.openapi.yaml, docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml, docs/openapi/policy/unsafe/unsafe.openapi.yaml, docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml
Entity/action/resource schemas are reshaped with oneOf variants, nullable fields, updated regex descriptions, revised enum placement, example changes, and Connect error payload updates.
Endpoint-specific contract rewrites
docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml, docs/openapi/policy/namespaces/namespaces.openapi.yaml, docs/openapi/policy/attributes/attributes.openapi.yaml, docs/openapi/policy/obligations/obligations.openapi.yaml, docs/openapi/policy/registeredresources/registered_resources.openapi.yaml, docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml, docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml, docs/openapi/policy/unsafe/unsafe.openapi.yaml
Request models gain exclusive-field oneOf structures, validation text, search fields, and URI/name rules across the service-specific OpenAPI specs.

Sequence Diagram(s)

None.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • opentdf/platform#3554: Adds search support for ListAttributes, which is the same feature family as the new policy.Search contract here.
  • opentdf/platform#3556: Implements search for ListSubjectConditionSets, matching one of the new request fields in this PR.
  • opentdf/platform#3555: Implements search for ListSubjectMappings, matching another new request field in this PR.

Suggested reviewers

  • elizabethhealy
  • alkalescent

Poem

🐇 I hop through schemas, neat and true,
A Search term blooms in docs anew.
Paths and payloads line up tight,
With regex moons and enums in sight.
Hop, hop — the contracts shine tonight!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dspx-2716-protos

@c-r33d c-r33d changed the title feat(core): Add search query information to protos. feat(core): Add search term information to protos. May 29, 2026
@c-r33d c-r33d force-pushed the dspx-2716-protos branch from 419fb96 to 6fca8b9 Compare May 29, 2026 20:55
@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 179.908162ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 104.540904ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 448.614989ms
Throughput 222.91 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.393216848s
Average Latency 431.781758ms
Throughput 115.23 requests/second

@c-r33d c-r33d marked this pull request as ready for review June 12, 2026 18:19
@c-r33d c-r33d requested review from a team as code owners June 12, 2026 18:19
@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 148.240624ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 74.361098ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 355.899129ms
Throughput 280.98 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 34.639431171s
Average Latency 344.94968ms
Throughput 144.34 requests/second

Signed-off-by: Chris Reed <creed@virtru.com>
@c-r33d c-r33d force-pushed the dspx-2716-protos branch from 4825285 to acac21f Compare June 12, 2026 18:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
docs/openapi/common/common.openapi.yaml (1)

23-56: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore the “at least one identifier” constraint on these shared schemas.

After dropping the conditional required branches, both common.IdFqnIdentifier and common.IdNameIdentifier now accept {}. That weakens every request that reuses them and moves a shared validation rule into each individual service.

Possible fix
     common.IdFqnIdentifier:
       type: object
+      oneOf:
+        - required:
+            - id
+        - required:
+            - fqn
       properties:
         id:
           type: string
@@
     common.IdNameIdentifier:
       type: object
+      oneOf:
+        - required:
+            - id
+        - required:
+            - name
       properties:
         id:
           type: string
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/common/common.openapi.yaml` around lines 23 - 56, The shared
schemas common.IdFqnIdentifier and common.IdNameIdentifier currently allow an
empty object because the conditional required branches were removed; restore the
"at least one identifier" constraint by adding a oneOf to each schema that
enforces either id is present or the other identifier is present (for
common.IdFqnIdentifier oneOf: required ['id'] OR required ['fqn']; for
common.IdNameIdentifier oneOf: required ['id'] OR required ['name']), keeping
additionalProperties: false and existing property definitions intact so
consumers cannot pass {}.
docs/openapi/authorization/authorization.openapi.yaml (1)

799-808: ⚠️ Potential issue | 🟠 Major

Fix policy.KasPublicKey.alg guard using not.enum: [0] with $ref: policy.KasPublicKeyAlgEnum
policy.KasPublicKeyAlgEnum is a type: string enum that includes KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED, but the alg schema excludes numeric 0 (not: enum: - 0) while still $ref’ing the string enum—so the guard no longer excludes the intended unspecified value and widens the accepted contract. In these openapi: 3.1.0 specs, sibling not with $ref is applied, so this impacts validation.

  • docs/openapi/authorization/authorization.openapi.yaml (799-808)
  • docs/openapi/authorization/v2/authorization.openapi.yaml
  • docs/openapi/policy/actions/actions.openapi.yaml
  • Also present in: docs/openapi/policy/attributes/attributes.openapi.yaml, docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml, docs/openapi/policy/namespaces/namespaces.openapi.yaml, docs/openapi/policy/objects.openapi.yaml, docs/openapi/policy/obligations/obligations.openapi.yaml, docs/openapi/policy/registeredresources/registered_resources.openapi.yaml, docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml, docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml, docs/openapi/policy/unsafe/unsafe.openapi.yaml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/authorization/authorization.openapi.yaml` around lines 799 -
808, The alg schema for policy.KasPublicKey currently uses a numeric exclusion
(not: enum: [- 0]) alongside a $ref to policy.KasPublicKeyAlgEnum (a string
enum), which fails to exclude the unspecified string value; locate every
occurrence of the alg property referencing policy.KasPublicKeyAlgEnum (e.g.,
policy.KasPublicKey.alg in authorization.openapi.yaml and the listed policy
files) and replace the numeric guard with a string exclusion that matches the
enum member (for example change not: enum: [0] to not: const:
"KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED" or not: enum:
["KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED"]); ensure the $ref to
policy.KasPublicKeyAlgEnum remains and apply the same replacement in all files
mentioned.
docs/openapi/policy/registeredresources/registered_resources.openapi.yaml (1)

1380-1425: ⚠️ Potential issue | 🟠 Major

Fix parent additionalProperties: false so oneOf/allOf branch fields aren’t rejected as “additional”.

  • policy.registeredresources.ActionAttributeValue (1387-1432): the parent object sets additionalProperties: false, but actionId/actionName and attributeValueFqn/attributeValueId are only defined inside oneOf branches—so validators reject the intended fields.
  • policy.registeredresources.GetRegisteredResourceRequest (1569-1605): same pattern—additionalProperties: false at the parent allows only namespaceFqn/namespaceId, while id/name exist only in oneOf branches and are rejected.

Promote these fields into the parent schema’s properties (keeping the required lists inside the branch schemas), or remove additionalProperties: false from the parent object. json-schema.org

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/registeredresources/registered_resources.openapi.yaml`
around lines 1380 - 1425, The parent schemas
policy.registeredresources.ActionAttributeValue and
policy.registeredresources.GetRegisteredResourceRequest set
additionalProperties: false while defining actionable fields only inside
oneOf/allOf branches, causing validators to treat actionId/actionName and
attributeValueFqn/attributeValueId (and id/name for
GetRegisteredResourceRequest) as additional; fix by promoting those branch
properties into the parent schema's properties (declare actionId, actionName,
attributeValueFqn, attributeValueId in ActionAttributeValue and
id/name/namespaceFqn/namespaceId in GetRegisteredResourceRequest) and keep the
branch-specific required arrays inside the oneOf branches, or alternatively
remove additionalProperties: false from the parent—ensure the parent titles
(ActionAttributeValue, GetRegisteredResourceRequest) reflect the updated
properties so branch validation succeeds.
docs/openapi/policy/obligations/obligations.openapi.yaml (1)

981-990: ⚠️ Potential issue | 🟠 Major

Fix KasPublicKey.alg not.enum mismatch (0 vs KasPublicKeyAlgEnum strings)

In docs/openapi/policy/obligations/obligations.openapi.yaml, docs/openapi/policy/registeredresources/registered_resources.openapi.yaml, and docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml, policy.KasPublicKey.alg uses not: { enum: [0] }, but #/components/schemas/policy.KasPublicKeyAlgEnum is type: string with enum values including KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED. Since 0 won’t match any of the allowed string enum members, the not clause doesn’t exclude the intended UNSPECIFIED value. Replace 0 with KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED in the not.enum list. (json-schema.org)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/obligations/obligations.openapi.yaml` around lines 981 -
990, The not.enum value for KasPublicKey.alg currently contains the numeric 0
which doesn't match the string-based policy.KasPublicKeyAlgEnum; update the
not.enum list to use the enum member name KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED
instead of 0 in the KasPublicKey.alg schema (affecting the KasPublicKey
definition in obligations.openapi.yaml, registered_resources.openapi.yaml, and
resource_mapping.openapi.yaml) so the not constraint correctly excludes the
unspecified string enum value from policy.KasPublicKeyAlgEnum.
docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml (1)

848-857: ⚠️ Potential issue | 🟠 Major

Fix ineffective alg UNSPECIFIED guard in both OpenAPI files

In docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml (lines 848-857) and docs/openapi/policy/unsafe/unsafe.openapi.yaml (lines 814-823), alg uses $ref: '#/components/schemas/policy.KasPublicKeyAlgEnum' where policy.KasPublicKeyAlgEnum is a string enum including KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED, but the not guard excludes enum: [0] (a number). Since no valid alg string value equals numeric 0, the not condition never matches, so KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED remains permitted by the contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml` around lines
848 - 857, The `alg` property uses $ref to policy.KasPublicKeyAlgEnum (a string
enum) but the `not` guard incorrectly excludes numeric 0 so the UNSPECIFIED
string value still passes; update the `not.enum` to exclude the actual enum
member string (e.g., "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED") or otherwise use
`not.const: "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED"` for the `alg` property in
both subject_mapping.openapi.yaml and unsafe.openapi.yaml so the UNSPECIFIED
string is correctly forbidden; locate the `alg` field and modify its `not`
clause referencing policy.KasPublicKeyAlgEnum accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/openapi/authorization/authorization.openapi.yaml`:
- Around line 651-656: The examples for the schema symbol
google.protobuf.Timestamp are invalid for format: date-time (they are duration
literals like "1s"); update the examples to RFC3339 date-time strings (e.g.
"1970-01-01T00:00:01Z" and "2020-01-01T00:00:00.000340012Z" or similar ISO8601
UTC timestamps) so they match the declared format: date-time and the schema
description; ensure this change is applied wherever google.protobuf.Timestamp is
declared.
- Around line 297-359: The parent schema authorization.Entity (and other similar
schemas mentioned) has additionalProperties: false at the top level while all
valid variant keys live inside oneOf branches, causing legitimate payloads to be
rejected; update the schema by removing or relaxing the parent-level
additionalProperties and instead add additionalProperties: false inside each
oneOf branch (or refactor to an allOf + shared base or use unevaluatedProperties
where supported) so that when a branch is selected its properties are allowed;
target the authorization.Entity schema (and the other listed schemas like
entity.Entity, authorization.v2.Resource, policy.Action,
authorization.v2.EntityIdentifier, kas.KeyAccessRewrapResult) and ensure each
oneOf variant object includes its own additionalProperties rule or convert to an
allOf pattern that composes the shared fields with branch-specific constraints.

In `@docs/openapi/policy/attributes/attributes.openapi.yaml`:
- Around line 1097-1100: The OpenAPI schemas emit array-only keywords
(minItems/maxItems/uniqueItems) inside scalar item schemas — locate the array
schemas in attributes.openapi.yaml and objects.openapi.yaml where you have an
enclosing "type: array" with "items: { type: string, minItems: ... }" (look for
the "items" blocks shown in the diff) and remove any
minItems/maxItems/uniqueItems from the scalar "items" objects; then add those
constraints to the enclosing array node (the object with "type: array") so the
constraints apply to the array itself rather than the item schema.
- Around line 1150-1153: The schema for policy.KasPublicKey.alg is attempting to
exclude the UNSPECIFIED enum by using not.enum: [0], but the generated enums are
strings so that exclusion never matches; update the YAMLs (where you see
policy.KasPublicKey.alg and references to policy.KasPublicKeyAlgEnum /
KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED) to exclude the string value instead (change
not.enum: - 0 to not.enum: - "0") so the UNSPECIFIED value is actually rejected
by the OpenAPI schema.
- Around line 1946-1984: The top-level deprecated id property was moved out of
the oneOf, so the oneOf no longer enforces exclusivity; restore exclusivity by
moving the deprecated id back into the oneOf as its own alternative (i.e., add a
oneOf branch with properties: id (type: string, format: uuid, deprecated: true)
and required: [id]) for each affected schema (e.g., GetAttributeRequest with
attributeId/fqn, and the equivalent request schemas in
key_access_server_registry and namespaces that reference
valueId/kasId/namespaceId), so oneOf again enforces mutual exclusion between id
and the new identifiers. Ensure additionalProperties/description stay unchanged.
- Around line 2352-2397: The connect.error schema uses a single "detail"
property of type google.protobuf.Any but Connect's HTTP JSON uses "details" as
an array of google.protobuf.Any; update the connect.error schema (symbol:
connect.error) in the affected OpenAPI files to rename "detail" to "details" and
change its type to an array of google.protobuf.Any (i.e., items referencing
google.protobuf.Any), preserving additionalProperties and descriptions; apply
the same change in the other listed schemas (those that define connect.error) so
the property name and cardinality match Connect’s HTTP error representation.

In `@docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml`:
- Around line 1325-1371: The schema GetKeyAccessServerRequest currently declares
"id" as a top-level property so objects like {id, kasId} validate; to enforce
the intended exclusivity, remove the top-level properties.id and instead add a
separate oneOf branch that defines an id-only object (properties: id {type:
string, format: uuid}, required: [id], deprecated: true) alongside the existing
kasId/name/uri branches — i.e. make the top-level oneOf include the id-only
branch and the kasId-only, name-only, uri-only branches so requests must match
exactly one branch.

In `@docs/openapi/policy/obligations/obligations.openapi.yaml`:
- Around line 726-731: The examples for the schema google.protobuf.Timestamp
currently use duration literals; update the examples to valid RFC3339 date-time
strings (e.g., "2023-01-02T15:04:05Z" and "2023-01-02T15:04:05.000340012Z") and
replace the duration examples in the google.protobuf.Timestamp example blocks so
all three files (the google.protobuf.Timestamp schema in
obligations.openapi.yaml, registered_resources.openapi.yaml, and
resource_mapping.openapi.yaml) use the same RFC3339 example values for
consistency.
- Around line 822-857: The policy.Action schema currently defines custom and
standard only inside the oneOf branches while the parent object sets
additionalProperties: false and lacks those properties, causing validators to
reject them; fix by adding top-level properties entries for "custom" and
"standard" in the policy.Action properties block (matching the types/$ref used
in the oneOf branches) and keep the existing oneOf to enforce which variant is
present (or adjust the oneOf to reference the top-level properties), ensuring
additionalProperties: false no longer blocks the defined fields; update the
policy.Action schema (symbols: policy.Action, custom, standard,
additionalProperties, oneOf) accordingly in all three files.

In `@docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml`:
- Around line 593-598: The examples for the schema named
google.protobuf.Timestamp are invalid duration literals; update the examples in
docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml (and the same
change in docs/openapi/policy/unsafe/unsafe.openapi.yaml) so they are valid RFC
3339 date-time strings that match the declared type/format (e.g.,
"2023-01-01T00:00:00Z", "2023-01-01T12:34:56.789Z") instead of
"1s"/"1.000340012s", ensuring the google.protobuf.Timestamp entry remains type:
string and format: date-time.
- Around line 689-724: The schemas policy.Action and policy.PublicKey are
unsatisfiable because their variant fields live only inside oneOf branches while
the parent sets additionalProperties: false; fix by either (A) moving the
variant properties (for policy.Action: custom, standard; for policy.PublicKey:
cached, remote) into the parent properties block and keep the oneOf for
required/validation, or (B) replace the parent-level additionalProperties: false
with unevaluatedProperties: false so properties validated by oneOf branches are
not treated as "additional"; update the policy.Action and policy.PublicKey
schema blocks accordingly and retain existing oneOf titles/required constraints.
- Around line 1821-1866: The connect.error schema currently defines a singular
property named "detail" of type google.protobuf.Any but must match
ConnectRPC/google.rpc.Status which uses a repeated "details" field; update the
connect.error schema (the connect.error component) to replace the "detail"
property with "details" as an array of google.protobuf.Any (e.g., type: array,
items: $ref: '`#/components/schemas/google.protobuf.Any`'), keep
additionalProperties/title/description unchanged and ensure any references or
examples that mention google.rpc.Status.details remain consistent with the new
plural field name.

In `@service/policy/selectors_test.go`:
- Around line 12-25: Extend TestSearchTermValidation to cover edge cases: add
assertions that an empty Term and a whitespace-only Term pass validation, that
multibyte/unicode strings are validated according to the intended length
semantics (add a test using runes vs bytes to confirm whether policy.Search.Term
limit applies to characters or bytes), and that strings containing special
characters pass/fail appropriately; update tests that call protovalidate.New()
and v.Validate(&policy.Search{Term: ...}) to include these new cases and assert
success or string.max_len error as appropriate.

In `@service/policy/selectors.proto`:
- Around line 67-69: Add a short comment above the Search.message or the term
field explaining why the string max_len is set to 253 (e.g., to match DNS FQDN
limits used by the system and to constrain substring matching semantics in
Postgres), so future maintainers see the rationale; reference the Search message
and the term field with the (buf.validate.field).string.max_len = 253 constraint
in the comment.

---

Outside diff comments:
In `@docs/openapi/authorization/authorization.openapi.yaml`:
- Around line 799-808: The alg schema for policy.KasPublicKey currently uses a
numeric exclusion (not: enum: [- 0]) alongside a $ref to
policy.KasPublicKeyAlgEnum (a string enum), which fails to exclude the
unspecified string value; locate every occurrence of the alg property
referencing policy.KasPublicKeyAlgEnum (e.g., policy.KasPublicKey.alg in
authorization.openapi.yaml and the listed policy files) and replace the numeric
guard with a string exclusion that matches the enum member (for example change
not: enum: [0] to not: const: "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED" or not:
enum: ["KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED"]); ensure the $ref to
policy.KasPublicKeyAlgEnum remains and apply the same replacement in all files
mentioned.

In `@docs/openapi/common/common.openapi.yaml`:
- Around line 23-56: The shared schemas common.IdFqnIdentifier and
common.IdNameIdentifier currently allow an empty object because the conditional
required branches were removed; restore the "at least one identifier" constraint
by adding a oneOf to each schema that enforces either id is present or the other
identifier is present (for common.IdFqnIdentifier oneOf: required ['id'] OR
required ['fqn']; for common.IdNameIdentifier oneOf: required ['id'] OR required
['name']), keeping additionalProperties: false and existing property definitions
intact so consumers cannot pass {}.

In `@docs/openapi/policy/obligations/obligations.openapi.yaml`:
- Around line 981-990: The not.enum value for KasPublicKey.alg currently
contains the numeric 0 which doesn't match the string-based
policy.KasPublicKeyAlgEnum; update the not.enum list to use the enum member name
KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED instead of 0 in the KasPublicKey.alg schema
(affecting the KasPublicKey definition in obligations.openapi.yaml,
registered_resources.openapi.yaml, and resource_mapping.openapi.yaml) so the not
constraint correctly excludes the unspecified string enum value from
policy.KasPublicKeyAlgEnum.

In `@docs/openapi/policy/registeredresources/registered_resources.openapi.yaml`:
- Around line 1380-1425: The parent schemas
policy.registeredresources.ActionAttributeValue and
policy.registeredresources.GetRegisteredResourceRequest set
additionalProperties: false while defining actionable fields only inside
oneOf/allOf branches, causing validators to treat actionId/actionName and
attributeValueFqn/attributeValueId (and id/name for
GetRegisteredResourceRequest) as additional; fix by promoting those branch
properties into the parent schema's properties (declare actionId, actionName,
attributeValueFqn, attributeValueId in ActionAttributeValue and
id/name/namespaceFqn/namespaceId in GetRegisteredResourceRequest) and keep the
branch-specific required arrays inside the oneOf branches, or alternatively
remove additionalProperties: false from the parent—ensure the parent titles
(ActionAttributeValue, GetRegisteredResourceRequest) reflect the updated
properties so branch validation succeeds.

In `@docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml`:
- Around line 848-857: The `alg` property uses $ref to
policy.KasPublicKeyAlgEnum (a string enum) but the `not` guard incorrectly
excludes numeric 0 so the UNSPECIFIED string value still passes; update the
`not.enum` to exclude the actual enum member string (e.g.,
"KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED") or otherwise use `not.const:
"KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED"` for the `alg` property in both
subject_mapping.openapi.yaml and unsafe.openapi.yaml so the UNSPECIFIED string
is correctly forbidden; locate the `alg` field and modify its `not` clause
referencing policy.KasPublicKeyAlgEnum accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2a2a25cd-745b-4647-810e-d9bcd3956a99

📥 Commits

Reviewing files that changed from the base of the PR and between 85cb816 and 6fca8b9.

⛔ Files ignored due to path filters (7)
  • protocol/go/policy/attributes/attributes.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/kasregistry/key_access_server_registry.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/namespaces/namespaces.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/obligations/obligations.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/registeredresources/registered_resources.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/selectors.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/subjectmapping/subject_mapping.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (29)
  • docs/grpc/index.html
  • docs/openapi/authorization/authorization.openapi.yaml
  • docs/openapi/authorization/v2/authorization.openapi.yaml
  • docs/openapi/common/common.openapi.yaml
  • docs/openapi/entity/entity.openapi.yaml
  • docs/openapi/entityresolution/entity_resolution.openapi.yaml
  • docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml
  • docs/openapi/kas/kas.openapi.yaml
  • docs/openapi/policy/actions/actions.openapi.yaml
  • docs/openapi/policy/attributes/attributes.openapi.yaml
  • docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml
  • docs/openapi/policy/keymanagement/key_management.openapi.yaml
  • docs/openapi/policy/namespaces/namespaces.openapi.yaml
  • docs/openapi/policy/objects.openapi.yaml
  • docs/openapi/policy/obligations/obligations.openapi.yaml
  • docs/openapi/policy/registeredresources/registered_resources.openapi.yaml
  • docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml
  • docs/openapi/policy/selectors.openapi.yaml
  • docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
  • docs/openapi/policy/unsafe/unsafe.openapi.yaml
  • docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml
  • service/policy/attributes/attributes.proto
  • service/policy/kasregistry/key_access_server_registry.proto
  • service/policy/namespaces/namespaces.proto
  • service/policy/obligations/obligations.proto
  • service/policy/registeredresources/registered_resources.proto
  • service/policy/selectors.proto
  • service/policy/selectors_test.go
  • service/policy/subjectmapping/subject_mapping.proto

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

🛑 Comments failed to post (14)
docs/openapi/authorization/authorization.openapi.yaml (2)

297-359: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/authorization/authorization.openapi.yaml"
  "docs/openapi/authorization/v2/authorization.openapi.yaml"
  "docs/openapi/entity/entity.openapi.yaml"
  "docs/openapi/entityresolution/entity_resolution.openapi.yaml"
  "docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml"
  "docs/openapi/kas/kas.openapi.yaml"
  "docs/openapi/policy/actions/actions.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "===== $f ====="
  if [[ ! -f "$f" ]]; then
    echo "MISSING FILE"
    continue
  fi

  # Show all oneOf blocks that are near additionalProperties:false
  rg -n --context=6 "oneOf:" "$f" | head -n 80

  echo "--- occurrences of additionalProperties: false ---"
  rg -n "additionalProperties:\s*false" "$f" || true

  echo "--- show surrounding lines for each additionalProperties:false ---"
  while IFS=: read -r lineNo _; do
    start=$((lineNo-20))
    end=$((lineNo+30))
    echo "---- context around $lineNo ----"
    sed -n "${start},${end}p" "$f" | nl -ba | sed 's/^/CTX: /'
  done < <(rg -n "additionalProperties:\s*false" "$f" | head -n 50)
done

Repository: opentdf/platform

Length of output: 2772


🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/authorization/authorization.openapi.yaml"
  "docs/openapi/authorization/v2/authorization.openapi.yaml"
  "docs/openapi/entity/entity.openapi.yaml"
  "docs/openapi/entityresolution/entity_resolution.openapi.yaml"
  "docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml"
  "docs/openapi/kas/kas.openapi.yaml"
  "docs/openapi/policy/actions/actions.openapi.yaml"
)

python3 - <<'PY'
from pathlib import Path

files = [
  "docs/openapi/authorization/authorization.openapi.yaml",
  "docs/openapi/authorization/v2/authorization.openapi.yaml",
  "docs/openapi/entity/entity.openapi.yaml",
  "docs/openapi/entityresolution/entity_resolution.openapi.yaml",
  "docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml",
  "docs/openapi/kas/kas.openapi.yaml",
  "docs/openapi/policy/actions/actions.openapi.yaml",
]

for f in files:
    p = Path(f)
    if not p.exists():
        print(f"===== MISSING: {f} =====")
        continue

    lines = p.read_text(encoding="utf-8").splitlines()
    oneofs = []
    adds = []

    for i, line in enumerate(lines):
        if "oneOf:" in line:
            indent = len(line) - len(line.lstrip(" "))
            oneofs.append((i, indent, line.strip()))
        if "additionalProperties:" in line and "false" in line:
            indent = len(line) - len(line.lstrip(" "))
            adds.append((i, indent, line.strip()))

    # Find additionalProperties false that is "inside" an object having a same-indent oneOf shortly above it.
    hits = []
    for ai, aindent, astrip in adds:
        for oi, oindent, ostrip in oneofs:
            if oindent == aindent and 0 < ai - oi <= 120:
                # likely in same object scope; record the closest oneOf above
                hits.append((ai, aindent, oi))
                break

    print(f"\n===== {f} =====")
    if not hits:
        print("No same-indent oneOf/additionalProperties:false pairs found in proximity.")
        continue

    # de-dupe by additionalProperties line index
    seen=set()
    unique=[]
    for h in hits:
        if h[0] not in seen:
            unique.append(h); seen.add(h[0])
    unique.sort()

    for ai, aindent, oi in unique:
        print(f"\n-- oneOf at line {oi+1} (indent {aindent}) and additionalProperties:false at line {ai+1} --")
        start = max(0, oi-5)
        end = min(len(lines), ai+20)
        for j in range(start, end):
            # keep output readable
            print(f"{j+1:4d}:{lines[j]}")
PY

Repository: opentdf/platform

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import re
from pathlib import Path

files = [
  "docs/openapi/authorization/authorization.openapi.yaml",
  "docs/openapi/authorization/v2/authorization.openapi.yaml",
  "docs/openapi/entity/entity.openapi.yaml",
  "docs/openapi/entityresolution/entity_resolution.openapi.yaml",
  "docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml",
  "docs/openapi/kas/kas.openapi.yaml",
  "docs/openapi/policy/actions/actions.openapi.yaml",
]

def indent_of(line):
    return len(line) - len(line.lstrip(" "))

def find_pairs(lines):
    pairs=[]
    oneofs=[]
    adds=[]
    for i,l in enumerate(lines):
        if re.match(r'^\s*oneOf:\s*$', l):
            oneofs.append((i, indent_of(l)))
        if re.match(r'^\s*additionalProperties:\s*false\s*$', l):
            adds.append((i, indent_of(l)))
    # candidate: same-indent within next 200 lines
    for oi, ind in oneofs:
        cand=None
        for ai, aind in adds:
            if aind==ind and 0 < ai-oi <= 200:
                cand=ai
                break
        if cand is not None:
            pairs.append((oi, cand, ind))
    return pairs

def extract_schema_title(lines, start, ind):
    # search backwards from 'start' for 'title:' at indent ind+2 or ind+0 (best-effort)
    for j in range(start, max(-1,start-80), -1):
        l=lines[j]
        if re.match(r'^\s*title:\s*.+', l):
            if indent_of(l) in (ind+2, ind):
                return l.strip()
    return None

def extract_parent_properties_keys(lines, oneof_i, ind, add_i):
    # parent properties at indent == ind and after oneOf line (best-effort: first "properties:" at indent ind)
    for j in range(oneof_i, add_i):
        l=lines[j]
        if re.match(r'^\s*properties:\s*$', l) and indent_of(l)==ind:
            # collect subsequent property keys at indent ind+2 until next sibling key (title/description/additionalProperties/oneOf/etc)
            keys=[]
            for k in range(j+1, add_i):
                lk=lines[k]
                if indent_of(lk)<=ind:
                    break
                m=re.match(r'^(\s*)([A-Za-z0-9_]+):\s*$', lk)
                if m and len(m.group(1))==ind+2:
                    keys.append(m.group(2))
            return keys
    return []

def extract_branch_keys(lines, oneof_i, add_i, ind):
    # within oneOf range: capture keys declared under branch "- properties:" blocks
    keys=set()
    # brute regex by indent: keys lines at indent ind+4 (as in example)
    key_indent=ind+4
    for j in range(oneof_i, add_i):
        l=lines[j]
        # match "- properties:" then subsequent property keys at indent ind+4
        if re.match(r'^\s*-\s*properties:\s*$', l):
            # read forward until indent <= ind+2
            for k in range(j+1, min(add_i, j+80)):
                lk=lines[k]
                if indent_of(lk)<=ind+2:
                    break
                m=re.match(r'^(\s*)([A-Za-z0-9_]+):\s*$', lk)
                if m and len(m.group(1))==key_indent:
                    keys.add(m.group(2))
    return sorted(keys)

for f in files:
    p=Path(f)
    if not p.exists():
        print(f"\n===== MISSING {f} =====")
        continue
    lines=p.read_text(encoding="utf-8").splitlines()

    pairs=find_pairs(lines)
    # only show first 1-2 pairs per file to keep output small, but enough to check claim
    shown=0
    print(f"\n===== {f} =====")
    if not pairs:
        print("No same-indent oneOf/additionalProperties:false pairs found within 200 lines.")
        continue

    for (oi, ai, ind) in pairs[:3]:
        title=extract_schema_title(lines, oi, ind) or "(no title found)"
        parent_keys=extract_parent_properties_keys(lines, oi, ind, ai)
        branch_keys=extract_branch_keys(lines, oi, ai, ind)
        overlap=sorted(set(parent_keys) & set(branch_keys))

        print(f"- oneOf line {oi+1}, additionalProperties:false line {ai+1}, indent {ind}")
        print(f"  title: {title}")
        print(f"  parent properties keys: {parent_keys}")
        print(f"  oneOf-branch property keys: {branch_keys}")
        print(f"  overlap: {overlap}")
        shown+=1
        if shown>=2:
            break
PY

Repository: opentdf/platform

Length of output: 2895


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import re
from pathlib import Path

files = [
  "docs/openapi/authorization/authorization.openapi.yaml",
  "docs/openapi/authorization/v2/authorization.openapi.yaml",
  "docs/openapi/entity/entity.openapi.yaml",
  "docs/openapi/entityresolution/entity_resolution.openapi.yaml",
  "docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml",
  "docs/openapi/kas/kas.openapi.yaml",
  "docs/openapi/policy/actions/actions.openapi.yaml",
]

def indent_of(s): return len(s) - len(s.lstrip(" "))

def find_schema_key(lines, from_i, indent_key):
    for j in range(from_i, max(-1, from_i-2000), -1):
        m = re.match(r'^' + ' ' * indent_key + r'([A-Za-z0-9_.-]+):\s*$', lines[j])
        if m:
            return m.group(1)
    return None

def extract_keys_after_properties(lines, props_i, props_indent):
    keys=[]
    # collect immediate keys at props_indent+2
    key_indent = props_indent + 2
    for k in range(props_i+1, min(len(lines), props_i+200)):
        lk=lines[k]
        if not lk.strip():
            continue
        if indent_of(lk) <= props_indent:
            break
        m = re.match(r'^' + ' ' * key_indent + r'([A-Za-z0-9_.-]+):\s*$', lk)
        if m:
            keys.append(m.group(1))
    return keys

def extract_branch_properties_keys(lines, oneof_i, end_i, oneof_indent):
    # find "- properties:" lines within oneOf; keys follow at dash_indent+4
    keys=set()
    for j in range(oneof_i, min(len(lines), end_i+1)):
        m = re.match(r'^' + ' ' * (oneof_indent+2) + r'-\s*properties:\s*$', lines[j])
        if not m:
            continue
        dash_indent = oneof_indent+2
        key_indent = dash_indent + 4
        # collect keys until indent <= dash_indent
        for k in range(j+1, min(len(lines), j+120)):
            lk=lines[k]
            if not lk.strip():
                continue
            if indent_of(lk) <= dash_indent:
                break
            m2 = re.match(r'^' + ' ' * key_indent + r'([A-Za-z0-9_.-]+):\s*$', lk)
            if m2:
                keys.add(m2.group(1))
    return sorted(keys)

for f in files:
    p=Path(f)
    if not p.exists():
        print(f"\n===== MISSING: {f} =====")
        continue
    lines=p.read_text(encoding="utf-8").splitlines()

    # Find first additionalProperties:false and nearest preceding oneOf at same indent
    best=None
    for ai, l in enumerate(lines):
        m = re.match(r'^( *)additionalProperties:\s*false\s*$', l)
        if not m:
            continue
        ind = len(m.group(1))
        # search backwards for oneOf at same indent within 200 lines
        oneof_i=None
        for j in range(ai-1, max(-1, ai-1-200), -1):
            m2 = re.match(r'^( *)oneOf:\s*$', lines[j])
            if m2 and len(m2.group(1))==ind:
                oneof_i=j
                break
        if oneof_i is None:
            continue
        best=(oneof_i, ai, ind)
        break

    print(f"\n===== {f} =====")
    if not best:
        print("No same-indent oneOf/additionalProperties:false pair found (heuristic).")
        continue

    oneof_i, add_i, ind = best
    schema_indent = ind - 2
    schema_key = find_schema_key(lines, oneof_i, schema_indent) or "(schema key not found)"
    parent_props_keys=[]
    # find first 'properties:' at indent ind after oneOf and before add_i
    props_i=None
    for k in range(oneof_i, min(add_i, oneof_i+300)):
        m3 = re.match(r'^' + ' ' * ind + r'properties:\s*$', lines[k])
        if m3:
            props_i=k
            break
    if props_i is not None:
        parent_props_keys = extract_keys_after_properties(lines, props_i, ind)

    branch_keys = extract_branch_properties_keys(lines, oneof_i, add_i, ind)

    print(f"schema: {schema_key}")
    print(f"oneOf: line {oneof_i+1} (indent {ind}), additionalProperties:false: line {add_i+1}")
    print(f"parent properties keys (at indent {ind+2}): {parent_props_keys}")
    print(f"oneOf-branch properties keys (immediate keys under branch properties): {branch_keys}")
    if parent_props_keys:
        missing=[k for k in branch_keys if k not in parent_props_keys]
        print(f"branch keys missing from parent properties (would be 'additional'): {missing[:30]}{'...' if len(missing)>30 else ''}")
    else:
        print("parent properties keys not found by heuristic")
PY

Repository: opentdf/platform

Length of output: 3170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

declare -A ranges
ranges["docs/openapi/authorization/authorization.openapi.yaml"]="290 370"
ranges["docs/openapi/authorization/v2/authorization.openapi.yaml"]="235 320"
ranges["docs/openapi/entity/entity.openapi.yaml"]="1 80"
ranges["docs/openapi/entityresolution/entity_resolution.openapi.yaml"]="85 175"
ranges["docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml"]="80 150"
ranges["docs/openapi/kas/kas.openapi.yaml"]="165 215"
ranges["docs/openapi/policy/actions/actions.openapi.yaml"]="405 480"

for f in "${!ranges[@]}"; do
  read -r start end <<< "${ranges[$f]}"
  echo "===== $f ($start-$end) ====="
  if [[ ! -f "$f" ]]; then echo "MISSING"; continue; fi
  sed -n "${start},${end}p" "$f" | nl -ba | sed 's/^/LN: /'
  echo
done

Repository: opentdf/platform

Length of output: 193


🏁 Script executed:

#!/bin/bash
set -euo pipefail

declare -A ranges
ranges["docs/openapi/authorization/authorization.openapi.yaml"]="290 370"
ranges["docs/openapi/authorization/v2/authorization.openapi.yaml"]="235 320"
ranges["docs/openapi/entity/entity.openapi.yaml"]="1 120"
ranges["docs/openapi/entityresolution/entity_resolution.openapi.yaml"]="85 190"
ranges["docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml"]="80 155"
ranges["docs/openapi/kas/kas.openapi.yaml"]="165 230"
ranges["docs/openapi/policy/actions/actions.openapi.yaml"]="405 485"

for f in "${!ranges[@]}"; do
  read -r start end <<< "${ranges[$f]}"
  echo "===== $f ($start-$end) ====="
  if [[ ! -f "$f" ]]; then echo "MISSING"; echo; continue; fi
  awk -v s="$start" -v e="$end" '{ln=NR; if(ln>=s && ln<=e) printf "LN:%5d %s\n", ln, $0}' "$f"
  echo
done

Repository: opentdf/platform

Length of output: 25016


🏁 Script executed:

#!/bin/bash
set -euo pipefail
f="docs/openapi/kas/kas.openapi.yaml"
python3 - <<'PY'
import re
from pathlib import Path

p=Path("docs/openapi/kas/kas.openapi.yaml")
lines=p.read_text(encoding="utf-8").splitlines()

def indent_of(line):
    return len(line)-len(line.lstrip(" "))

oneofs=[]
adds=[]
for i,l in enumerate(lines):
    if re.match(r'^\s*oneOf:\s*$', l):
        oneofs.append((i, indent_of(l)))
    if re.match(r'^\s*additionalProperties:\s*false\s*$', l):
        adds.append((i, indent_of(l)))

# find pairs where same indentation and additionalProperties:false occurs after oneOf within 120 lines
pairs=[]
for oi,oi_indent in oneofs:
    for ai,ai_indent in adds:
        if oi_indent==ai_indent and 0 < ai-oi <= 120:
            pairs.append((oi,ai,oi_indent))

# print a small snippet around each pair
print(f"Found {len(pairs)} oneOf/additionalProperties:false same-indent pairs (within 120 lines).")
for idx,(oi,ai,ind) in enumerate(pairs[:20],1):
    # find schema title just above the oneOf if possible
    title=None
    for j in range(oi, max(-1,oi-200), -1):
        m=re.match(r'^' + ' '*ind + r'title:\s*(.+)\s*$', lines[j])
        if m:
            title=m.group(1)
            break
    print(f"\n[{idx}] oneOf at line {oi+1} indent {ind}, additionalProperties:false at line {ai+1} indent {ind}, title={title}")
    start=max(0,oi-10); end=min(len(lines),ai+25)
    for k in range(start,end):
        print(f"{k+1:5d}:{lines[k]}")
PY

Repository: opentdf/platform

Length of output: 36769


Fix parent-level additionalProperties: false with oneOf-only variant fields (breaks validation/codegen)

In these OpenAPI schemas, the parent object keeps additionalProperties: false while the allowed variant-only keys live solely inside oneOf branches (e.g., authorization.Entity, entity.Entity, authorization.Entity (entity resolution), authorization.v2.Resource, policy.Action, authorization.v2.EntityIdentifier, and kas.KeyAccessRewrapResult). With JSON Schema semantics, those variant keys are treated as “additional” by the parent-level constraint, so legitimate payloads are rejected.

Move additionalProperties: false into each oneOf branch (or refactor using an allOf pattern / unevaluated-properties approach) so variant-only properties are permitted when their branch is selected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/authorization/authorization.openapi.yaml` around lines 297 -
359, The parent schema authorization.Entity (and other similar schemas
mentioned) has additionalProperties: false at the top level while all valid
variant keys live inside oneOf branches, causing legitimate payloads to be
rejected; update the schema by removing or relaxing the parent-level
additionalProperties and instead add additionalProperties: false inside each
oneOf branch (or refactor to an allOf + shared base or use unevaluatedProperties
where supported) so that when a branch is selected its properties are allowed;
target the authorization.Entity schema (and the other listed schemas like
entity.Entity, authorization.v2.Resource, policy.Action,
authorization.v2.EntityIdentifier, kas.KeyAccessRewrapResult) and ensure each
oneOf variant object includes its own additionalProperties rule or convert to an
allOf pattern that composes the shared fields with branch-specific constraints.

651-656: ⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/authorization/authorization.openapi.yaml"
  "docs/openapi/authorization/v2/authorization.openapi.yaml"
  "docs/openapi/common/common.openapi.yaml"
  "docs/openapi/policy/actions/actions.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "=== $f ==="
  if [[ ! -f "$f" ]]; then
    echo "MISSING"
    continue
  fi
  # Print the google.protobuf.Timestamp block with nearby lines (keep output small)
  rg -n --no-heading --context 3 'google\.protobuf\.Timestamp:' "$f" || true
  # Print the examples/format lines nearby for quick inspection
  rg -n --no-heading --context 2 'google\.protobuf\.Timestamp:' "$f" | head -n 50 || true
  rg -n --no-heading 'examples:|format:|RFC3339|date-time' "$f" | head -n 80 || true
  echo
done

Repository: opentdf/platform

Length of output: 2810


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ranges=(
  "docs/openapi/authorization/authorization.openapi.yaml:645-660"
  "docs/openapi/authorization/v2/authorization.openapi.yaml:680-700"
  "docs/openapi/common/common.openapi.yaml:105-130"
  "docs/openapi/policy/actions/actions.openapi.yaml:310-340"
)

for spec in "${ranges[@]}"; do
  file="${spec%%:*}"
  range="${spec#*:}"
  echo "=== $file ($range) ==="
  sed -n "${range}p" "$file" | nl -ba -w2 -s': ' | sed "s/^/${file} /"
  echo
done

# Also check whether any RFC3339 description exists near those blocks
for file in \
  "docs/openapi/authorization/authorization.openapi.yaml" \
  "docs/openapi/authorization/v2/authorization.openapi.yaml" \
  "docs/openapi/common/common.openapi.yaml" \
  "docs/openapi/policy/actions/actions.openapi.yaml"; do
  echo "=== $file: RFC3339 near Timestamp ==="
  # small window around first match for Timestamp
  line=$(rg -n 'google\.protobuf\.Timestamp:' "$file" | head -n1 | cut -d: -f1)
  if [[ -n "${line:-}" ]]; then
    start=$((line-20))
    end=$((line+40))
    sed -n "${start},${end}p" "$file" | nl -ba -w2 -s': ' | head -n 120
  fi
  echo
done

Repository: opentdf/platform

Length of output: 298


🏁 Script executed:

#!/bin/bash
set -euo pipefail

print_range() {
  local file="$1"
  local start="$2"
  local end="$3"
  echo "=== $file (${start}-${end}) ==="
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d: %s\n", NR, $0}' "$file"
  echo
}

print_range "docs/openapi/authorization/authorization.openapi.yaml" 645 660
print_range "docs/openapi/authorization/v2/authorization.openapi.yaml" 680 700
print_range "docs/openapi/common/common.openapi.yaml" 105 130
print_range "docs/openapi/policy/actions/actions.openapi.yaml" 310 340

for file in \
  "docs/openapi/authorization/authorization.openapi.yaml" \
  "docs/openapi/authorization/v2/authorization.openapi.yaml" \
  "docs/openapi/common/common.openapi.yaml" \
  "docs/openapi/policy/actions/actions.openapi.yaml"; do
  echo "=== $file: nearest `google.protobuf.Timestamp:` block (line +/- 35) ==="
  line=$(rg -n 'google\.protobuf\.Timestamp:' "$file" | head -n1 | cut -d: -f1)
  if [[ -z "${line:-}" ]]; then
    echo "(no Timestamp block found)"
    continue
  fi
  start=$((line-35))
  end=$((line+35))
  print_range "$file" "$start" "$end"
done

# Check if any of these blocks contain `format: date-time` and `examples:` together
for file in \
  "docs/openapi/authorization/authorization.openapi.yaml" \
  "docs/openapi/authorization/v2/authorization.openapi.yaml" \
  "docs/openapi/common/common.openapi.yaml" \
  "docs/openapi/policy/actions/actions.openapi.yaml"; do
  echo "=== $file: context around `examples:` and `format: date-time` ==="
  rg -n --no-heading -C 5 'examples:|format: date-time' "$file" | head -n 200 || true
  echo
done

Repository: opentdf/platform

Length of output: 20713


Fix google.protobuf.Timestamp examples mismatch format: date-time

In these specs, google.protobuf.Timestamp is declared with format: date-time, but the examples are duration literals (1s, 1.000340012s) while the schema description states it converts to RFC3339 date strings, making the example values misleading/invalid:

  • docs/openapi/authorization/authorization.openapi.yaml (651-656)
  • docs/openapi/authorization/v2/authorization.openapi.yaml (688-693)
  • docs/openapi/common/common.openapi.yaml (112-117)
  • docs/openapi/policy/actions/actions.openapi.yaml (321-326)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/authorization/authorization.openapi.yaml` around lines 651 -
656, The examples for the schema symbol google.protobuf.Timestamp are invalid
for format: date-time (they are duration literals like "1s"); update the
examples to RFC3339 date-time strings (e.g. "1970-01-01T00:00:01Z" and
"2020-01-01T00:00:00.000340012Z" or similar ISO8601 UTC timestamps) so they
match the declared format: date-time and the schema description; ensure this
change is applied wherever google.protobuf.Timestamp is declared.
docs/openapi/policy/attributes/attributes.openapi.yaml (4)

1097-1100: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Array-only keywords were emitted on scalar item schemas in docs/openapi/policy/attributes/attributes.openapi.yaml and docs/openapi/policy/objects.openapi.yaml. minItems, maxItems, and uniqueItems only apply to arrays, so placing them under items: { type: string } makes them ineffective noise for validators and generators. Keep those constraints on the enclosing array nodes only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/attributes/attributes.openapi.yaml` around lines 1097 -
1100, The OpenAPI schemas emit array-only keywords
(minItems/maxItems/uniqueItems) inside scalar item schemas — locate the array
schemas in attributes.openapi.yaml and objects.openapi.yaml where you have an
enclosing "type: array" with "items: { type: string, minItems: ... }" (look for
the "items" blocks shown in the diff) and remove any
minItems/maxItems/uniqueItems from the scalar "items" objects; then add those
constraints to the enclosing array node (the object with "type: array") so the
constraints apply to the array itself rather than the item schema.

1150-1153: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

policy.KasPublicKey.alg lost its UNSPECIFIED exclusion in docs/openapi/policy/attributes/attributes.openapi.yaml, docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml, docs/openapi/policy/namespaces/namespaces.openapi.yaml, and docs/openapi/policy/objects.openapi.yaml. Each schema emits policy.KasPublicKeyAlgEnum as strings, so not.enum: [0] never matches and no longer excludes KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED. This weakens the published contract across every touched policy/KAS surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/attributes/attributes.openapi.yaml` around lines 1150 -
1153, The schema for policy.KasPublicKey.alg is attempting to exclude the
UNSPECIFIED enum by using not.enum: [0], but the generated enums are strings so
that exclusion never matches; update the YAMLs (where you see
policy.KasPublicKey.alg and references to policy.KasPublicKeyAlgEnum /
KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED) to exclude the string value instead (change
not.enum: - 0 to not.enum: - "0") so the UNSPECIFIED value is actually rejected
by the OpenAPI schema.

1946-1984: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The identifier oneOf refactors no longer enforce deprecated-id exclusivity in docs/openapi/policy/attributes/attributes.openapi.yaml, docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml, and docs/openapi/policy/namespaces/namespaces.openapi.yaml. In each file, the replacement identifiers stay inside the oneOf, but deprecated id moved to a sibling top-level property. That means requests like {id, attributeId}, {id, valueId}, {id, kasId}, or {id, namespaceId} still validate even though the descriptions say those combinations are invalid.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/attributes/attributes.openapi.yaml` around lines 1946 -
1984, The top-level deprecated id property was moved out of the oneOf, so the
oneOf no longer enforces exclusivity; restore exclusivity by moving the
deprecated id back into the oneOf as its own alternative (i.e., add a oneOf
branch with properties: id (type: string, format: uuid, deprecated: true) and
required: [id]) for each affected schema (e.g., GetAttributeRequest with
attributeId/fqn, and the equivalent request schemas in
key_access_server_registry and namespaces that reference
valueId/kasId/namespaceId), so oneOf again enforces mutual exclusion between id
and the new identifiers. Ensure additionalProperties/description stay unchanged.

2352-2397: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

According to Connect RPC's HTTP JSON error representation, is the error details field named details, and can a single error include multiple detail entries?

💡 Result:

Yes. In Connect RPC’s HTTP JSON error representation, the error details field is named details, and it can contain multiple detail entries for a single error. In the Connect error docs, the HTTP JSON example includes a top-level details field whose value is a JSON array (e.g., "details": [ ... ]) [1]. The docs also describe passing details as a list/array of strongly-typed error detail messages [1][2], and the Connect error model includes an optional collection of arbitrary protobuf messages called details [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  docs/openapi/policy/attributes/attributes.openapi.yaml
  docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml
  docs/openapi/policy/keymanagement/key_management.openapi.yaml
  docs/openapi/policy/namespaces/namespaces.openapi.yaml
)

for f in "${files[@]}"; do
  echo "===== $f ====="
  # Print the connect.error schema block near the mentioned line ranges (best-effort).
  # Use rg to find the connect.error block header, then show surrounding lines.
  rg -n --context 5 'connect\.error:' "$f" || true
  rg -n --context 40 'connect\.error:' "$f" || true
  # Also search for the singular/plural field usage around the schema.
  rg -n --context 5 '\bdetail(s)?\b' "$f" | head -n 80 || true
  # Show any mention of google.rpc.Status.details near connect.error schema.
  rg -n --context 3 'google\.rpc\.Status\.details' "$f" || true
done

Repository: opentdf/platform

Length of output: 19176


Fix connect.error schema to match Connect HTTP JSON: it uses detail (single google.protobuf.Any) but Connect models details as a list/array (multiple entries). This mismatch appears in docs/openapi/policy/attributes/attributes.openapi.yaml (lines 2359-2389), docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml (lines 2272-2302), docs/openapi/policy/keymanagement/key_management.openapi.yaml (lines 574-604), and docs/openapi/policy/namespaces/namespaces.openapi.yaml (lines 1087-1117). Verify the property name and cardinality against Connect’s HTTP error representation before publishing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/attributes/attributes.openapi.yaml` around lines 2352 -
2397, The connect.error schema uses a single "detail" property of type
google.protobuf.Any but Connect's HTTP JSON uses "details" as an array of
google.protobuf.Any; update the connect.error schema (symbol: connect.error) in
the affected OpenAPI files to rename "detail" to "details" and change its type
to an array of google.protobuf.Any (i.e., items referencing
google.protobuf.Any), preserving additionalProperties and descriptions; apply
the same change in the other listed schemas (those that define connect.error) so
the property name and cardinality match Connect’s HTTP error representation.
docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml (1)

1325-1371: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

id is no longer actually exclusive with the new identifier fields.

With id declared outside the oneOf, requests like {id, kasId} or {id, uri} still satisfy exactly one branch and validate. That no longer matches the description’s “either deprecated id or one of the new identifiers” contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml`
around lines 1325 - 1371, The schema GetKeyAccessServerRequest currently
declares "id" as a top-level property so objects like {id, kasId} validate; to
enforce the intended exclusivity, remove the top-level properties.id and instead
add a separate oneOf branch that defines an id-only object (properties: id
{type: string, format: uuid}, required: [id], deprecated: true) alongside the
existing kasId/name/uri branches — i.e. make the top-level oneOf include the
id-only branch and the kasId-only, name-only, uri-only branches so requests must
match exactly one branch.
docs/openapi/policy/obligations/obligations.openapi.yaml (2)

726-731: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/policy/obligations/obligations.openapi.yaml"
  "docs/openapi/policy/registeredresources/registered_resources.openapi.yaml"
  "docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "=== $f (requested lines) ==="
  if [ -f "$f" ]; then
    nl -ba "$f" | sed -n '700,760p' | sed -n '1,120p'
  else
    echo "Missing file: $f"
  fi
  echo
done

echo "=== rg: google.protobuf.Timestamp blocks ==="
rg -n "google\.protobuf\.Timestamp" docs/openapi/policy -S

echo
echo "=== rg: duration-like examples for Timestamp ==="
rg -n "1s|1\.000340012s|format:\s*date-time" docs/openapi/policy/ -S

Repository: opentdf/platform

Length of output: 205


🏁 Script executed:

#!/bin/bash
set -euo pipefail

show_range() {
  local f="$1" start="$2" end="$3"
  echo "=== $f (lines $start-$end) ==="
  if [ -f "$f" ]; then
    sed -n "${start},${end}p" "$f"
  else
    echo "Missing file: $f"
  fi
  echo
}

show_range "docs/openapi/policy/obligations/obligations.openapi.yaml" 716 740
show_range "docs/openapi/policy/registeredresources/registered_resources.openapi.yaml" 540 570
show_range "docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml" 520 560

echo "=== rg: google.protobuf.Timestamp blocks ==="
rg -n "google\.protobuf\.Timestamp" docs/openapi/policy -S || true

echo
echo "=== rg: duration-like examples for Timestamp ==="
rg -n "examples:[[:space:]]*\n[[:space:]]*-[[:space:]]*1s\b|1\.000340012s\b|format:[[:space:]]*date-time\b" docs/openapi/policy -S || true

Repository: opentdf/platform

Length of output: 8463


Replace google.protobuf.Timestamp examples with RFC3339 timestamps

google.protobuf.Timestamp is declared with format: date-time, but the shared examples use duration literals (1s, 1.000340012s) in:

  • docs/openapi/policy/obligations/obligations.openapi.yaml (lines 726-731)
  • docs/openapi/policy/registeredresources/registered_resources.openapi.yaml (lines 551-556)
  • docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml (lines 531-536)

Update those examples to valid RFC3339 timestamp strings and keep the example block consistent across files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/obligations/obligations.openapi.yaml` around lines 726 -
731, The examples for the schema google.protobuf.Timestamp currently use
duration literals; update the examples to valid RFC3339 date-time strings (e.g.,
"2023-01-02T15:04:05Z" and "2023-01-02T15:04:05.000340012Z") and replace the
duration examples in the google.protobuf.Timestamp example blocks so all three
files (the google.protobuf.Timestamp schema in obligations.openapi.yaml,
registered_resources.openapi.yaml, and resource_mapping.openapi.yaml) use the
same RFC3339 example values for consistency.

822-857: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/policy/obligations/obligations.openapi.yaml"
  "docs/openapi/policy/registeredresources/registered_resources.openapi.yaml"
  "docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "===== $f ====="
  # Show the relevant region with line numbers (as requested by the original comment)
  nl -ba "$f" | sed -n '780,900p' | sed -n '1,200p' || true
done

Repository: opentdf/platform

Length of output: 437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/policy/obligations/obligations.openapi.yaml"
  "docs/openapi/policy/registeredresources/registered_resources.openapi.yaml"
  "docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "===== $f ====="
  awk -v start=780 -v end=900 'NR>=start && NR<=end { printf "%d\t%s\n", NR, $0 }' "$f" | sed -n '1,140p'
done

Repository: opentdf/platform

Length of output: 14485


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find and display the policy.Action schema blocks in all 3 files
files=(
  "docs/openapi/policy/obligations/obligations.openapi.yaml"
  "docs/openapi/policy/registeredresources/registered_resources.openapi.yaml"
  "docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "===== $f (policy.Action occurrences) ====="
  # Print line numbers where policy.Action appears
  # Then print ~60 lines around the first few matches
  matches=$(rg -n "policy\.Action:" "$f" || true)
  echo "$matches" | head -n 20
  if [ -n "$matches" ]; then
    # Extract first matching line number
    first_ln=$(echo "$matches" | head -n1 | cut -d: -f1)
    start=$((first_ln-20))
    end=$((first_ln+80))
    echo "===== $f (context around first match: lines $start-$end) ====="
    awk -v start="$start" -v end="$end" 'NR>=start && NR<=end { printf "%d\t%s\n", NR, $0 }' "$f"
  fi
done

Repository: opentdf/platform

Length of output: 13360


🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/policy/obligations/obligations.openapi.yaml"
  "docs/openapi/policy/registeredresources/registered_resources.openapi.yaml"
  "docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "===== $f: policy.Action oneOf block around custom/standard ====="
  # Print from oneOf start to just past parent additionalProperties to spot any additionalProperties in branches
  ln=$(rg -n "policy\.Action:\s*$" "$f" | head -n1 | cut -d: -f1)
  start=$((ln))
  end=$((ln+80))
  awk -v start="$start" -v end="$end" 'NR>=start && NR<=end { print NR "\t" $0 }' "$f" \
    | sed -n '1,120p'
done

echo "===== $f: searching for additionalProperties within +/- 15 lines of custom/standard keys ====="
for f in "${files[@]}"; do
  echo "--- $f ---"
  rg -n "additionalProperties" "$f" | head -n 30
done

Repository: opentdf/platform

Length of output: 12468


Fix policy.Action schema so custom/standard aren’t rejected by additionalProperties: false.

In all three files’ policy.Action schemas, custom and standard are defined only inside the oneOf branches, while the parent object sets additionalProperties: false and only lists id, name, namespace, and metadata. JSON Schema validators will treat custom/standard as additional (unknown) properties and fail validation. Move custom and standard into the parent properties (and keep oneOf to enforce exactly one variant) or otherwise relax/adjust additionalProperties. (json-schema.org)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/obligations/obligations.openapi.yaml` around lines 822 -
857, The policy.Action schema currently defines custom and standard only inside
the oneOf branches while the parent object sets additionalProperties: false and
lacks those properties, causing validators to reject them; fix by adding
top-level properties entries for "custom" and "standard" in the policy.Action
properties block (matching the types/$ref used in the oneOf branches) and keep
the existing oneOf to enforce which variant is present (or adjust the oneOf to
reference the top-level properties), ensuring additionalProperties: false no
longer blocks the defined fields; update the policy.Action schema (symbols:
policy.Action, custom, standard, additionalProperties, oneOf) accordingly in all
three files.
docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml (3)

593-598: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

google.protobuf.Timestamp examples are invalid in docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml and docs/openapi/policy/unsafe/unsafe.openapi.yaml. Both files still declare google.protobuf.Timestamp as a string with format: date-time and describe RFC 3339 JSON encoding, but the new examples are duration literals (1s, 1.000340012s). That makes the published examples contradict the schema and can trip example-aware validators or generated docs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml` around lines
593 - 598, The examples for the schema named google.protobuf.Timestamp are
invalid duration literals; update the examples in
docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml (and the same
change in docs/openapi/policy/unsafe/unsafe.openapi.yaml) so they are valid RFC
3339 date-time strings that match the declared type/format (e.g.,
"2023-01-01T00:00:00Z", "2023-01-01T12:34:56.789Z") instead of
"1s"/"1.000340012s", ensuring the google.protobuf.Timestamp entry remains type:
string and format: date-time.

689-724: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml"
  "docs/openapi/policy/unsafe/unsafe.openapi.yaml"
)

for f in "${files[@]}"; do
  echo "==== $f ===="
  # show the exact cited ranges if file is long enough
  for r in "689-724" "596-631" "1087-1113" "1046-1073"; do
    start=${r%-*}; end=${r#*-}
    if [ -f "$f" ]; then
      total=$(wc -l < "$f")
      if [ "$total" -ge "$end" ]; then
        echo "-- lines $r --"
        sed -n "${start},${end}p" "$f"
      else
        echo "-- lines $r (skipped; file only $total lines) --"
      fi
    fi
  done

  echo
  # locate schema definitions by name
  echo "-- occurrences: policy.Action / policy.PublicKey --"
  rg -n "policy\.Action|policy\.PublicKey" "$f" || true

  echo
  # Extract policy.Action and policy.PublicKey blocks (best-effort via line windows)
  echo "-- block windows around policy.Action and policy.PublicKey --"
  for sym in "policy.Action" "policy.PublicKey"; do
    line=$(rg -n "$sym" "$f" | head -n1 | cut -d: -f1 || true)
    if [ -n "$line" ]; then
      start=$((line-20)); end=$((line+140))
      echo "-- $sym around line $line (show $start-$end) --"
      sed -n "${start},${end}p" "$f"
    else
      echo "-- $sym not found (or rg pattern didn't match) --"
    fi
  done

  echo "==== end $f ===="
  echo
done

Repository: opentdf/platform

Length of output: 31923


Fix schema unsatisfiability from oneOf-only fields + parent additionalProperties: false.

  • In both docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml and docs/openapi/policy/unsafe/unsafe.openapi.yaml, components/schemas/policy.Action and components/schemas/policy.PublicKey define the variant fields only inside oneOf branches (custom/standard, cached/remote) while the parent object sets additionalProperties: false, so instances that satisfy a oneOf branch are still rejected by the parent-level guard.
  • Move those variant properties onto the parent properties, or replace the parent guard with unevaluatedProperties: false so oneOf-validated properties aren’t treated as additional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml` around lines
689 - 724, The schemas policy.Action and policy.PublicKey are unsatisfiable
because their variant fields live only inside oneOf branches while the parent
sets additionalProperties: false; fix by either (A) moving the variant
properties (for policy.Action: custom, standard; for policy.PublicKey: cached,
remote) into the parent properties block and keep the oneOf for
required/validation, or (B) replace the parent-level additionalProperties: false
with unevaluatedProperties: false so properties validated by oneOf branches are
not treated as "additional"; update the policy.Action and policy.PublicKey
schema blocks accordingly and retain existing oneOf titles/required constraints.

1821-1866: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the referenced YAML regions
for f in \
  "docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml" \
  "docs/openapi/policy/unsafe/unsafe.openapi.yaml" \
  "docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml"
do
  echo "===== $f ====="
  # Print the specified line ranges (from the review comment)
  case "$f" in
    *subject_mapping.openapi.yaml) sed -n '1800,1880p' "$f" ;;
    *unsafe.openapi.yaml) sed -n '1630,1710p' "$f" ;;
    *wellknown_configuration.openapi.yaml) sed -n '120,240p' "$f" ;;
    *) sed -n '1,120p' "$f" ;;
  esac
done

# Global search for connect error schema fields across repo
echo "===== rg for connect error details fields ====="
rg -n --hidden --no-ignore-vcs "connect\.error|google\.rpc\.Status\.details|details:\s|\bdetail:\s" docs/openapi -S

# Search for "detail:" under connect.error specifically
echo "===== rg for "connect.error" blocks containing detail(s) ====="
rg -n --hidden --no-ignore-vcs -g'*.yaml' "connect\.error:" docs/openapi -S

# Extract any schema snippets defining the field names "detail" or "details" near connect.error
# Limit output by filtering to the key lines
echo "===== rg for "detail:" "details:" near connect.error in each file ====="
for f in \
  "docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml" \
  "docs/openapi/policy/unsafe/unsafe.openapi.yaml" \
  "docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml"
do
  echo "---- $f ----"
  # show lines containing 'connect.error:' and nearby 'detail'/'details' lines
  rg -n "connect\.error:|detail:|details:" "$f" -n || true
done

Repository: opentdf/platform

Length of output: 32356


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find any `details:` key usages in OpenAPI YAMLs (to see if any connect.error uses plural)
rg -n --hidden --no-ignore-vcs -g'*.yaml' '\bdetails\s*:' docs/openapi -S | head -n 200

Repository: opentdf/platform

Length of output: 227


🌐 Web query:

ConnectRPC HTTP error representation field name detail vs details google.rpc.Status.details

💡 Result:

In the ConnectRPC protocol, the field used to represent error details is named "details" (plural), which is consistent with the google.rpc.Status definition [1][2][3][4]. There is no "detail" field in the standard ConnectRPC error representation [1][5][6]. Key points regarding the error representation: 1. Consistency with google.rpc.Status: ConnectRPC follows the google.rpc.Status error model [2][3][4]. The Status message structure includes a "details" field, which is a repeated list of google.protobuf.Any messages [2][4]. 2. HTTP Representation: When errors are transmitted over HTTP in the Connect protocol, they are serialized as a JSON object [1]. This JSON object includes a "details" array [1][6]. Each item in this array typically contains "type" and "value" fields, and may optionally include a "debug" field (which provides a human-readable representation of the error detail) [1][5][6]. 3. Programming Interfaces: Across ConnectRPC SDKs (e.g., Go, Python, Rust), the interface for accessing these errors consistently uses "details" (e.g., a Details method or a details attribute) to retrieve or manipulate this list [1][7][8][3][9]. In summary, the correct term is always "details" (plural), both in the protocol specification's JSON output and within the library code implementations [1][7][8][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the surrounding lines for the `details:` occurrences
for f in \
  "docs/openapi/entityresolution/entity_resolution.openapi.yaml" \
  "docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml"
do
  echo "===== $f (around details:) ====="
  rg -n "details:" "$f" | while read -r line; do
    ln=$(echo "$line" | cut -d: -f1)
    sed -n "$((ln-20)),$((ln+20))p" "$f"
    echo "-----"
  done
done

# For each entityresolution file, print its connect.error schema block header and nearby fields
for f in \
  "docs/openapi/entityresolution/entity_resolution.openapi.yaml" \
  "docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml"
do
  echo "===== $f (connect.error block) ====="
  # Extract from connect.error: down until google.protobuf.Any (heuristic)
  awk '
    /connect\.error:/ {inblock=1}
    inblock {print}
    inblock && /google\.protobuf\.Any:/ {exit}
  ' "$f" | sed -n '1,200p'
  echo "-----"
done

Repository: opentdf/platform

Length of output: 5210


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Collect files with connect.error schema blocks
mapfile -t files < <(rg -l --hidden --no-ignore-vcs -g'*.yaml' 'connect\.error:' docs/openapi)

echo "Found ${`#files`[@]} files with connect.error:"
for f in "${files[@]}"; do
  echo "---- $f ----"
  # Determine whether `detail:` or `details:` appears inside the connect.error block
  # Heuristic: start at connect.error:, end at next top-level component schema (2-space indent + word + :) or end of file.
  awk '
    function trim(s){gsub(/^[ \t]+|[ \t]+$/,"",s); return s}
    /connect\.error:/ {in=1}
    in {
      # detect key names at indentation >= 8 (common in schemas)
      if ($0 ~ /^[[:space:]]*details[[:space:]]*:/) has_details=1
      if ($0 ~ /^[[:space:]]*detail[[:space:]]*:/) has_detail=1
      # crude end: next schema name at same indent as connect.error (4 spaces) followed by colon
      if (NR>1 && $0 ~ /^[[:space:]]{4}[A-Za-z0-9_.-]+[[:space:]]*:/) { if ($0 !~ /connect\.error:/) {in=0} }
    }
    END{
      if (has_details) print "connect.error contains: details:"
      if (has_detail) print "connect.error contains: detail:"
      if (!has_details && !has_detail) print "connect.error contains neither detail: nor details:"
    }
  ' "$f"
done

# Also, any global plural usage in connect.error specifically (sanity)
echo "===== Global check: connect.error block lines containing details: ====="
rg -n --hidden --no-ignore-vcs -g'*.yaml' 'connect\.error:|^\s*details\s*:' docs/openapi | head -n 200

Repository: opentdf/platform

Length of output: 117


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# List all yaml files containing connect.error schema blocks
rg -l --hidden --no-ignore-vcs -g'*.yaml' 'connect\.error:' docs/openapi > /tmp/connect_error_files.txt
mapfile -t files < /tmp/connect_error_files.txt

echo "Found ${`#files`[@]} files with connect.error:"
printf '%s\n' "${files[@]}"

echo
echo "===== Per-file: whether connect.error uses detail vs details ====="
for f in "${files[@]}"; do
  # Take only the connect.error block by cutting from "connect.error:" until "google.protobuf.Any:" (if present),
  # otherwise until the next top-level schema key (heuristic).
  block="$(awk '
    /connect\.error:/ {in=1}
    in {
      print
      # stop near end of connect.error schema block
      if ($0 ~ /google\.protobuf\.Any:/) {exit}
      # fallback end: next schema block at same indent (very heuristic)
      if (NR>1 && $0 ~ /^[[:space:]]{4}[A-Za-z0-9_.-]+[[:space:]]*:/ && $0 !~ /connect\.error:/) {exit}
    }
  ' "$f")"

  has_detail="$(printf '%s\n' "$block" | rg -n '^[[:space:]]*detail[[:space:]]*:' -m1 || true)"
  has_details="$(printf '%s\n' "$block" | rg -n '^[[:space:]]*details[[:space:]]*:' -m1 || true)"

  if [[ -n "$has_details" ]]; then
    echo "DETAILS  : $f"
  elif [[ -n "$has_detail" ]]; then
    echo "DETAIL   : $f"
  else
    echo "NEITHER  : $f"
  fi
done

echo
echo "===== Global check: lines with connect.error or detail(s): ====="
rg -n --hidden --no-ignore-vcs -g'*.yaml' '(connect\.error:|^\s*details\s*:|^\s*detail\s*:)' docs/openapi | head -n 200

Repository: opentdf/platform

Length of output: 1228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -l --hidden --no-ignore-vcs -g'*.yaml' 'connect\.error:' docs/openapi > /tmp/connect_error_files.txt
mapfile -t files < /tmp/connect_error_files.txt

echo "Found ${`#files`[@]} files with connect.error:"
printf '%s\n' "${files[@]}"

echo
echo "===== Per-file: whether connect.error uses detail vs details ====="
for f in "${files[@]}"; do
  # Extract from connect.error: until (first) google.protobuf.Any: line, or until next top-level schema key
  block="$(awk '
    /connect\.error:/ {inblock=1}
    inblock {print}
    inblock && /google\.protobuf\.Any:/ {exit}
    inblock && NR>1 && $0 ~ /^[[:space:]]{4}[A-Za-z0-9_.-]+[[:space:]]*:/ && $0 !~ /connect\.error:/ {exit}
  ' "$f")"

  if printf '%s\n' "$block" | rg -q '^[[:space:]]*details[[:space:]]*:'; then
    echo "DETAILS  : $f"
  elif printf '%s\n' "$block" | rg -q '^[[:space:]]*detail[[:space:]]*:'; then
    echo "DETAIL   : $f"
  else
    echo "NEITHER  : $f"
  fi
done

echo
echo "===== Global check: lines with connect.error or detail(s): (head) ====="
rg -n --hidden --no-ignore-vcs -g'*.yaml' '(connect\.error:|^\s*details\s*:|^\s*detail\s*:)' docs/openapi | head -n 200

Repository: opentdf/platform

Length of output: 5064


Fix OpenAPI connect.error schema: it models detail (singular) but ConnectRPC uses details (collection), making google.rpc.Status.details inconsistent.
connect.error in docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml (1821-1866), docs/openapi/policy/unsafe/unsafe.openapi.yaml (1653-1698), and docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml (152-197) defines detail: google.protobuf.Any while the descriptions reference google.rpc.Status.details. Update the contract to use details as an array/collection (matching ConnectRPC/google.rpc.Status.details).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml` around lines
1821 - 1866, The connect.error schema currently defines a singular property
named "detail" of type google.protobuf.Any but must match
ConnectRPC/google.rpc.Status which uses a repeated "details" field; update the
connect.error schema (the connect.error component) to replace the "detail"
property with "details" as an array of google.protobuf.Any (e.g., type: array,
items: $ref: '`#/components/schemas/google.protobuf.Any`'), keep
additionalProperties/title/description unchanged and ensure any references or
examples that mention google.rpc.Status.details remain consistent with the new
plural field name.
service/policy/selectors_test.go (1)

12-25: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider expanding test coverage for edge cases.

The test correctly validates the 253-character boundary, but additional test cases would improve robustness:

  • Empty string (should pass)
  • Whitespace-only strings
  • Unicode/multibyte characters (ensure byte vs. character length handling)
  • Strings with special characters
🧪 Example additional test cases
 func TestSearchTermValidation(t *testing.T) {
 	v, err := protovalidate.New()
 	require.NoError(t, err)
 
 	require.NoError(t, v.Validate(&policy.Search{
 		Term: strings.Repeat("a", 253),
 	}))
 
 	err = v.Validate(&policy.Search{
 		Term: strings.Repeat("a", 254),
 	})
 	require.Error(t, err)
 	require.Contains(t, err.Error(), "string.max_len")
+
+	// Empty string should be valid
+	require.NoError(t, v.Validate(&policy.Search{
+		Term: "",
+	}))
+
+	// Test unicode handling
+	require.NoError(t, v.Validate(&policy.Search{
+		Term: strings.Repeat("☃", 253),
+	}))
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

func TestSearchTermValidation(t *testing.T) {
	v, err := protovalidate.New()
	require.NoError(t, err)

	require.NoError(t, v.Validate(&policy.Search{
		Term: strings.Repeat("a", 253),
	}))

	err = v.Validate(&policy.Search{
		Term: strings.Repeat("a", 254),
	})
	require.Error(t, err)
	require.Contains(t, err.Error(), "string.max_len")

	// Empty string should be valid
	require.NoError(t, v.Validate(&policy.Search{
		Term: "",
	}))

	// Test unicode handling
	require.NoError(t, v.Validate(&policy.Search{
		Term: strings.Repeat("☃", 253),
	}))
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/policy/selectors_test.go` around lines 12 - 25, Extend
TestSearchTermValidation to cover edge cases: add assertions that an empty Term
and a whitespace-only Term pass validation, that multibyte/unicode strings are
validated according to the intended length semantics (add a test using runes vs
bytes to confirm whether policy.Search.Term limit applies to characters or
bytes), and that strings containing special characters pass/fail appropriately;
update tests that call protovalidate.New() and v.Validate(&policy.Search{Term:
...}) to include these new cases and assert success or string.max_len error as
appropriate.
service/policy/selectors.proto (1)

67-69: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider documenting the rationale for the 253 character limit.

The max_len constraint of 253 characters aligns with DNS FQDN limits, but documenting why this specific value was chosen would help future maintainers understand the design decision, especially given the PR description mentions "substring matching in Postgres" where text field limits are typically much higher.

📝 Suggested documentation addition
 message Search {
+  // Maximum length of 253 characters (DNS FQDN limit)
   string term = 1 [(buf.validate.field).string.max_len = 253];
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

message Search {
  // Maximum length of 253 characters (DNS FQDN limit)
  string term = 1 [(buf.validate.field).string.max_len = 253];
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/policy/selectors.proto` around lines 67 - 69, Add a short comment
above the Search.message or the term field explaining why the string max_len is
set to 253 (e.g., to match DNS FQDN limits used by the system and to constrain
substring matching semantics in Postgres), so future maintainers see the
rationale; reference the Search message and the term field with the
(buf.validate.field).string.max_len = 253 constraint in the comment.

@elizabethhealy elizabethhealy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 180.784655ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 93.443541ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 408.577046ms
Throughput 244.75 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.632024644s
Average Latency 434.970488ms
Throughput 114.59 requests/second

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • examples
  • otdfctl
  • sdk
  • service
  • lib/fixtures
  • tests-bdd

See the workflow run for details.

@c-r33d c-r33d enabled auto-merge June 12, 2026 18:45
@c-r33d c-r33d added this pull request to the merge queue Jun 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 12, 2026
@c-r33d c-r33d added this pull request to the merge queue Jun 12, 2026
Merged via the queue into main with commit 40f35df Jun 12, 2026
39 checks passed
@c-r33d c-r33d deleted the dspx-2716-protos branch June 12, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:policy Policy Configuration ( attributes, subject mappings, resource mappings, kas registry) docs Documentation size/s

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants