fix(studio): review round 1 (symlink-safe writes, remote allowlist, upload cap/types/no-overwrite, owner rule at publish/sync/resolve, studio/repos layout and read-only imports, side-effect-free list)
C1: writes lstat every path component and refuse symlinks (dangling included), check the parent's real path after mkdir, and open with O_NOFOLLOW. I1: only https://, ssh:// and git@host: remotes; protocol.file/ext/fd=never on every fetch and push; global/system git config ignored. I2: STC_UPLOAD_MAX_MB cap (default 100) by Content-Length and while streaming; extension allowlist (415); stcourse.yaml refused; 409 unless overwrite=true; sandboxing CSP on draft raw files. I3: owner-only visibility/source.kind rule checked on the merged result at publish and sync, and at resolve; publish also validates the merged tree. I4: bare repos in <dataDir>/studio/repos/<id>.git; imported courses read-only. I5: list status comes from the bare repo; drafts are created lazily. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
667f3e814b
commit
2d162a4282
12 files changed
+563
-76
No files matched your search
@@ -12,16 +12,17 @@ Proposal:
|
||||
|
||||
## 2. Learners must read studio courses from the published worktree (config / main.ts)
|
||||
|
||||
Studio courses live at:
|
||||
- `<dataDir>/courses/<id>`: a bare repo with no `stcourse.yaml`, so discovery never loads it;
|
||||
- `<dataDir>/worktrees/<id>/published`: the published branch, which is what learners must see;
|
||||
Layout after the review (controller ruling on I4):
|
||||
- `<dataDir>/studio/repos/<id>.git`: studio bare repos. They contain no `stcourse.yaml`, so discovery never loads them.
|
||||
- `<dataDir>/worktrees/<id>/published`: the published branch, which is what learners read.
|
||||
- `<dataDir>/worktrees/<id>/drafts/<author>`: drafts. They sit at depth 3, so the depth-2 discovery never loads them.
|
||||
- `<dataDir>/courses/<id>`: the importer's plain folders (WP-B), untouched by the studio.
|
||||
|
||||
`createCourseRepository(config.courseDirs)` does not scan `<dataDir>/worktrees`. The package exports `studioCourseDirs(config)`, which appends it. Tests and the dev entry use it.
|
||||
`createCourseRepository(config.courseDirs)` scans neither `<dataDir>/worktrees` nor `<dataDir>/courses`. The package exports `studioCourseDirs(config)`, which returns the configured dirs plus `<dataDir>/courses` and `<dataDir>/worktrees`. Tests and the dev entry use it.
|
||||
|
||||
Proposal: main.ts calls `createCourseRepository(studioCourseDirs(config))`, or `loadConfig` always appends `<dataDir>/worktrees`.
|
||||
Proposal: main.ts calls `createCourseRepository(studioCourseDirs(config))`, or `loadConfig` appends both dirs.
|
||||
|
||||
Related, for Phase 2: the importer writes plain folders to `<dataDir>/courses/<id>`. These coexist with the studio's bare repos. A course id that exists either way cannot be created again in the studio. Adopting an imported course into the studio (git init, move it to the bare/worktree layout) is not part of v1.
|
||||
Imported courses (`source.kind` other than `authored`, with their root under `<dataDir>/courses`) are read-only in the studio for v1, with the message "Imported course: read-only in the studio for now". There is a `TODO(studio)` in `feature.ts` (`readOnlyMessage`) for converting one into a studio-managed repo. The studio refuses to create a course whose id already exists as `<dataDir>/courses/<id>`. WP-B should likewise refuse an import id that `<dataDir>/studio/repos/<id>.git` already uses.
|
||||
|
||||
## 3. Live preview: swap in the learner `LessonView`
|
||||
|
||||
@@ -80,3 +81,9 @@ Creating a course inserts the creator into `course_authors`. No new tables are a
|
||||
## 8. External pushes straight into the bare repo (no action needed)
|
||||
|
||||
The platform reloads when the published worktree's HEAD changes: an external commit in that worktree, a studio publish, or a Sync. A direct `git push` into `<dataDir>/courses/<id>` is refused by git itself ("branch is currently checked out", checked with git 2.47), so the published files cannot silently fall behind `main`. Outside tools should commit in the published worktree, or push to a configured remote that the author then Syncs.
|
||||
|
||||
## 9. Core media route serves SVG/HTML inline (core, Phase 2)
|
||||
|
||||
The studio's draft `raw` route now sends `Content-Security-Policy: …; sandbox` and `X-Content-Type-Options: nosniff`. The learner-facing `/api/media/:courseId/*` in `packages/core` serves author-supplied SVG inline from the app's origin and sets neither header.
|
||||
|
||||
Proposal: add the same two headers in `serveMedia`.
|
||||
Reference in new issue
Block a user