


















@@ -2,6 +2,7 @@ import { Buffer } from "node:buffer";
22import { readFileSync } from "node:fs";
33import { createRequire } from "node:module";
44import { compileFunction } from "node:vm";
5+import { deflateRawSync } from "node:zlib";
56import { describe, expect, it } from "vitest";
67import { parse } from "yaml";
78import { markdownToIR } from "../../packages/markdown-core/src/ir.js";
@@ -316,41 +317,47 @@ function u32(value: number): Buffer {
316317return buffer;
317318}
318319319-function makeZip(files: Record<string, string>): Buffer {
320+function makeZip(
321+files: Record<string, string>,
322+options: { compressionMethod?: 0 | 8 } = {},
323+): Buffer {
320324const localParts: Buffer[] = [];
321325const centralParts: Buffer[] = [];
322326let offset = 0;
327+const compressionMethod = options.compressionMethod ?? 0;
323328324329for (const [name, contents] of Object.entries(files)) {
325330const nameBuffer = Buffer.from(name, "utf8");
326331const contentsBuffer = Buffer.from(contents, "utf8");
332+const compressedBuffer =
333+compressionMethod === 8 ? deflateRawSync(contentsBuffer) : contentsBuffer;
327334const checksum = crc32(contentsBuffer);
328335const localHeader = Buffer.concat([
329336u32(0x04034b50),
330337u16(20),
331338u16(0),
332-u16(0),
339+u16(compressionMethod),
333340u16(0),
334341u16(0),
335342u32(checksum),
336-u32(contentsBuffer.length),
343+u32(compressedBuffer.length),
337344u32(contentsBuffer.length),
338345u16(nameBuffer.length),
339346u16(0),
340347nameBuffer,
341348]);
342-localParts.push(localHeader, contentsBuffer);
349+localParts.push(localHeader, compressedBuffer);
343350centralParts.push(
344351Buffer.concat([
345352u32(0x02014b50),
346353u16(20),
347354u16(20),
348355u16(0),
349-u16(0),
356+u16(compressionMethod),
350357u16(0),
351358u16(0),
352359u32(checksum),
353-u32(contentsBuffer.length),
360+u32(compressedBuffer.length),
354361u32(contentsBuffer.length),
355362u16(nameBuffer.length),
356363u16(0),
@@ -362,7 +369,7 @@ function makeZip(files: Record<string, string>): Buffer {
362369nameBuffer,
363370]),
364371);
365-offset += localHeader.length + contentsBuffer.length;
372+offset += localHeader.length + compressedBuffer.length;
366373}
367374368375const localData = Buffer.concat(localParts);
@@ -391,11 +398,27 @@ function markFirstCentralDirectoryEntryEncrypted(archive: Buffer): Buffer {
391398return result;
392399}
393400401+function setFirstEntryUncompressedSize(archive: Buffer, size: number): Buffer {
402+const result = Buffer.from(archive);
403+if (result.readUInt32LE(0) !== 0x04034b50) {
404+throw new Error("missing ZIP local file header");
405+}
406+result.writeUInt32LE(size, 22);
407+const centralOffset = result.indexOf(Buffer.from([0x50, 0x4b, 0x01, 0x02]));
408+if (centralOffset < 0) {
409+throw new Error("missing ZIP central directory entry");
410+}
411+result.writeUInt32LE(size, centralOffset + 24);
412+return result;
413+}
414+394415describe("iOS Periphery comment workflow", () => {
395416it("parses the workflow YAML and embedded github-script JavaScript", () => {
396-expect(() => commenterScript()).not.toThrow();
417+const script = commenterScript();
418+expect(script).not.toContain("node:child_process");
419+expect(script).not.toContain("execFileSync");
397420expect(() =>
398-compileFunction(`return (async () => {\n${commenterScript()}\n})();`, [
421+compileFunction(`return (async () => {\n${script}\n})();`, [
399422"require",
400423"context",
401424"core",
@@ -488,6 +511,55 @@ describe("iOS Periphery comment workflow", () => {
488511expect(result.core.warnings).toEqual([]);
489512});
490513514+it("accepts deflated Periphery artifacts", async () => {
515+const archive = makeZip(
516+{
517+"periphery.json": "[]\n",
518+"periphery.status": "0\n",
519+},
520+{ compressionMethod: 8 },
521+);
522+const result = await runCommenter(
523+{
524+expired: false,
525+id: 77,
526+name: ARTIFACT_NAME,
527+size_in_bytes: archive.length,
528+},
529+archive,
530+);
531+532+expect(result.downloadCount).toBe(1);
533+expect(result.core.warnings).toEqual([]);
534+});
535+536+it("rejects deflated entries that inflate past the per-file limit", async () => {
537+const archive = setFirstEntryUncompressedSize(
538+makeZip(
539+{
540+"periphery.json": `${" ".repeat(2 * 1024 * 1024 + 1)}[]\n`,
541+"periphery.status": "0\n",
542+},
543+{ compressionMethod: 8 },
544+),
545+1,
546+);
547+const result = await runCommenter(
548+{
549+expired: false,
550+id: 77,
551+name: ARTIFACT_NAME,
552+size_in_bytes: archive.length,
553+},
554+archive,
555+);
556+557+expectUnavailableComment(result.createdBodies);
558+expect(result.core.warnings).toEqual([
559+`Skipping ${ARTIFACT_NAME}; periphery.json exceeded the per-file size limit while reading.`,
560+]);
561+});
562+491563it("rejects oversized artifact metadata before download", async () => {
492564const result = await runCommenter(
493565{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。