@cosmicdrift/kumiko-bundled-features 0.269.2 → 0.270.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 (76) hide show
  1. package/package.json +9 -9
  2. package/src/__tests__/account-security-composition.boot.test.ts +25 -7
  3. package/src/__tests__/account-security-composition.integration.test.ts +23 -1
  4. package/src/agent-tools/__tests__/agent-doc-lint.test.ts +3 -1
  5. package/src/agent-tools/__tests__/feature.test.ts +10 -4
  6. package/src/auth-email-password/handlers/invite-accept.write.ts +8 -1
  7. package/src/auth-mfa/handlers/disable.write.ts +7 -1
  8. package/src/auth-mfa/handlers/enable-confirm.write.ts +7 -1
  9. package/src/auth-mfa/handlers/enable-start.write.ts +7 -1
  10. package/src/auth-mfa/handlers/regenerate-recovery.write.ts +7 -1
  11. package/src/auth-mfa/handlers/status.query.ts +7 -1
  12. package/src/auth-mfa/screens.ts +22 -3
  13. package/src/channel-in-app/handlers/inbox.query.ts +7 -1
  14. package/src/channel-in-app/handlers/mark-all-read.write.ts +7 -1
  15. package/src/channel-in-app/handlers/mark-read.write.ts +7 -1
  16. package/src/channel-in-app/handlers/unread-count.query.ts +7 -1
  17. package/src/compliance-profiles/handlers/for-tenant.query.ts +7 -1
  18. package/src/compliance-profiles/handlers/list-profiles.query.ts +7 -1
  19. package/src/config/__tests__/config.integration.test.ts +1 -1
  20. package/src/config/__tests__/tz-resolution.integration.test.ts +1 -1
  21. package/src/config/handlers/cascade.query.ts +7 -1
  22. package/src/config/handlers/readiness.query.ts +7 -1
  23. package/src/config/handlers/reset.write.ts +7 -1
  24. package/src/config/handlers/schema.query.ts +7 -1
  25. package/src/config/handlers/set.write.ts +7 -1
  26. package/src/config/handlers/values.query.ts +7 -1
  27. package/src/data-retention/handlers/policy-for.query.ts +8 -1
  28. package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +2 -2
  29. package/src/delivery/__tests__/delivery-screens.boot.test.ts +2 -4
  30. package/src/delivery/__tests__/delivery.integration.test.ts +5 -5
  31. package/src/delivery/handlers/preferences.query.ts +7 -1
  32. package/src/delivery/handlers/set-preference.write.ts +7 -1
  33. package/src/derivatives-sharp/__tests__/derivatives-sharp.integration.test.ts +1 -1
  34. package/src/folders/__tests__/feature.test.ts +14 -5
  35. package/src/folders/__tests__/folders-parent-visibility.integration.test.ts +6 -1
  36. package/src/folders/__tests__/folders-read-gate.integration.test.ts +6 -1
  37. package/src/folders/__tests__/folders.integration.test.ts +6 -1
  38. package/src/folders/feature.ts +2 -2
  39. package/src/form-draft/constants.ts +7 -1
  40. package/src/notes-history/__tests__/notes-read-gate.integration.test.ts +10 -2
  41. package/src/notes-history/feature.ts +2 -2
  42. package/src/personal-access-tokens/handlers/available-scopes.query.ts +7 -1
  43. package/src/personal-access-tokens/handlers/create.write.ts +7 -1
  44. package/src/personal-access-tokens/handlers/list.query.ts +7 -1
  45. package/src/personal-access-tokens/handlers/revoke.write.ts +7 -1
  46. package/src/personal-access-tokens/screens.ts +15 -2
  47. package/src/secrets/__tests__/access-options-precedence.test.ts +11 -4
  48. package/src/secrets/__tests__/access-options.integration.test.ts +5 -1
  49. package/src/secrets/feature.ts +2 -2
  50. package/src/sessions/handlers/mine.query.ts +7 -1
  51. package/src/sessions/handlers/revoke-all-others.write.ts +7 -1
  52. package/src/sessions/handlers/revoke.write.ts +7 -1
  53. package/src/sessions/screens.ts +7 -1
  54. package/src/tags/__tests__/feature.test.ts +25 -8
  55. package/src/tags/__tests__/tags-parent-visibility.integration.test.ts +6 -1
  56. package/src/tags/__tests__/tags-read-gate.integration.test.ts +7 -2
  57. package/src/tags/__tests__/tags.integration.test.ts +6 -1
  58. package/src/tags/constants.ts +1 -1
  59. package/src/tags/feature.ts +2 -2
  60. package/src/tenant/__tests__/tenant-timezone-boot.integration.test.ts +2 -2
  61. package/src/tenant/__tests__/tenant.integration.test.ts +5 -1
  62. package/src/tenant/handlers/me.query.ts +7 -1
  63. package/src/user/handlers/me.query.ts +7 -1
  64. package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +6 -1
  65. package/src/user-data-rights/feature.ts +8 -1
  66. package/src/user-data-rights/handlers/cancel-deletion.write.ts +7 -1
  67. package/src/user-data-rights/handlers/download-by-job.query.ts +7 -1
  68. package/src/user-data-rights/handlers/export-status.query.ts +7 -1
  69. package/src/user-data-rights/handlers/my-audit-log.query.ts +8 -1
  70. package/src/user-data-rights/handlers/request-deletion.write.ts +7 -1
  71. package/src/user-data-rights/handlers/request-export.write.ts +7 -1
  72. package/src/user-data-rights/handlers/restrict-account.write.ts +8 -1
  73. package/src/user-data-rights/lib/is-admin-actor.ts +1 -1
  74. package/src/user-profile/__tests__/profile-screen.boot.test.ts +6 -1
  75. package/src/user-profile/feature.ts +8 -1
  76. package/src/workflow-runner/__tests__/pending-projection.integration.test.ts +1 -1
