@mccaskie-labs/workbench-config 0.1.23 → 0.1.25

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @mccaskie-labs/workbench-config
2
2
 
3
+ ## 0.1.25
4
+
5
+ ### Patch Changes
6
+
7
+ - Vendor settings baseline: two new Supabase Auth rows. `supabase.auth.signup_skips_email_confirm` keeps "Confirm email" on (`mailer_autoconfirm` false), and `supabase.auth.anonymous_sign_ins` keeps anonymous sign-ins off (`external_anonymous_users_enabled` false). An app that trusts the email in a session is only safe while both hold.
8
+
9
+ ## 0.1.24
10
+
11
+ ### Patch Changes
12
+
13
+ - The database manifest gains sign-in: `createSessions`, `safeReturnPath` and `sameOrigin` (workbench-supabase 0.3.0), the optional `SESSION_SECRET`, two rules (sign-in through the app's own endpoints; session writes same-origin) and the probes `database.sign-in` and `database.session-cookie`.
14
+
3
15
  ## 0.1.23
4
16
 
5
17
  ### Patch Changes
@@ -12,7 +12,10 @@
12
12
  "createServerClient",
13
13
  "createUserClient",
14
14
  "createServiceClient",
15
- "WorkbenchSupabaseError"
15
+ "WorkbenchSupabaseError",
16
+ "createSessions",
17
+ "safeReturnPath",
18
+ "sameOrigin"
16
19
  ],
17
20
  "tools": [
18
21
  "sb_project_create",
@@ -56,6 +59,17 @@
56
59
  "production"
57
60
  ],
58
61
  "description": "The sb_secret_ key, only for code that must bypass RLS (createServiceClient); written by cf_worker_secret_put source=supabase_secret, always two-step"
62
+ },
63
+ {
64
+ "name": "SESSION_SECRET",
65
+ "required": false,
66
+ "secret": true,
67
+ "environments": [
68
+ "development",
69
+ "staging",
70
+ "production"
71
+ ],
72
+ "description": "Seals the sign-in session cookie (createSessions). 32 random bytes written by cf_worker_secret_put source=generate:32; replacing it signs everybody out, so it is two-step. Only needed by an app with sign-in"
59
73
  }
60
74
  ],
61
75
  "actions": [
@@ -142,6 +156,20 @@
142
156
  "text": "Keep Supabase's sign-in settings at the Workbench baseline (workbench-config VENDOR_SETTINGS), or record a project's change in vendor-settings.json with a reason. Control applies it with vendor_settings_apply and checks it every day.",
143
157
  "enforced_by": "control",
144
158
  "probe": "database.vendor-settings"
159
+ },
160
+ {
161
+ "id": "database.sign-in-server-side",
162
+ "kind": "must",
163
+ "text": "Sign people in through the app's own endpoints with createSessions: the browser never talks to Supabase Auth and never holds the access token, and codes are typed, never links.",
164
+ "enforced_by": "review",
165
+ "probe": "database.sign-in"
166
+ },
167
+ {
168
+ "id": "database.session-writes-same-origin",
169
+ "kind": "must",
170
+ "text": "Refuse a write (anything but GET or HEAD) made with the session cookie unless sameOrigin(request) is true.",
171
+ "enforced_by": "review",
172
+ "probe": "database.session-cookie"
145
173
  }
146
174
  ],
147
175
  "examples": [
@@ -210,6 +238,20 @@
210
238
  "smoke": true,
211
239
  "cost_hint_usd": 0,
212
240
  "note": "plainError and plainAuthError (workbench-supabase) turn a raw database error and an email_exists auth error into plain words that name the app and never say whether an account exists"
241
+ },
242
+ {
243
+ "id": "database.sign-in",
244
+ "half": "runtime",
245
+ "expect": "pass",
246
+ "cost_hint_usd": 0,
247
+ "note": "Ask for a code, read it from the captured sign-in email, type it, and get a session that reads the user's own rows; a wrong code gets the plain line"
248
+ },
249
+ {
250
+ "id": "database.session-cookie",
251
+ "half": "runtime",
252
+ "expect": "pass",
253
+ "cost_hint_usd": 0,
254
+ "note": "The session cookie is __Host-, Secure, HttpOnly, SameSite=Lax and sealed; a cross-site write is refused; sign-out clears it with every attribute"
213
255
  }
214
256
  ]
215
257
  }
