Skip to content

Commit 7da4841

Browse files
authored
Merge pull request #7406 from LibreSign/backport/7405/stable32
[stable32] fix: prevent Imagick crash caused by invalid signature box dimensions
2 parents 2440ec3 + 426fd70 commit 7da4841

11 files changed

Lines changed: 200 additions & 15 deletions

File tree

lib/Controller/AdminController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,8 @@ public function signatureBackgroundDelete(): DataResponse {
451451
* @param string $template Template to signature text
452452
* @param float $templateFontSize Font size used when print the parsed text of this template at PDF file
453453
* @param float $signatureFontSize Font size used when the signature mode is SIGNAME_AND_DESCRIPTION
454-
* @param float $signatureWidth Signature width
455-
* @param float $signatureHeight Signature height
454+
* @param float $signatureWidth Signature box width, minimum 1
455+
* @param float $signatureHeight Signature box height, minimum 1
456456
* @param string $renderMode Signature render mode
457457
* @return DataResponse<Http::STATUS_OK, LibresignSignatureTextSettingsResponse, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, LibresignErrorResponse, array{}>
458458
*
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Libresign\Migration;
11+
12+
use Closure;
13+
use OCA\Libresign\AppInfo\Application;
14+
use OCA\Libresign\Service\SignatureTextService;
15+
use OCP\DB\ISchemaWrapper;
16+
use OCP\IAppConfig;
17+
use OCP\Migration\IOutput;
18+
use OCP\Migration\SimpleMigrationStep;
19+
20+
class Version17003Date20260404000000 extends SimpleMigrationStep {
21+
public function __construct(
22+
private IAppConfig $appConfig,
23+
) {
24+
}
25+
26+
/**
27+
* @param IOutput $output
28+
* @param Closure(): ISchemaWrapper $schemaClosure
29+
* @param array $options
30+
*/
31+
#[\Override]
32+
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
33+
$this->sanitizeDimension(
34+
$output,
35+
'signature_width',
36+
SignatureTextService::DEFAULT_SIGNATURE_WIDTH,
37+
);
38+
$this->sanitizeDimension(
39+
$output,
40+
'signature_height',
41+
SignatureTextService::DEFAULT_SIGNATURE_HEIGHT,
42+
);
43+
}
44+
45+
private function sanitizeDimension(IOutput $output, string $key, float $default): void {
46+
$stored = $this->appConfig->getValueFloat(Application::APP_ID, $key, $default);
47+
if (is_finite($stored) && $stored >= SignatureTextService::SIGNATURE_DIMENSION_MINIMUM) {
48+
return;
49+
}
50+
51+
$this->appConfig->setValueFloat(Application::APP_ID, $key, $default);
52+
$output->warning(sprintf(
53+
'Restored invalid "%s" value "%s" to default "%s".',
54+
$key,
55+
(string)$stored,
56+
(string)$default,
57+
));
58+
}
59+
}

