Compare commits

...

10 Commits

Author SHA1 Message Date
Joakim Svensson
c3e078c9c6 Add Invite Agent action to editor menu and allow CSP connect-src 2025-12-31 00:08:30 +00:00
Erik Michelson
7185a44448 docs: update for release 1.10.5
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
2025-12-06 17:45:55 +01:00
Philip Molares
f51e402a48 docs: update release-checklist
The 1.10.4 release taught us a few new things. They are documented
now.

Signed-off-by: Philip Molares <philip.molares@udo.edu>
2025-12-05 23:45:35 +01:00
Erik Michelson
0a5f4ccefd docs: update for release 1.10.4
Co-authored-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
2025-12-05 23:36:12 +01:00
Erik Michelson
92522e3f33 fix(deps): downgrade formidable to v2 to fix uploads
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
2025-12-05 23:36:12 +01:00
Erik Michelson
35f36fccba fix(auth): add state parameters and PKCE support
Only the OAuth2 auth strategy was using the state parameter,
which should be used as described in the RFC. The other
auth strategies such as GitHub, GitLab or Google were lacking
the state parameter.
This change adds the required state parameter as well as
enabling PKCE support on providers where it's possible.

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
2025-12-05 22:06:30 +01:00
renovate[bot]
53f2ada7a3 chore(deps): lock file maintenance
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 22:02:12 +01:00
renovate[bot]
b6ab3e0c16 fix(deps): update dependency cookie to v1.1.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 21:59:07 +01:00
Erik Michelson
ef724d0fc2 fix(renderer): use credentialless and sandbox attributes on iframes
Websites loaded via an iframe could interrupt the user's workflow by initiating certain actions like opening print dialogs, alert boxes, etc. on the user's browser or even initiate file downloads.
By using the sandbox attribute, the iframe is limited in it's actions and can't access browser APIs such as to download files.
With the additional credentialless attribute, the page in the iframe is loaded in a completely separate browsing context on Chromium-based browsers, thus isolating the content even more.
The functionality could previously be abused to initiate certain actions on 3rd-party websites where the user is logged-in, if these 3rd-party websites have no proper CSRF protection. However, this is not a security risk to HedgeDoc itself.

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
2025-12-05 21:57:19 +01:00
renovate[bot]
61e3421697 chore(deps): update actions/setup-node action to v6.1.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 21:56:52 +01:00
27 changed files with 229 additions and 118 deletions

View File

@@ -26,7 +26,7 @@ runs:
key: ${{ runner.os }}-yarn-master
- name: Set up NodeJS
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: ${{ inputs.NODEJS_VERSION }}

View File

@@ -1,6 +1,7 @@
# This file lists all individuals having contributed content to the repository.
# To regenerate, use `git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf`.
Achilleas Pipinellis <axilleas@users.noreply.github.com>
Adam Hoka <hoka.adam@nexogen.hu>
Adam Worley <28906234+AdamWorley@users.noreply.github.com>
alecdwm <alec@owls.io>
@@ -8,6 +9,7 @@ Alex Garcia <alexsebastian.garcia@gmail.com>
Alexander Hesse <alexander.hesse@sandstorm-media.de>
Alexander Wellbrock <a.wellbrock@mailbox.org>
Amolith <amolith@nixnet.xyz>
Andreas Boesen <618847+Happy86@users.noreply.github.com>
Antoine Aflalo <antoine@warrantymaster.com>
aptalca <aptalca@users.noreply.github.com>
Augustin Trancart <augustin.trancart@oslandia.com>
@@ -31,6 +33,7 @@ Colin Maudry <colin@maudry.com>
CrazyPython <CrazyPython@users.noreply.github.com>
Cédric Couralet <cedric.couralet@gmail.com>
Daan Sprenkels <hello@dsprenkels.com>
Daniel Koschützki <daniel.koschuetzki@adfinis.com>
Daniel Lublin <daniel@lublin.se>
Danilo Bargen <mail@dbrgn.ch>
Dario Ernst <daddel9@nebuk.de>
@@ -90,9 +93,11 @@ Julian Rother <julian@jrother.eu>
Jun SAKATA <jun.bj141400@gmail.com>
Juned Khan <junedkhanc101@gmail.com>
Kaiyu Shi <skyisno.1@gmail.com>
Kim Brose <2803622+HarHarLinks@users.noreply.github.com>
knjcode <knjcode@gmail.com>
Kotaro Yamamoto <kota.crk@gmail.com>
Lars Karlsson <lars@kajes.se>
Lars Kiesow <lkiesow@uos.de>
Laura Kyle <laura.kyle91@gmail.com>
Lautaro Alvarez <lautarolalvarez@gmail.com>
LaysDragon <laysdra7265@gmail.com>
@@ -172,6 +177,7 @@ Stratos Gerakakis <stratosgear@gmail.com>
Stéphane Guillou <stephane.guillou@member.fsf.org>
Stéphane Maniaci <stephane.maniaci@beta.gouv.fr>
Takeaki Matsumoto <takeaki.matsumoto@ntt.com>
Thary <thary@riseup.net>
The Gitter Badger <badger@gitter.im>
Thomas De Backer <thomasisdebacker5@gmail.com>
Thor77 <thor77@thor77.org>
@@ -196,6 +202,7 @@ xnum <s000032001@gmail.com>
Yannick Bungers <git@innay.de>
Yukai Huang <yukaihuangtw@gmail.com>
zachariast <zachariastraianos@gmail.com>
Zachery Faria <zacheryfaria@gmail.com>
Zankio <xxoojoeooxx1@gmail.com>
Zearin <Zearin@users.noreply.github.com>
Ádám Hóka <hoka.adam@nexogen.hu>

