-
Notifications
You must be signed in to change notification settings - Fork 9
docs: clarify that @hidden, @export are not access controls + allowRead trusted-context / filter caveats #553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,6 +174,10 @@ type MyTable @table @export(name: "my-table") { | |
|
|
||
| The optional `name` parameter specifies the URL path segment (e.g., `/my-table/`). Without `name`, the type name is used. | ||
|
|
||
| :::warning `@export` is a routing directive, not an access control | ||
| Omitting `@export` removes the REST/MQTT route for a table (callers get 404), but it does **not** protect the data. The table still exists in the database and its records remain fully accessible to administrators via the Operations API and SQL. For data confidentiality, use role permissions (`attribute_permissions`, `read: false`) rather than relying on the absence of an export route. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While administrators certainly have full access, the table and its records also remain accessible to any non-admin user/role that has permissions to execute SQL or NoSQL operations via the Operations API. To prevent a common security misconception that only administrators can bypass the missing Suggested change: ... and its records remain fully accessible to administrators (and any roles with SQL or Operations API permissions) via the Operations API and SQL. ... |
||
| ::: | ||
|
|
||
| ### `@sealed` | ||
|
|
||
| Prevents records from including any properties beyond those explicitly declared in the type. By default, Harper allows records to have additional properties. | ||
|
|
@@ -196,6 +200,10 @@ type InternalConfig @table @hidden { | |
| } | ||
| ``` | ||
|
|
||
| :::warning `@hidden` does not restrict data access | ||
| `@hidden` only suppresses a type or field from generated API specs and MCP tool schemas. The underlying data is returned on **all** read surfaces — REST, SQL, and the Operations API — for any user with table-level `read` permission. Do not use `@hidden` as a confidentiality control. To restrict which users can read a field or table, use role `attribute_permissions` with `read: false`. | ||
| ::: | ||
|
|
||
| `@hidden` is also available as a [field directive](#hidden-field-directive) to suppress individual attributes. | ||
|
|
||
| ## Documenting Types and Fields | ||
|
|
@@ -326,7 +334,7 @@ type Event @table { | |
|
|
||
| ### `@hidden` (Field Directive) | ||
|
|
||
| Suppresses the field from MCP tool descriptors and the OpenAPI document. The attribute still exists in the table; data is still queryable through other interfaces subject to RBAC. Use this for fields that should not appear in introspectable surfaces. | ||
| Suppresses the field from MCP tool descriptors and the OpenAPI document. The attribute still exists in the table; data is still returned on all read surfaces (REST GET, SQL, Operations API) for any user with read permission on the table. Use this for fields that should not appear in generated specs or tool schemas, not to restrict data access. | ||
|
|
||
| ```graphql | ||
| type Customer @table { | ||
|
|
@@ -340,7 +348,7 @@ type Customer @table { | |
| } | ||
| ``` | ||
|
|
||
| `@hidden` is a metadata-visibility directive, not access control: `attribute_permissions` on roles remains the data-access enforcement mechanism. | ||
| `@hidden` is a metadata-visibility directive, not access control: `attribute_permissions` on roles remains the data-access enforcement mechanism. A field marked `@hidden` is still readable by any role with table `read` access — to prevent a role from reading a field value, set `read: false` in `attribute_permissions` for that role. | ||
|
|
||
| ## Relationships | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -173,6 +173,10 @@ Each table entry defines CRUD access: | |
| - `DELETE` is not an attribute-level permission. Deleting rows is controlled at the table level. | ||
| - The `__createdtime__` and `__updatedtime__` attributes managed by Harper can have `read` permissions set; other attribute-level permissions for these fields are ignored. | ||
|
|
||
| :::note Attribute `read: false` — filter side-channel | ||
| Setting `read: false` on an attribute prevents the **value** from appearing in any response body (REST, SQL, Operations API, GraphQL all omit or reject it). However, the attribute can still be used as a **filter predicate** by that role — e.g. `GET /Table/?salary=95000` returns 0 or 1 rows, revealing whether any record holds that exact value. Binary-search enumeration of the restricted column is possible without ever reading a value directly. If preventing any inference from query results is a requirement, the application must reject or ignore filter conditions on `read: false` attributes in a custom resource handler. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The statement that Consider clarifying this to refer to matching rows or the count of matching rows. Suggested change: ... — e.g. `GET /Table/?salary=95000` returns matching rows (or a count of matching rows), revealing whether any record holds that exact value. ... |
||
| ::: | ||
|
|
||
| ## Role-Based Operation Restrictions | ||
|
|
||
| ### Databases and Tables | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Harper JavaScript environment,
getCurrentUser()is not a global or standalone function; the global function isgetUser().getCurrentUser()is a method on theResourceclass/instance. Therefore, callinggetCurrentUser()directly will result in aReferenceError.Inside a custom resource handler, you should call
this.getCurrentUser()or import and use the globalgetUser()function.Suggested change: