Commit Graph

34 Commits

Author SHA1 Message Date
claude code agent 227
7e26239c3f Merge remote-tracking branch 'gitea/develop' into fix/review-batch-2
# Conflicts:
#	AGENTS.md
#	CHANGELOG.md
#	README.md
#	apps/server/src/collaboration/collaboration.handler.ts
#	apps/server/src/common/helpers/prosemirror/html-embed.spec.ts
#	apps/server/src/common/helpers/prosemirror/html-embed.util.ts
#	apps/server/src/core/ai-chat/public-share-chat.service.ts
#	apps/server/src/core/ai-chat/public-share-chat.spec.ts
#	apps/server/src/core/ai-chat/public-share-workspace-limiter.ts
#	apps/server/src/core/page/services/page.service.ts
#	apps/server/src/core/page/transclusion/transclusion.service.ts
#	apps/server/src/integrations/import/services/file-import-task.service.ts
#	apps/server/src/integrations/import/services/import.service.ts
2026-06-21 05:32:44 +03:00
claude code agent 227
5215913533 fix(security): env-configurable trustProxy with a safe default (#61)
trustProxy was unconditionally true, so req.ip came from a client-forgeable
X-Forwarded-For and the per-IP throttles (share-AI, /mcp brute-force) were
spoofable. Make it env-configurable (TRUST_PROXY) with a safe default that
trusts XFF only from loopback/private proxies, documented in .env.example.
NOTE: this changes the default from trust-all; deployments whose proxy is on a
public IP must set TRUST_PROXY (caveat documented).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:17:37 +03:00
claude_code
64818cf9df Merge branch 'feat/share-ai-cost-guards' into develop 2026-06-21 02:21:04 +03:00
claude_code
262a0707d9 feat(share-ai): cap per-request output tokens and fail closed on Redis loss
Harden the anonymous public-share AI assistant against token-cost abuse
before exposing it to the internet:

- Add an env-tunable per-request output ceiling (maxOutputTokens) to the
  public-share streamText call so one anonymous request cannot run up the
  provider bill even if the per-IP throttle is evaded. New
  resolveShareAiMaxOutputTokens() / SHARE_AI_MAX_OUTPUT_TOKENS_DEFAULT
  (env SHARE_AI_MAX_OUTPUT_TOKENS, default 512), mirroring
  resolveShareAiWorkspaceMax().
- Flip the per-workspace cost limiter to FAIL CLOSED on Redis failure
  (was fail-open): if Redis is unavailable we cannot prove the workspace is
  under its cap, so deny rather than admit an unmetered, billable call.
- Update the limiter spec (fail-open -> fail-closed) and add resolver tests;
  document both knobs in .env.example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 02:15:54 +03:00
claude_code
70c26f356a docs(security): warn that APP_SECRET must never change after setup
APP_SECRET does double duty: it signs JWTs and derives the AES-256-GCM key
that encrypts stored AI-provider credentials. Rotating it makes every saved
AI API key undecryptable and invalidates existing sessions. Document this
footgun where operators set the value (RT-30 from the red-team report).

- .env.example: dual-role warning block above APP_SECRET
- README.md / README.ru.md: warning callout in the upgrade section

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 02:06:26 +03:00
claude code agent 227
1f457b060c fix(mcp): security review follow-ups (#24)
Post-merge hardening from the #13 security review:
- isInitializeRequestBody now delegates to the SDK isInitializeRequest (same
  predicate as packages/mcp/http.ts), so a bare {method:'initialize'} with no
  id/params no longer triggers the side-effecting login() (audit-spam /
  user_sessions growth) before http.ts 400s it.
- Bind the Bearer path to the instance workspace: verifyBearerAccess rejects a
  token whose payload.workspaceId != the instance workspace (resolved via
  workspaceRepo.findFirst, consistent with the Basic path); optional param so
  it's a no-op when unset.
- Close the user-enumeration timing oracle in verifyUserCredentials: the
  missing/disabled branch now runs a bcrypt compare against a module-level dummy
  hash whose cost (12) matches production saltRounds, so both paths take one
  equal-cost bcrypt compare; the exact CREDENTIALS_MISMATCH_MESSAGE is preserved.
- Document the trusted-proxy requirement for the spoofable per-IP brute-force
  limiter in .env.example (trustProxy is on; deploy behind a trusted proxy).
- Add real-execution coverage for enforceBasicLoginGate (SSO enforced / EE-MFA
  bundled vs not / user-MFA / workspace-enforced-MFA) instead of stubbing the gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:36:53 +03:00
claude_code
f72e44c9b7 Merge pull request 'feat(mcp): per-user auth for /mcp (HTTP Basic, server-validated)' (#13) from feat/mcp-per-user-auth into develop 2026-06-20 19:32:02 +03:00
vvzvlad
90e9b0a3f4 docs(public-share): document trusted-proxy XFF requirement + cost cap
The anonymous public-share AI assistant's per-IP rate limit is only
effective behind a trusted reverse proxy that overwrites X-Forwarded-For
with the real client IP (the app runs with trustProxy). Document this
deployment requirement and the per-workspace cost backstop env var
(SHARE_AI_WORKSPACE_MAX_PER_HOUR, default 300) in .env.example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 18:34:16 +03:00
claude code agent 227
4a00dfc3b2 feat(mcp): per-user auth for the embedded /mcp endpoint
The embedded MCP server acted as a single service account; now each /mcp
session authenticates as the current user, so tools run under that user's
CASL and edits attribute to them.

- HTTP Basic (chosen path): Authorization: Basic email:password, validated
  server-side via AuthService; the session carries the issued user JWT (not
  the raw password). Password may contain ':' (split on first only).
- Bearer fallback: Authorization: Bearer <access JWT>, verified as ACCESS and
  additionally checked for an active session + non-disabled user (matching
  JwtStrategy), so revoked/disabled users are rejected.
- Service account stays as an optional fallback (no creds + env configured).
- packages/mcp createMcpHttpHandler accepts a per-request config resolver
  (back-compat: static config / stdio unchanged); identity is bound to the
  mcp-session-id at init and re-validated from the caller's own credentials on
  every request (anti session-fixation: a guessed session id can't be reused
  without matching creds).
- A full login (session + audit) happens only once at session init; later
  requests re-verify credentials via a new non-side-effecting
  AuthService.verifyUserCredentials (no session/audit spam).
- Failed-login limiter (5/60s, keyed per-IP, per-IP+email, and per-email so IP
  rotation can't brute one account) since direct login bypasses the controller
  throttler. Only real credential failures count.
- MCP_TOKEN shared guard moved off Authorization to an X-MCP-Token header
  (timing-safe compare); credsConfigured 503 gate replaced by a clear 401.
- No secrets logged; all auth resolved before res.hijack() so failures return
  clean 401 JSON. .env.example marks the service account optional.

Implements docs/backlog/mcp-per-user-auth.md (variant L).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:19:31 +03:00
vvzvlad
80c900eb54 fix(ai): make RAG indexer observable and bound hung embedding calls
The bulk embedding reindex could hang on a single page forever
("Indexed 27 of 34 pages") with zero log output:
- all progress logs were debug-level, suppressed in production (pino info);
- embedMany() had no timeout, so a slow/hung embeddings endpoint blocked
  the sequential per-page loop indefinitely.

Changes:
- ai.service.embedTexts: bound embedMany with AbortSignal.timeout
  (configurable via AI_EMBEDDING_TIMEOUT_MS, default 120000ms); on timeout
  throw a clear, greppable message, classified by both signal.aborted and
  the error name (TimeoutError/AbortError/ResponseAborted) so a real
  provider error racing the timer keeps its diagnostics.
- embedding-indexer.reindexWorkspace: promote lifecycle/progress logs to
  info; log "[i/N] indexing page <id>" BEFORE the await so a hang names the
  stuck page; warn on slow pages (>30s); add timing + final summary.
- .env.example: document AI_EMBEDDING_TIMEOUT_MS.
2026-06-18 03:07:02 +03:00
vvzvlad
1f5987d6b0 feat(mcp): serve embedded community MCP server at /mcp
Replace the removed enterprise EE MCP (private apps/server/src/ee submodule,
license-gated /mcp route) with our docmost-mcp, vendored as an isolated ESM
workspace package and served by the server over HTTP — no enterprise license.

Backend:
- Add packages/mcp (@docmost/mcp): vendored docmost-mcp refactored into a
  side-effect-free createDocmostMcpServer() factory (38 tools preserved),
  stdio entry kept in stdio.ts, Streamable-HTTP session manager in http.ts.
- Add apps/server McpModule: @Post/@Get/@Delete('mcp') (served at /mcp via the
  existing global-prefix exclude), @SkipTransform + reply.hijack to bridge raw
  Fastify req/res into the SDK transport. The module dynamically imports the
  ESM-only package from CommonJS via a Function-indirected import resolved with
  require.resolve + file:// URL. Gated by the workspace ai.mcp toggle, a
  service-account (MCP_DOCMOST_EMAIL/PASSWORD/API_URL) and optional MCP_TOKEN;
  per-session idle eviction (MCP_SESSION_IDLE_MS).
- Drop the enterprise license check on mcpEnabled in workspace.service.
- Dockerfile: copy packages/mcp into the production image.
- .env.example: document MCP_DOCMOST_*, MCP_TOKEN, MCP_SESSION_IDLE_MS.

Frontend:
- Recreate the community "AI & MCP" workspace-settings panel (mcp-settings.tsx):
  admin-only toggle on settings.ai.mcp with optimistic update, copyable
  ${APP_URL}/mcp URL; wired into workspace-settings page. Reuses existing i18n.

Fixes:
- Pin packages/mcp tiptap deps to 3.20.4 (matching the client) and inline
  getStyleProperty, preventing a duplicate @tiptap/core@3.26.1 from leaking into
  the client editor via pnpm shamefully-hoist (was breaking apps/client tsc).
2026-06-16 23:54:53 +03:00
Philipinho
61a91cd086 fix: remove duplicate storage key 2026-05-22 14:54:52 +01:00
Philip Okugbe
4295ea09f6 feat(storage): add Azure Blob Storage driver (#2222) 2026-05-21 12:18:58 +01:00
Philipinho
0d6538ab1a feat: iframe configuration 2026-05-18 22:02:31 +01:00
Philip Okugbe
a6a7e4370a feat(ee): PDF export api (#2112)
* feat(ee): server side PDF export

* feat: pdf export queue

* sync

* sync
2026-04-14 16:26:54 +01:00
Philip Okugbe
918f4508d2 feat: switch to pino for logs (#1855)
- switch to json logs in production
- add option to support http logging
2026-01-21 01:23:50 +00:00
Philip Okugbe
5a3377790e feat: debug mode env variable (#1450) 2025-08-06 18:16:30 +01:00
Philipinho
8300c5b731 update env file 2025-03-23 13:14:20 +00:00
Philip Okugbe
72f64e7b10 revert sentry (#808)
* revert sentry
* remove sentry env
2025-02-27 15:58:32 +00:00
Philipinho
54d27af76a * Add SENTRY_DNS env variable
* Commit lock file
2025-02-26 17:38:25 +00:00
Philip Okugbe
040d6625df fix: enforce 32-character minimum length for APP_SECRET (#702)
* Enforce 32 characters minimum APP_SECRET length

* update APP_SECRET comment
2025-02-06 17:46:32 +00:00
Philip Okugbe
d97baf5824 add env variable (#513) 2024-11-28 18:48:25 +00:00
Philip Okugbe
384f11f2b7 make file upload size limit configurable (#386) 2024-10-10 21:28:28 +01:00
Orion
9390b39e35 Implement nodemailer ignore tls property (#299) 2024-09-20 17:57:50 +01:00
sidnelui-krystal
c810d0b314 fix: added env variable for support for forcepathstyle on s3 (#181) 2024-08-20 13:05:59 +01:00
Philipinho
4967849e3a add SMTP_SECURE 2024-08-02 11:19:12 +02:00
Philipinho
bc7cd033f2 more env validations 2024-06-27 22:47:59 +01:00
Philipinho
a582d4786d make env validation errors clear
* modify mail smtp variable names
2024-06-27 17:55:17 +01:00
Philipinho
38ef610e5e fixes
* integrate websocket redis adapter
* use APP_SECRET for jwt signing
* auto migrate database on startup in production
* add updatedAt to update db operations
* create enterprise ee package directory
* fix comment editor focus
* other fixes
2024-06-07 17:29:34 +01:00
Philipinho
eefe63d1cd implement new invitation system
* fix comments on the frontend
* move jwt token service to its own module
* other fixes and updates
2024-05-14 22:55:11 +01:00
Philipinho
7f933addff Implement BullMQ for background job processing
* new REDIS_URL environment variable
2024-05-03 02:56:03 +01:00
Philipinho
4c573b9bc2 email integration
* Nest email module with smtp, postmark and console log drivers
* react-email package
2024-05-02 03:12:40 +01:00
Philipinho
f01b77dbd6 fixes
* remove vite env for now
* remove unnecessary comment
2024-04-25 22:36:03 +01:00
Philipinho
616da875cd move .env to root 2024-01-17 18:36:54 +01:00