Conversation
* feat: Add support for Docker Swarm style secrets * fix: Handle empty-string env values and bare _FILE key in resolveFileSecrets Use undefined check instead of truthiness so empty-string values are treated as "already set" and not overridden by _FILE variants. Skip processing when the key is exactly "_FILE" to avoid creating an empty-key entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Preload share popover data on hover with useShareDataLoader hook Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: Route programmatic closes through handleOpenChange and fix race conditions - closePopover now calls handleOpenChange(false) so reset() fires on all close paths, including programmatic closes via onRequestClose - Reset requestedRef when entity id changes so preload fires for new targets - Use request counter to prevent stale loading state when reset() is called during an in-flight request Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Wrap collection and document names in the header breadcrumb with ContextMenu components, enabling right-click menus with relevant actions. Each breadcrumb item type has its own component to scope hooks. Breadcrumb links prevent navigation when a context menu is open. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…11913) * fix: Integrations list missing when language is not English The group filter on the Integrations settings page compared against the hardcoded string "Integrations" instead of the translated value from t("Integrations"), causing no integrations to appear in non-English locales. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: Sidebar shows unconnected integrations in non-English locales Same hardcoded "Integrations" string comparison issue as the main integrations page — the sidebar filter skipped the connected-check when the translated group name didn't match the English literal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Initial plan * chore: upgrade Node.js base image from 22.21.0 to 24.14.1 (LTS) * chore: include node version in node_modules cache keys * Add canary build for docker changes * fix: Try docker driver --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Initial plan * Fix mention trigger to work after CJK characters without preceding space Agent-Logs-Url: https://github.com/outline/outline/sessions/b34bba3f-fe94-408c-bf09-794f8e3d05ff Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> Co-authored-by: Tom Moor <tom@getoutline.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Re-highlight result context client-side using the current search query instead of relying on server-generated <b> tags. This prevents stale highlights when results from a previous query are still displayed while a newer search is in-flight. Also guards against setting stale results by checking the query ref before updating state. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
) * Initial plan * fix: allow Tab to indent list items inside toggle blocks When the cursor is inside a list within a toggle block, the indentBlock command was consuming the Tab key event before the list's sinkListItem handler could run. This happened because indentBlock found a previous container_toggle sibling at the ancestor level and returned true. Fix: return false early in indentBlock when the cursor is inside a list, allowing the list's Tab handler to handle indentation correctly. Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
* Add Node LTS auto-update script * fix: Validate LTS version and update CI step name in Node update workflow Add semver validation for the fetched LTS version to prevent creating PRs with invalid node versions (e.g. null) if the upstream API changes. Also update the human-readable step name in ci.yml during major bumps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ine#11929) * Use CSS highlights instead of editor decorations when available * Fix scroll target for non-HTML elements and refresh highlights on toggle fold - Use `Element` instead of `HTMLElement` for scroll target so SVG/MathML elements are handled correctly - Bump highlight generation on toggle fold/unfold transactions so newly visible matches get proper highlight ranges - Cache decoration getter result to avoid redundant mapping Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Add email subscriptions to public docs
* Improve scoping of public share subscriptions
* fix: Add missing transaction, includeChildDocuments check, and test documentId
- Pass { transaction } to ShareSubscription.create in the subscribe handler
so the insert runs atomically with the duplicate-check findOne/lock
- Skip ancestor-scoped subscription notifications when the share has
includeChildDocuments=false, preventing notifications for inaccessible docs
- Add required documentId field to all share subscription tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: Resolve type error for nullable share.documentId in tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* JSDoc
* Hide subscription option for logged-in users
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: Move welcome email to processor * fix: Restore welcome email on invite acceptance
* fix: move redirect logic one component up * fix: use <Redirect>
…e#11942) (outline#11948) Move document icons out of the ContextMenu trigger span and into the action's icon property, consistent with how collection icons work. Block-level icon elements (FontAwesome, custom emoji) inside the inline span were taking a full line and pushing text below the overflow clip. Also remove display:flex from breadcrumb Item so text-overflow:ellipsis works correctly. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Remove share pre-creation * Disable toggle while saving * cleanup unused methods
* Apply suggested fix to shared/editor/plugins/SuggestionsMenuPlugin.ts from Copilot Autofix Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Apply suggested fix to shared/editor/plugins/SuggestionsMenuPlugin.ts from Copilot Autofix Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Apply suggested fix to shared/editor/plugins/SuggestionsMenuPlugin.ts from Copilot Autofix Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: Last active timestamp should always read as now for current user * Shorten language on members table
* Display keyboard shortcuts in menus where available * feat: Display keyboard shortcuts in action menus Pass shortcut data from Action definitions through to menu items and render formatted key symbols on the right side of menu entries. Handles platform differences via normalizeKeyDisplay. Also adds Control key display support and uppercase formatting for single-letter shortcuts. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
use SMB_NAME instead of foss.
* fix: Autofocus inside lazy-loaded modal/popover * use wrapperRef * remove unused import
* chore: resolve no-explicit-any lint warnings in plugins Replaces uses of `any` in the plugins directory with concrete types, `unknown`, or structured type assertions, addressing the remaining typescript-eslint(no-explicit-any) warnings flagged by oxlint. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: address review feedback in GitLabIssueProvider Drop trailing semicolon from log string and add early return in `destroyNamespace` when neither `user_id` nor `full_path` is present to avoid an unnecessary full-scan transaction. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: Add breadcrumb to MCP responses
* test: Update MCP test expectations for new response envelope
Tests were reading the old flat document shape; update them to read
through the new { document, breadcrumb } envelope.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* perf: Batch collection lookups when building breadcrumbs
Add getBreadcrumbsForDocuments helper that loads all referenced
collections in one query (with the user's memberships) and resolves
breadcrumbs from the per-collection cached documentStructure. Use it
in list_documents and move_document, replacing the per-document
Collection.findByPk that produced an N+1 query pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test: Add coverage for getBreadcrumbsForDocuments and parallelize doc + breadcrumb loads
Run presentDocument and getDocumentBreadcrumb concurrently in fetch,
create_document, and update_document so the breadcrumb lookup no
longer adds latency on top of the presenter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…#12243) Replaces `as any` casts when constructing OAuth2Server Request/Response with explicit objects containing the fields the library actually consumes, and switches BaseStorage's manual header spread to a node-fetch `Headers` instance to avoid the no-misused-spread warning. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* Add title guidance for MCP * Scope H1 guidance to documents only
* chore: promote no-explicit-any from warn to error and resolve violations Upgrades the oxlint rule severity and removes all 40 existing `no-explicit-any` warnings across the codebase. Most call sites gained proper types (SharedEditor refs, JSONNode/JSONMark for ProseMirror JSON walking, DocumentsStore, dd-trace `Span` parameter inference, prosemirror Fragment public API in place of internal `(fragment as any).content`). A few load-bearing `any` uses were preserved with scoped disable comments where changing the type would cascade widely (Sequelize JSONB columns on `Event`, the `withTracing` higher-order function generic, `Extension.options` consumed by many subclasses, dd-trace's `req` patching). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…e#12245) * feat: Add delete_document and delete_collection MCP tools Adds MCP tools for deleting (or archiving) documents and collections. Refactors Document#delete into destroyWithCtx and extracts collection archive logic into Collection#archiveWithCtx so the same code paths can be shared between the REST API and MCP entry points. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: Wrap MCP delete tools in DB transaction Ensures delete/archive of documents and collections via MCP is atomic and that row locks (transaction.LOCK.UPDATE) inside *WithCtx methods actually apply, matching the pattern used by move_document. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: Clarify delete_collection MCP tool description Reflects that collection deletion only soft-deletes non-archived documents via the BeforeDestroy hook. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: Update editor generics * fix: Address PR review on editor generics - Restore null-guard on Link click handler so anchors aren't inert when no onClickLink is provided - Mark onClickLink optional in LinkOptions and openLink command to match runtime - Remove dead `collapsed` option from HeadingOptions - Make ToggleBlock dictionary optional and restore optional-chained access for server-side schema instantiation Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* Refactor of OAuth account linking flows * PR feedback
* fix: strip first subdomain for portal redirect on signout Replace hardcoded "moneta." prefix with an empty string so the regex strips just the leading subdomain (e.g. app.moneta.askii.ai → moneta.askii.ai) regardless of the deployment domain. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: use lookahead regex to safely strip leading subdomain Switch to /^[^.]+\.(?=[^.]*\.[^.]*\.)/ so the subdomain is only stripped when at least two dot-separated parts remain, preventing over-stripping on bare domains. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Outline's accessToken cookie was hardcoded to 3 months
(addMonths(new Date(), 3)) at three call sites — outliving the rest of
the foss-server-bundle's 7-day session window. After a stack-wide
session expiry users would stay signed in to Outline alone.
Changes:
- Export JWT_COOKIE_TTL_DAYS = 7 from server/utils/authentication.ts as
a single source of truth (with rationale in a docstring).
- Use addDays(new Date(), JWT_COOKIE_TTL_DAYS) at all three mint sites:
- server/middlewares/authentication.ts (FORWARDAUTH login)
- server/routes/auth/index.ts (/auth/redirect)
- server/utils/authentication.ts (signIn callback)
- Fix a pre-existing upstream issue at /auth/redirect: getJwtToken was
called with no expiresAt arg, producing a JWT the validator at
utils/jwt.ts:47 never rejects (claim missing → check skipped). Now
passes `expires` into both getJwtToken and the cookie set, matching
the pattern the other two paths already use.
- Tests cover both halves:
- middlewares/authentication.test.ts asserts the FORWARDAUTH cookie's
expires is ~now + JWT_COOKIE_TTL_DAYS (±60s).
- routes/auth/index.test.ts asserts /auth/redirect mints a JWT whose
expiresAt claim is set and ~now + JWT_COOKIE_TTL_DAYS (±60s).
In future, lift JWT_COOKIE_TTL_DAYS to an env var if deployment-specific
control is needed.
Outline's accessToken cookie was hardcoded to 3 months
(addMonths(new Date(), 3)) at three call sites — outliving the rest of
the foss-server-bundle's 7-day session window. After a stack-wide
session expiry users would stay signed in to Outline alone.
Changes:
- Export JWT_COOKIE_TTL_DAYS = 7 from server/utils/authentication.ts as
a single source of truth (with rationale in a docstring).
- Use addDays(new Date(), JWT_COOKIE_TTL_DAYS) at all three mint sites:
- server/middlewares/authentication.ts (FORWARDAUTH login)
- server/routes/auth/index.ts (/auth/redirect)
- server/utils/authentication.ts (signIn callback)
- Fix a pre-existing upstream issue at /auth/redirect: getJwtToken was
called with no expiresAt arg, producing a JWT the validator at
utils/jwt.ts:47 never rejects (claim missing → check skipped). Now
passes `expires` into both getJwtToken and the cookie set, matching
the pattern the other two paths already use.
- Tests cover both halves:
- middlewares/authentication.test.ts asserts the FORWARDAUTH cookie's
expires is ~now + JWT_COOKIE_TTL_DAYS (±60s).
- routes/auth/index.test.ts asserts /auth/redirect mints a JWT whose
expiresAt claim is set and ~now + JWT_COOKIE_TTL_DAYS (±60s).
In future, lift JWT_COOKIE_TTL_DAYS to an env var if deployment-specific
control is needed.
The ForwardAuth (AUTH_TYPE=SSO) branch of validateAuthentication looked
users up with User.findOne({ where: { email: { [Op.iLike]: email } } }).
ILIKE treats % and _ in the supplied value as wildcards, so a request
with "x-auth-request-email: %@%.%" produces ILIKE '%@%.%' — which
matches every row. The first matched user (typically the bootstrap
admin) was then issued a 3-month JWT cookie.
Switching to exact match (where: { email }) strips all special meaning
from those characters. Email is already .toLowerCase().trim()'d before
the lookup, matching the existing User.findByEmail pattern; emails are
stored canonically lowercased so case-insensitive matching is not
required.
Not exploitable in production: oauth2-proxy intercepts unauthenticated
requests before they reach Outline and overwrites x-auth-request-*
headers from the verified OIDC identity. Defense-in-depth fix at the
code level in case backend reachability ever changes.
Adds a regression test asserting wildcard input does not impersonate
an existing user.
) * fix(auth): re-key session on proxy-vs-JWT identity mismatch When the bundle's portal "Log out of all apps" clears the shared oauth2-proxy cookie + Cognito SSO session but a different user then logs in, Outline's per-subdomain accessToken JWT survives — every subsequent request keeps serving the previous user's session. Fixed in two layers: Server-side (validateAuthentication): - JWT branch detects mismatch when SSO mode + cookie transport + x-auth-request-email header email != user.email (lowercase + trim on both sides; required by openspec). Throws StaleSessionRedirect (302 → /home + Set-Cookie clearing accessToken) so the followed request re-enters the fwd: header path, provisions the new user, and issues a fresh JWT. 302 chosen over 401 to avoid SPA "no access" toasts on doc URLs the new user can't see. - fwd: provisioning serialised behind pg_advisory_xact_lock keyed on hashtext(email). Outline's users table has no unique email constraint (multi-team support), so parallel first-login requests would race to create duplicate rows — production hit 5 dupes before audit caught it. Lock is per-email so distinct users don't contend. - normalizeProxyEmail rewritten to use indexOf chain instead of regex to close a polynomial-backtracking CodeQL finding; same semantics. Client-side (userContinuity, ApiClient, AuthStore): - checkUserContinuity at SPA boot compares accessToken JWT id to a localStorage marker; on mismatch wipes localStorage + IndexedDB + Cache API + unregisters service workers before window.location .replace("/home") so AuthStore rehydrates from clean state. - ApiClient.fetch detects 302→HTML bounces on /api/* (either via response.redirected off the /api namespace or text/html Content-Type) and triggers the same wipeAndReload. Belt-and- suspenders fallback in AuthStore.fetchAuth wipes if /auth.info returns without data.user. Required because Outline's auth middleware only runs on /api/*, not on / — so at first SPA mount after switch the accessToken cookie hasn't rotated yet and the boot-time check can't see the mismatch. Tests cover: server-side mismatch + cookie clearing, case- and whitespace-variant emails, SQL-LIKE wildcard rejection (uses findOne with exact-equality, not LIKE), advisory lock acquisition before User.create. * fix(auth): correct Logger.warn call signature in AuthStore.ts Agent-Logs-Url: https://github.com/Pressingly/outline/sessions/2112e912-6aad-49c0-b0d5-8e0bdc465145 Co-authored-by: awais786 <445320+awais786@users.noreply.github.com> * fix(auth): clear remembered redirect paths on user switch The 302 → wipe-and-reload flow already clears localStorage on user switch, but `postLoginRedirectPath` lives in sessionStorage AND a cookie, so it survived the wipe. AuthenticatedLayout reads that key on mount via `usePostLoginPath` and issues `<Redirect to={path} />`, sending the new user to the previous user's URL (e.g. a Collection they have no access to). Net effect: even after the wipe lands the SPA on /home, layout immediately bounces it back to the stale URL. Three changes: - `userContinuity.clearAuxiliaryUserState`: drops `sessionStorage.postLoginRedirectPath`, the `postLoginRedirectPath` cookie, and the `lastSignedIn` cookie. Called from all wipe paths in `userContinuity.ts` (cookie-present mismatch, cookie-absent + marker-present, `wipeAndReload`). - `userContinuity.checkUserContinuity`: also wipes when the accessToken cookie is absent but a `LAST_USER_KEY` marker exists — the portal-logout + re-login-as-different-user path, where oauth2-proxy clears the cookie but the SPA still has the previous user's `AUTH_STORE` rehydrated. - `useLastVisitedPath.clearLastVisitedPath` + call from `AuthStore.logout`: belt-and-suspenders so an explicit in-app logout also resets the remembered path. Without this, a same- browser sign-out + sign-in-as-different-user via the in-app button would still surface the previous path. - `ApiClient.fetch`: tightened the `text/html` fallback to fire only on success (2xx) responses, so a transient Traefik 502 or oauth2-proxy login-redirect HTML page no longer triggers an unnecessary state wipe. * fix(auth): force /home after user switch via one-shot sessionStorage flag After the wipe + reload to /home, `AuthStore.fetchAuth` may still redirect away (team domain change, default collection, postLogin redirects in AuthenticatedLayout) before the previous fix's cleanup takes hold. Add a one-shot sessionStorage flag `outline_post_switch_redirect_home_once` set immediately before each wipe-path navigation. `fetchAuth` consumes it after data is loaded and forces `window.location.replace(homePath())`, overriding any subsequent redirect logic. Flag is sessionStorage-scoped so it naturally clears on tab close and doesn't leak across switches. * fix(auth): back the post-switch redirect-home flag with a cookie Adds a 1-hour cookie fallback alongside the sessionStorage entry for the one-shot post-switch redirect flag. sessionStorage is per-tab and may be unavailable in private-browsing or quota-exhausted modes; the cookie ensures `consumePostSwitchRedirectHome` still returns true on the post-wipe boot in those environments. Consume path clears both surfaces. * fix(auth): skip force-replace when already on /home If the post-switch wipe lands the SPA on /home and AuthStore.fetchAuth consumes the redirect flag, calling window.location.replace(homePath()) on a page already at homePath() causes a redundant reload. Guard the replace with a pathname comparison so the flag still gets consumed (clearing the marker) but the navigation is a no-op when unnecessary. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>
Resolved conflict in server/routes/auth/index.ts: - Adopted upstream's AuthenticationType.APP middleware type - Retained foss-main's JWT_COOKIE_TTL_DAYS 7-day TTL for token/cookie binding Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lint: authentication.test.ts used `any` in several places (createCtx overrides, error catch variables, ctx type annotations). Added CtxOverrides and MiddlewareError interfaces; replaced all explicit `any` with proper types so oxlint no-explicit-any passes. Audit: three new high-severity advisories flagged by yarn npm audit: - @babel/plugin-transform-modules-systemjs <=7.29.3 (GHSA-fv7c-fp4j-7gwp): arbitrary code generation. Fixed by bumping @babel/preset-env to ^7.29.5 (matching upstream) so the transitive dep resolves to 7.29.4, then deduping the ^7.11.0 range into the same entry. - axios <1.15.2 (GHSA-pmwg-cvhr-8vh7, GHSA-pf86-5x62-jrwf, GHSA-6chq-wfr3-2hj9, GHSA-q8qp-cvcw-x6jj): NO_PROXY bypass and prototype pollution via aws-crt. Forced to ^1.15.2 via resolution. - fast-uri <=3.1.1 (GHSA-v39h-62p7-jpjc, GHSA-q3j6-qgpj-74h6) and fast-xml-builder <=1.1.6 (GHSA-5wm8-gmm8-39j9) via ajv/fast-xml-parser. Forced to patched versions via resolutions. All resolution ranges are scoped to the exact descriptors present in yarn.lock, matching upstream's approach for the same advisories. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merged
Sync/1.7.1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Diff comparison purposes only – do not merge