Release 2.58 (#5952)

* bump version
* fix formatting locale reset after embedded controller sub-requests (#5944)
* fix GHSA-c6w6-57jj-62vh
* fix GHSA-m492-gv72-xvxj
* fix GHSA-jr9p-4h4j-6c58
* make sure to only use JS logic to call API endpoints
* fixes GHSA-r8vr-m544-qh4h
* make sure to only use JS logic to call API endpoints
* fix GHSA-rw46-qg69-vg6h
* fix GHSA-pj8j-p4g4-4vw8 - prevent kimai from rendering images via markdown
* fix GHSA-pj8j-p4g4-4vw8 - use a safe network client to prevent SSRF via images
* fix GHSA-xv4r-4885-gwpg
* fix GHSA-pgcc-vfmc-7cw5 - move GET routes to API with POST method to prevent CSRF
* fix tooltip survives page reload
* updated wizard images
* split wizard and password reset subscriber into two classes
* relax upper php limit
* added zizmor workflow scans and apply findings
* user permissions <name>_other_profile  now respect teams
* move all linting steps to new job
* updated docker image version names
* use .env.local for storing APP_SECRET
* improve build order and use given tag as ref for checkout, not default main branch
* improved APP_SECRET handling, see entrypoint.sh
* use local code for building the image for more flexibility, added dockerignore
This commit is contained in:
Kevin Papst
2026-05-25 15:39:47 +02:00
committed by GitHub
parent 8d245ae223
commit 31a8f887a5
85 changed files with 2737 additions and 472 deletions

View File

@@ -15,7 +15,7 @@ namespace App\Utils;
final class Markdown
{
private ?ParsedownExtension $parser = null;
private ?\Parsedown $parserFull = null;
private ?Parsedown $parserFull = null;
public function toHtml(string $text): string
{

View File

@@ -17,6 +17,20 @@ class Parsedown extends \Parsedown
/** @var array<string> */
private array $ids = [];
/**
* Overwritten to open links in new windows
*/
protected function inlineUrl($Excerpt): ?array // @phpstan-ignore missingType.parameter,missingType.iterableValue
{
$block = parent::inlineUrl($Excerpt);
if (isset($block['element']['attributes']) && \is_array($block['element']['attributes'])) {
$block['element']['attributes']['target'] = '_blank';
}
return $block;
}
protected function blockHeader($Line)
{
$block = parent::blockHeader($Line);
@@ -84,4 +98,42 @@ class Parsedown extends \Parsedown
return $Block;
}
/**
* Markdown image syntax `![alt](url)` is rewritten to a link `<a href="url">alt</a>`.
*
* Rationale: emitting `<img src="url">` would cause downstream renderers
* (e.g. mPDF on the server, browsers in the UI) to automatically fetch
* the remote URL. For server-side renderers this is a server-side request
* forgery vector; in the UI it is a tracking/privacy issue. Hand-written
* `<img>` in Twig templates (custom invoice templates etc.) is not
* affected — only images derived from Markdown input are neutralised
* here. The resulting `<a href>` is still passed through Parsedown's
* `safeLinksWhitelist` filtering when safe-mode is enabled.
*
* @see https://github.com/kimai/kimai/security/advisories/GHSA-pj8j-p4g4-4vw8
*/
protected function inlineImage($Excerpt): ?array // @phpstan-ignore missingType.parameter,missingType.iterableValue
{
$Image = parent::inlineImage($Excerpt);
if ($Image === null) {
return null;
}
$src = $Image['element']['attributes']['src'] ?? '';
$alt = $Image['element']['attributes']['alt'] ?? '';
$Image['element'] = [
'name' => 'a',
'text' => $alt !== '' ? $alt : $src,
'attributes' => [
'href' => $src,
'rel' => 'noopener noreferrer',
'target' => '_blank',
],
];
return $Image;
}
}

View File

@@ -10,7 +10,7 @@
namespace App\Utils;
/**
* This Class extends the default Parsedown Class for custom methods.
* The default markdown implementation.
*/
final class ParsedownExtension extends Parsedown
{
@@ -42,31 +42,4 @@ final class ParsedownExtension extends Parsedown
'|' => ['Table'],
'~' => ['FencedCode'],
];
/**
* Overwritten to open links in new windows
*/
protected function inlineUrl($Excerpt): ?array
{
$block = parent::inlineUrl($Excerpt);
if (isset($block['element']['attributes']) && \is_array($block['element']['attributes'])) {
$block['element']['attributes']['target'] = '_blank';
}
return $block;
}
protected function blockTable($Line, ?array $Block = null) // @phpstan-ignore missingType.return,missingType.iterableValue,missingType.parameter
{
$Block = parent::blockTable($Line, $Block);
if ($Block === null) {
return null;
}
$Block['element']['attributes']['class'] = 'table';
return $Block;
}
}