@@ -94,7 +94,12 @@ describe("privacy-center screen (fw#2312 projectionDetail conversion)", () => {
94
94
  expect(screen?.type).toBe("projectionDetail");
95
95
  if (screen?.type === "projectionDetail") {
96
96
  expect(screen.query).toBe("user:query:user:me");
97
- expect(screen.access).toEqual({ openToAll: true });
97
+ expect(screen.access).toEqual({
98
+ openToAll: {
99
+ reason:
100
+ "each signed-in user manages GDPR self-service actions (export, restriction, deletion) for only their own account; the bound query and every action handler operate on the caller's own userId, and no app role name is portable enough to gate this",
101
+ },
102
+ });
98
103
  }
99
104
  });
100
105
 
@@ -344,7 +344,14 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
344
344
  type: "projectionDetail",
345
345
  query: USER_ME_QUERY,
346
346
  singleton: true,
347
- access: { openToAll: true },
347
+ access: {
348
+ openToAll: {
349
+ reason:
350
+ "each signed-in user manages GDPR self-service actions (export, restriction, " +
351
+ "deletion) for only their own account; the bound query and every action handler " +
352
+ "operate on the caller's own userId, and no app role name is portable enough to gate this",
353
+ },
354
+ },
348
355
  description:
349
356
  "Logged-in GDPR self-service page where a user requests and downloads a data export (Art. 20), restricts processing of their account (Art. 18) and requests its deletion (Art. 17).",