@@ -1,4 +1,4 @@
1
- export declare const MANIFEST_VERSION: "7f14dfc78268";
1
+ export declare const MANIFEST_VERSION: "ecc54c728515";
2
2
  export declare const capabilities: {
3
3
  readonly ai: {
4
4
  readonly $schema: "./schema.json";
@@ -422,7 +422,7 @@ export declare const capabilities: {
422
422
  readonly status: "built";
423
423
  readonly surfaces: {
424
424
  readonly tools: readonly ["sb_project_create", "sb_migration_apply", "sb_migration_rollback", "sb_query", "sb_project_delete", "cf_worker_secret_put", "vendor_settings_apply"];
425
- readonly package: readonly ["supabaseConfig", "createBrowserClient", "createServerClient", "createUserClient", "createServiceClient", "WorkbenchSupabaseError"];
425
+ readonly package: readonly ["supabaseConfig", "createBrowserClient", "createServerClient", "createUserClient", "createServiceClient", "WorkbenchSupabaseError", "createSessions", "safeReturnPath", "sameOrigin"];
426
426
  };
427
427
  readonly config: readonly [{
428
428
  readonly name: "SUPABASE_URL";
@@ -442,6 +442,12 @@ export declare const capabilities: {
442
442
  readonly secret: true;
443
443
  readonly environments: readonly ["staging", "production"];
444
444
  readonly description: "The sb_secret_ key, only for code that must bypass RLS (createServiceClient); written by cf_worker_secret_put source=supabase_secret, always two-step";
445
+ }, {
446
+ readonly name: "SESSION_SECRET";
447
+ readonly required: false;
448
+ readonly secret: true;
449
+ readonly environments: readonly ["development", "staging", "production"];
450
+ readonly description: "Seals the sign-in session cookie (createSessions). 32 random bytes written by cf_worker_secret_put source=generate:32; replacing it signs everybody out, so it is two-step. Only needed by an app with sign-in";
445
451
  }];
446
452
  readonly actions: readonly [{
447
453
  readonly tool: "sb_project_create";
@@ -513,6 +519,18 @@ export declare const capabilities: {
513
519
  readonly text: "Keep Supabase's sign-in settings at the Workbench baseline (workbench-config VENDOR_SETTINGS), or record a project's change in vendor-settings.json with a reason. Control applies it with vendor_settings_apply and checks it every day.";
514
520
  readonly enforced_by: "control";
515
521
  readonly probe: "database.vendor-settings";
522
+ }, {
523
+ readonly id: "database.sign-in-server-side";
524
+ readonly kind: "must";
525
+ readonly text: "Sign people in through the app's own endpoints with createSessions: the browser never talks to Supabase Auth and never holds the access token, and codes are typed, never links.";
526
+ readonly enforced_by: "review";
527
+ readonly probe: "database.sign-in";
528
+ }, {
529
+ readonly id: "database.session-writes-same-origin";
530
+ readonly kind: "must";
531
+ readonly text: "Refuse a write (anything but GET or HEAD) made with the session cookie unless sameOrigin(request) is true.";
532
+ readonly enforced_by: "review";
533
+ readonly probe: "database.session-cookie";
516
534
  }];
517
535
  readonly examples: readonly [{
518
536
  readonly id: "database.example.user-client";
@@ -570,6 +588,18 @@ export declare const capabilities: {
570
588
  readonly smoke: true;
571
589
  readonly cost_hint_usd: 0;
572
590
  readonly note: "plainError and plainAuthError (workbench-supabase) turn a raw database error and an email_exists auth error into plain words that name the app and never say whether an account exists";
591
+ }, {
592
+ readonly id: "database.sign-in";
593
+ readonly half: "runtime";
594
+ readonly expect: "pass";
595
+ readonly cost_hint_usd: 0;
596
+ readonly note: "Ask for a code, read it from the captured sign-in email, type it, and get a session that reads the user's own rows; a wrong code gets the plain line";
597
+ }, {
598
+ readonly id: "database.session-cookie";
599
+ readonly half: "runtime";
600
+ readonly expect: "pass";
601
+ readonly cost_hint_usd: 0;
602
+ readonly note: "The session cookie is __Host-, Secure, HttpOnly, SameSite=Lax and sealed; a cross-site write is refused; sign-out clears it with every attribute";
573
603
  }];
574
604
  };
575
605
  readonly email: {
@@ -1,6 +1,6 @@
1
1
  // Generated by `npm run capabilities:build` from capabilities/*.json. Do not edit.
2
2
  // check:capabilities fails in Checks when this file is stale.
3
- export const MANIFEST_VERSION = "7f14dfc78268";
3
+ export const MANIFEST_VERSION = "ecc54c728515";
4
4
  export const capabilities = {
5
5
  "ai": {
6
6
  "$schema": "./schema.json",
@@ -629,7 +629,10 @@ export const capabilities = {
629
629
  "createServerClient",
630
630
  "createUserClient",
631
631
  "createServiceClient",
632
- "WorkbenchSupabaseError"
632
+ "WorkbenchSupabaseError",
633
+ "createSessions",
634
+ "safeReturnPath",
635
+ "sameOrigin"
633
636
  ]
634
637
  },
635
638
  "config": [
@@ -664,6 +667,17 @@ export const capabilities = {
664
667
  "production"
665
668
  ],
666
669
  "description": "The sb_secret_ key, only for code that must bypass RLS (createServiceClient); written by cf_worker_secret_put source=supabase_secret, always two-step"
670
+ },
671
+ {
672
+ "name": "SESSION_SECRET",
673
+ "required": false,
674
+ "secret": true,
675
+ "environments": [
676
+ "development",
677
+ "staging",
678
+ "production"
679
+ ],
680
+ "description": "Seals the sign-in session cookie (createSessions). 32 random bytes written by cf_worker_secret_put source=generate:32; replacing it signs everybody out, so it is two-step. Only needed by an app with sign-in"
667
681
  }
668
682
  ],
669
683
  "actions": [
@@ -750,6 +764,20 @@ export const capabilities = {
750
764
  "text": "Keep Supabase's sign-in settings at the Workbench baseline (workbench-config VENDOR_SETTINGS), or record a project's change in vendor-settings.json with a reason. Control applies it with vendor_settings_apply and checks it every day.",
751
765
  "enforced_by": "control",
752
766
  "probe": "database.vendor-settings"
767
+ },
768
+ {
769
+ "id": "database.sign-in-server-side",
770
+ "kind": "must",
771
+ "text": "Sign people in through the app's own endpoints with createSessions: the browser never talks to Supabase Auth and never holds the access token, and codes are typed, never links.",
772
+ "enforced_by": "review",
773
+ "probe": "database.sign-in"
774
+ },
775
+ {
776
+ "id": "database.session-writes-same-origin",
777
+ "kind": "must",
778
+ "text": "Refuse a write (anything but GET or HEAD) made with the session cookie unless sameOrigin(request) is true.",
779
+ "enforced_by": "review",
780
+ "probe": "database.session-cookie"
753
781
  }
754
782
  ],
755
783
  "examples": [
@@ -818,6 +846,20 @@ export const capabilities = {
818
846
  "smoke": true,
819
847
  "cost_hint_usd": 0,
820
848
  "note": "plainError and plainAuthError (workbench-supabase) turn a raw database error and an email_exists auth error into plain words that name the app and never say whether an account exists"
849
+ },
850
+ {
851
+ "id": "database.sign-in",
852
+ "half": "runtime",
853
+ "expect": "pass",
854
+ "cost_hint_usd": 0,
855
+ "note": "Ask for a code, read it from the captured sign-in email, type it, and get a session that reads the user's own rows; a wrong code gets the plain line"
856
+ },
857
+ {
858
+ "id": "database.session-cookie",
859
+ "half": "runtime",
860
+ "expect": "pass",
861
+ "cost_hint_usd": 0,
862
+ "note": "The session cookie is __Host-, Secure, HttpOnly, SameSite=Lax and sealed; a cross-site write is refused; sign-out clears it with every attribute"
821
863
  }
822
864
  ]
823
865
  },
@@ -87,6 +87,26 @@ export const VENDOR_SETTINGS = Object.freeze([
87
87
  where: "Supabase: Authentication, Sign In / Providers, Email, Secure email change",
88
88
  breaks: "Off: an address change is confirmed from the new address only, so the owner of the old one never hears of it.",
89
89
  },
90
+ {
91
+ id: "supabase.auth.signup_skips_email_confirm",
92
+ vendor: "supabase",
93
+ area: "auth",
94
+ field: "mailer_autoconfirm",
95
+ type: "boolean",
96
+ value: false,
97
+ where: "Supabase: Authentication, Sign In / Providers, Email, Confirm email (on means this row is false)",
98
+ breaks: "On: anyone with the publishable key can sign up as any address without owning it. An app that trusts the email in the session (the template's admin claim, invitations) then hands that address's rights to a stranger.",
99
+ },
100
+ {
101
+ id: "supabase.auth.anonymous_sign_ins",
102
+ vendor: "supabase",
103
+ area: "auth",
104
+ field: "external_anonymous_users_enabled",
105
+ type: "boolean",
106
+ value: false,
107
+ where: "Supabase: Authentication, Sign In / Providers, Allow anonymous sign-ins",
108
+ breaks: "On: anyone with the publishable key gets a signed-in session with no address at all, and can then attach an address it doesn't own. Every \"signed in\" check in the app lets them through.",
109
+ },
90
110
  ]);
91
111
  const override = z.strictObject({
92
112
  id: z.string().min(1).max(100),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mccaskie-labs/workbench-config",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "Workbench app configuration: WORKBENCH_ENV, typed variables that fail by name, secrets that are never printed, and the capabilities manifest.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",