feat(external-links): add whitelist to bypass redirect page (#6548)
Signed-off-by: Tobias Dillig <tobias.dillig@frachtwerk.de> Signed-off-by: Tobi <tldev-de@users.noreply.github.com>
This commit is contained in:
@@ -44,6 +44,7 @@ to work.*
|
|||||||
| `dbURL` | `CMD_DB_URL` | **`undefined`**, `postgres://username:password@localhost:5432/hedgedoc`, `mysql://username:password@localhost:3306/hedgedoc`, or `sqlite:///path/to/hedgedoc.db` | Set the db in URL style. If set, then the relevant `db` config entries will be overridden. |
|
| `dbURL` | `CMD_DB_URL` | **`undefined`**, `postgres://username:password@localhost:5432/hedgedoc`, `mysql://username:password@localhost:3306/hedgedoc`, or `sqlite:///path/to/hedgedoc.db` | Set the db in URL style. If set, then the relevant `db` config entries will be overridden. |
|
||||||
| `loglevel` | `CMD_LOGLEVEL` | **`info`**, `debug` ... | Defines what kind of logs are provided to stdout. Available options: `debug`, `verbose`, `info`, `warn`, `error` |
|
| `loglevel` | `CMD_LOGLEVEL` | **`info`**, `debug` ... | Defines what kind of logs are provided to stdout. Available options: `debug`, `verbose`, `info`, `warn`, `error` |
|
||||||
| `forbiddenNoteIDs` | `CMD_FORBIDDEN_NOTE_IDS` | **`['robots.txt', 'favicon.ico', 'api', 'build', 'css', 'docs', 'fonts', 'js', 'uploads', 'vendor', 'views']`**, `['robots.txt']` or `'robots.txt'` | disallow creation of notes, even if `allowFreeUrl` or `CMD_ALLOW_FREEURL` is `true` |
|
| `forbiddenNoteIDs` | `CMD_FORBIDDEN_NOTE_IDS` | **`['robots.txt', 'favicon.ico', 'api', 'build', 'css', 'docs', 'fonts', 'js', 'uploads', 'vendor', 'views']`**, `['robots.txt']` or `'robots.txt'` | disallow creation of notes, even if `allowFreeUrl` or `CMD_ALLOW_FREEURL` is `true` |
|
||||||
|
| `externalLinkWhitelist` | `CMD_EXTERNAL_LINK_WHITELIST` | **`[]`**, `example.com,trusted.org` | Comma-separated list of domains (and their subdomains) that bypass the external link warning page. Links to these domains open directly without the `/_link` redirect. |
|
||||||
| `imageUploadType` | `CMD_IMAGE_UPLOAD_TYPE` | **`filesystem`**, `imgur`, `s3`, `minio`, `azure`, `lutim` | Where to upload images. For S3, see our Image Upload Guides for [S3](guides/media/s3-image-upload.md) or [Minio](guides/media/minio-image-upload.md), also there's a whole section on their respective env vars below. |
|
| `imageUploadType` | `CMD_IMAGE_UPLOAD_TYPE` | **`filesystem`**, `imgur`, `s3`, `minio`, `azure`, `lutim` | Where to upload images. For S3, see our Image Upload Guides for [S3](guides/media/s3-image-upload.md) or [Minio](guides/media/minio-image-upload.md), also there's a whole section on their respective env vars below. |
|
||||||
| `sourceURL` | `CMD_SOURCE_URL` | **no default**, `https://github.com/hedgedoc/hedgedoc/tree/<current commit>` | Provides the link to the source code of HedgeDoc on the entry page (Please, make sure you change this when you run a modified version) |
|
| `sourceURL` | `CMD_SOURCE_URL` | **no default**, `https://github.com/hedgedoc/hedgedoc/tree/<current commit>` | Provides the link to the source code of HedgeDoc on the entry page (Please, make sure you change this when you run a modified version) |
|
||||||
| `tooBusyLag` | `CMD_TOOBUSY_LAG` | **`70`** | CPU time for one event loop tick until node throttles connections. (milliseconds) |
|
| `tooBusyLag` | `CMD_TOOBUSY_LAG` | **`70`** | CPU time for one event loop tick until node throttles connections. (milliseconds) |
|
||||||
|
|||||||
@@ -192,5 +192,6 @@ module.exports = {
|
|||||||
// 2nd appearance: "31-good-morning-my-friend---do-you-have-5-1"
|
// 2nd appearance: "31-good-morning-my-friend---do-you-have-5-1"
|
||||||
// 3rd appearance: "31-good-morning-my-friend---do-you-have-5-2"
|
// 3rd appearance: "31-good-morning-my-friend---do-you-have-5-2"
|
||||||
linkifyHeaderStyle: 'keep-case',
|
linkifyHeaderStyle: 'keep-case',
|
||||||
enableStatsApi: true
|
enableStatsApi: true,
|
||||||
|
externalLinkWhitelist: []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,5 +166,6 @@ module.exports = {
|
|||||||
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
|
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
|
||||||
openID: toBooleanConfig(process.env.CMD_OPENID),
|
openID: toBooleanConfig(process.env.CMD_OPENID),
|
||||||
linkifyHeaderStyle: process.env.CMD_LINKIFY_HEADER_STYLE,
|
linkifyHeaderStyle: process.env.CMD_LINKIFY_HEADER_STYLE,
|
||||||
enableStatsApi: toBooleanConfig(process.env.CMD_ENABLE_STATS_API)
|
enableStatsApi: toBooleanConfig(process.env.CMD_ENABLE_STATS_API),
|
||||||
|
externalLinkWhitelist: toArrayConfig(process.env.CMD_EXTERNAL_LINK_WHITELIST)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ statusRouter.get('/config', function (req, res) {
|
|||||||
linkifyHeaderStyle: config.linkifyHeaderStyle,
|
linkifyHeaderStyle: config.linkifyHeaderStyle,
|
||||||
cookiePolicy: config.cookiePolicy,
|
cookiePolicy: config.cookiePolicy,
|
||||||
enableUploads: config.enableUploads,
|
enableUploads: config.enableUploads,
|
||||||
|
externalLinkWhitelist: config.externalLinkWhitelist,
|
||||||
userToken: req.user ? req.user.deleteToken : ''
|
userToken: req.user ? req.user.deleteToken : ''
|
||||||
}
|
}
|
||||||
res.set({
|
res.set({
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
## <i class="fa fa-tag"></i> 1.x.x <i class="fa fa-calendar-o"></i> UNRELEASED
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
- Added external link whitelist setting (`externalLinkWhitelist` in config.json or `CMD_EXTERNAL_LINK_WHITELIST`) to skip warning page for certain domains
|
||||||
|
|
||||||
## <i class="fa fa-tag"></i> 1.11.0 <i class="fa fa-calendar-o"></i> 2026-06-18
|
## <i class="fa fa-tag"></i> 1.11.0 <i class="fa fa-calendar-o"></i> 2026-06-18
|
||||||
|
|
||||||
### Security fixes
|
### Security fixes
|
||||||
|
|||||||
@@ -592,6 +592,7 @@ window.postProcess = postProcess
|
|||||||
|
|
||||||
// rewrite external links to go through the /_link warning page
|
// rewrite external links to go through the /_link warning page
|
||||||
export function rewriteExternalLinks (view) {
|
export function rewriteExternalLinks (view) {
|
||||||
|
const whitelist = window.externalLinkWhitelist || []
|
||||||
view.find('a[href]').each((index, linkTag) => {
|
view.find('a[href]').each((index, linkTag) => {
|
||||||
const anchor = $(linkTag)
|
const anchor = $(linkTag)
|
||||||
const href = anchor.attr('href')
|
const href = anchor.attr('href')
|
||||||
@@ -608,6 +609,14 @@ export function rewriteExternalLinks (view) {
|
|||||||
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.origin === window.location.origin) {
|
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.origin === window.location.origin) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// skip rewriting for whitelisted domains (exact match or subdomain, case-insensitive)
|
||||||
|
const hostname = parsed.hostname.toLowerCase()
|
||||||
|
if (whitelist.some(domain => {
|
||||||
|
const lowercaseDomain = domain.toLowerCase()
|
||||||
|
return hostname === lowercaseDomain || hostname.endsWith('.' + lowercaseDomain)
|
||||||
|
})) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const noteURL = window.location.pathname.split('/').pop()
|
const noteURL = window.location.pathname.split('/').pop()
|
||||||
anchor.attr('href', `${window.location.origin}/_link?url=${encodeURIComponent(parsed.href)}¬e=${noteURL}`)
|
anchor.attr('href', `${window.location.origin}/_link?url=${encodeURIComponent(parsed.href)}¬e=${noteURL}`)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>
|
|||||||
|
|
||||||
window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'
|
window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'
|
||||||
|
|
||||||
|
window.externalLinkWhitelist = <%- JSON.stringify(externalLinkWhitelist) %>
|
||||||
|
|
||||||
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
|
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
|
||||||
|
|
||||||
window.cookiePolicy = '<%- cookiePolicy %>'
|
window.cookiePolicy = '<%- cookiePolicy %>'
|
||||||
|
|||||||
Reference in New Issue
Block a user