View File

@@ -106,6 +106,19 @@
"email": "change or delete this: attribute map for `email` (default: NameID)"
}
},
"oauth2": {
"baseURL": "https://auth.example.com/",
"userProfileURL": "https://auth.example.com/oauth2/userinfo/",
"tokenURL": "https://auth.example.com/oauth2/token/",
"authorizationURL": "https://auth.example.com/oauth2/authorize/",
"clientID": "change-this-id",
"clientSecret": "change-this-secret",
"scope": "openid profile user",
"userProfileUsernameAttr": "preferred_username",
"userProfileEmailAttr": "email",
"userProfileDisplayNameAttr": "name",
"pkce": true
},
"imgur": {
"clientID": "change this"
},

View File

@@ -209,22 +209,23 @@ these are rarely used for various reasons.
### OAuth2 Login
| config file | environment | **default** and example value | description |
|-------------|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `oauth2` | | `{baseURL: ..., userProfileURL: ..., userProfileUsernameAttr: ..., userProfileDisplayNameAttr: ..., userProfileEmailAttr: ..., tokenURL: ..., authorizationURL: ..., clientID: ..., clientSecret: ..., scope: ...}` | An object detailing your OAuth2 provider. Refer to the [Mattermost](guides/auth/mattermost-self-hosted.md) or [Nextcloud](guides/auth/nextcloud.md) examples for more details! |
| | `CMD_OAUTH2_USER_PROFILE_URL` | **no default**, `https://example.com` | Where to retrieve information about a user after successful login. Needs to output JSON. (no default value) Refer to the [Mattermost](guides/auth/mattermost-self-hosted.md) or [Nextcloud](guides/auth/nextcloud.md) examples for more details on all of the `CMD_OAUTH2...` options. |
| | `CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR` | **no default**, `name` | where to find the username in the JSON from the user profile URL. (no default value) |
| | `CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR` | **no default**, `display-name` | where to find the display-name in the JSON from the user profile URL. (no default value) |
| | `CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR` | **no default**, `email` | where to find the email address in the JSON from the user profile URL. (no default value) |
| | `CMD_OAUTH2_USER_PROFILE_ID_ATTR` | **no default**, `user_uuid` | where to find the dedicated user ID (optional, overrides `CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR`) |
| | `CMD_OAUTH2_TOKEN_URL` | **no default**, `https://example.com` | sometimes called token endpoint, please refer to the documentation of your OAuth2 provider (no default value) |
| | `CMD_OAUTH2_AUTHORIZATION_URL` | **no default**, `https://example.com` | authorization URL of your provider, please refer to the documentation of your OAuth2 provider (no default value) |
| | `CMD_OAUTH2_CLIENT_ID` | **no default**, `afae02fckafd...` | you will get this from your OAuth2 provider when you register HedgeDoc as OAuth2-client, (no default value) |
| | `CMD_OAUTH2_CLIENT_SECRET` | **no default**, `afae02fckafd...` | you will get this from your OAuth2 provider when you register HedgeDoc as OAuth2-client, (no default value) |
| | `CMD_OAUTH2_PROVIDERNAME` | **no default**, `My institution` | Optional name to be displayed at login form indicating the oAuth2 provider |
| | `CMD_OAUTH2_SCOPE` | **no default**, `openid email profile` | Scope to request for OIDC (OpenID Connect) providers. |
| | `CMD_OAUTH2_ROLES_CLAIM` | **no default**, `roles` | ID token claim, which is supposed to provide an array of strings of roles |
| | `CMD_OAUTH2_ACCESS_ROLE` | **no default**, `role/hedgedoc` | The role which should be included in the ID token roles claim to grant access |
| config file | environment | **default** and example value | description |
|-------------|---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `oauth2` | | `{baseURL: ..., userProfileURL: ..., userProfileUsernameAttr: ..., userProfileDisplayNameAttr: ..., userProfileEmailAttr: ..., tokenURL: ..., authorizationURL: ..., clientID: ..., clientSecret: ..., scope: ..., pkce: ...}` | An object detailing your OAuth2 provider. Refer to the [Mattermost](guides/auth/mattermost-self-hosted.md) or [Nextcloud](guides/auth/nextcloud.md) examples for more details! |
| | `CMD_OAUTH2_USER_PROFILE_URL` | **no default**, `https://example.com` | Where to retrieve information about a user after successful login. Needs to output JSON. (no default value) Refer to the [Mattermost](guides/auth/mattermost-self-hosted.md) or [Nextcloud](guides/auth/nextcloud.md) examples for more details on all of the `CMD_OAUTH2...` options. |
| | `CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR` | **no default**, `name` | where to find the username in the JSON from the user profile URL. (no default value) |
| | `CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR` | **no default**, `display-name` | where to find the display-name in the JSON from the user profile URL. (no default value) |
| | `CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR` | **no default**, `email` | where to find the email address in the JSON from the user profile URL. (no default value) |
| | `CMD_OAUTH2_USER_PROFILE_ID_ATTR` | **no default**, `user_uuid` | where to find the dedicated user ID (optional, overrides `CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR`) |
| | `CMD_OAUTH2_TOKEN_URL` | **no default**, `https://example.com` | sometimes called token endpoint, please refer to the documentation of your OAuth2 provider (no default value) |
| | `CMD_OAUTH2_AUTHORIZATION_URL` | **no default**, `https://example.com` | authorization URL of your provider, please refer to the documentation of your OAuth2 provider (no default value) |
| | `CMD_OAUTH2_CLIENT_ID` | **no default**, `afae02fckafd...` | you will get this from your OAuth2 provider when you register HedgeDoc as OAuth2-client, (no default value) |
| | `CMD_OAUTH2_CLIENT_SECRET` | **no default**, `afae02fckafd...` | you will get this from your OAuth2 provider when you register HedgeDoc as OAuth2-client, (no default value) |
| | `CMD_OAUTH2_PROVIDERNAME` | **no default**, `My institution` | Optional name to be displayed at login form indicating the oAuth2 provider |
| | `CMD_OAUTH2_SCOPE` | **no default**, `openid email profile` | Scope to request for OIDC (OpenID Connect) providers. |
| | `CMD_OAUTH2_ROLES_CLAIM` | **no default**, `roles` | ID token claim, which is supposed to provide an array of strings of roles |
| | `CMD_OAUTH2_ACCESS_ROLE` | **no default**, `role/hedgedoc` | The role which should be included in the ID token roles claim to grant access |
| | `CMD_OAUTH2_PKCE` | **`false`**, `true` | Whether to use PKCE auth. Defaults to false since not every OAuth2 provider supports it. |
!!! info
If you are using a [CA not trusted by Node.js](https://github.com/nodejs/node/issues/4175) (like Let's Encrypt e.g) for

View File

@@ -3,7 +3,7 @@ openapi: 3.0.1
info:
title: HedgeDoc
description: HedgeDoc is an open source collaborative note editor. Several tasks of HedgeDoc can be automated through this API.
version: 1.10.3
version: 1.10.5
contact:
name: HedgeDoc on GitHub
url: https://github.com/hedgedoc/hedgedoc

View File

@@ -110,7 +110,7 @@ and put this into your config:
"password": "hd1db",
"database": "hd1db",
"host": "localhost",
"port": "5432",
"port": "3306",
"dialect": "mariadb"
},
```
@@ -145,6 +145,26 @@ Click in them an try to play around with them. Don't just check they exist and s
- [ ] Testing each option if it works
```
---
title: yaml metadata testing
description: This is a test description
tags: features, cool, updated
robots: noindex, nofollow
lang: en-US
dir: rtl
breaks: false
type: slide
slideOptions:
transition: fade
theme: white
opengraph:
title: Special title for OpenGraph protocol
image: https://dummyimage.com/600x600/000/fff
image:type: image/png
---
```
### GDPR features
- [ ] Delete account works
@@ -195,11 +215,13 @@ Click in them an try to play around with them. Don't just check they exist and s
"scope": "openid profile user",
"userProfileUsernameAttr": "preferred_username",
"userProfileEmailAttr": "email",
"userProfileDisplayNameAttr": "name"
"userProfileDisplayNameAttr": "name",
"pkce": true
}
```
- [ ] GitHub
- [ ] Rate-limiting for basic user/password
- [ ] Rate-limiting for basic user/password (try to login with e.g. test@example.com and invalid password about 10 to 15 times in a row -> you should receive a message "Too many requests" at some point)
## Release:

