feat: make skill package upload size limit configurable (#39799)

This commit is contained in:
林玮 (Jade Lin) 2026-07-30 16:42:24 +08:00 committed by GitHub
parent 48bb46a72b
commit 8cca3959ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 28 additions and 9 deletions

View File

@ -436,6 +436,7 @@ UPLOAD_FILE_BATCH_LIMIT=5
UPLOAD_IMAGE_FILE_SIZE_LIMIT=10
UPLOAD_VIDEO_FILE_SIZE_LIMIT=100
UPLOAD_AUDIO_FILE_SIZE_LIMIT=50
UPLOAD_SKILL_FILE_SIZE_LIMIT=50
# Comma-separated list of file extensions blocked from upload for security reasons.
# Extensions should be lowercase without dots (e.g., exe,bat,sh,dll).

View File

@ -470,6 +470,11 @@ class FileUploadConfig(BaseSettings):
default=50,
)
UPLOAD_SKILL_FILE_SIZE_LIMIT: NonNegativeInt = Field(
description="Maximum allowed Skill package size for uploads in megabytes",
default=50,
)
BATCH_UPLOAD_LIMIT: NonNegativeInt = Field(
description="Maximum number of files allowed in a batch upload operation",
default=20,

View File

@ -107,6 +107,7 @@ class FileApi(Resource):
image_file_size_limit=dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT,
video_file_size_limit=dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT,
audio_file_size_limit=dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT,
skill_file_size_limit=dify_config.UPLOAD_SKILL_FILE_SIZE_LIMIT,
workflow_file_upload_limit=dify_config.WORKFLOW_FILE_UPLOAD_LIMIT,
image_file_batch_limit=dify_config.IMAGE_FILE_BATCH_LIMIT,
single_chunk_attachment_limit=dify_config.SINGLE_CHUNK_ATTACHMENT_LIMIT,

View File

@ -15,6 +15,7 @@ class UploadConfig(ResponseModel):
image_file_size_limit: int
video_file_size_limit: int
audio_file_size_limit: int
skill_file_size_limit: int
workflow_file_upload_limit: int
image_file_batch_limit: int
single_chunk_attachment_limit: int

View File

@ -22989,6 +22989,7 @@ Payload for updating a snippet.
| image_file_batch_limit | integer | | Yes |
| image_file_size_limit | integer | | Yes |
| single_chunk_attachment_limit | integer | | Yes |
| skill_file_size_limit | integer | | Yes |
| video_file_size_limit | integer | | Yes |
| workflow_file_upload_limit | integer | | Yes |

View File

@ -26,8 +26,9 @@ import zlib
import yaml
from pydantic import BaseModel
from configs import dify_config
# Bounds — generous but finite so a hostile upload can't exhaust memory/disk.
_MAX_ARCHIVE_BYTES = 50 * 1024 * 1024
_MAX_UNCOMPRESSED_BYTES = 200 * 1024 * 1024
_MAX_SKILL_MD_BYTES = 1 * 1024 * 1024
_MAX_ENTRIES = 5000
@ -127,7 +128,8 @@ class SkillPackageService:
self._check_extension(filename)
if not content:
raise SkillPackageError("empty_archive", "skill archive is empty", status_code=400)
if len(content) > _MAX_ARCHIVE_BYTES:
max_archive_bytes = dify_config.UPLOAD_SKILL_FILE_SIZE_LIMIT * 1024 * 1024
if len(content) > max_archive_bytes:
raise SkillPackageError("archive_too_large", "skill archive exceeds size limit", status_code=400)
try:

View File

@ -37,6 +37,7 @@ def test_file_upload_config_returns_console_limits(
"image_file_size_limit": dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT,
"video_file_size_limit": dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT,
"audio_file_size_limit": dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT,
"skill_file_size_limit": dify_config.UPLOAD_SKILL_FILE_SIZE_LIMIT,
"workflow_file_upload_limit": dify_config.WORKFLOW_FILE_UPLOAD_LIMIT,
"image_file_batch_limit": dify_config.IMAGE_FILE_BATCH_LIMIT,
"single_chunk_attachment_limit": dify_config.SINGLE_CHUNK_ATTACHMENT_LIMIT,

View File

@ -5,6 +5,7 @@ import pytest
from flask import Flask
from werkzeug.exceptions import Forbidden
from configs import dify_config
from constants import DOCUMENT_EXTENSIONS
from controllers.common.errors import (
BlockedFileExtensionError,
@ -92,6 +93,7 @@ class TestFileApiGet:
assert status == 200
assert "file_size_limit" in data
assert "batch_count_limit" in data
assert data["skill_file_size_limit"] == dify_config.UPLOAD_SKILL_FILE_SIZE_LIMIT
class TestFileApiPost:

View File

@ -72,15 +72,17 @@ def test_remote_file_info_and_upload_config() -> None:
image_file_size_limit=4,
video_file_size_limit=5,
audio_file_size_limit=6,
workflow_file_upload_limit=7,
image_file_batch_limit=8,
single_chunk_attachment_limit=9,
attachment_image_file_size_limit=10,
skill_file_size_limit=7,
workflow_file_upload_limit=8,
image_file_batch_limit=9,
single_chunk_attachment_limit=10,
attachment_image_file_size_limit=11,
)
dumped = config.model_dump(mode="json")
assert dumped["file_upload_limit"] == 3
assert dumped["attachment_image_file_size_limit"] == 10
assert dumped["skill_file_size_limit"] == 7
assert dumped["attachment_image_file_size_limit"] == 11
@pytest.mark.parametrize(

View File

@ -210,10 +210,10 @@ def test_validate_and_normalize_rejects_archive_too_large_uncompressed(monkeypat
def test_validate_and_normalize_rejects_archive_too_large_uploaded_bytes(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(skill_package_service_module, "_MAX_ARCHIVE_BYTES", 8)
monkeypatch.setattr(skill_package_service_module.dify_config, "UPLOAD_SKILL_FILE_SIZE_LIMIT", 1)
with pytest.raises(SkillPackageError) as exc_info:
SkillPackageService().validate_and_normalize(content=b"x" * 9, filename="skill.zip")
SkillPackageService().validate_and_normalize(content=b"x" * (1024 * 1024 + 1), filename="skill.zip")
assert exc_info.value.code == "archive_too_large"

View File

@ -60,6 +60,7 @@ MULTIMODAL_SEND_FORMAT=base64
UPLOAD_IMAGE_FILE_SIZE_LIMIT=10
UPLOAD_VIDEO_FILE_SIZE_LIMIT=100
UPLOAD_AUDIO_FILE_SIZE_LIMIT=50
UPLOAD_SKILL_FILE_SIZE_LIMIT=50
API_SENTRY_DSN=
API_SENTRY_TRACES_SAMPLE_RATE=1.0
API_SENTRY_PROFILES_SAMPLE_RATE=1.0

View File

@ -17,6 +17,7 @@ export type UploadConfig = {
image_file_batch_limit: number
image_file_size_limit: number
single_chunk_attachment_limit: number
skill_file_size_limit: number
video_file_size_limit: number
workflow_file_upload_limit: number
}

View File

@ -21,6 +21,7 @@ export const zUploadConfig = z.object({
image_file_batch_limit: z.int(),
image_file_size_limit: z.int(),
single_chunk_attachment_limit: z.int(),
skill_file_size_limit: z.int(),
video_file_size_limit: z.int(),
workflow_file_upload_limit: z.int(),
})