@opensaas/stack-core 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +61 -0
  3. package/dist/access/access-filter.d.ts +76 -20
  4. package/dist/access/access-filter.d.ts.map +1 -1
  5. package/dist/access/access-filter.js +102 -70
  6. package/dist/access/access-filter.js.map +1 -1
  7. package/dist/access/access-filter.test.js +175 -10
  8. package/dist/access/access-filter.test.js.map +1 -1
  9. package/dist/access/depth-limits.d.ts +26 -0
  10. package/dist/access/depth-limits.d.ts.map +1 -0
  11. package/dist/access/depth-limits.js +26 -0
  12. package/dist/access/depth-limits.js.map +1 -0
  13. package/dist/access/errors.d.ts +43 -0
  14. package/dist/access/errors.d.ts.map +1 -0
  15. package/dist/access/errors.js +55 -0
  16. package/dist/access/errors.js.map +1 -0
  17. package/dist/access/field-visibility.d.ts.map +1 -1
  18. package/dist/access/field-visibility.js +82 -20
  19. package/dist/access/field-visibility.js.map +1 -1
  20. package/dist/access/index.d.ts +4 -1
  21. package/dist/access/index.d.ts.map +1 -1
  22. package/dist/access/index.js +5 -1
  23. package/dist/access/index.js.map +1 -1
  24. package/dist/access/multi-column-read-write.test.js +1 -1
  25. package/dist/access/multi-column-read-write.test.js.map +1 -1
  26. package/dist/access/relationship-count.test.js +1 -1
  27. package/dist/access/relationship-count.test.js.map +1 -1
  28. package/dist/access/relationship-label-filter.test.js +1 -1
  29. package/dist/access/relationship-label-filter.test.js.map +1 -1
  30. package/dist/access/types.d.ts +15 -7
  31. package/dist/access/types.d.ts.map +1 -1
  32. package/dist/context/index.d.ts.map +1 -1
  33. package/dist/context/index.js +15 -9
  34. package/dist/context/index.js.map +1 -1
  35. package/dist/context/nested-operations.d.ts +1 -1
  36. package/dist/context/nested-operations.d.ts.map +1 -1
  37. package/dist/context/nested-operations.js +1 -5
  38. package/dist/context/nested-operations.js.map +1 -1
  39. package/dist/context/transaction-boundary.d.ts.map +1 -1
  40. package/dist/context/transaction-boundary.js +43 -6
  41. package/dist/context/transaction-boundary.js.map +1 -1
  42. package/dist/context/write-pipeline.d.ts.map +1 -1
  43. package/dist/context/write-pipeline.js +5 -4
  44. package/dist/context/write-pipeline.js.map +1 -1
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +9 -0
  48. package/dist/index.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/access/access-filter.test.ts +258 -7
  51. package/src/access/access-filter.ts +146 -72
  52. package/src/access/depth-limits.ts +26 -0
  53. package/src/access/errors.ts +62 -0
  54. package/src/access/field-visibility.ts +97 -21
  55. package/src/access/index.ts +6 -0
  56. package/src/access/multi-column-read-write.test.ts +1 -1
  57. package/src/access/relationship-count.test.ts +1 -1
  58. package/src/access/relationship-label-filter.test.ts +1 -1
  59. package/src/access/types.ts +12 -5
  60. package/src/context/index.ts +15 -6
  61. package/src/context/nested-operations.ts +0 -7
  62. package/src/context/transaction-boundary.ts +48 -7
  63. package/src/context/write-pipeline.ts +5 -4
  64. package/src/index.ts +11 -0
  65. package/tests/access-relationships.test.ts +78 -64
  66. package/tests/context.test.ts +106 -24
  67. package/tests/default-value-create.test.ts +1 -1
  68. package/tests/hook-pipeline.test.ts +1 -1
  69. package/tests/nav-count.test.ts +2 -2
  70. package/tests/resolve-chain.test.ts +394 -0
  71. package/tests/transaction-boundary-hooks.test.ts +246 -1
  72. package/tests/write-pipeline.test.ts +1 -1
  73. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { buildIncludeWithAccessControl, mergeIncludeWithAccessControl } from './access-filter.js';
2
+ import { buildIncludeWithAccessControl, mergeIncludeWithAccessControl, toPrismaInclude, } from './access-filter.js';
3
+ import { AccessScopeDepthExceededError } from './errors.js';
4
+ import { READ_INCLUDE_MAX_DEPTH } from './depth-limits.js';
3
5
  /**
4
6
  * Regression coverage for the cyclic readable-relationship auto-include.
5
7
  *
@@ -37,11 +39,15 @@ function cyclicConfig() {
37
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
38
40
  };
39
41
  }
40
- function makeContext() {
42
+ function makeContext(resolveOutputDepth = 0) {
43
+ const chain = Array.from({ length: resolveOutputDepth }, (_, i) => ({
44
+ listKey: 'TestHook',
45
+ fieldKey: `hook${i}`,
46
+ }));
41
47
  return {
42
48
  session: null,
43
49
  _isSudo: false,
44
- _resolveOutputCounter: { depth: 0 },
50
+ _resolveOutputChain: chain,
45
51
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal context for unit test
46
52
  };
47
53
  }
@@ -64,10 +70,60 @@ function includeDepth(include) {
64
70
  }
65
71
  return max;
66
72
  }
73
+ // A straight-line chain of `count` lists, each with a scalar field and a
74
+ // single relationship to the next list: L0 → L1 → … → L(count-1). Every list
75
+ // past the root is query-scoped with a filter unique to it, so a test can
76
+ // assert the merged tree actually carries the right `where` at a given hop
77
+ // (not just that it happens not to throw).
78
+ function chainConfig(count) {
79
+ const lists = {};
80
+ for (let i = 0; i < count; i++) {
81
+ const listName = `L${i}`;
82
+ const fields = { name: { type: 'text' } };
83
+ if (i < count - 1) {
84
+ fields.next = rel(`L${i + 1}.prev`);
85
+ }
86
+ if (i > 0) {
87
+ fields.prev = rel(`L${i - 1}.next`);
88
+ }
89
+ const filter = { ownerId: { equals: listName } };
90
+ lists[listName] = {
91
+ fields,
92
+ access: { operation: { query: i === 0 ? () => true : () => filter } },
93
+ };
94
+ }
95
+ return {
96
+ db: { provider: 'sqlite', url: 'file:./dev.db' },
97
+ lists,
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
99
+ };
100
+ }
101
+ // A caller `include` value selecting `next` `hops` more times beyond the
102
+ // point at which this value is attached, ending in a bare `true` leaf. E.g.
103
+ // `nestedInclude(0) === true` (stop here); `{ next: nestedInclude(2) }` at the
104
+ // top level names 3 hops total (the outer `next` plus 2 more).
105
+ function nestedInclude(hops) {
106
+ if (hops <= 0)
107
+ return true;
108
+ return { include: { next: nestedInclude(hops - 1) } };
109
+ }
110
+ // Read the `where` at the end of a chain of nested `next` includes (used to
111
+ // assert row-scoping survived down to a specific hop).
112
+ function whereAtHop(merged, hops) {
113
+ let current = merged;
114
+ for (let i = 0; i < hops; i++) {
115
+ const entry = current?.next;
116
+ if (i === hops - 1)
117
+ return entry?.where;
118
+ current = entry?.include;
119
+ }
120
+ return undefined;
121
+ }
67
122
  describe('buildIncludeWithAccessControl — cyclic graph', () => {
68
123
  it('stops re-descending a relationship cycle instead of walking to MAX_DEPTH', async () => {
69
124
  const config = cyclicConfig();
70
- const include = await buildIncludeWithAccessControl(config.lists.A.fields, { session: null, context: makeContext() }, config, 0, ['A']);
125
+ const result = await buildIncludeWithAccessControl(config.lists.A.fields, { session: null, context: makeContext() }, config, 0, ['A']);
126
+ const include = toPrismaInclude(result);
71
127
  // A → B → C, then C.a closes the cycle back to A → flat (no further nesting).
72
128
  expect(include).toEqual({
73
129
  b: { include: { c: { include: { a: true } } } },
@@ -79,7 +135,7 @@ describe('buildIncludeWithAccessControl — cyclic graph', () => {
79
135
  it('flattens a self-referential relationship to a single level', async () => {
80
136
  const allowQuery = () => true;
81
137
  const config = {
82
- db: { provider: 'sqlite', url: 'file:./dev.db' },
138
+ db: { provider: 'sqlite' },
83
139
  lists: {
84
140
  Category: {
85
141
  fields: {
@@ -92,7 +148,8 @@ describe('buildIncludeWithAccessControl — cyclic graph', () => {
92
148
  },
93
149
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
94
150
  };
95
- const include = await buildIncludeWithAccessControl(config.lists.Category.fields, { session: null, context: makeContext() }, config, 0, ['Category']);
151
+ const result = await buildIncludeWithAccessControl(config.lists.Category.fields, { session: null, context: makeContext() }, config, 0, ['Category']);
152
+ const include = toPrismaInclude(result);
96
153
  // Both self-references come back flat — no infinite parent/children descent.
97
154
  expect(include).toEqual({ parent: true, children: true });
98
155
  expect(includeDepth(include)).toBe(1);
@@ -105,7 +162,7 @@ describe('mergeIncludeWithAccessControl — bare-true leaf on a cyclic graph', (
105
162
  // Caller asks for A → b with a bare-`true` leaf. The merge must keep the
106
163
  // access-controlled (cycle-bounded) nested include rather than re-expanding
107
164
  // the leaf into an unbounded auto-include.
108
- const merged = mergeIncludeWithAccessControl({ b: true }, accessControlledInclude, config.lists.A.fields, config);
165
+ const merged = mergeIncludeWithAccessControl({ b: true }, accessControlledInclude, config.lists.A.fields, config, 'A');
109
166
  expect(merged).toEqual({
110
167
  b: { include: { c: { include: { a: true } } } },
111
168
  });
@@ -124,7 +181,7 @@ describe('mergeIncludeWithAccessControl — caller take on a to-many relation (i
124
181
  // A one-list config whose `posts` to-many is query-scoped by an access filter.
125
182
  function scopedConfig() {
126
183
  return {
127
- db: { provider: 'sqlite', url: 'file:./dev.db' },
184
+ db: { provider: 'sqlite' },
128
185
  lists: {
129
186
  User: {
130
187
  fields: { name: { type: 'text' }, posts: rel('Post.author', true) },
@@ -142,7 +199,7 @@ describe('mergeIncludeWithAccessControl — caller take on a to-many relation (i
142
199
  it('preserves the take and AND-combines it with the access where', async () => {
143
200
  const config = scopedConfig();
144
201
  const accessControlledInclude = await buildIncludeWithAccessControl(config.lists.User.fields, { session: null, context: makeContext() }, config, 0, ['User']);
145
- const merged = mergeIncludeWithAccessControl({ posts: { take: 10 } }, accessControlledInclude, config.lists.User.fields, config);
202
+ const merged = mergeIncludeWithAccessControl({ posts: { take: 10 } }, accessControlledInclude, config.lists.User.fields, config, 'User');
146
203
  // The bound rides on top of the access filter — neither is dropped. The
147
204
  // access-controlled include also auto-nests Post's `author` back-relation.
148
205
  expect(merged).toEqual({
@@ -158,9 +215,117 @@ describe('mergeIncludeWithAccessControl — caller take on a to-many relation (i
158
215
  // Deny Post reads entirely.
159
216
  config.lists.Post.access = { operation: { query: () => false } };
160
217
  const accessControlledInclude = await buildIncludeWithAccessControl(config.lists.User.fields, { session: null, context: makeContext() }, config, 0, ['User']);
161
- const merged = mergeIncludeWithAccessControl({ posts: { take: 10 } }, accessControlledInclude, config.lists.User.fields, config);
218
+ const merged = mergeIncludeWithAccessControl({ posts: { take: 10 } }, accessControlledInclude, config.lists.User.fields, config, 'User');
162
219
  // A denied relation is dropped wholesale — the take cannot resurrect it.
163
220
  expect(merged).toEqual({});
164
221
  });
165
222
  });
223
+ /**
224
+ * Regression coverage for issue #830: the read pipeline used to FAIL OPEN past
225
+ * `READ_INCLUDE_MAX_DEPTH` — a caller-supplied `include` nested deeper than the
226
+ * engine could scope was passed through unscoped rather than denied. These
227
+ * tests pin the exact boundary the fix introduces (ADR-0022): a caller include
228
+ * one level past the cap throws, the same include one level shallower still
229
+ * works, and an unrequested auto-include past the cap stays silent.
230
+ */
231
+ describe('mergeIncludeWithAccessControl — fail-closed at the read-include depth cap (#830)', () => {
232
+ // A relation `hops` hops from the root sits AT the cap boundary (correctly
233
+ // where-scoped, matching the triage report's "F" list); one hop further is
234
+ // the first one the engine cannot scope ("G"). With
235
+ // READ_INCLUDE_MAX_DEPTH = 5 that boundary is hop 5 / hop 6.
236
+ const HOPS_AT_CAP = READ_INCLUDE_MAX_DEPTH;
237
+ it('throws AccessScopeDepthExceededError when the caller include reaches past the cap', async () => {
238
+ // L0 → L1 → … → L6 (one more list than HOPS_AT_CAP + 1) so a caller
239
+ // include can walk `next` one hop past the boundary.
240
+ const chainLength = HOPS_AT_CAP + 2;
241
+ const config = chainConfig(chainLength);
242
+ const accessControlledInclude = await buildIncludeWithAccessControl(config.lists.L0.fields, { session: null, context: makeContext() }, config, 0, ['L0']);
243
+ // Outer `next` (hop 1) + nestedInclude(HOPS_AT_CAP) (HOPS_AT_CAP more) = HOPS_AT_CAP + 1 hops.
244
+ const callerInclude = { next: nestedInclude(HOPS_AT_CAP) };
245
+ expect(() => mergeIncludeWithAccessControl(callerInclude, accessControlledInclude, config.lists.L0.fields, config, 'L0')).toThrow(AccessScopeDepthExceededError);
246
+ });
247
+ it('still row-scopes a caller include one level shallower than the cap', async () => {
248
+ const chainLength = HOPS_AT_CAP + 1;
249
+ const config = chainConfig(chainLength);
250
+ const accessControlledInclude = await buildIncludeWithAccessControl(config.lists.L0.fields, { session: null, context: makeContext() }, config, 0, ['L0']);
251
+ // Outer `next` (hop 1) + nestedInclude(HOPS_AT_CAP - 1) = HOPS_AT_CAP hops total — right at the boundary.
252
+ const callerInclude = { next: nestedInclude(HOPS_AT_CAP - 1) };
253
+ // Must NOT throw — this depth is within what the engine can scope.
254
+ const merged = mergeIncludeWithAccessControl(callerInclude, accessControlledInclude, config.lists.L0.fields, config, 'L0');
255
+ expect(includeDepth(merged)).toBe(HOPS_AT_CAP);
256
+ // The last list in the chain (at the boundary) is genuinely row-scoped —
257
+ // not just present without a throw.
258
+ expect(whereAtHop(merged, HOPS_AT_CAP)).toEqual({ ownerId: { equals: `L${HOPS_AT_CAP}` } });
259
+ });
260
+ it('does not throw for an ordinary read with no caller include, even on a deep schema', async () => {
261
+ // No caller include at all — the auto-include just stops at the cap
262
+ // silently. This must never throw; only an EXPLICIT caller selection past
263
+ // the cap is a denial.
264
+ const chainLength = HOPS_AT_CAP + 3;
265
+ const config = chainConfig(chainLength);
266
+ const result = await buildIncludeWithAccessControl(config.lists.L0.fields, { session: null, context: makeContext() }, config, 0, ['L0']);
267
+ expect(() => toPrismaInclude(result)).not.toThrow();
268
+ const include = toPrismaInclude(result);
269
+ expect(includeDepth(include)).toBeLessThanOrEqual(HOPS_AT_CAP);
270
+ });
271
+ it('a list with no relationships still passes the caller include through unchanged', async () => {
272
+ const config = {
273
+ db: { provider: 'sqlite' },
274
+ lists: {
275
+ Leaf: {
276
+ fields: { name: { type: 'text' } },
277
+ access: { operation: { query: () => true } },
278
+ },
279
+ },
280
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
281
+ };
282
+ const accessControlledInclude = await buildIncludeWithAccessControl(config.lists.Leaf.fields, { session: null, context: makeContext() }, config, 0, ['Leaf']);
283
+ expect(accessControlledInclude).toEqual({ kind: 'nothing-to-scope' });
284
+ // An arbitrary (non-declared-relationship) key passed through unchanged —
285
+ // access control does not govern keys it doesn't recognize as relationships.
286
+ const merged = mergeIncludeWithAccessControl({ someUnrelatedKey: true }, accessControlledInclude, config.lists.Leaf.fields, config, 'Leaf');
287
+ expect(merged).toEqual({ someUnrelatedKey: true });
288
+ });
289
+ });
290
+ /**
291
+ * Regression coverage for the resolveOutput/virtual-field trigger of #830: a
292
+ * read issued from inside a resolveOutput hook used to lose relation row
293
+ * scoping ENTIRELY (whole-object passthrough) rather than scoping the
294
+ * immediate relation and simply not auto-expanding further. The loop guard
295
+ * that motivated the original passthrough (hooks making DB queries that
296
+ * include relationships back to the same entity) must still hold.
297
+ */
298
+ describe('buildIncludeWithAccessControl — inside a resolveOutput context', () => {
299
+ it('scopes the immediate relation with its access where but does not auto-expand nested relations', async () => {
300
+ const config = chainConfig(4); // L0 → L1 → L2 → L3
301
+ config.lists.L1.access = { operation: { query: () => ({ tenantId: { equals: 'mine' } }) } };
302
+ const result = await buildIncludeWithAccessControl(config.lists.L0.fields, { session: null, context: makeContext(1) }, // depth > 0 → inside resolveOutput
303
+ config, 0, ['L0']);
304
+ expect(result.kind).toBe('scoped');
305
+ const include = toPrismaInclude(result);
306
+ // L1's own access `where` is applied...
307
+ expect(include).toEqual({ next: { where: { tenantId: { equals: 'mine' } } } });
308
+ // ...but L1's own nested relations (`next` → L2) are NOT auto-included.
309
+ expect(includeDepth(include)).toBe(1);
310
+ });
311
+ it('terminates on a self-referential relationship instead of looping', async () => {
312
+ const config = {
313
+ db: { provider: 'sqlite' },
314
+ lists: {
315
+ Category: {
316
+ fields: {
317
+ name: { type: 'text' },
318
+ parent: rel('Category.children'),
319
+ children: rel('Category.parent', true),
320
+ },
321
+ access: { operation: { query: () => true } },
322
+ },
323
+ },
324
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
325
+ };
326
+ const result = await buildIncludeWithAccessControl(config.lists.Category.fields, { session: null, context: makeContext(1) }, config, 0, ['Category']);
327
+ const include = toPrismaInclude(result);
328
+ expect(include).toEqual({ parent: true, children: true });
329
+ });
330
+ });
166
331
  //# sourceMappingURL=access-filter.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-filter.test.js","sourceRoot":"","sources":["../../src/access/access-filter.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"access-filter.test.js","sourceRoot":"","sources":["../../src/access/access-filter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Maximum nesting depth of relation `include`s that the Access Filter
3
+ * (`buildIncludeWithAccessControl`) will auto-scope on a read.
4
+ *
5
+ * Security implication: this is an access-control boundary, not just a cost
6
+ * bound. Past this depth the engine cannot compute a row/field scope for a
7
+ * relation, so a caller-supplied `include` naming a relation at or beyond it
8
+ * must be treated as a denial (see `AccessScopeDepthExceededError`) rather
9
+ * than passed through unscoped — see ADR-0022 and issue #830.
10
+ */
11
+ export declare const READ_INCLUDE_MAX_DEPTH = 5;
12
+ /**
13
+ * Maximum length of the resolve chain — the ordered sequence of `resolveOutput`
14
+ * hooks a read has entered, each entry extended by a hook that issues its own
15
+ * read (see `_resolveOutputChain` on `AccessContext`).
16
+ *
17
+ * This is a COST limit, not an access-control boundary: an acyclic chain
18
+ * genuinely can exceed it and still be correct (e.g. a virtual `Order.total`
19
+ * reading a virtual `LineItem.subtotal` reading further virtuals), so
20
+ * exceeding it omits the field and emits a single `console.warn` rather than
21
+ * throwing. Termination itself is guaranteed by the separate cycle guard,
22
+ * which refuses to re-enter a `(list, field)` pair already on the chain
23
+ * regardless of this cap. See ADR-0023.
24
+ */
25
+ export declare const RESOLVE_CHAIN_MAX_LENGTH = 5;
26
+ //# sourceMappingURL=depth-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-limits.d.ts","sourceRoot":"","sources":["../../src/access/depth-limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAA"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Maximum nesting depth of relation `include`s that the Access Filter
3
+ * (`buildIncludeWithAccessControl`) will auto-scope on a read.
4
+ *
5
+ * Security implication: this is an access-control boundary, not just a cost
6
+ * bound. Past this depth the engine cannot compute a row/field scope for a
7
+ * relation, so a caller-supplied `include` naming a relation at or beyond it
8
+ * must be treated as a denial (see `AccessScopeDepthExceededError`) rather
9
+ * than passed through unscoped — see ADR-0022 and issue #830.
10
+ */
11
+ export const READ_INCLUDE_MAX_DEPTH = 5;
12
+ /**
13
+ * Maximum length of the resolve chain — the ordered sequence of `resolveOutput`
14
+ * hooks a read has entered, each entry extended by a hook that issues its own
15
+ * read (see `_resolveOutputChain` on `AccessContext`).
16
+ *
17
+ * This is a COST limit, not an access-control boundary: an acyclic chain
18
+ * genuinely can exceed it and still be correct (e.g. a virtual `Order.total`
19
+ * reading a virtual `LineItem.subtotal` reading further virtuals), so
20
+ * exceeding it omits the field and emits a single `console.warn` rather than
21
+ * throwing. Termination itself is guaranteed by the separate cycle guard,
22
+ * which refuses to re-enter a `(list, field)` pair already on the chain
23
+ * regardless of this cap. See ADR-0023.
24
+ */
25
+ export const RESOLVE_CHAIN_MAX_LENGTH = 5;
26
+ //# sourceMappingURL=depth-limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-limits.js","sourceRoot":"","sources":["../../src/access/depth-limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAEvC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAA"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Thrown when a caller-supplied `include` names a relation nested deeper than
3
+ * the Access Filter can scope (see `READ_INCLUDE_MAX_DEPTH`). Deliberately
4
+ * distinct from `ValidationError`: this is not bad user input, it is the
5
+ * engine refusing to return data it cannot prove is row/field scoped. Code
6
+ * that catches `ValidationError` to report form errors must not silently
7
+ * swallow this.
8
+ *
9
+ * Only an explicit caller selection past the depth cap triggers this — the
10
+ * auto-include silently stopping at the cap (no caller `include` involved)
11
+ * never throws. See ADR-0022 and issue #830.
12
+ */
13
+ export declare class AccessScopeDepthExceededError extends Error {
14
+ listKey: string;
15
+ fieldKey: string;
16
+ depth: number;
17
+ constructor(listKey: string, fieldKey: string, depth: number);
18
+ }
19
+ /**
20
+ * Thrown when a `resolveOutput` hook re-enters a `(list, field)` pair already
21
+ * on its own resolve chain — a hook whose own read (directly or transitively)
22
+ * comes back around to itself. Deliberately distinct from `ValidationError`
23
+ * (same reasoning as `AccessScopeDepthExceededError`): this is not bad user
24
+ * input, it is the engine refusing to run a hook chain that cannot terminate.
25
+ *
26
+ * This is a loud failure, not a Silent one, on purpose: a repeated pair never
27
+ * terminated before this guard existed, so no working application can depend
28
+ * on the old (hanging) behaviour, and staying silent here would return
29
+ * `undefined` for a field with nothing in the logs to explain why. Contrast
30
+ * with exceeding `RESOLVE_CHAIN_MAX_LENGTH`, which can happen on a chain that
31
+ * would otherwise terminate correctly and therefore only warns. See ADR-0023.
32
+ */
33
+ export declare class ResolveOutputCycleError extends Error {
34
+ chain: readonly {
35
+ listKey: string;
36
+ fieldKey: string;
37
+ }[];
38
+ constructor(chain: readonly {
39
+ listKey: string;
40
+ fieldKey: string;
41
+ }[]);
42
+ }
43
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/access/errors.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAC/C,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IAEpB,YAAY,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAW3D;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IACzC,KAAK,EAAE,SAAS;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAE9D,YAAY,KAAK,EAAE,SAAS;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,EAUlE;CACF"}
@@ -0,0 +1,55 @@
1
+ import { READ_INCLUDE_MAX_DEPTH } from './depth-limits.js';
2
+ /**
3
+ * Thrown when a caller-supplied `include` names a relation nested deeper than
4
+ * the Access Filter can scope (see `READ_INCLUDE_MAX_DEPTH`). Deliberately
5
+ * distinct from `ValidationError`: this is not bad user input, it is the
6
+ * engine refusing to return data it cannot prove is row/field scoped. Code
7
+ * that catches `ValidationError` to report form errors must not silently
8
+ * swallow this.
9
+ *
10
+ * Only an explicit caller selection past the depth cap triggers this — the
11
+ * auto-include silently stopping at the cap (no caller `include` involved)
12
+ * never throws. See ADR-0022 and issue #830.
13
+ */
14
+ export class AccessScopeDepthExceededError extends Error {
15
+ listKey;
16
+ fieldKey;
17
+ depth;
18
+ constructor(listKey, fieldKey, depth) {
19
+ super(`Cannot compute an access scope for "${listKey}.${fieldKey}" at include depth ${depth}: ` +
20
+ `this exceeds the Access Filter's maximum read-include depth (${READ_INCLUDE_MAX_DEPTH}). ` +
21
+ `A caller-supplied include this deep cannot be row- and field-scoped, so the read is denied ` +
22
+ `rather than returned unscoped. Restructure the query to fetch this relation separately.`);
23
+ this.name = 'AccessScopeDepthExceededError';
24
+ this.listKey = listKey;
25
+ this.fieldKey = fieldKey;
26
+ this.depth = depth;
27
+ }
28
+ }
29
+ /**
30
+ * Thrown when a `resolveOutput` hook re-enters a `(list, field)` pair already
31
+ * on its own resolve chain — a hook whose own read (directly or transitively)
32
+ * comes back around to itself. Deliberately distinct from `ValidationError`
33
+ * (same reasoning as `AccessScopeDepthExceededError`): this is not bad user
34
+ * input, it is the engine refusing to run a hook chain that cannot terminate.
35
+ *
36
+ * This is a loud failure, not a Silent one, on purpose: a repeated pair never
37
+ * terminated before this guard existed, so no working application can depend
38
+ * on the old (hanging) behaviour, and staying silent here would return
39
+ * `undefined` for a field with nothing in the logs to explain why. Contrast
40
+ * with exceeding `RESOLVE_CHAIN_MAX_LENGTH`, which can happen on a chain that
41
+ * would otherwise terminate correctly and therefore only warns. See ADR-0023.
42
+ */
43
+ export class ResolveOutputCycleError extends Error {
44
+ chain;
45
+ constructor(chain) {
46
+ const path = chain.map((link) => `${link.listKey}.${link.fieldKey}`).join(' → ');
47
+ super(`resolveOutput cycle detected: ${path}. A hook that re-enters a (list, field) pair ` +
48
+ `already on its own resolve chain cannot terminate, so the read is refused rather than ` +
49
+ `left to recurse until the process runs out of memory. Restructure the hooks so the read ` +
50
+ `does not loop back into itself.`);
51
+ this.name = 'ResolveOutputCycleError';
52
+ this.chain = chain;
53
+ }
54
+ }
55
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/access/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAC/C,OAAO,CAAQ;IACf,QAAQ,CAAQ;IAChB,KAAK,CAAQ;IAEpB,YAAY,OAAe,EAAE,QAAgB,EAAE,KAAa;QAC1D,KAAK,CACH,uCAAuC,OAAO,IAAI,QAAQ,sBAAsB,KAAK,IAAI;YACvF,gEAAgE,sBAAsB,KAAK;YAC3F,6FAA6F;YAC7F,yFAAyF,CAC5F,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAA;QAC3C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACzC,KAAK,CAAkD;IAE9D,YAAY,KAAuD;QACjE,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChF,KAAK,CACH,iCAAiC,IAAI,+CAA+C;YAClF,wFAAwF;YACxF,0FAA0F;YAC1F,iCAAiC,CACpC,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAA;QACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"field-visibility.d.ts","sourceRoot":"","sources":["../../src/access/field-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAqGrE;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,IAAI,EAAE,CAAC,EACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC/C,EACD,MAAM,CAAC,EAAE,cAAc,EACvB,KAAK,GAAE,MAAU,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAwJrB"}
1
+ {"version":3,"file":"field-visibility.d.ts","sourceRoot":"","sources":["../../src/access/field-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAwKrE;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,IAAI,EAAE,CAAC,EACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC/C,EACD,MAAM,CAAC,EAAE,cAAc,EACvB,KAAK,GAAE,MAAU,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAiKrB"}
@@ -1,5 +1,47 @@
1
1
  import { getRelatedListConfig } from './engine.js';
2
2
  import { checkFieldAccess } from './field-access.js';
3
+ import { RESOLVE_CHAIN_MAX_LENGTH } from './depth-limits.js';
4
+ import { ResolveOutputCycleError } from './errors.js';
5
+ // NOTE: `context/index.ts` imports `filterReadableFields` from this module
6
+ // (via the `access/index.ts` barrel) — this is an intentional cyclic
7
+ // dependency, the same shape and for the same reason as the one documented in
8
+ // `context/write-pipeline.ts`. `buildDbDelegate` is only INVOKED when a
9
+ // `resolveOutput` hook actually runs (never during module evaluation), so by
10
+ // the time it runs the export is fully initialised.
11
+ import { buildDbDelegate } from '../context/index.js';
12
+ /**
13
+ * Derive the context passed to a single `resolveOutput` hook invocation: a
14
+ * NEW context object whose `_resolveOutputChain` extends the caller's chain
15
+ * with this hook's own `(list, field)` link. The chain is never mutated in
16
+ * place — this is what lets concurrent hook invocations (e.g. sibling rows in
17
+ * a to-many relation, filtered via `Promise.all`) each see their own chain
18
+ * rather than racing on one shared value (ADR-0023).
19
+ *
20
+ * A plain `{ ...context, _resolveOutputChain }` spread is not enough on its
21
+ * own: `context.db`'s operations capture their `context` at construction
22
+ * (see `populateDbDelegate`), so a hook that calls `context.db.x.findMany(…)`
23
+ * would otherwise reach the ORIGINAL closures — bound to the ORIGINAL
24
+ * context — and its read would silently fall back to the un-extended chain,
25
+ * defeating the cycle guard entirely. Rebuilding `db` via `buildDbDelegate`
26
+ * against the derived context is what makes a hook-issued read's own nested
27
+ * hooks actually observe the extended chain.
28
+ *
29
+ * `config` is required to rebuild `db`; callers that cannot supply one (e.g. a
30
+ * narrow unit test exercising field access in isolation) still get a correct
31
+ * chain for THIS hook's own cycle/cap check, but a read that hook issues
32
+ * would not carry the chain any further — those callers are not exercising
33
+ * the read pipeline, so there is nothing for it to reach.
34
+ */
35
+ function deriveResolveOutputContext(context, link, config) {
36
+ const derived = {
37
+ ...context,
38
+ _resolveOutputChain: [...context._resolveOutputChain, link],
39
+ };
40
+ if (config) {
41
+ derived.db = buildDbDelegate(config, context.prisma, derived);
42
+ }
43
+ return derived;
44
+ }
3
45
  /**
4
46
  * The core Field Visibility step for a single field: check read access and, if
5
47
  * granted, produce the output value by running any `resolveOutput` hook.
@@ -15,7 +57,7 @@ import { checkFieldAccess } from './field-access.js';
15
57
  * see the already-filtered output so they can read sibling fields).
16
58
  */
17
59
  async function resolveReadableFieldValue(params) {
18
- const { fieldConfig, fieldName, value, accessItem, hookItem, listKey, args } = params;
60
+ const { fieldConfig, fieldName, value, accessItem, hookItem, listKey, args, config } = params;
19
61
  // Check field access (checkFieldAccess already handles sudo mode)
20
62
  const canRead = await checkFieldAccess(fieldConfig?.access, 'read', {
21
63
  ...args,
@@ -29,24 +71,35 @@ async function resolveReadableFieldValue(params) {
29
71
  // Cast to runtime type for generic execution
30
72
  // At runtime, the hook will receive the correct value type for the field
31
73
  const hook = fieldConfig.hooks.resolveOutput;
32
- // Increment depth counter to prevent infinite loops from hooks making DB queries
33
- // that include relationships back to the same entity
34
- args.context._resolveOutputCounter.depth++;
35
- try {
36
- // Use Promise.resolve() to handle both sync and async hooks
37
- const resolved = await Promise.resolve(hook({
38
- value,
39
- operation: 'query',
40
- fieldName,
41
- listKey,
42
- item: hookItem,
43
- context: args.context,
44
- }));
45
- return { readable: true, value: resolved };
74
+ const link = { listKey, fieldKey: fieldName };
75
+ const chain = args.context._resolveOutputChain;
76
+ // Cycle guard: a hook that would re-enter a (list, field) pair already on
77
+ // its own chain cannot terminate — refuse loudly rather than recurse
78
+ // until the process runs out of memory (issue #844, ADR-0023).
79
+ const alreadyOnChain = chain.some((entry) => entry.listKey === link.listKey && entry.fieldKey === link.fieldKey);
80
+ if (alreadyOnChain) {
81
+ throw new ResolveOutputCycleError([...chain, link]);
46
82
  }
47
- finally {
48
- args.context._resolveOutputCounter.depth--;
83
+ // Cost cap: a chain this long is refused only as a cost limit, never a
84
+ // correctness one — an acyclic chain that works today can legitimately
85
+ // reach this. Omit the field and warn instead of throwing.
86
+ if (chain.length >= RESOLVE_CHAIN_MAX_LENGTH) {
87
+ const path = [...chain, link].map((entry) => `${entry.listKey}.${entry.fieldKey}`).join(' → ');
88
+ console.warn(`resolveOutput: omitting "${listKey}.${fieldName}" — its resolve chain exceeded ` +
89
+ `RESOLVE_CHAIN_MAX_LENGTH (${RESOLVE_CHAIN_MAX_LENGTH}): ${path}. This is a cost limit, ` +
90
+ `not an access denial.`);
91
+ return { readable: false };
49
92
  }
93
+ // Use Promise.resolve() to handle both sync and async hooks
94
+ const resolved = await Promise.resolve(hook({
95
+ value,
96
+ operation: 'query',
97
+ fieldName,
98
+ listKey,
99
+ item: hookItem,
100
+ context: deriveResolveOutputContext(args.context, link, config),
101
+ }));
102
+ return { readable: true, value: resolved };
50
103
  }
51
104
  return { readable: true, value };
52
105
  }
@@ -56,7 +109,6 @@ async function resolveReadableFieldValue(params) {
56
109
  */
57
110
  export async function filterReadableFields(item, fieldConfigs, args, config, depth = 0, listKey) {
58
111
  const filtered = {};
59
- const MAX_DEPTH = 5; // Prevent infinite recursion
60
112
  // Multi-column fields (e.g. storage image()/file() in Keystone-parity mode)
61
113
  // back several physical columns rather than one. Before the per-field pass,
62
114
  // assemble each such field's logical value from its raw columns and remove the
@@ -91,13 +143,21 @@ export async function filterReadableFields(item, fieldConfigs, args, config, dep
91
143
  // Handle relationship fields - recursively filter fields within related items
92
144
  // Note: Access control filtering is now done at database level via buildIncludeWithAccessControl
93
145
  // This only handles field-level access (hiding sensitive fields)
146
+ //
147
+ // Deliberately uncapped: the row/relation scoping in access-filter.ts bounds
148
+ // what gets FETCHED (a caller include past its depth cap is now a denial,
149
+ // not a passthrough — see ADR-0022), so by the time a result reaches this
150
+ // function it is already a finite, acyclic tree whose depth was decided at
151
+ // the pre-query phase. Capping recursion again here independently of that
152
+ // cap used to let a relation be scoped correctly at the DB level while
153
+ // still returning with unfiltered fields past this function's own,
154
+ // separately-tracked limit (issue #830).
94
155
  if (config &&
95
156
  fieldConfig?.type === 'relationship' &&
96
157
  'ref' in fieldConfig &&
97
158
  fieldConfig.ref &&
98
159
  value !== null &&
99
- value !== undefined &&
100
- depth < MAX_DEPTH) {
160
+ value !== undefined) {
101
161
  // Gate the relationship on read access before recursing.
102
162
  const canRead = await checkFieldAccess(fieldConfig?.access, 'read', {
103
163
  ...args,
@@ -135,6 +195,7 @@ export async function filterReadableFields(item, fieldConfigs, args, config, dep
135
195
  hookItem: workingItem,
136
196
  listKey,
137
197
  args,
198
+ config,
138
199
  });
139
200
  if (result.readable) {
140
201
  filtered[fieldName] = result.value;
@@ -168,6 +229,7 @@ export async function filterReadableFields(item, fieldConfigs, args, config, dep
168
229
  hookItem: filtered,
169
230
  listKey,
170
231
  args,
232
+ config,
171
233
  });
172
234
  if (result.readable) {
173
235
  filtered[fieldName] = result.value;