mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-11-10 16:31:05 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf106498cf |
No files matched your search
@@ -1157,6 +1157,9 @@ they use underscores instead of dashes.
|
|||||||
be changed by trusted users. This applies to the `AppConfig` permission as well,
|
be changed by trusted users. This applies to the `AppConfig` permission as well,
|
||||||
which allows setting these arguments from the UI.
|
which allows setting these arguments from the UI.
|
||||||
|
|
||||||
|
Arguments paperless manages itself (`input_file_or_options`, `output_file`
|
||||||
|
and `sidecar`) are rejected by the API and ignored if set here.
|
||||||
|
|
||||||
Specify arguments as a JSON dictionary. Keep note of lower case
|
Specify arguments as a JSON dictionary. Keep note of lower case
|
||||||
booleans and double quoted parameter names and strings. Examples:
|
booleans and double quoted parameter names and strings. Examples:
|
||||||
|
|
||||||
|
|||||||
@@ -287,6 +287,28 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
|||||||
self.assertEqual(config.user_args, user_args)
|
self.assertEqual(config.user_args, user_args)
|
||||||
self.assertEqual(config.barcode_tag_mapping, barcode_tag_mapping)
|
self.assertEqual(config.barcode_tag_mapping, barcode_tag_mapping)
|
||||||
|
|
||||||
|
def test_api_update_config_rejects_reserved_user_args(self) -> None:
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- API request to update app config with user_args which set an
|
||||||
|
argument the parser manages itself
|
||||||
|
WHEN:
|
||||||
|
- API is called
|
||||||
|
THEN:
|
||||||
|
- Request is rejected with a 400 naming the argument
|
||||||
|
- Config is not updated
|
||||||
|
"""
|
||||||
|
response = self.client.patch(
|
||||||
|
f"{self.ENDPOINT}1/",
|
||||||
|
json.dumps({"user_args": json.dumps({"sidecar": "/tmp/x.py"})}),
|
||||||
|
content_type="application/json",
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertIn("sidecar", str(response.data["user_args"]))
|
||||||
|
config = ApplicationConfiguration.objects.first()
|
||||||
|
assert config is not None
|
||||||
|
self.assertIsNone(config.user_args)
|
||||||
|
|
||||||
def test_api_update_config_rejects_invalid_json_objects(self) -> None:
|
def test_api_update_config_rejects_invalid_json_objects(self) -> None:
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
|
|||||||
@@ -12,6 +12,17 @@ from paperless.models import ModeChoices
|
|||||||
from paperless.models import OutputTypeChoices
|
from paperless.models import OutputTypeChoices
|
||||||
from paperless.models import RemoteOCRMode
|
from paperless.models import RemoteOCRMode
|
||||||
|
|
||||||
|
# OCRmyPDF arguments the parser sets itself, pointing into its own temporary
|
||||||
|
# directory. The parser reads these files back after OCR, so user supplied
|
||||||
|
# values can only break consumption (or write to arbitrary paths), never help.
|
||||||
|
OCR_RESERVED_USER_ARGS: frozenset[str] = frozenset(
|
||||||
|
{
|
||||||
|
"input_file_or_options",
|
||||||
|
"output_file",
|
||||||
|
"sidecar",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class BaseConfig:
|
class BaseConfig:
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from documents.parsers import make_thumbnail_from_pdf
|
|||||||
from documents.utils import copy_file_with_basic_stats
|
from documents.utils import copy_file_with_basic_stats
|
||||||
from documents.utils import maybe_override_pixel_limit
|
from documents.utils import maybe_override_pixel_limit
|
||||||
from documents.utils import remove_alpha_channel
|
from documents.utils import remove_alpha_channel
|
||||||
|
from paperless.config import OCR_RESERVED_USER_ARGS
|
||||||
from paperless.config import OcrConfig
|
from paperless.config import OcrConfig
|
||||||
from paperless.models import CleanChoices
|
from paperless.models import CleanChoices
|
||||||
from paperless.models import ModeChoices
|
from paperless.models import ModeChoices
|
||||||
@@ -351,7 +352,22 @@ class RasterisedDocumentParser:
|
|||||||
|
|
||||||
if self.settings.user_args is not None:
|
if self.settings.user_args is not None:
|
||||||
try:
|
try:
|
||||||
ocrmypdf_args = {**ocrmypdf_args, **self.settings.user_args}
|
reserved = sorted(
|
||||||
|
OCR_RESERVED_USER_ARGS & self.settings.user_args.keys(),
|
||||||
|
)
|
||||||
|
if reserved:
|
||||||
|
self.log.warning(
|
||||||
|
f"Ignoring OCR user arguments managed by paperless: "
|
||||||
|
f"{', '.join(reserved)}",
|
||||||
|
)
|
||||||
|
ocrmypdf_args = {
|
||||||
|
**ocrmypdf_args,
|
||||||
|
**{
|
||||||
|
k: v
|
||||||
|
for k, v in self.settings.user_args.items()
|
||||||
|
if k not in OCR_RESERVED_USER_ARGS
|
||||||
|
},
|
||||||
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
f"There is an issue with PAPERLESS_OCR_USER_ARGS, so "
|
f"There is an issue with PAPERLESS_OCR_USER_ARGS, so "
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from PIL import Image
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
||||||
|
|
||||||
|
from paperless.config import OCR_RESERVED_USER_ARGS
|
||||||
from paperless.models import ApplicationConfiguration
|
from paperless.models import ApplicationConfiguration
|
||||||
from paperless.network import validate_outbound_http_url
|
from paperless.network import validate_outbound_http_url
|
||||||
from paperless.validators import reject_dangerous_svg
|
from paperless.validators import reject_dangerous_svg
|
||||||
@@ -251,6 +252,13 @@ class ApplicationConfigurationSerializer(
|
|||||||
|
|
||||||
def validate_user_args(self, value):
|
def validate_user_args(self, value):
|
||||||
self._require_json_object("user_args", value)
|
self._require_json_object("user_args", value)
|
||||||
|
if value is not None:
|
||||||
|
reserved = sorted(OCR_RESERVED_USER_ARGS & value.keys())
|
||||||
|
if reserved:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
f"user_args may not set arguments managed by paperless: "
|
||||||
|
f"{', '.join(reserved)}.",
|
||||||
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate_barcode_tag_mapping(self, value):
|
def validate_barcode_tag_mapping(self, value):
|
||||||
|
|||||||
@@ -243,6 +243,34 @@ class TestParserSettingsFromDb(DirectoriesMixin, FileSystemAssertsMixin, TestCas
|
|||||||
"UseDeviceIndependentColor",
|
"UseDeviceIndependentColor",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_ocr_user_args_cannot_override_reserved(self) -> None:
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- User arguments which attempt to override arguments the parser
|
||||||
|
manages itself, alongside a legitimate argument
|
||||||
|
WHEN:
|
||||||
|
- OCR parameters are constructed
|
||||||
|
THEN:
|
||||||
|
- Reserved arguments keep the parser supplied values
|
||||||
|
- The legitimate argument is still applied
|
||||||
|
"""
|
||||||
|
instance = ApplicationConfiguration.objects.all().first()
|
||||||
|
assert instance is not None
|
||||||
|
instance.user_args = {
|
||||||
|
"sidecar": "/tmp/evil.py",
|
||||||
|
"output_file": "/tmp/out.pdf",
|
||||||
|
"input_file_or_options": "/etc/passwd",
|
||||||
|
"unpaper_args": "--pre-rotate 90",
|
||||||
|
}
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
params = self.get_params()
|
||||||
|
|
||||||
|
self.assertEqual(params["sidecar"], "sidecar.txt")
|
||||||
|
self.assertEqual(params["output_file"], "output.pdf")
|
||||||
|
self.assertEqual(params["input_file_or_options"], "input.pdf")
|
||||||
|
self.assertEqual(params["unpaper_args"], "--pre-rotate 90")
|
||||||
|
|
||||||
def test_ocr_user_args(self) -> None:
|
def test_ocr_user_args(self) -> None:
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
|
|||||||
Reference in new issue
Block a user