hedgeagent/1.12.0 #1

Open
sa6anw wants to merge 201 commits from hedgeagent/1.12.0 into hedgeagent/1.10.5
8 changed files with 125 additions and 7 deletions
Showing only changes of commit 814271dc0b - Show all commits

28
lib/links.js Normal file
View File

@@ -0,0 +1,28 @@
module.exports = {
serveLinkWarningPage: function (req, res) {
let targetURL = typeof req.query.url === 'string' ? req.query.url : ''
let noteURL = typeof req.query.note === 'string' ? req.query.note : ''
if (noteURL !== '' && !/^[\w-]+$/.test(noteURL)) {
noteURL = ''
}
let valid = false
try {
targetURL = decodeURIComponent(targetURL)
const parsed = new URL(targetURL)
valid = ['http:', 'https:'].includes(parsed.protocol)
targetURL = parsed.href
} catch (err) {
valid = false
}
res.set({
'Cache-Control': 'no-store'
})
res.render('link.ejs', {
title: 'External link',
valid,
noteURL,
targetURL,
opengraph: []
})
}
}

View File

@@ -8,6 +8,8 @@ const baseRouter = module.exports = Router()
const errors = require('../errors') const errors = require('../errors')
const links = require('../links')
// get index // get index
baseRouter.get('/', response.showIndex) baseRouter.get('/', response.showIndex)
// get 403 forbidden // get 403 forbidden
@@ -22,3 +24,5 @@ baseRouter.get('/404', function (req, res) {
baseRouter.get('/500', function (req, res) { baseRouter.get('/500', function (req, res) {
errors.errorInternalError(res) errors.errorInternalError(res)
}) })
// get external link warning page
baseRouter.get('/_link', links.serveLinkWarningPage)

View File

@@ -126,5 +126,13 @@
"Protected - Only owner can edit (forbid guests)": "Geschützt - Nur Besitzer kann bearbeiten (Gäste verboten)", "Protected - Only owner can edit (forbid guests)": "Geschützt - Nur Besitzer kann bearbeiten (Gäste verboten)",
"Private - Only owner can view & edit": "Privat - Nur Besitzer kann anschauen & bearbeiten", "Private - Only owner can view & edit": "Privat - Nur Besitzer kann anschauen & bearbeiten",
"changed": "verändert", "changed": "verändert",
"created": "erstellt" "created": "erstellt",
"External link": "Externer Link",
"You've clicked a link that points to an external page.": "Es wurde ein Link zu einer externen Seite angeklickt.",
"The content of that page is unrelated to HedgeDoc. Do not enter your HedgeDoc credentials there.": "Der Inhalt dieser Seite ist unabhängig von HedgeDoc. Dort keine HedgeDoc-Zugangsdaten eingeben.",
"Do you want to continue?": "Fortfahren?",
"Continue to external page": "Weiter zur externen Seite",
"Back to your note": "Zurück zur Notiz",
"Invalid link": "Ungültiger Link",
"The provided link is not a valid URL.": "Die angegebene URL ist nicht gültig."
} }

View File

@@ -126,5 +126,13 @@
"Protected - Only owner can edit (forbid guests)": "Protected - Only owner can edit (forbid guests)", "Protected - Only owner can edit (forbid guests)": "Protected - Only owner can edit (forbid guests)",
"Private - Only owner can view & edit": "Private - Only owner can view & edit", "Private - Only owner can view & edit": "Private - Only owner can view & edit",
"changed": "changed", "changed": "changed",
"created": "created" "created": "created",
"You've clicked a link that points to an external page.": "You've clicked a link that points to an external page.",
"External link": "External link",
"Invalid link": "Invalid link",
"Do you want to continue?": "Do you want to continue?",
"Continue to external page": "Continue to external page",
"Back to your note": "Back to your note",
"The provided link is not a valid URL.": "The provided link is not a valid URL.",
"The content of that page is unrelated to HedgeDoc. Do not enter your HedgeDoc credentials there.": "The content of that page is unrelated to HedgeDoc. Do not enter your HedgeDoc credentials there."
} }

View File

@@ -1,5 +1,11 @@
# Release Notes # Release Notes
## <i class="fa fa-tag"></i> 1.x <i class="fa fa-calendar-o"></i> UNRELEASED
### Enhancements
- Added a warning page when clicking external links
## <i class="fa fa-tag"></i> 1.10.8 <i class="fa fa-calendar-o"></i> 2026-04-15 ## <i class="fa fa-tag"></i> 1.10.8 <i class="fa fa-calendar-o"></i> 2026-04-15
### Bugfixes ### Bugfixes

View File

@@ -557,10 +557,6 @@ export function postProcess (code) {
html = html.replace(/@import url\(([^)]*)\);?/gi, '') html = html.replace(/@import url\(([^)]*)\);?/gi, '')
$(value).html(html) $(value).html(html)
}) })
// link should open in new window or tab
// also add noopener to prevent clickjacking
// See details: https://mathiasbynens.github.io/rel-noopener/
result.find('a:not([href^="#"]):not([target])').attr('target', '_blank').attr('rel', 'noopener')
// If it's hashtag link then make it base uri independent // If it's hashtag link then make it base uri independent
result.find('a[href^="#"]').each((index, linkTag) => { result.find('a[href^="#"]').each((index, linkTag) => {
@@ -568,6 +564,7 @@ export function postProcess (code) {
currentLocation.hash = linkTag.hash currentLocation.hash = linkTag.hash
linkTag.href = currentLocation.toString() linkTag.href = currentLocation.toString()
}) })
rewriteExternalLinks(result)
// update continue line numbers // update continue line numbers
const linenumberdivs = result.find('.gutter.linenumber').toArray() const linenumberdivs = result.find('.gutter.linenumber').toArray()
@@ -593,6 +590,30 @@ export function postProcess (code) {
} }
window.postProcess = postProcess window.postProcess = postProcess
// rewrite external links to go through the /_link warning page
export function rewriteExternalLinks (view) {
view.find('a[href]').each((index, linkTag) => {
const anchor = $(linkTag)
const href = anchor.attr('href')
if (!href || href.startsWith('#')) {
return
}
let parsed
try {
parsed = new URL(href)
} catch (err) {
return
}
// only rewrite links that have an absolute http(s) URL on a different origin
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.origin === window.location.origin) {
return
}
const noteURL = window.location.pathname.split('/').pop()
anchor.attr('href', `${window.location.origin}/_link?url=${encodeURIComponent(parsed.href)}&note=${noteURL}`)
})
}
window.rewriteExternalLinks = rewriteExternalLinks
const domevents = Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function (i) { const domevents = Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function (i) {
return !i.indexOf('on') && (document[i] === null || typeof document[i] === 'function') return !i.indexOf('on') && (document[i] === null || typeof document[i] === 'function')
}).filter(function (elem, pos, self) { }).filter(function (elem, pos, self) {

View File

@@ -55,6 +55,7 @@ import {
renderTOC, renderTOC,
renderTags, renderTags,
renderTitle, renderTitle,
rewriteExternalLinks,
scrollToHash, scrollToHash,
smoothHashScroll, smoothHashScroll,
updateLastChange, updateLastChange,
@@ -3498,6 +3499,7 @@ function updateViewInner () {
} }
removeDOMEvents(ui.area.markdown) removeDOMEvents(ui.area.markdown)
finishView(ui.area.markdown) finishView(ui.area.markdown)
rewriteExternalLinks(ui.area.markdown)
autoLinkify(ui.area.markdown) autoLinkify(ui.area.markdown)
deduplicatedHeaderId(ui.area.markdown) deduplicatedHeaderId(ui.area.markdown)
renderTOC(ui.area.markdown) renderTOC(ui.area.markdown)

41
public/views/link.ejs Normal file
View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<%- include('hedgedoc/head') %>
<link rel="stylesheet" href="<%- serverURL %>/css/center.css">
</head>
<body>
<%- include('hedgedoc/header') %>
<div class="container-fluid text-center">
<div class="vertical-center-row">
<h1>
<% if (valid) { %>
<%= __('External link') %>
<% } else { %>
<%= __('Invalid link') %>
<% } %>
</h1>
<p>
<%= __('You\'ve clicked a link that points to an external page.') %>
<br>
<%= __('The content of that page is unrelated to HedgeDoc. Do not enter your HedgeDoc credentials there.') %>
</p>
<% if (valid) { %>
<p><code class="text-break"><%- targetURL %></code></p>
<p><%= __('Do you want to continue?') %></p>
<p>
<a class="btn btn-primary" href="<%- targetURL %>"><%= __('Continue to external page') %></a>
<a class="btn btn-default" href="<%- serverURL %>/<%- noteURL %>"><%= __('Back to your note') %></a>
</p>
<% } else { %>
<p class="text-danger"><strong><%= __('The provided link is not a valid URL.') %></strong></p>
<p>
<a class="btn btn-default" href="<%- serverURL %>/<%- noteURL %>"><%= __('Back to your note') %></a>
</p>
<% } %>
</div>
</div>
</body>
</html>