hedgeagent/1.12.0 #1
28
lib/links.js
Normal file
28
lib/links.js
Normal 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: []
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ const baseRouter = module.exports = Router()
|
||||
|
||||
const errors = require('../errors')
|
||||
|
||||
const links = require('../links')
|
||||
|
||||
// get index
|
||||
baseRouter.get('/', response.showIndex)
|
||||
// get 403 forbidden
|
||||
@@ -22,3 +24,5 @@ baseRouter.get('/404', function (req, res) {
|
||||
baseRouter.get('/500', function (req, res) {
|
||||
errors.errorInternalError(res)
|
||||
})
|
||||
// get external link warning page
|
||||
baseRouter.get('/_link', links.serveLinkWarningPage)
|
||||
|
||||
@@ -126,5 +126,13 @@
|
||||
"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",
|
||||
"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."
|
||||
}
|
||||
@@ -126,5 +126,13 @@
|
||||
"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",
|
||||
"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."
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# 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
|
||||
|
||||
### Bugfixes
|
||||
|
||||
@@ -557,10 +557,6 @@ export function postProcess (code) {
|
||||
html = html.replace(/@import url\(([^)]*)\);?/gi, '')
|
||||
$(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
|
||||
result.find('a[href^="#"]').each((index, linkTag) => {
|
||||
@@ -568,6 +564,7 @@ export function postProcess (code) {
|
||||
currentLocation.hash = linkTag.hash
|
||||
linkTag.href = currentLocation.toString()
|
||||
})
|
||||
rewriteExternalLinks(result)
|
||||
|
||||
// update continue line numbers
|
||||
const linenumberdivs = result.find('.gutter.linenumber').toArray()
|
||||
@@ -593,6 +590,30 @@ export function postProcess (code) {
|
||||
}
|
||||
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)}¬e=${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) {
|
||||
return !i.indexOf('on') && (document[i] === null || typeof document[i] === 'function')
|
||||
}).filter(function (elem, pos, self) {
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
renderTOC,
|
||||
renderTags,
|
||||
renderTitle,
|
||||
rewriteExternalLinks,
|
||||
scrollToHash,
|
||||
smoothHashScroll,
|
||||
updateLastChange,
|
||||
@@ -3498,6 +3499,7 @@ function updateViewInner () {
|
||||
}
|
||||
removeDOMEvents(ui.area.markdown)
|
||||
finishView(ui.area.markdown)
|
||||
rewriteExternalLinks(ui.area.markdown)
|
||||
autoLinkify(ui.area.markdown)
|
||||
deduplicatedHeaderId(ui.area.markdown)
|
||||
renderTOC(ui.area.markdown)
|
||||
|
||||
41
public/views/link.ejs
Normal file
41
public/views/link.ejs
Normal 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>
|
||||
Reference in New Issue
Block a user