lib/Service/SignatureTextService.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
class SignatureTextService {
3030
public const TEMPLATE_DEFAULT_FONT_SIZE = 10;
3131
public const SIGNATURE_DEFAULT_FONT_SIZE = 20;
32+
public const SIGNATURE_DIMENSION_MINIMUM = 1;
3233
public const FONT_SIZE_MINIMUM = 0.1;
3334
public const FRONT_SIZE_MAX = 30;
3435
public const DEFAULT_SIGNATURE_WIDTH = 350;
@@ -69,6 +70,19 @@ public function save(
6970
// within the accepted range.
7071
throw new LibresignException($this->l10n->t('Invalid signature font size. The value must be between %.1f and %.0f.', [self::FONT_SIZE_MINIMUM, self::FRONT_SIZE_MAX]));
7172
}
73+
if (
74+
!is_finite($signatureWidth)
75+
|| !is_finite($signatureHeight)
76+
|| $signatureWidth < self::SIGNATURE_DIMENSION_MINIMUM
77+
|| $signatureHeight < self::SIGNATURE_DIMENSION_MINIMUM
78+
) {
79+
// TRANSLATORS This message is shown when the visible signature box size
80+
// configured by the admin is invalid. "Signature box" is the rectangular
81+
// area reserved for the handwritten-style signature image in the signed
82+
// PDF. "Width" and "height" are its pixel dimensions. %.0f is the
83+
// minimum allowed value for each dimension.
84+
throw new LibresignException($this->l10n->t('Invalid signature box size. Width and height must be at least %.0f.', [self::SIGNATURE_DIMENSION_MINIMUM]));
85+
}
7286
$template = trim($template);
7387
$template = preg_replace(
7488
[
@@ -432,11 +446,11 @@ public function getDefaultTemplate(): string {
432446
}
433447

434448
public function getFullSignatureWidth(): float {
435-
return $this->appConfig->getValueFloat(Application::APP_ID, 'signature_width', self::DEFAULT_SIGNATURE_WIDTH);
449+
return $this->getSanitizedDimension('signature_width', self::DEFAULT_SIGNATURE_WIDTH);
436450
}
437451

438452
public function getFullSignatureHeight(): float {
439-
return $this->appConfig->getValueFloat(Application::APP_ID, 'signature_height', self::DEFAULT_SIGNATURE_HEIGHT);
453+
return $this->getSanitizedDimension('signature_height', self::DEFAULT_SIGNATURE_HEIGHT);
440454
}
441455

442456
public function getSignatureWidth(): float {
@@ -448,7 +462,21 @@ public function getSignatureWidth(): float {
448462
}
449463

450464
public function getSignatureHeight(): float {
451-
return $this->appConfig->getValueFloat(Application::APP_ID, 'signature_height', self::DEFAULT_SIGNATURE_HEIGHT);
465+
return $this->getFullSignatureHeight();
466+
}
467+
468+
private function getSanitizedDimension(string $key, float $default): float {
469+
$value = $this->appConfig->getValueFloat(Application::APP_ID, $key, $default);
470+
if (!is_finite($value) || $value < self::SIGNATURE_DIMENSION_MINIMUM) {
471+
$this->appConfig->setValueFloat(Application::APP_ID, $key, $default);
472+
$this->logger->warning('Invalid signature dimension found in app config. Falling back to default.', [
473+
'key' => $key,
474+
'value' => $value,
475+
'default' => $default,
476+
]);
477+
return $default;
478+
}
479+
return $value;
452480
}
453481

454482
public function getTemplateFontSize(): float {

openapi-administration.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,13 +1820,13 @@
18201820
"type": "number",
18211821
"format": "double",
18221822
"default": 350,
1823-
"description": "Signature width"
1823+
"description": "Signature box width, minimum 1"
18241824
},
18251825
"signatureHeight": {
18261826
"type": "number",
18271827
"format": "double",
18281828
"default": 100,
1829-
"description": "Signature height"
1829+
"description": "Signature box height, minimum 1"
18301830
},
18311831
"renderMode": {
18321832
"type": "string",

openapi-full.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11080,13 +11080,13 @@
1108011080
"type": "number",
1108111081
"format": "double",
1108211082
"default": 350,
11083-
"description": "Signature width"
11083+
"description": "Signature box width, minimum 1"
1108411084
},
1108511085
"signatureHeight": {
1108611086
"type": "number",
1108711087
"format": "double",
1108811088
"default": 100,
11089-
"description": "Signature height"
11089+
"description": "Signature box height, minimum 1"
1109011090
},
1109111091
"renderMode": {
1109211092
"type": "string",

src/tests/views/Settings/SignatureStamp.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,4 +316,41 @@ describe('SignatureStamp.vue', () => {
316316
expect(wrapper.vm.signatureWidth).toBe(180)
317317
expect(wrapper.vm.signatureHeight).toBe(90)
318318
})
319+
320+
it('regression: signature dimension fields bind min="1" matching the backend SIGNATURE_DIMENSION_MINIMUM', () => {
321+
const wrapper = createWrapper()
322+
323+
const textFields = wrapper.findAllComponents({ name: 'NcTextField' })
324+
const widthField = textFields.find(c => c.props('label') === 'Default signature width')
325+
const heightField = textFields.find(c => c.props('label') === 'Default signature height')
326+
327+
expect(widthField).toBeDefined()
328+
expect(heightField).toBeDefined()
329+
expect(widthField!.attributes('min')).toBe('1')
330+
expect(heightField!.attributes('min')).toBe('1')
331+
})
332+
333+
it('regression: shows API error when saveTemplate receives a 400 for invalid signature box size', async () => {
334+
axiosPostMock.mockRejectedValue({
335+
response: {
336+
data: {
337+
ocs: {
338+
data: {
339+
error: 'Invalid signature box size. Width and height must be at least 1.',
340+
},
341+
},
342+
},
343+
},
344+
})
345+
const wrapper = createWrapper()
346+
347+
await wrapper.vm.saveTemplate()
348+
await flushPromises()
349+
350+
expect(wrapper.vm.errorMessageTemplate).toContain(
351+
'Invalid signature box size. Width and height must be at least 1.',
352+
)
353+
expect(wrapper.vm.parsed).toBe('')
354+
expect(wrapper.vm.templateSaved).toBe(false)
355+
})
319356
})

src/types/openapi/openapi-administration.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,13 +1195,13 @@ export interface operations {
11951195
signatureFontSize?: number;
11961196
/**
11971197
* Format: double
1198-
* @description Signature width
1198+
* @description Signature box width, minimum 1
11991199
* @default 350
12001200
*/
12011201
signatureWidth?: number;
12021202
/**
12031203
* Format: double
1204-
* @description Signature height
1204+
* @description Signature box height, minimum 1
12051205
* @default 100
12061206
*/
12071207
signatureHeight?: number;

src/types/openapi/openapi-full.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5946,13 +5946,13 @@ export interface operations {
59465946
signatureFontSize?: number;
59475947
/**
59485948
* Format: double
5949-
* @description Signature width
5949+
* @description Signature box width, minimum 1
59505950
* @default 350
59515951
*/
59525952
signatureWidth?: number;
59535953
/**
59545954
* Format: double
5955-
* @description Signature height
5955+
* @description Signature box height, minimum 1
59565956
* @default 100
59575957
*/
59585958
signatureHeight?: number;

src/views/Settings/SignatureStamp.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136
:label="t('libresign', 'Default signature width')"
137137
:placeholder="t('libresign', 'Default signature width')"
138138
type="number"
139-
:min="0.1"
139+
:min="1"
140140
:max="800"
141141
:step="0.01"
142142
:spellcheck="false"
@@ -157,7 +157,7 @@
157157
:label="t('libresign', 'Default signature height')"
158158
:placeholder="t('libresign', 'Default signature height')"
159159
type="number"
160-
:min="0.1"
160+
:min="1"
161161
:max="800"
162162
:step="0.01"
163163
:spellcheck="false"

tests/php/Unit/Handler/SignEngine/JSignPdfHandlerTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ public function testSignAffectedParams(
289289
$paramsAsOptions = preg_replace('/\\/\S+_text_image.png/', 'text_image.png', (string)$paramsAsOptions);
290290
$paramsAsOptions = preg_replace('/\\/\S+_background.png/', 'background.png', (string)$paramsAsOptions);
291291
$paramsAsOptions = preg_replace('/\\/\S+app-dark.png/', 'signature.png', (string)$paramsAsOptions);
292+
$paramsAsOptions = preg_replace('/ --tsa-server-url\s+\S+/', '', (string)$paramsAsOptions);
292293
$this->assertEquals($params, $paramsAsOptions);
293294
}
294295

@@ -507,6 +508,24 @@ public static function providerSignAffectedParams(): array {
507508
'hashAlgorithm' => '',
508509
'params' => '-a -kst PKCS12 --hash-algorithm SHA256 --l2-text "" -V -pg 2 -llx 10 -lly 20 -urx 30 -ury 40 --bg-path merged.png'
509510
],
511+
'regression: invalid stored dimensions should fallback to defaults and keep signing flow' => [
512+
'visibleElements' => [self::getElement([
513+
'page' => 2,
514+
'llx' => 10,
515+
'lly' => 20,
516+
'urx' => 30,
517+
'ury' => 40,
518+
], realpath(__DIR__ . '/../../../../../img/app-dark.png'))],
519+
'signatureWidth' => 0,
520+
'signatureHeight' => 0,
521+
'template' => '',
522+
'signatureBackgroundType' => 'default',
523+
'renderMode' => SignerElementsService::RENDER_MODE_GRAPHIC_AND_DESCRIPTION,
524+
'templateFontSize' => 10,
525+
'pdfContent' => '%PDF-1.6',
526+
'hashAlgorithm' => '',
527+
'params' => '-a -kst PKCS12 --hash-algorithm SHA256 --l2-text "" -V -pg 2 -llx 10 -lly 20 -urx 30 -ury 40 --bg-path merged.png'
528+
],
510529
];
511530
}
512531

0 commit comments

Comments
 (0)