diff --git a/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillInstallController.java b/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillInstallController.java index ac81767c..0f6e7b7d 100644 --- a/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillInstallController.java +++ b/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillInstallController.java @@ -3,6 +3,7 @@ package vip.mate.skill.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -33,6 +34,15 @@ public class SkillInstallController { private final SkillInstaller skillInstaller; private final SkillFrontmatterParser frontmatterParser; + /** Per-entry cap inside an uploaded skill ZIP (MB). */ + @Value("${mateclaw.skill.upload.max-entry-size-mb:1}") + private long maxEntrySizeMb = 1; + + /** Total size cap for an uploaded skill ZIP (MB). The archive is buffered + * in memory during extraction, so this also bounds peak heap usage. */ + @Value("${mateclaw.skill.upload.max-total-size-mb:50}") + private long maxTotalSizeMb = 50; + @Operation(summary = "搜索 ClawHub 市场") @GetMapping("/hub/search") @RequireWorkspaceRole("admin") @@ -90,7 +100,8 @@ public class SkillInstallController { return R.fail(400, "Only .zip files are accepted"); } try { - SkillBundle bundle = ZipSkillFetcher.parse(zipFile, frontmatterParser); + SkillBundle bundle = ZipSkillFetcher.parse(zipFile, frontmatterParser, + ZipSkillFetcher.Limits.ofMb(maxEntrySizeMb, maxTotalSizeMb)); Map result = skillInstaller.installFromBundle( bundle, enable, overwrite, targetName, workspaceId); return R.ok(result); diff --git a/mateclaw-server/src/main/java/vip/mate/skill/installer/SkillHubClient.java b/mateclaw-server/src/main/java/vip/mate/skill/installer/SkillHubClient.java index baf1d102..487305f6 100644 --- a/mateclaw-server/src/main/java/vip/mate/skill/installer/SkillHubClient.java +++ b/mateclaw-server/src/main/java/vip/mate/skill/installer/SkillHubClient.java @@ -3,6 +3,7 @@ package vip.mate.skill.installer; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import vip.mate.skill.installer.model.HubSkillInfo; import vip.mate.skill.installer.model.SkillBundle; @@ -37,6 +38,13 @@ import java.util.Map; @Service public class SkillHubClient { + /** Per-entry / total caps for marketplace bundle ZIPs — same knobs as upload. */ + @Value("${mateclaw.skill.upload.max-entry-size-mb:1}") + private long maxEntrySizeMb = 1; + + @Value("${mateclaw.skill.upload.max-total-size-mb:50}") + private long maxTotalSizeMb = 50; + private final SkillHubProperties properties; private final ObjectMapper objectMapper; private final SkillFrontmatterParser frontmatterParser; @@ -131,7 +139,9 @@ public class SkillHubClient { // Step 3: extract + assemble SkillBundle. try { - ZipSkillFetcher.ExtractedSkill extracted = ZipSkillFetcher.extract(new ByteArrayInputStream(zipBytes)); + ZipSkillFetcher.ExtractedSkill extracted = ZipSkillFetcher.extract( + new ByteArrayInputStream(zipBytes), + ZipSkillFetcher.Limits.ofMb(maxEntrySizeMb, maxTotalSizeMb)); var parsed = frontmatterParser.parse(extracted.skillMdContent()); Map fm = parsed.getFrontmatter(); diff --git a/mateclaw-server/src/main/java/vip/mate/skill/installer/ZipSkillFetcher.java b/mateclaw-server/src/main/java/vip/mate/skill/installer/ZipSkillFetcher.java index e1e8221f..d176f0ed 100644 --- a/mateclaw-server/src/main/java/vip/mate/skill/installer/ZipSkillFetcher.java +++ b/mateclaw-server/src/main/java/vip/mate/skill/installer/ZipSkillFetcher.java @@ -27,7 +27,9 @@ import java.util.zip.ZipInputStream; * path (downloaded ZIP bytes). Hardened against: *