Skip to content

feat(sdk): DSPX-2418 add attribute discovery methods#841

Open
marythought wants to merge 5 commits intomainfrom
feat/dspx-2418-discovery-methods
Open

feat(sdk): DSPX-2418 add attribute discovery methods#841
marythought wants to merge 5 commits intomainfrom
feat/dspx-2418-discovery-methods

Conversation

@marythought
Copy link

@marythought marythought commented Feb 17, 2026

Summary

  • Add five discovery methods in lib/src/policy/discovery.ts, exported from the package root:
    listAttributes(), validateAttributes(), validateAttributeExists(),
    validateAttributeValue(), getEntityAttributes()
  • Add AttributeNotFoundError extends TdfError, exported from the package root so callers can
    distinguish "not found" from other errors
  • Add unit tests in discovery.spec.ts covering all methods and edge cases
  • docs update: Feat: DSPX-2418 add SDK convenience/discovery method documentation docs#186

Rationale

Addresses GitHub discussion developer feedback. These methods provide ergonomic wrappers over the
lower-level PlatformClient that:

  • Auto-paginate listAttributes() with a 1,000-page safety cap to prevent unbounded memory
    growth from a misbehaving server
  • Validate FQN format and existence before createZTDF() so failures happen at encryption time
    with a clear message rather than at decryption time with a cryptic "resource not found"
  • Distinguish enumerated from dynamic attributes in validateAttributeValue(): enforces the
    registered value list (case-insensitively) for enumerated attributes; accepts any non-empty
    value for dynamic attributes
  • Match entity ID in the GetEntitlements response to prevent silent wrong-entity attribute
    returns
  • Cap validateAttributes() at 250 FQNs client-side to match the server-side limit
  • Restrict FQN strings to safe URL characters to prevent XSS via FQNs in error messages

Matches the API shape and security properties of the Go SDK implementation: opentdf/platform#3082.

Test plan

  • npm test in lib/ — all tests pass, no regressions

@marythought marythought requested a review from a team as a code owner February 17, 2026 23:51
@gemini-code-assist
Copy link

Summary of Changes

Hello @marythought, 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 significantly enhances the SDK's capabilities for interacting with the platform's attribute system. It introduces a suite of new functions designed to simplify the process of discovering available attributes, validating attribute FQNs against the platform, and retrieving attributes associated with specific entities. These additions aim to improve developer experience by providing robust tools for attribute management and early detection of configuration issues, thereby streamlining the development of applications that rely on attribute-based access control.

Highlights

  • New Attribute Discovery Methods: Four convenience methods have been added for attribute discovery and validation: listAttributes, validateAttributes, validateAttributeValue, and getEntityAttributes.
  • listAttributes: This method auto-paginates through all active attributes on the platform, with an optional namespace filter.
  • validateAttributes and validateAttributeValue: These methods validate the format of Fully Qualified Names (FQNs) and confirm their existence on the platform, throwing an AttributeNotFoundError for any missing FQNs. validateAttributeValue is a single-FQN wrapper.
  • getEntityAttributes: This method returns the attribute value FQNs assigned to a Policy Enforcement (PE) or Non-Policy Enforcement (NPE) entity by leveraging GetEntitlements.
  • AttributeNotFoundError: A new error type, AttributeNotFoundError, has been introduced, extending TdfError, to specifically indicate when attribute value FQNs are not found on the platform.

🧠 New Feature in Public Preview: You can now enable Memory 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.

Changelog
  • lib/src/errors.ts
    • Added AttributeNotFoundError class for specific attribute not found scenarios.
  • lib/src/index.ts
    • Exported new attribute discovery and validation functions (listAttributes, validateAttributes, validateAttributeValue, getEntityAttributes).
    • Exported AttributeNotFoundError for public API access.
  • lib/src/policy/discovery.ts
    • Implemented listAttributes for paginated retrieval of active attributes.
    • Implemented validateAttributes to check FQN format and existence on the platform.
    • Implemented validateAttributeValue as a convenience wrapper for single FQN validation.
    • Implemented getEntityAttributes to fetch attribute FQNs assigned to an entity.
    • Defined constants for pagination limits and FQN validation regex.
  • lib/tests/mocha/unit/discovery.spec.ts
    • Added unit tests for validateAttributes covering null/empty input, FQN count limits, and invalid FQN formats.
    • Added unit tests for validateAttributeValue to verify invalid FQN format handling.
    • Added unit tests for getEntityAttributes to ensure proper handling of null/undefined entity input.
    • Included a test to confirm AttributeNotFoundError correctly extends Error.
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 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

Copy link

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

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 several useful convenience methods for attribute discovery and validation, along with a new AttributeNotFoundError. While the implementation is generally well-structured, there are critical security concerns regarding the handling of user-supplied URLs and FQNs, which could lead to reflected Cross-Site Scripting (XSS) and potential credential theft if not properly addressed. Additionally, I've provided a couple of suggestions to improve code clarity and conciseness in the new discovery.ts file.