View File

@@ -18,7 +18,7 @@ The easiest way to get started with HedgeDoc and Docker is to use the following
version: '3'
services:
database:
image: postgres:13.4-alpine
image: postgres:17.7-alpine
environment:
- POSTGRES_USER=hedgedoc
- POSTGRES_PASSWORD=password
@@ -28,7 +28,7 @@ services:
restart: always
app:
# Make sure to use the latest release from https://hedgedoc.org/latest-release
image: quay.io/hedgedoc/hedgedoc:1.10.3
image: quay.io/hedgedoc/hedgedoc:1.10.5
environment:
- CMD_DB_URL=postgres://hedgedoc:password@database:5432/hedgedoc
- CMD_DOMAIN=localhost

View File

@@ -19,7 +19,7 @@
1. Check if you meet the [requirements at the top of this document](#manual-installation).
2. Download the [latest release](https://hedgedoc.org/latest-release/) and extract it.
<small>Alternatively, you can use Git to clone the repository and checkout a release, e.g. with `git clone -b 1.10.3 https://github.com/hedgedoc/hedgedoc.git`.</small>
<small>Alternatively, you can use Git to clone the repository and checkout a release, e.g. with `git clone -b 1.10.5 https://github.com/hedgedoc/hedgedoc.git`.</small>
3. Enter the directory and execute `bin/setup`, which will install the dependencies and create example configs.
4. Configure HedgeDoc: To get started, you can use this minimal `config.json`:
```json
@@ -61,7 +61,7 @@ If you want to upgrade HedgeDoc from an older version, follow these steps:
and the latest release.
2. Fully stop your old HedgeDoc server.
3. [Download](https://hedgedoc.org/latest-release/) the new release and extract it over the old directory.
<small>If you use Git, you can check out the new tag with e.g. `git fetch origin && git checkout 1.10.3`</small>
<small>If you use Git, you can check out the new tag with e.g. `git fetch origin && git checkout 1.10.5`</small>
5. Run `bin/setup`. This will take care of installing dependencies. It is safe to run on an existing installation.
6. *:octicons-light-bulb-16: If you used the release tarball for 1.7.0 or newer, this step can be skipped.*
Build the frontend bundle by running `yarn install --immutable` and `yarn build`. The extra `yarn install --immutable` is necessary as `bin/setup` does not install the build dependencies.

View File

@@ -104,7 +104,8 @@ module.exports = {
tokenURL: undefined,
clientID: undefined,
clientSecret: undefined,
scope: undefined
scope: undefined,
pkce: false
},
facebook: {
clientID: undefined,

View File

@@ -115,7 +115,8 @@ module.exports = {
clientSecret: process.env.CMD_OAUTH2_CLIENT_SECRET,
scope: process.env.CMD_OAUTH2_SCOPE,
rolesClaim: process.env.CMD_OAUTH2_ROLES_CLAIM,
accessRole: process.env.CMD_OAUTH2_ACCESS_ROLE
accessRole: process.env.CMD_OAUTH2_ACCESS_ROLE,
pkce: toBooleanConfig(process.env.CMD_OAUTH2_PKCE)
},
dropbox: {
clientID: process.env.CMD_DROPBOX_CLIENTID,

View File

@@ -7,7 +7,7 @@ const CspStrategy = {}
const defaultDirectives = {
defaultSrc: ['\'none\''],
baseUri: ['\'self\''],
connectSrc: ['\'self\'', buildDomainOriginWithProtocol(config, 'ws'), 'https://vimeo.com/api/v2/video/'],
connectSrc: ['\'self\'', buildDomainOriginWithProtocol(config, 'ws'), 'https://vimeo.com/api/v2/video/', 'https://hedgeagent.sa6anw.se/add_note'],
fontSrc: ['\'self\''],
manifestSrc: ['\'self\''],
frameSrc: ['\'self\'', 'https://player.vimeo.com', 'https://www.youtube.com', 'https://gist.github.com'],

View File

@@ -12,7 +12,9 @@ passport.use(new DropboxStrategy({
apiVersion: '2',
clientID: config.dropbox.clientID,
clientSecret: config.dropbox.clientSecret,
callbackURL: config.serverURL + '/auth/dropbox/callback'
callbackURL: config.serverURL + '/auth/dropbox/callback',
state: true,
pkce: true
}, passportGeneralCallback))
dropboxAuth.get('/auth/dropbox', function (req, res, next) {

View File

@@ -12,7 +12,9 @@ const facebookAuth = module.exports = Router()
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.serverURL + '/auth/facebook/callback'
callbackURL: config.serverURL + '/auth/facebook/callback',
state: true,
pkce: true
}, passportGeneralCallback))
facebookAuth.get('/auth/facebook', function (req, res, next) {

View File

@@ -12,7 +12,9 @@ const githubAuth = module.exports = Router()
passport.use(new GithubStrategy({
clientID: config.github.clientID,
clientSecret: config.github.clientSecret,
callbackURL: config.serverURL + '/auth/github/callback'
callbackURL: config.serverURL + '/auth/github/callback',
pkce: true,
state: true
}, passportGeneralCallback))
githubAuth.get('/auth/github', function (req, res, next) {

View File

@@ -14,7 +14,9 @@ passport.use(new GitlabStrategy({
clientID: config.gitlab.clientID,
clientSecret: config.gitlab.clientSecret,
scope: config.gitlab.scope,
callbackURL: config.serverURL + '/auth/gitlab/callback'
callbackURL: config.serverURL + '/auth/gitlab/callback',
pkce: true,
state: true
}, passportGeneralCallback))
gitlabAuth.get('/auth/gitlab', function (req, res, next) {

View File

@@ -12,7 +12,9 @@ passport.use(new GoogleStrategy({
clientID: config.google.clientID,
clientSecret: config.google.clientSecret,
callbackURL: config.serverURL + '/auth/google/callback',
userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo'
userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo',
pkce: true,
state: true
}, passportGeneralCallback))
googleAuth.get('/auth/google', function (req, res, next) {

View File

@@ -16,7 +16,8 @@ const mattermostStrategy = new OAuthStrategy({
tokenURL: config.mattermost.baseURL + '/oauth/access_token',
clientID: config.mattermost.clientID,
clientSecret: config.mattermost.clientSecret,
callbackURL: config.serverURL + '/auth/mattermost/callback'
callbackURL: config.serverURL + '/auth/mattermost/callback',
state: true
}, passportGeneralCallback)
mattermostStrategy.userProfile = (accessToken, done) => {

View File

@@ -138,6 +138,7 @@ passport.use(new OAuth2CustomStrategy({
callbackURL: config.serverURL + '/auth/oauth2/callback',
userProfileURL: config.oauth2.userProfileURL,
scope: config.oauth2.scope,
pkce: config.oauth2.pkce,
state: true
}, passportGeneralCallback))

View File

@@ -40,6 +40,7 @@
"Extra": "Extra",
"Revision": "Revision",
"Slide Mode": "Slide Mode",
"Invite Agent": "Invite Agent",
"Export": "Export",
"Import": "Import",
"Clipboard": "Clipboard",

View File

@@ -40,6 +40,7 @@
"Extra": "Extra",
"Revision": "Revision",
"Slide Mode": "Slide Mode",
"Invite Agent": "Invite Agent",
"Export": "Exportera",
"Import": "Importera",
"Clipboard": "Urklipp",

View File

@@ -1,6 +1,6 @@
{
"name": "HedgeDoc",
"version": "1.10.3",
"version": "1.10.5",
"description": "The best platform to write and share markdown.",
"main": "app.js",
"license": "AGPL-3.0",
@@ -33,7 +33,7 @@
"compression": "1.8.1",
"connect-flash": "0.1.1",
"connect-session-sequelize": "8.0.4",
"cookie": "1.0.2",
"cookie": "1.1.1",
"cookie-parser": "1.4.7",
"deep-freeze": "0.0.1",
"diff-match-patch": "git+https://github.com/hackmdio/diff-match-patch.git#commit=59a9395ad9fe143e601e7ae5765ed943bdd2b11e",
@@ -42,7 +42,7 @@
"express-rate-limit": "8.2.1",
"express-session": "1.18.2",
"file-type": "21.1.1",
"formidable": "3.5.4",
"formidable": "2.1.5",
"graceful-fs": "4.2.11",
"helmet": "8.1.0",
"i18n": "0.15.3",

View File

@@ -1,6 +1,20 @@
# Release Notes
## <i class="fa fa-tag"></i> 1.x.x <i class="fa fa-calendar-o"></i> UNRELEASED
## <i class="fa fa-tag"></i> 1.10.5 <i class="fa fa-calendar-o"></i> 2025-12-06
This release is just a fix for the docker container. It does not contain any
changes to HedgeDoc itself.
### Bugfixes
- Fix the bundled healthcheck in the docker container
## <i class="fa fa-tag"></i> 1.10.4 <i class="fa fa-calendar-o"></i> 2025-12-05
### Security fixes
This release contains two low severity security fixes:
- [GHSA-gmgw-rcmh-7x47](https://github.com/hedgedoc/hedgedoc/security/advisories/GHSA-gmgw-rcmh-7x47) reports potential cross-site side-effects due to not applying sandboxing to iframes.
- [GHSA-6wm6-3vpq-6qvv](https://github.com/hedgedoc/hedgedoc/security/advisories/GHSA-6wm6-3vpq-6qvv) reports a possible CSRF vulnerability when using certain social login providers because the `state` parameter is not used and checked.
### Enhancements
- Add `enableUploads` (`CMD_ENABLE_UPLOADS`) config option to restrict uploads to `registered` users, `all` users or
@@ -9,12 +23,29 @@
- Switch from deprecated shortid to nanoid module, with 10 character long aliases in "public" links
- Ensure compatibility with Node 24
- Protect user history from accidental or malicious deletion by adding a CSRF-like token
- Many enhancements in the documentation at [docs.hedgedoc.org](https://docs.hedgedoc.org)
### Bugfixes
- Ignore the healthcheck endpoint in the "too busy" limiter
- Send the referrer origin for YouTube embeddings due to their requirement
- Force kill the server after a timeout when waiting for the realtime server to close connections on shutdown
- Secure iframes with `credentialless` and `sandbox` attributes
- Fix regexes for `[time=...]`, `[name=...]` and `[color=...]` shortcodes in lists
- Use `state` parameter for OAuth2 flows and PKCE where applicable
### Node compatibility
- Support for Node 24 was verified. The docker image now uses Node 24 as its base image.
### Contributors
- [Nora Matthias Schiffer](https://github.com/neocturne) (#6096)
- [4censord](https://github.com/4censord) (#6102)
- [Zachery Faria](https://github.com/ZacheryFaria) (#6105)
- [pl7ofit](https://github.com/pl7ofit) (#6106)
- [Lars Kiesow](https://github.com/lkiesow) (#6107)
- [Kim Brose](https://github.com/HarHarLinks) (#6114)
- [Achilleas Pipinellis](https://github.com/axilleas) (#6119)
- [Andreas Boesen](https://github.com/Happy86) (#6148, #6149)
- [Thary](https://github.com/tharynot) (#6155)
## <i class="fa fa-tag"></i> 1.10.3 <i class="fa fa-calendar-o"></i> 2025-04-09

View File

@@ -461,7 +461,7 @@ export function finishView (view) {
inner.attr('target', '_blank')
$(value).append(inner)
})
// pdf
// pdf
view.find('div.pdf.raw').removeClass('raw')
.each(function (key, value) {
const url = $(value).attr('data-pdfurl')
@@ -471,7 +471,12 @@ export function finishView (view) {
height: '400px'
})
})
// syntax highlighting
// iframe
view.find('iframe')
.each((key, value) => {
$(value).attr('credentialless', '').attr('sandbox', '')
})
// syntax highlighting
view.find('code.raw').removeClass('raw')
.each((key, value) => {
const langDiv = $(value)

View File

@@ -1206,6 +1206,23 @@ ui.toolbar.publish.attr('href', noteurl + '/publish')
// extra
// slide
ui.toolbar.extra.slide.attr('href', noteurl + '/slide')
// invite agent
ui.toolbar.extra.inviteAgent.click(function (e) {
e.preventDefault()
$.ajax({
url: 'https://hedgeagent.sa6anw.se/add_note',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ note_id: noteid }),
error: function (xhr, status, err) {
console.error('Invite agent failed:', status, err)
alert('Failed to invite agent.')
}
})
})
// download
// markdown
ui.toolbar.download.markdown.click(function (e) {

View File

@@ -12,7 +12,8 @@ export const getUIElements = () => ({
publish: $('.ui-publish'),
extra: {
revision: $('.ui-extra-revision'),
slide: $('.ui-extra-slide')
slide: $('.ui-extra-slide'),
inviteAgent: $('.ui-invite-agent')
},
download: {
markdown: $('.ui-download-markdown'),

View File

@@ -34,6 +34,8 @@
</li>
<li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
</li>
<li role="presentation"><a role="menuitem" class="ui-invite-agent" tabindex="-1" href="#"><i class="fa fa-robot fa-fw"></i> <%= __('Invite Agent') %></a>
</li>
<% if(enableGitHubGist || enableDropBoxSave || enableGitlabSnippets) { %>
<li class="divider"></li>
<li class="dropdown-header"><%= __('Export') %></li>
@@ -136,6 +138,8 @@
</li>
<li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
</li>
<li role="presentation"><a role="menuitem" class="ui-invite-agent" tabindex="-1" href="#"><i class="fa fa-robot fa-fw"></i> <%= __('Invite Agent') %></a>
</li>
<% if(enableGitHubGist || enableDropBoxSave || enableGitlabSnippets) { %>
<li class="divider"></li>
<li class="dropdown-header"><%= __('Export') %></li>

148
yarn.lock
View File

@@ -291,7 +291,7 @@ __metadata:
languageName: node
linkType: hard
"@eslint/eslintrc@npm:3.3.3":
"@eslint/eslintrc@npm:3.3.3, @eslint/eslintrc@npm:^3.3.1":
version: 3.3.3
resolution: "@eslint/eslintrc@npm:3.3.3"
dependencies:
@@ -308,23 +308,6 @@ __metadata:
languageName: node
linkType: hard
"@eslint/eslintrc@npm:^3.3.1":
version: 3.3.1
resolution: "@eslint/eslintrc@npm:3.3.1"
dependencies:
ajv: "npm:^6.12.4"
debug: "npm:^4.3.2"
espree: "npm:^10.0.1"
globals: "npm:^14.0.0"
ignore: "npm:^5.2.0"
import-fresh: "npm:^3.2.1"
js-yaml: "npm:^4.1.0"
minimatch: "npm:^3.1.2"
strip-json-comments: "npm:^3.1.1"
checksum: 10/cc240addbab3c5fceaa65b2c8d5d4fd77ddbbf472c2f74f0270b9d33263dc9116840b6099c46b64c9680301146250439b044ed79278a1bcc557da412a4e3c1bb
languageName: node
linkType: hard
"@eslint/js@npm:9.39.1":
version: 9.39.1
resolution: "@eslint/js@npm:9.39.1"
@@ -864,13 +847,13 @@ __metadata:
linkType: hard
"@types/express@npm:*":
version: 5.0.5
resolution: "@types/express@npm:5.0.5"
version: 5.0.6
resolution: "@types/express@npm:5.0.6"
dependencies:
"@types/body-parser": "npm:*"
"@types/express-serve-static-core": "npm:^5.0.0"
"@types/serve-static": "npm:^1"
checksum: 10/9e72410286fbc80bea8a57d1b374c25235f6019dabf8af67e5b72c2f9be548f36ccc09d048fc88172731450e80f06018f90c17e05beb5afc4dbdaf5f7200dbb3
"@types/serve-static": "npm:^2"
checksum: 10/da2cc3de1b1a4d7f20ed3fb6f0a8ee08e99feb3c2eb5a8d643db77017d8d0e70fee9e95da38a73f51bcdf5eda3bb6435073c0271dc04fb16fda92e55daf911fa
languageName: node
linkType: hard
@@ -1076,6 +1059,16 @@ __metadata:
languageName: node
linkType: hard
"@types/serve-static@npm:^2":
version: 2.2.0
resolution: "@types/serve-static@npm:2.2.0"
dependencies:
"@types/http-errors": "npm:*"
"@types/node": "npm:*"
checksum: 10/f2bad1304c7d0d3b7221faff3e490c40129d3803f4fb1b2fb84f31f561071c5e6a4b876c41bbbe82d5645034eea936e946bcaaf993dac1093ce68b56effad6e0
languageName: node
linkType: hard
"@types/source-list-map@npm:*":
version: 0.1.6
resolution: "@types/source-list-map@npm:0.1.6"
@@ -1460,7 +1453,7 @@ __metadata:
compression: "npm:1.8.1"
connect-flash: "npm:0.1.1"
connect-session-sequelize: "npm:8.0.4"
cookie: "npm:1.0.2"
cookie: "npm:1.1.1"
cookie-parser: "npm:1.4.7"
copy-webpack-plugin: "npm:6.4.1"
css-loader: "npm:5.2.7"
@@ -1485,7 +1478,7 @@ __metadata:
file-type: "npm:21.1.1"
flowchart.js: "npm:1.18.0"
fork-awesome: "npm:1.2.0"
formidable: "npm:3.5.4"
formidable: "npm:2.1.5"
globals: "npm:16.5.0"
graceful-fs: "npm:4.2.11"
helmet: "npm:8.1.0"
@@ -3031,12 +3024,12 @@ __metadata:
languageName: node
linkType: hard
"baseline-browser-mapping@npm:^2.8.25":
version: 2.8.31
resolution: "baseline-browser-mapping@npm:2.8.31"
"baseline-browser-mapping@npm:^2.9.0":
version: 2.9.3
resolution: "baseline-browser-mapping@npm:2.9.3"
bin:
baseline-browser-mapping: dist/cli.js
checksum: 10/aefad7523ab6e93a28d278c2faae08b934d6f8899f9b6868a50cccb2e2cbb12bad0f5eda3ccb70d7f2e2f9e58cc1973050523e867e6bb0793ab0c63e194956b6
checksum: 10/d2ebf146af3e17fc1c0ecf0437dc9257bca8f262068975859c6fffccf344771a533a6ca39b47a981c203f0f2f29abd202cd1a37878da65db76c27712e9f47911
languageName: node
linkType: hard
@@ -3373,17 +3366,17 @@ __metadata:
linkType: hard
"browserslist@npm:^4.0.0, browserslist@npm:^4.21.4":
version: 4.28.0
resolution: "browserslist@npm:4.28.0"
version: 4.28.1
resolution: "browserslist@npm:4.28.1"
dependencies:
baseline-browser-mapping: "npm:^2.8.25"
caniuse-lite: "npm:^1.0.30001754"
electron-to-chromium: "npm:^1.5.249"
baseline-browser-mapping: "npm:^2.9.0"
caniuse-lite: "npm:^1.0.30001759"
electron-to-chromium: "npm:^1.5.263"
node-releases: "npm:^2.0.27"
update-browserslist-db: "npm:^1.1.4"
update-browserslist-db: "npm:^1.2.0"
bin:
browserslist: cli.js
checksum: 10/59dc88f8d950e44a064361cb874f486e532a8ba932e0cf549aee8b36dd2b791da2bc11f36c1cf820ebb9c1f3250b100f8c56364dd6e86dbc90495af424100e19
checksum: 10/64f2a97de4bce8473c0e5ae0af8d76d1ead07a5b05fc6bc87b848678bb9c3a91ae787b27aa98cdd33fc00779607e6c156000bed58fefb9cf8e4c5a183b994cdb
languageName: node
linkType: hard
@@ -3616,10 +3609,10 @@ __metadata:
languageName: node
linkType: hard
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30000844, caniuse-lite@npm:^1.0.30001754":
version: 1.0.30001756
resolution: "caniuse-lite@npm:1.0.30001756"
checksum: 10/1aa412f539bf2f3b9120caa6a3552579914cc9de7bc075212d1196e625dc6243317005a45c6aa7675610e5e23d47418564b7b3e7700244005bd665b01625b0ae
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30000844, caniuse-lite@npm:^1.0.30001759":
version: 1.0.30001759
resolution: "caniuse-lite@npm:1.0.30001759"
checksum: 10/da0ec28dd993dffa99402914903426b9466d2798d41c1dc9341fcb7dd10f58fdd148122e2c65001246c030ba1c939645b7b4597f6321e3246dc792323bb11541
languageName: node
linkType: hard
@@ -4200,10 +4193,10 @@ __metadata:
languageName: node
linkType: hard
"cookie@npm:1.0.2":
version: 1.0.2
resolution: "cookie@npm:1.0.2"
checksum: 10/f5817cdc84d8977761b12549eba29435e675e65c7fef172bc31737788cd8adc83796bf8abe6d950554e7987325ad2d9ac2971c5bd8ff0c4f81c145f82e4ab1be
"cookie@npm:1.1.1":
version: 1.1.1
resolution: "cookie@npm:1.1.1"
checksum: 10/85538153054791155cf4d38d2e807e3b9382d71bf71d92fc46fca348515ea574049d0d9ef8eb84d2d54a681ad1d7a7316b1989b901dace50a6c0f4c3858dbdb2
languageName: node
linkType: hard
@@ -5788,10 +5781,10 @@ __metadata:
languageName: node
linkType: hard
"electron-to-chromium@npm:^1.3.47, electron-to-chromium@npm:^1.5.249":
version: 1.5.259
resolution: "electron-to-chromium@npm:1.5.259"
checksum: 10/259aa8b9c2a82e54c60b2c9af283aa9f618f42aad3c54613fcea61d8e12e4c8f0641d625ba2e6687fe69eda1fc2f3c747e8dafad1c789ac59ac47d4afceeaa23
"electron-to-chromium@npm:^1.3.47, electron-to-chromium@npm:^1.5.263":
version: 1.5.266
resolution: "electron-to-chromium@npm:1.5.266"
checksum: 10/2c7e05d1df189013e01b9fa19f5794dc249b80f330ab87f78674fa7416df153e2d32738d16914eee1112b5d8878b6181336e502215a34c63c255da078de5209d
languageName: node
linkType: hard
@@ -5971,11 +5964,11 @@ __metadata:
linkType: hard
"envinfo@npm:^7.7.3":
version: 7.20.0
resolution: "envinfo@npm:7.20.0"
version: 7.21.0
resolution: "envinfo@npm:7.21.0"
bin:
envinfo: dist/cli.js
checksum: 10/9dab1e64e0b6614243b72b07d782434e0984ff6104ef8906c3b5e1562f96e0cde8bf38a9dcf373494a020b9752a0edeeec2a4fdf7bcd41d5d704791f93097ffe
checksum: 10/2469a72802ded4e43c007dcd1c5dd44d8049b7d18276874dcc3f3f14a54bc72806fa35e82760974ca1442d82f5f9df3651048204e72791f81bcdd5f07422a561
languageName: node
linkType: hard
@@ -7247,14 +7240,15 @@ __metadata:
languageName: node
linkType: hard
"formidable@npm:3.5.4":
version: 3.5.4
resolution: "formidable@npm:3.5.4"
"formidable@npm:2.1.5":
version: 2.1.5
resolution: "formidable@npm:2.1.5"
dependencies:
"@paralleldrive/cuid2": "npm:^2.2.2"
dezalgo: "npm:^1.0.4"
once: "npm:^1.4.0"
checksum: 10/4645e6ce3d8bbefd3dd873dcd6211362da3bf8a04c8426d7f454c238be0142975f02e5bdbc792fdbd2be493fdcf5442fe01d9a246bd8c3fd8e779738290cc630
qs: "npm:^6.11.0"
checksum: 10/ee96de12e91d63fe86479ffe5bf59004bb3f43e00ce7ccecd1b1ff10b5d1a89a19b1ede727e1fe57ef596c377b9f9300212a5f7bab14fd28f3c4ffe12dbb4cc7
languageName: node
linkType: hard
@@ -8370,9 +8364,9 @@ __metadata:
linkType: hard
"ipaddr.js@npm:^2.0.1":
version: 2.2.0
resolution: "ipaddr.js@npm:2.2.0"
checksum: 10/9e1cdd9110b3bca5d910ab70d7fb1933e9c485d9b92cb14ef39f30c412ba3fe02a553921bf696efc7149cc653453c48ccf173adb996ec27d925f1f340f872986
version: 2.3.0
resolution: "ipaddr.js@npm:2.3.0"
checksum: 10/be3d01bc2e20fc2dc5349b489ea40883954b816ce3e57aa48ad943d4e7c4ace501f28a7a15bde4b96b6b97d0fbb28d599ff2f87399f3cda7bd728889402eed3b
languageName: node
linkType: hard
@@ -9741,9 +9735,9 @@ __metadata:
linkType: hard
"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1":
version: 11.2.2
resolution: "lru-cache@npm:11.2.2"
checksum: 10/fa7919fbf068a739f79a1ad461eb273514da7246cebb9dca68e3cd7ba19e3839e7e2aaecd9b72867e08038561eeb96941189e89b3d4091c75ced4f56c71c80db
version: 11.2.4
resolution: "lru-cache@npm:11.2.4"
checksum: 10/3b2da74c0b6653767f8164c38c4c4f4d7f0cc10c62bfa512663d94a830191ae6a5af742a8d88a8b30d5f9974652d3adae53931f32069139ad24fa2a18a199aca
languageName: node
linkType: hard
@@ -11322,11 +11316,11 @@ __metadata:
linkType: hard
"nan@npm:^2.12.1":
version: 2.23.1
resolution: "nan@npm:2.23.1"
version: 2.24.0
resolution: "nan@npm:2.24.0"
dependencies:
node-gyp: "npm:latest"
checksum: 10/4a1f2948c5d4f59633d7fa4b90a9c2bce6c3feb556a0de598cd20c0b836922c970353d3634fc1678f2643038c7c343e43a9b891238d812ec8222d42e4fde09ae
checksum: 10/479f6960119b5ef9b488c14e9069eb534c3545d50b621f51b247d1e3b40828ee619c4d9f8efe30786c5b18c21c60b3cda3f0d0b92e9a3a26cb3e4ab5492a7032
languageName: node
linkType: hard
@@ -12983,12 +12977,12 @@ __metadata:
linkType: hard
"postcss-selector-parser@npm:^7.0.0":
version: 7.1.0
resolution: "postcss-selector-parser@npm:7.1.0"
version: 7.1.1
resolution: "postcss-selector-parser@npm:7.1.1"
dependencies:
cssesc: "npm:^3.0.0"
util-deprecate: "npm:^1.0.2"
checksum: 10/2caf09e66e2be81d45538f8afdc5439298c89bea71e9943b364e69dce9443d9c5ab33f4dd8b237f1ed7d2f38530338dcc189c1219d888159e6afb5b0afe58b19
checksum: 10/bb3c6455b20af26a556e3021e21101d8470252644e673c1612f7348ff8dd41b11321329f0694cf299b5b94863f823480b72d3e2f4bd3a89dc43e2d8c0dbad341
languageName: node
linkType: hard
@@ -13138,9 +13132,9 @@ __metadata:
linkType: hard
"proc-log@npm:^6.0.0":
version: 6.0.0
resolution: "proc-log@npm:6.0.0"
checksum: 10/98831f35d30f254f89836ff3eb89e5970ed8f88ad1bde2ce6c0baa70e0f53166408ba8d9c6a5e3c44d10b611bb415ac46d9b2c78277a397608890c044f9d5942
version: 6.1.0
resolution: "proc-log@npm:6.1.0"
checksum: 10/9033f30f168ed5a0991b773d0c50ff88384c4738e9a0a67d341de36bf7293771eed648ab6a0562f62276da12fde91f3bbfc75ffff6e71ad49aafd74fc646be66
languageName: node
linkType: hard
@@ -13311,7 +13305,7 @@ __metadata:
languageName: node
linkType: hard
"qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.5.2, qs@npm:~6.14.0":
"qs@npm:^6.11.0, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.5.2, qs@npm:~6.14.0":
version: 6.14.0
resolution: "qs@npm:6.14.0"
dependencies:
@@ -16780,9 +16774,9 @@ __metadata:
languageName: node
linkType: hard
"update-browserslist-db@npm:^1.1.4":
version: 1.1.4
resolution: "update-browserslist-db@npm:1.1.4"
"update-browserslist-db@npm:^1.2.0":
version: 1.2.2
resolution: "update-browserslist-db@npm:1.2.2"
dependencies:
escalade: "npm:^3.2.0"
picocolors: "npm:^1.1.1"
@@ -16790,7 +16784,7 @@ __metadata:
browserslist: ">= 4.21.0"
bin:
update-browserslist-db: cli.js
checksum: 10/79b2c0a31e9b837b49dc55d5cb7b77f44a69502847c7be352a44b1d35ac2032bf0e1bb7543f992809ed427bf9d32aa3f7ad41cef96198fa959c1666870174c06
checksum: 10/ae2102d3c83fca35e9deb012d82bfde6f734998ced937e34a3bf239a4b67577108fdd144283aafc0e5e3cf38ca1aecd7714906ba6f562896c762d2f2fa391026
languageName: node
linkType: hard
@@ -17722,11 +17716,11 @@ __metadata:
linkType: hard
"yaml@npm:^2.0.0":
version: 2.8.1
resolution: "yaml@npm:2.8.1"
version: 2.8.2
resolution: "yaml@npm:2.8.2"
bin:
yaml: bin.mjs
checksum: 10/eae07b3947d405012672ec17ce27348aea7d1fa0534143355d24a43a58f5e05652157ea2182c4fe0604f0540be71f99f1173f9d61018379404507790dff17665
checksum: 10/4eab0074da6bc5a5bffd25b9b359cf7061b771b95d1b3b571852098380db3b1b8f96e0f1f354b56cc7216aa97cea25163377ccbc33a2e9ce00316fe8d02f4539
languageName: node
linkType: hard