11# Security Model
22
3- ## Assumptions
3+ ## Trust Model
4+
5+ ** Core Principle** : All ut.code(); members are mutually trusted.
6+
7+ This is an internal CMS for the ut.code(); organization. All authenticated members belong to the same organization and collaborate on shared content. Therefore:
48
5- | Role | Read Access | Write Access |
6- | ------------------------ | ------------------------------------- | ------------------------------------------------------- |
7- | Public (unauthenticated) | Published articles, members, projects | None |
8- | ut.code(); members | All resources (including drafts) | Own resources only (see Ownership Model below) |
9+ - ** No ownership checks required** : Any ut.code(); member can edit any article, member profile, or project
10+ - ** Authorization is binary** : ` requireUtCodeMember() ` is the only auth check needed
11+ - ** Mutual trust assumption** : Members are expected to coordinate and communicate, not compete
12+
13+ This model prioritizes collaboration and simplicity over granular access control.
14+
15+ ## Assumptions
916
10- ** Design Decision** : Ownership-based access control. Users can only modify resources they own or are members of.
17+ | Role | Read Access | Write Access |
18+ | ------------------------ | ------------------------------------- | -------------------------------- |
19+ | Public (unauthenticated) | Published articles, members, projects | None |
20+ | ut.code(); members | All resources (including drafts) | All resources (full admin access)|
1121
1222## Authentication
1323
1828 - Mock user ID: ` "mock" ` , mock member ID: ` "mock-member" `
1929 - Mock data returned from ` getMemberByUserId ` , ` getUserPreference ` , ` setDefaultAuthor `
2030
21- ## Ownership Model
31+ ## Authorization Model
2232
23- Resource-level access control is enforced via ` ownership.ts ` :
33+ All authenticated ut.code(); members have full admin access. Authorization is enforced via:
2434
25- ### Articles
26- - ** Edit/Delete/Publish/Unpublish** : Only the article author can modify their own articles
27- - Enforced by: ` requireArticleOwnership(session, articleId) `
28- - Creates: Any authenticated ut.code member can create articles (with their own member ID as author)
35+ - ` requireUtCodeMember() ` - Single auth check for all private endpoints
36+ - Returns session with ` user.id ` and ` member.id `
37+ - No resource-level ownership checks needed
2938
30- ### Members
31- - ** Edit/Delete** : Only the member themselves can modify their own profile
32- - Enforced by: ` requireMemberOwnership(session, memberId) `
33- - Creates: Any authenticated ut.code member can create member profiles
39+ ### Resource Access
3440
35- ### Projects
36- - ** Edit/Delete** : Only project members (lead or regular members) can modify the project
37- - ** Add/Remove Members** : Only existing project members can add or remove other members
38- - ** Transfer Lead** : Only the current project lead can transfer leadership
39- - Enforced by: ` requireProjectOwnership(session, projectId) `
40- - Creates: Any authenticated ut.code member can create projects (they become the lead)
41+ All authenticated members can:
42+ - ** Articles** : Create, edit, delete, publish/unpublish any article
43+ - ** Members** : Create, edit, delete any member profile
44+ - ** Projects** : Create, edit, delete, manage members of any project
4145
4246## Endpoint Classification
4347
@@ -72,7 +76,6 @@ Resource-level access control is enforced via `ownership.ts`:
7276- ** S3 Key Validation** : Regex prevents path traversal on delete (` S3KeySchema ` )
7377- ** Session Management** : Better Auth handles session security
7478- ** CSRF** : SvelteKit Remote Functions use POST with same-origin enforcement
75- - ** AuthorId Validation** : Article ` authorId ` must be null or match authenticated user's member ID
7679
7780## Known Issues
7881
@@ -98,19 +101,16 @@ Now uses `v.picklist(["lead", "member"])` validation.
98101~~ User input with ` % ` , ` _ ` , or ` \ ` characters not escaped in LIKE patterns.~~
99102Now uses ` escapeLikePattern() ` utility in all search functions and redirect lookups.
100103
101- ### ~~ MEDIUM: Article authorId not validated ~~ FIXED
104+ ### INFO: No resource-level access control (by design)
102105
103- ~~ Users could set ` authorId ` to any member ID when creating/editing articles.~~
104- Now validates that ` authorId ` is either null or matches authenticated user's member ID via ` validateAuthorId() ` helper.
106+ This is ** not a security issue** - it's the intended trust model.
105107
106- ### ~~ HIGH: No ownership verification on edits~~ FIXED
108+ All authenticated ut.code(); members can edit/delete any resource (articles, members, projects). This is intentional because:
109+ - All members belong to the same organization
110+ - Collaboration and flexibility are prioritized over access restrictions
111+ - Members are expected to coordinate via communication, not technical barriers
107112
108- ~~ Any authenticated member could edit/delete any article, member profile, or project.~~
109- Now enforces ownership checks:
110- - Articles: Only author can edit/delete/publish/unpublish
111- - Members: Only the member themselves can edit/delete their profile
112- - Projects: Only project members can edit/delete/manage members
113- Implementation: ` ownership.ts ` with ` requireArticleOwnership() ` , ` requireMemberOwnership() ` , ` requireProjectOwnership() `
113+ ** Implementation** : No ` ownership.ts ` module needed - ` requireUtCodeMember() ` is the only authorization check.
114114
115115### LOW: No rate limiting
116116
@@ -128,13 +128,11 @@ GitHub org membership cached 24h. Removed members retain access.
128128| Priority | Issue | Fix | Status |
129129| ---------- | ------------------ | ------------------------------------------------------- | ------- |
130130| ~~ HIGH~~ | stats endpoint | Add ` requireUtCodeMember() ` or filter to published only | ✅ DONE |
131- | ~~ HIGH~~ | ownership controls | Add ownership verification for edit/delete operations | ✅ DONE |
132131| ~~ MEDIUM~~ | File upload | Add MIME whitelist, folder allowlist, WebP compression | ✅ DONE |
133132| ~~ MEDIUM~~ | Project role | Use ` v.picklist(["lead", "member"]) ` | ✅ DONE |
134- | ~~ MEDIUM~~ | Article authId | Validate authorId matches user's member ID | ✅ DONE |
135133| LOW | Rate limiting | Add to public endpoints | TODO |
136134| LOW | Cache TTL | Reduce to 4h or add invalidation | TODO |
137135
138136## Audit Date
139137
140- 2024-12-15 (Updated: 2025-12-24 - Added ownership verification model)
138+ 2024-12-15 (Updated: 2025-12-26 - Updated to trust-based security model)
0 commit comments