350
357
  fieldLabels: {
@@ -22,7 +22,13 @@ const APPEND_LIFECYCLE_EVENT_REASON =
22
22
  export const cancelDeletionWrite = defineWriteHandler({
23
23
  name: "cancel-deletion",
24
24
  schema: z.object({}),
25
- access: { openToAll: true },
25
+ access: {
26
+ openToAll: {
27
+ reason:
28
+ "each signed-in user cancels only their own pending deletion request; the " +
29
+ "lookup and update are scoped to the caller's own id",
30
+ },
31
+ },
26
32
  description:
27
33
  "Withdraws the calling user's own pending account-deletion request and puts the account back to active, accepted only while the grace period is still running.",
28
34
  escapeHatch: {
@@ -63,7 +63,13 @@ export const downloadByJobQuery = defineQueryHandler({
63
63
  schema: z.object({
64
64
  jobId: z.string().min(1, "jobId required"),
65
65
  }),
66
- access: { openToAll: true }, // openToAll = auth-required, kein anonymous
66
+ access: {
67
+ openToAll: {
68
+ reason:
69
+ "each signed-in user downloads only their own finished export job; the job " +
70
+ "lookup checks jobRow.userId against the caller's own id and 404s otherwise",
71
+ },
72
+ }, // openToAll = auth-required, kein anonymous
67
73
  description:
68
74
  "Returns a short-lived signed download URL for the calling user's own finished data-export job named by job id, backing the download button in the privacy center once export-status reports the job done.",
69
75
  escapeHatch: {
@@ -35,7 +35,13 @@ type ExportJobRow = {
35
35
  export const exportStatusQuery = defineQueryHandler({
36
36
  name: "export-status",
37
37
  schema: z.object({}),
38
- access: { openToAll: true },
38
+ access: {
39
+ openToAll: {
40
+ reason:
41
+ "each signed-in user reads only their own most recent export job; the query " +
42
+ "filters exportJobsTable by the caller's own userId",
43
+ },
44
+ },
39
45
  description:
40
46
  "Returns the calling user's own most recent data-export job with its status, expiry and error, or hasJob false, for polling after a request-export while the job is still running.",
41
47
  escapeHatch: {
@@ -28,7 +28,14 @@ export const myAuditLogQuery = defineQueryHandler({
28
28
  message: "`from` must be less than or equal to `to`",
29
29
  path: ["from"],
30
30
  }),
31
- access: { openToAll: true },
31
+ access: {
32
+ openToAll: {
33
+ reason:
34
+ "each signed-in user reads only their own event-store history for the GDPR " +
35
+ "Art. 15 self-disclosure; the query hard-codes createdBy = caller and can never " +
36
+ "read another user's history",
37
+ },
38
+ },
32
39
  description:
33
40
  "Returns the calling user's own event-store entries across all their tenant memberships, paged and filterable by aggregate type, event type and time range, for the GDPR Art. 15 self-disclosure; it can never read another user's history.",
34
41
  escapeHatch: {
@@ -43,7 +43,13 @@ export function createRequestDeletionHandler(opts: RequestDeletionOptions = {})
43
43
  return defineWriteHandler({
44
44
  name: "request-deletion",
45
45
  schema: z.object({}),
46
- access: { openToAll: true },
46
+ access: {
47
+ openToAll: {
48
+ reason:
49
+ "each signed-in user starts deletion of only their own account; the grace " +
50
+ "period is armed for the caller's own userId",
51
+ },
52
+ },
47
53
  escapeHatch: {
48
54
  reason:
49
55
  "startDeletionGracePeriod reads the tenant compliance profile via ctx.queryAs(SYSTEM, " +
@@ -60,7 +60,13 @@ function isActiveJobConflict(failure: WriteFailure): boolean {
60
60
  export const requestExportWrite = defineWriteHandler({
61
61
  name: "request-export",
62
62
  schema: z.object({}),
63
- access: { openToAll: true },
63
+ access: {
64
+ openToAll: {
65
+ reason:
66
+ "each signed-in user queues only their own data export; the active-job " +
67
+ "check and the created job are both keyed by the caller's own userId",
68
+ },
69
+ },
64
70
  description:
65
71
  "Queues a GDPR Art. 15 and 20 data export for the calling user and returns its job id, handing back the running job with isExisting true instead of a second one when an export is already pending.",
66
72
  escapeHatch: {
@@ -40,7 +40,14 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle";
40
40
  export const restrictAccountWrite = defineWriteHandler({
41
41
  name: "restrict-account",
42
42
  schema: z.object({ userId: z.string().uuid().optional() }),
43
- access: { openToAll: true },
43
+ access: {
44
+ openToAll: {
45
+ reason:
46
+ "each signed-in user may restrict only their own account by default; targeting " +
47
+ "another userId additionally requires isAdminActor(event.user) and a membership " +
48
+ "row for the target in the admin's own tenant, both checked in the handler body",
49
+ },
50
+ },
44
51
  escapeHatch: {
45
52
  reason:
46
53
  "Revokes all live sessions of the (possibly self-)restricted user via ctx.writeAs(SYSTEM, " +
@@ -4,7 +4,7 @@ import { access } from "@cosmicdrift/kumiko-framework/engine";
4
4
  const ADMIN_ROLES: ReadonlySet<string> = new Set(access.admin);
5
5
  const SYSTEM_ADMIN_ROLES: ReadonlySet<string> = new Set(access.systemAdmin);
6
6
 
7
- // Reused by handlers that are `openToAll: true` but still need a manual
7
+ // Reused by handlers that declare `openToAll` but still need a manual
8
8
  // self-or-admin gate (e.g. restrict-account.write.ts) — Set-lookup instead
9
9
  // of the O(n·m) `roles.some(role => access.admin.includes(role))` re-scan.
10
10
  export function isAdminActor(user: Pick<SessionUser, "roles">): boolean {
@@ -46,7 +46,12 @@ describe("user-profile screen (fw#2312 projectionDetail conversion)", () => {
46
46
  expect(screen?.type).toBe("projectionDetail");
47
47
  if (screen?.type === "projectionDetail") {
48
48
  expect(screen.query).toBe("user:query:user:me");
49
- expect(screen.access).toEqual({ openToAll: true });
49
+ expect(screen.access).toEqual({
50
+ openToAll: {
51
+ reason:
52
+ "each signed-in user manages only their own profile: change-email/password re-authenticate the caller, and deletion request/cancel act on the caller's own userId; no app role name is portable enough to gate this",
53
+ },
54
+ });
50
55
  }
51
56
  });
52
57
 
@@ -77,7 +77,14 @@ export function createUserProfileFeature(): FeatureDefinition {
77
77
  type: "projectionDetail",
78
78
  query: UserProfileQueries.me,
79
79
  singleton: true,
80
- access: { openToAll: true },
80
+ access: {
81
+ openToAll: {
82
+ reason:
83
+ "each signed-in user manages only their own profile: change-email/password " +
84
+ "re-authenticate the caller, and deletion request/cancel act on the caller's " +
85
+ "own userId; no app role name is portable enough to gate this",
86
+ },
87
+ },
81
88
  description:
82
89
  "Self-service account page: change password, change email (with re-auth and " +
83
90
  "a verification-mail follow-up), and request or cancel account deletion " +
@@ -52,7 +52,7 @@ const testEventsFeature = defineFeature("wr-pending-test", (r) => {
52
52
  });
53
53
  return { isSuccess: true, data: {} };
54
54
  },
55
- { access: { openToAll: true } },
55
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
56
56
  );
57
57
  });
58
58