Ctrl K
ComunidadGuías…

lib/sdk-archive.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { gzipSync } from "node:zlib";
import type { SdkBundle } from "./sdk-export";

const BLOCK_SIZE = 512;

function octal(value: number, bytes: number) {
  const text = value.toString(8);
  return `${text.padStart(bytes - 1, "0")}\0`;
}

function writeString(buffer: Buffer, value: string, offset: number, length: number) {
  buffer.write(value.slice(0, length), offset, length, "utf8");
}

function splitTarPath(path: string) {
  if (path.length <= 100) return { name: path, prefix: "" };
  const index = path.lastIndexOf("/", 155);
  if (index <= 0) throw new Error(`SDK archive path is too long for tar: ${path}`);
  const prefix = path.slice(0, index);
  const name = path.slice(index + 1);
  if (name.length > 100 || prefix.length > 155) {
    throw new Error(`SDK archive path is too long for tar: ${path}`);
  }
  return { name, prefix };
}

function assertSafePath(path: string) {
  if (!path || path.startsWith("/") || path.includes("..") || path.includes("\0")) {
    throw new Error(`Unsafe SDK archive path: ${path}`);
  }
}

function tarHeader(path: string, size: number, mtime: number) {
  assertSafePath(path);
  const { name, prefix } = splitTarPath(path);
  const header = Buffer.alloc(BLOCK_SIZE, 0);
  writeString(header, name, 0, 100);
  writeString(header, octal(0o644, 8), 100, 8);
  writeString(header, octal(0, 8), 108, 8);
  writeString(header, octal(0, 8), 116, 8);
  writeString(header, octal(size, 12), 124, 12);
  writeString(header, octal(mtime, 12), 136, 12);
  header.fill(" ", 148, 156);
  header[156] = "0".charCodeAt(0);
  writeString(header, "ustar", 257, 6);
  writeString(header, "00", 263, 2);
  if (prefix) writeString(header, prefix, 345, 155);

  let checksum = 0;
  for (let index = 0; index < header.length; index += 1) checksum += header[index] ?? 0;
  writeString(header, octal(checksum, 8), 148, 8);
  return header;
}

function padBlock(buffer: Buffer) {
  const remainder = buffer.length % BLOCK_SIZE;
  if (remainder === 0) return Buffer.alloc(0);
  return Buffer.alloc(BLOCK_SIZE - remainder, 0);
}

export function buildSdkTarGz(bundle: SdkBundle) {
  const mtime = Math.floor(Date.now() / 1000);
  const chunks: Buffer[] = [];

  for (const file of bundle.files) {
    assertSafePath(file.path);
    const content = Buffer.from(file.content, "utf8");
    chunks.push(tarHeader(file.path, content.length, mtime));
    chunks.push(content);
    chunks.push(padBlock(content));
  }

  chunks.push(Buffer.alloc(BLOCK_SIZE * 2, 0));
  return gzipSync(Buffer.concat(chunks), { level: 9 });
}