mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-07-27 03:31:35 +00:00
Properly HTML escape fields for label template placeholders
Fixes issue #1446
This commit is contained in:
parent
5054642d41
commit
dcc431d28c
10 changed files with 43 additions and 42 deletions
|
|
@ -67,8 +67,8 @@ class BarcodeHelper
|
||||||
{
|
{
|
||||||
$svg = $this->barcodeAsSVG($content, $type);
|
$svg = $this->barcodeAsSVG($content, $type);
|
||||||
$base64 = $this->dataUri($svg, 'image/svg+xml');
|
$base64 = $this->dataUri($svg, 'image/svg+xml');
|
||||||
$alt_text ??= $content;
|
$alt_text ??= htmlspecialchars($content);
|
||||||
|
|
||||||
return '<img src="'.$base64.'" width="'.$width.'" style="min-height: 25px;" alt="'.$alt_text.'"/>';
|
return '<img src="'.$base64.'" width="'.$width.'" style="min-height: 25px;" alt="'.$alt_text.'"/>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,4 +94,4 @@ class BarcodeHelper
|
||||||
|
|
||||||
return $repr;
|
return $repr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,9 @@ namespace App\Services\LabelSystem\PlaceholderProviders;
|
||||||
use App\Entity\Base\AbstractDBElement;
|
use App\Entity\Base\AbstractDBElement;
|
||||||
use App\Services\ElementTypeNameGenerator;
|
use App\Services\ElementTypeNameGenerator;
|
||||||
|
|
||||||
final class AbstractDBElementProvider implements PlaceholderProviderInterface
|
final readonly class AbstractDBElementProvider implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
public function __construct(private readonly ElementTypeNameGenerator $elementTypeNameGenerator)
|
public function __construct(private ElementTypeNameGenerator $elementTypeNameGenerator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ final class AbstractDBElementProvider implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
if ($label_target instanceof AbstractDBElement) {
|
if ($label_target instanceof AbstractDBElement) {
|
||||||
if ('[[TYPE]]' === $placeholder) {
|
if ('[[TYPE]]' === $placeholder) {
|
||||||
return $this->elementTypeNameGenerator->getLocalizedTypeLabel($label_target);
|
return $this->elementTypeNameGenerator->typeLabel($label_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[ID]]' === $placeholder) {
|
if ('[[ID]]' === $placeholder) {
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,11 @@ use App\Services\LabelSystem\LabelBarcodeGenerator;
|
||||||
use App\Services\LabelSystem\Barcodes\BarcodeContentGenerator;
|
use App\Services\LabelSystem\Barcodes\BarcodeContentGenerator;
|
||||||
use Com\Tecnick\Barcode\Exception;
|
use Com\Tecnick\Barcode\Exception;
|
||||||
|
|
||||||
final class BarcodeProvider implements PlaceholderProviderInterface
|
final readonly class BarcodeProvider implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
public function __construct(private readonly LabelBarcodeGenerator $barcodeGenerator,
|
public function __construct(private LabelBarcodeGenerator $barcodeGenerator,
|
||||||
private readonly BarcodeContentGenerator $barcodeContentGenerator,
|
private BarcodeContentGenerator $barcodeContentGenerator,
|
||||||
private readonly BarcodeHelper $barcodeHelper)
|
private BarcodeHelper $barcodeHelper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,11 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
* Provides Placeholders for infos about global infos like Installation name or datetimes.
|
* Provides Placeholders for infos about global infos like Installation name or datetimes.
|
||||||
* @see \App\Tests\Services\LabelSystem\PlaceholderProviders\GlobalProvidersTest
|
* @see \App\Tests\Services\LabelSystem\PlaceholderProviders\GlobalProvidersTest
|
||||||
*/
|
*/
|
||||||
final class GlobalProviders implements PlaceholderProviderInterface
|
final readonly class GlobalProviders implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Security $security,
|
private Security $security,
|
||||||
private readonly UrlGeneratorInterface $url_generator,
|
private UrlGeneratorInterface $url_generator,
|
||||||
private CustomizationSettings $customizationSettings,
|
private CustomizationSettings $customizationSettings,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -66,13 +66,13 @@ final class GlobalProviders implements PlaceholderProviderInterface
|
||||||
public function replace(string $placeholder, object $label_target, array $options = []): ?string
|
public function replace(string $placeholder, object $label_target, array $options = []): ?string
|
||||||
{
|
{
|
||||||
if ('[[INSTALL_NAME]]' === $placeholder) {
|
if ('[[INSTALL_NAME]]' === $placeholder) {
|
||||||
return $this->customizationSettings->instanceName;
|
return htmlspecialchars($this->customizationSettings->instanceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->security->getUser();
|
$user = $this->security->getUser();
|
||||||
if ('[[USERNAME]]' === $placeholder) {
|
if ('[[USERNAME]]' === $placeholder) {
|
||||||
if ($user instanceof User) {
|
if ($user instanceof User) {
|
||||||
return $user->getName();
|
return htmlspecialchars($user->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'anonymous';
|
return 'anonymous';
|
||||||
|
|
@ -80,7 +80,7 @@ final class GlobalProviders implements PlaceholderProviderInterface
|
||||||
|
|
||||||
if ('[[USERNAME_FULL]]' === $placeholder) {
|
if ('[[USERNAME_FULL]]' === $placeholder) {
|
||||||
if ($user instanceof User) {
|
if ($user instanceof User) {
|
||||||
return $user->getFullName(true);
|
return htmlspecialchars($user->getFullName(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'anonymous';
|
return 'anonymous';
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ final class NamedElementProvider implements PlaceholderProviderInterface
|
||||||
public function replace(string $placeholder, object $label_target, array $options = []): ?string
|
public function replace(string $placeholder, object $label_target, array $options = []): ?string
|
||||||
{
|
{
|
||||||
if ($label_target instanceof NamedElementInterface && '[[NAME]]' === $placeholder) {
|
if ($label_target instanceof NamedElementInterface && '[[NAME]]' === $placeholder) {
|
||||||
return $label_target->getName();
|
return htmlspecialchars($label_target->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,9 @@ use Locale;
|
||||||
/**
|
/**
|
||||||
* @see \App\Tests\Services\LabelSystem\PlaceholderProviders\PartLotProviderTest
|
* @see \App\Tests\Services\LabelSystem\PlaceholderProviders\PartLotProviderTest
|
||||||
*/
|
*/
|
||||||
final class PartLotProvider implements PlaceholderProviderInterface
|
final readonly class PartLotProvider implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
public function __construct(private readonly LabelTextReplacer $labelTextReplacer, private readonly AmountFormatter $amountFormatter)
|
public function __construct(private LabelTextReplacer $labelTextReplacer, private AmountFormatter $amountFormatter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,11 +66,11 @@ final class PartLotProvider implements PlaceholderProviderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[LOT_NAME]]' === $placeholder) {
|
if ('[[LOT_NAME]]' === $placeholder) {
|
||||||
return $label_target->getName();
|
return htmlspecialchars($label_target->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[LOT_COMMENT]]' === $placeholder) {
|
if ('[[LOT_COMMENT]]' === $placeholder) {
|
||||||
return $label_target->getComment();
|
return htmlspecialchars($label_target->getComment());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[EXPIRATION_DATE]]' === $placeholder) {
|
if ('[[EXPIRATION_DATE]]' === $placeholder) {
|
||||||
|
|
@ -95,19 +95,19 @@ final class PartLotProvider implements PlaceholderProviderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[LOCATION]]' === $placeholder) {
|
if ('[[LOCATION]]' === $placeholder) {
|
||||||
return $label_target->getStorageLocation() instanceof StorageLocation ? $label_target->getStorageLocation()->getName() : '';
|
return $label_target->getStorageLocation() instanceof StorageLocation ? htmlspecialchars($label_target->getStorageLocation()->getName()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[LOCATION_FULL]]' === $placeholder) {
|
if ('[[LOCATION_FULL]]' === $placeholder) {
|
||||||
return $label_target->getStorageLocation() instanceof StorageLocation ? $label_target->getStorageLocation()->getFullPath() : '';
|
return $label_target->getStorageLocation() instanceof StorageLocation ? htmlspecialchars($label_target->getStorageLocation()->getFullPath()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[OWNER]]' === $placeholder) {
|
if ('[[OWNER]]' === $placeholder) {
|
||||||
return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getFullName() : '';
|
return $label_target->getOwner() instanceof User ? htmlspecialchars($label_target->getOwner()->getFullName()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[OWNER_USERNAME]]' === $placeholder) {
|
if ('[[OWNER_USERNAME]]' === $placeholder) {
|
||||||
return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getUsername() : '';
|
return $label_target->getOwner() instanceof User ? htmlspecialchars($label_target->getOwner()->getUsername()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->labelTextReplacer->handlePlaceholder($placeholder, $label_target->getPart());
|
return $this->labelTextReplacer->handlePlaceholder($placeholder, $label_target->getPart());
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
/**
|
/**
|
||||||
* @see \App\Tests\Services\LabelSystem\PlaceholderProviders\PartProviderTest
|
* @see \App\Tests\Services\LabelSystem\PlaceholderProviders\PartProviderTest
|
||||||
*/
|
*/
|
||||||
final class PartProvider implements PlaceholderProviderInterface
|
final readonly class PartProvider implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
private readonly MarkdownConverter $inlineConverter;
|
private MarkdownConverter $inlineConverter;
|
||||||
|
|
||||||
public function __construct(private readonly SIFormatter $siFormatter, private readonly TranslatorInterface $translator)
|
public function __construct(private SIFormatter $siFormatter, private TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
$environment = new Environment();
|
$environment = new Environment();
|
||||||
$environment->addExtension(new InlinesOnlyExtension());
|
$environment->addExtension(new InlinesOnlyExtension());
|
||||||
|
|
@ -72,27 +72,27 @@ final class PartProvider implements PlaceholderProviderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[CATEGORY]]' === $placeholder) {
|
if ('[[CATEGORY]]' === $placeholder) {
|
||||||
return $part->getCategory() instanceof Category ? $part->getCategory()->getName() : '';
|
return $part->getCategory() instanceof Category ? htmlspecialchars($part->getCategory()->getName()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[CATEGORY_FULL]]' === $placeholder) {
|
if ('[[CATEGORY_FULL]]' === $placeholder) {
|
||||||
return $part->getCategory() instanceof Category ? $part->getCategory()->getFullPath() : '';
|
return $part->getCategory() instanceof Category ? htmlspecialchars($part->getCategory()->getFullPath()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[MANUFACTURER]]' === $placeholder) {
|
if ('[[MANUFACTURER]]' === $placeholder) {
|
||||||
return $part->getManufacturer() instanceof Manufacturer ? $part->getManufacturer()->getName() : '';
|
return $part->getManufacturer() instanceof Manufacturer ? htmlspecialchars($part->getManufacturer()->getName()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[MANUFACTURER_FULL]]' === $placeholder) {
|
if ('[[MANUFACTURER_FULL]]' === $placeholder) {
|
||||||
return $part->getManufacturer() instanceof Manufacturer ? $part->getManufacturer()->getFullPath() : '';
|
return $part->getManufacturer() instanceof Manufacturer ? htmlspecialchars($part->getManufacturer()->getFullPath()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[FOOTPRINT]]' === $placeholder) {
|
if ('[[FOOTPRINT]]' === $placeholder) {
|
||||||
return $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : '';
|
return $part->getFootprint() instanceof Footprint ? htmlspecialchars($part->getFootprint()->getName()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[FOOTPRINT_FULL]]' === $placeholder) {
|
if ('[[FOOTPRINT_FULL]]' === $placeholder) {
|
||||||
return $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getFullPath() : '';
|
return $part->getFootprint() instanceof Footprint ? htmlspecialchars($part->getFootprint()->getFullPath()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[MASS]]' === $placeholder) {
|
if ('[[MASS]]' === $placeholder) {
|
||||||
|
|
@ -100,15 +100,15 @@ final class PartProvider implements PlaceholderProviderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[MPN]]' === $placeholder) {
|
if ('[[MPN]]' === $placeholder) {
|
||||||
return $part->getManufacturerProductNumber();
|
return htmlspecialchars($part->getManufacturerProductNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[IPN]]' === $placeholder) {
|
if ('[[IPN]]' === $placeholder) {
|
||||||
return $part->getIpn() ?? '';
|
return $part->getIpn() ? htmlspecialchars($part->getIpn()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[TAGS]]' === $placeholder) {
|
if ('[[TAGS]]' === $placeholder) {
|
||||||
return $part->getTags();
|
return htmlspecialchars($part->getTags());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[M_STATUS]]' === $placeholder) {
|
if ('[[M_STATUS]]' === $placeholder) {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ interface PlaceholderProviderInterface
|
||||||
/**
|
/**
|
||||||
* Determines the real value of this placeholder.
|
* Determines the real value of this placeholder.
|
||||||
* If the placeholder is not supported, null must be returned.
|
* If the placeholder is not supported, null must be returned.
|
||||||
|
* The placeholder provider has to handle HTML escaping, as the output of this function will be used directly in the label template.
|
||||||
*
|
*
|
||||||
* @param string $placeholder The placeholder (e.g. "%%PLACEHOLDER%%") that should be replaced
|
* @param string $placeholder The placeholder (e.g. "%%PLACEHOLDER%%") that should be replaced
|
||||||
* @param object $label_target The object that is targeted by the label
|
* @param object $label_target The object that is targeted by the label
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,11 @@ class StorelocationProvider implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
if ($label_target instanceof StorageLocation) {
|
if ($label_target instanceof StorageLocation) {
|
||||||
if ('[[OWNER]]' === $placeholder) {
|
if ('[[OWNER]]' === $placeholder) {
|
||||||
return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getFullName() : '';
|
return $label_target->getOwner() instanceof User ? htmlspecialchars($label_target->getOwner()->getFullName()) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[OWNER_USERNAME]]' === $placeholder) {
|
if ('[[OWNER_USERNAME]]' === $placeholder) {
|
||||||
return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getUsername() : '';
|
return $label_target->getOwner() instanceof User ? htmlspecialchars($label_target->getOwner()->getUsername()) : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,13 @@ final class StructuralDBElementProvider implements PlaceholderProviderInterface
|
||||||
return strip_tags((string) $label_target->getComment());
|
return strip_tags((string) $label_target->getComment());
|
||||||
}
|
}
|
||||||
if ('[[FULL_PATH]]' === $placeholder) {
|
if ('[[FULL_PATH]]' === $placeholder) {
|
||||||
return $label_target->getFullPath();
|
return htmlspecialchars($label_target->getFullPath());
|
||||||
}
|
}
|
||||||
if ('[[PARENT]]' === $placeholder) {
|
if ('[[PARENT]]' === $placeholder) {
|
||||||
return $label_target->getParent() instanceof AbstractStructuralDBElement ? $label_target->getParent()->getName() : '';
|
return $label_target->getParent() instanceof AbstractStructuralDBElement ? htmlspecialchars($label_target->getParent()->getName()) : '';
|
||||||
}
|
}
|
||||||
if ('[[PARENT_FULL_PATH]]' === $placeholder) {
|
if ('[[PARENT_FULL_PATH]]' === $placeholder) {
|
||||||
return $label_target->getParent() instanceof AbstractStructuralDBElement ? $label_target->getParent()->getFullPath() : '';
|
return $label_target->getParent() instanceof AbstractStructuralDBElement ? htmlspecialchars($label_target->getParent()->getFullPath()) : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue