Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Controller/AiScanInvoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use FacturaScripts\Plugins\AiScan\Lib\ExtractionService;
use FacturaScripts\Plugins\AiScan\Lib\HistoricalContextService;
use FacturaScripts\Plugins\AiScan\Lib\InvoiceMapper;
use FacturaScripts\Plugins\AiScan\Lib\StoragePathHelper;
use FacturaScripts\Plugins\AiScan\Lib\SupplierMatcher;
use FacturaScripts\Plugins\AiScan\Model\AiScanImportBatch;
use FacturaScripts\Plugins\AiScan\Model\AiScanImportDocument;
Expand Down Expand Up @@ -259,7 +260,7 @@ private function storeUploadedFile(array $file, int $clientIndex): array
);
}

$tmpDir = FS_FOLDER . '/MyFiles/aiscan_tmp';
$tmpDir = StoragePathHelper::absoluteDirectory();
if (!is_dir($tmpDir)) {
mkdir($tmpDir, 0700, true);
}
Expand Down Expand Up @@ -341,7 +342,7 @@ private function handleAnalyze(): void
echo json_encode(['error' => Tools::lang()->trans('aiscan-invalid-file-name')]);
return;
}
$tmpPath = FS_FOLDER . '/MyFiles/aiscan_tmp/' . $tmpFile;
$tmpPath = StoragePathHelper::absoluteFile($tmpFile);

if (!file_exists($tmpPath)) {
http_response_code(404);
Expand Down Expand Up @@ -631,7 +632,7 @@ private function handleGetText(): void
echo json_encode(['error' => Tools::lang()->trans('aiscan-invalid-file-name')]);
return;
}
$tmpPath = FS_FOLDER . '/MyFiles/aiscan_tmp/' . $tmpFile;
$tmpPath = StoragePathHelper::absoluteFile($tmpFile);

if (!file_exists($tmpPath)) {
http_response_code(404);
Expand Down
6 changes: 3 additions & 3 deletions Lib/AttachmentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public function attachTemporaryFile(FacturaProveedor $invoice, array $uploadData
return;
}

$tmpDir = realpath(FS_FOLDER . '/MyFiles/aiscan_tmp');
$tmpPath = realpath(FS_FOLDER . '/MyFiles/aiscan_tmp/' . $tmpFile);
$tmpDir = realpath(StoragePathHelper::absoluteDirectory());
$tmpPath = realpath(StoragePathHelper::absoluteFile($tmpFile));
$prefix = false === $tmpDir ? '' : rtrim($tmpDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if (
false === $tmpDir
Expand All @@ -58,7 +58,7 @@ public function attachTemporaryFile(FacturaProveedor $invoice, array $uploadData
}

$attachedFile = new AttachedFile();
$attachedFile->path = 'aiscan_tmp/' . $tmpFile;
$attachedFile->path = StoragePathHelper::relativeFile($tmpFile);
if (false === $attachedFile->save()) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion Lib/ExtractionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ public static function extractPdfText(string $filePath): string
}

$realPath = realpath($filePath);
$expectedDir = realpath(FS_FOLDER . '/MyFiles/aiscan_tmp');
$expectedDir = realpath(StoragePathHelper::absoluteDirectory());
if ($realPath === false || $expectedDir === false || strpos($realPath, $expectedDir) !== 0) {
return '';
}
Expand Down
41 changes: 41 additions & 0 deletions Lib/StoragePathHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/**
* This file is part of AiScan plugin for FacturaScripts.
* Copyright (C) 2026 Ernesto Serrano <info@ernesto.es>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

namespace FacturaScripts\Plugins\AiScan\Lib;

final class StoragePathHelper
{
private const DIRECTORY = 'aiscan';

public static function absoluteDirectory(): string
{
return FS_FOLDER . '/MyFiles/' . self::DIRECTORY;
}

public static function absoluteFile(string $filename): string
{
return self::absoluteDirectory() . '/' . basename($filename);
}

public static function relativeFile(string $filename): string
{
return self::DIRECTORY . '/' . basename($filename);
}
}
6 changes: 3 additions & 3 deletions Test/main/AiScanInvoiceControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ public function testNormalizeUploadedFilesExpandsMultipleUploadShape(): void
$this->assertSame('image/png', $result[1]['type']);
}

public function testResolveMimeTypeFallsBackToExtensionForOctetStream(): void
public function testResolveMimeTypeFallsBackToExtensionForGenericMimeTypes(): void
{
$controller = $this->buildController();
$tmpFile = tempnam(sys_get_temp_dir(), 'aiscan-octet-');
$tmpFile = tempnam(sys_get_temp_dir(), 'aiscan-generic-');
if (false === $tmpFile) {
self::fail('Failed to create temporary file for MIME fallback test.');
}

file_put_contents($tmpFile, random_bytes(32));
file_put_contents($tmpFile, 'This is plain text content without PDF structure.');

try {
$result = $this->callResolveMimeType($controller, $tmpFile, 'pdf');
Expand Down
41 changes: 41 additions & 0 deletions Test/main/StoragePathHelperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/**
* This file is part of AiScan plugin for FacturaScripts.
* Copyright (C) 2026 Ernesto Serrano <info@ernesto.es>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

namespace FacturaScripts\Test\Plugins;

use FacturaScripts\Plugins\AiScan\Lib\StoragePathHelper;
use PHPUnit\Framework\TestCase;

final class StoragePathHelperTest extends TestCase
{
public function testAbsoluteDirectoryUsesCleanAiScanFolder(): void
{
$this->assertSame(FS_FOLDER . '/MyFiles/aiscan', StoragePathHelper::absoluteDirectory());
}

public function testAbsoluteAndRelativeFileSanitizeNestedInput(): void
{
$this->assertSame(
FS_FOLDER . '/MyFiles/aiscan/invoice.pdf',
StoragePathHelper::absoluteFile('../nested/invoice.pdf')
);
$this->assertSame('aiscan/invoice.pdf', StoragePathHelper::relativeFile('../nested/invoice.pdf'));
}
}