Add four convenience methods for attribute discovery and validation:

- listAttributes(platformUrl, authProvider, namespace?) — auto-paginates
  through all active attributes; optional namespace filter
- validateAttributes(platformUrl, authProvider, fqns[]) — validates FQN
  format then confirms existence on the platform; throws AttributeNotFoundError
  for missing FQNs; capped at 250 FQNs to match the server limit
- validateAttributeValue(platformUrl, authProvider, fqn) — single-FQN
  convenience wrapper around validateAttributes
- getEntityAttributes(platformUrl, authProvider, entity) — returns the
  attribute value FQNs assigned to a PE or NPE via GetEntitlements

Also adds AttributeNotFoundError (extends TdfError) exported from the
package root for callers to distinguish "not found" from other errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@marythought marythought force-pushed the feat/dspx-2418-discovery-methods branch from c9abaa5 to 08b8f54 Compare February 18, 2026 00:02
@github-actions
Copy link

X-Test Failure Report

opentdf-sdk-lib
opentdf-ctl
✅ go-v0.9.0

@github-actions
Copy link

marythought and others added 2 commits February 17, 2026 16:37
…dd new validateAttributeValue

The old validateAttributeValue(fqn) only checked if a complete value FQN
was pre-registered on the platform. This gave false negatives for dynamic
(non-enumerated) attributes where any string value is valid.

- Rename validateAttributeValue(fqn) → validateAttributeExists(fqn).
- Add validateAttributeValue(platformUrl, authProvider, attributeFqn, value)
  which fetches the attribute definition via getAttribute:
  - Enumerated attribute (values non-empty): value must match case-insensitively.
  - Dynamic attribute (values empty): any string is accepted.
- Add ATTRIBUTE_FQN_RE regex for attribute-level FQN validation.
- Export validateAttributeExists from index.ts.
- Add 2 new input-validation tests; rename existing validateAttributeValue
  describe block to validateAttributeExists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link

If these changes look good, signoff on them with:

git pull && git commit --amend --signoff && git push --force-with-lease origin

If they aren't any good, please remove them with:

git pull && git reset --hard HEAD~1 && git push --force-with-lease origin

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@marythought marythought force-pushed the feat/dspx-2418-discovery-methods branch from 73e7966 to 580b0f8 Compare February 18, 2026 00:58
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link

* console.log("alice's entitlements:", fqns);
* ```
*/
export async function getEntityAttributes(
Copy link
Contributor

Choose a reason for hiding this comment

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

This API that allows the requester to retrieve entitlements of any users and as a result should always remain pretty locked down authz-wise to prevent just anyone from having the ability to mine users' entitlements. It is useful serverside within the platform for things like visibility trimming, or within PEPs for things like manual classification UIs. However, in web flows, there can be no distinction made between a PEP's auth (the server authentication/authorization itself) and a user's auth as no secrets remain secret in the browser.

TLDR: it may never make sense to call this API from the browser, so we may want to remove it from the SDK, but I'm not opposed to leaving it since the authz of the route is a separate concern from the ability to call it.

try {
resp = await platform.v1.attributes.listAttributes({
namespace: namespace ?? '',
pagination: { offset: nextOffset, limit: 0 },
Copy link
Contributor

@eugenioenko eugenioenko Feb 18, 2026

Choose a reason for hiding this comment

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

limit: 0
Does this mean it's gonna use default page size?
I don't remember what was the default limit (100? i think)

The query will iterate withMAX_LIST_ATTRIBUTES_PAGES. (1000)

If the limit is 100 per page, and we gonna load 1000 pages: thats 100k records. This call can get very very expensive

Copy link
Contributor

Choose a reason for hiding this comment

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

Another potential consideration with using default page size is that if the default value changes, it will affect how many records will be loaded.

1K pages of 100 records vs 1k pages of 1000 records

setting a page size will help mitigate that


// Matches the server-side limit on GetAttributeValuesByFqns so callers get a
// clear local error instead of a cryptic server rejection.
const MAX_VALIDATE_FQNS = 250;
Copy link
Contributor

@eugenioenko eugenioenko Feb 18, 2026

Choose a reason for hiding this comment

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

why 250 for MAX_VALIDATE_FQNS?

MAX_VALIDATE_FQNS will iterate over an array of items.
While MAX_LIST_ATTRIBUTES_PAGES will iterate over a network call which is way more expensive (and its 1000)


const match = vals.some((v) => v.value.toLowerCase() === value.toLowerCase());
if (!match) {
throw new AttributeNotFoundError('attribute value not permitted for this attribute');
Copy link
Contributor

Choose a reason for hiding this comment

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

nit :

value "${value}" is not permitted for attribute ${attributeFqn}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments