Skip to content

feat(project): added project support v1#7

Open
0xanshu wants to merge 2 commits into
ScrawnDotDev:devfrom
0xanshu:feat/projects
Open

feat(project): added project support v1#7
0xanshu wants to merge 2 commits into
ScrawnDotDev:devfrom
0xanshu:feat/projects

Conversation

@0xanshu

@0xanshu 0xanshu commented Jun 23, 2026

Copy link
Copy Markdown

No description provided.

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces project/org support by adding two new DB tables (org and project), a new "Project Name" onboarding step, and refactors the monolithic scrawn-server.ts into focused modules under src/lib/server/.

  • Schema: Adds org (one-per-user) and project (linked to org) tables with proper cascade-delete foreign keys; MASTER_API_KEY is now correctly read from process.env server-side.
  • Refactor: scrawn-server.ts is split into analytics.ts, apiKeys.ts, auth.ts, core.ts, expressions.ts, onboarding.ts, tags.ts, and webhooks.ts — purely organizational, no behavioral changes.
  • Onboarding: A new step 0 collects a project name before the existing DodoPayments configuration steps; however, the userId used to create the org record comes from the client rather than being derived from the server-side session.

Confidence Score: 3/5

Not safe to merge as-is — the onboarding handler trusts an arbitrary user ID from the client to create org records, breaking tenant isolation.

The onboarding server function uses a client-supplied userId to look up and insert org rows without verifying it against the authenticated session. An authenticated user can substitute any other user's ID and claim ownership of their org slot. This is an active authorization flaw on a write path, not a theoretical edge case.

src/lib/server/onboarding.ts is the primary file that needs attention — specifically how userId is sourced and how ctx.data is forwarded wholesale to the scrawn backend.

Security Review

  • Horizontal privilege escalation (src/lib/server/onboarding.ts, lines 40–53): submitOnboarding accepts userId as a client-supplied input and uses it verbatim to look up and create org rows. Any authenticated user can pass an arbitrary userId to create an org on behalf of any other user, bypassing tenant isolation. The user ID must be derived from the server-side session, not trusted from client input.

Important Files Changed

Filename Overview
src/lib/server/onboarding.ts New file encapsulating onboarding logic; reads MASTER_API_KEY server-side (fixing the previous review's concern), but accepts userId from the client and uses it without session verification — a horizontal privilege escalation risk.
src/db/schema.ts Adds org and project tables with proper cascade-delete foreign keys; project references org via lazy callback correctly.
src/lib/server/core.ts New shared helpers (validator, apiGet, apiPost, apiDelete) extracted from the monolithic scrawn-server.ts; no logic changes, straightforward refactor.
src/lib/scrawn-server.ts Large file split into focused modules under src/lib/server/; now only re-exports from those modules. No behavioral change.
src/routes/onboarding.tsx Adds a new Project Name step (step 0, shifting all existing steps by one); userId sourced from session client-side and sent to the server function, which is where the trust boundary issue originates.
drizzle.config.ts dotenv.config() now loads from .env instead of .env.local, which may break local dev workflows that store DATABASE_URL only in .env.local.

Reviews (2): Last reviewed commit: "fix(project): fixed issues by greptile" | Re-trigger Greptile

Comment thread src/lib/server/onboarding.ts
Comment thread src/lib/server/onboarding.ts
Comment thread src/db/schema.ts Outdated
Comment thread src/db/schema.ts Outdated
Comment on lines +40 to +53
let userOrg = await db.query.org.findFirst({
where: eq(org.userId, ctx.data.userId),
})

if (!userOrg) {
const newOrgId = randomUUID()
const [newOrg] = await db
.insert(org)
.values({
orgId: newOrgId,
userId: ctx.data.userId,
})
.returning()
userOrg = newOrg

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Horizontal privilege escalation via client-supplied userId

The server function blindly trusts ctx.data.userId to look up and create orgs. Since submitOnboarding is a POST server function whose input is fully client-controlled, any authenticated user can supply a different user's ID and onboard on their behalf — creating an org row tied to an arbitrary userId they do not own. The userId should instead be derived server-side from the authenticated session (e.g., via BetterAuth's server-side session API) and never accepted as client input for authorization decisions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant