From cf333396b14fc02a6648c3d3955320c9113d5e53 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 15:50:29 +0000 Subject: [PATCH 1/2] fix(security): open clm_party and clm_clause rows to update Legal could not edit a counterparty or a playbook clause it did not create: both objects were public_read, so every write was owner-scoped and no sharing rule covered legal (or finance on the banking pair). Under ruling D both become public_read_write. The platform opens the row scope for update only; deletes stay owner-only, and the object permissions and field locks in src/profiles (unchanged) still decide who writes which field. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn --- src/objects/clause.object.ts | 5 ++++- src/objects/party.object.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/objects/clause.object.ts b/src/objects/clause.object.ts index f0e25e1..5120210 100644 --- a/src/objects/clause.object.ts +++ b/src/objects/clause.object.ts @@ -15,7 +15,10 @@ export const Clause = ObjectSchema.create({ icon: 'book-open-text', description: 'A playbook clause: standard wording, fallback wording, risk level and the contract categories it applies to.', - sharingModel: 'public_read', + // Every clause is open to update (#152, ruling D): legal maintains the + // seeded playbook it does not own; the permission sets keep everyone else + // read-only. Deletes stay owner-only. + sharingModel: 'public_read_write', nameField: 'title', highlightFields: ['title', 'risk_level', 'is_active'], diff --git a/src/objects/party.object.ts b/src/objects/party.object.ts index b979d2b..440f47c 100644 --- a/src/objects/party.object.ts +++ b/src/objects/party.object.ts @@ -20,7 +20,10 @@ export const Party = ObjectSchema.create({ icon: 'building-2', description: 'The other party to a contract — company, individual or public body — with registration, contact and banking details.', - sharingModel: 'public_read', + // Shared master data: every row is open to update (#152, ruling D), and the + // permission sets decide who writes what — legal every field, finance the + // banking pair, requesters and records nothing. Deletes stay owner-only. + sharingModel: 'public_read_write', nameField: 'name', highlightFields: ['name', 'party_kind', 'country_code', 'risk_flag'], From 8ab630008bd6256ed90f75d47af6bbe8e660971c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 16:03:15 +0000 Subject: [PATCH 2/2] Comments: party and clause rows are public_read_write (#152) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn --- src/flows/contract-intake.flow.ts | 4 ++-- src/profiles/requester.profile.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/flows/contract-intake.flow.ts b/src/flows/contract-intake.flow.ts index 9745569..3cf1567 100644 --- a/src/flows/contract-intake.flow.ts +++ b/src/flows/contract-intake.flow.ts @@ -109,8 +109,8 @@ import { must, ref } from './_values.js'; * `create_party` writes only the identity and contact fields the screen * asks for: naming a locked field would refuse the whole write with 403 * "Field write denied". A new party is therefore born unflagged and - * unscreened, and legal maintains those fields afterwards (on a party - * legal does not own, writes are owner-scoped today, see #152). Should the + * unscreened, and legal maintains those fields afterwards (on any party, + * since `clm_party` is `public_read_write`, #152). Should the * create still be refused, the run leaves nothing behind: `create_party` * is the first write, after the last screen. * diff --git a/src/profiles/requester.profile.ts b/src/profiles/requester.profile.ts index fdab8d4..cd183fd 100644 --- a/src/profiles/requester.profile.ts +++ b/src/profiles/requester.profile.ts @@ -73,7 +73,8 @@ export const RequesterSet = definePermissionSet({ clm_signature: { allowCreate: false, allowRead: true, allowEdit: false, allowDelete: false }, clm_obligation: { allowCreate: false, allowRead: true, allowEdit: true, allowDelete: false }, clm_payment_plan: { allowCreate: false, allowRead: true, allowEdit: false, allowDelete: false }, - // Configuration objects are public_read: everyone reads every row. + // Configuration objects are readable by everyone (`public_read`, or + // `public_read_write` for clm_party and clm_clause, #152): every row. // // A requester may CREATE a counterparty, because the intake flow (§06 F1 // "相对方查找或新建") offers every launcher the inline branch and runs its