Restrict company imports to GitHub and zip packages

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-03-16 09:52:16 -05:00
parent 0b829ea20b
commit 4a5aba5bac
6 changed files with 163 additions and 94 deletions

View File

@@ -1,4 +1,5 @@
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
const crcTable = new Uint32Array(256);
for (let i = 0; i < 256; i++) {
@@ -37,6 +38,19 @@ function writeUint32(target: Uint8Array, offset: number, value: number) {
target[offset + 3] = (value >>> 24) & 0xff;
}
function readUint16(source: Uint8Array, offset: number) {
return source[offset]! | (source[offset + 1]! << 8);
}
function readUint32(source: Uint8Array, offset: number) {
return (
source[offset]! |
(source[offset + 1]! << 8) |
(source[offset + 2]! << 16) |
(source[offset + 3]! << 24)
) >>> 0;
}
function getDosDateTime(date: Date) {
const year = Math.min(Math.max(date.getFullYear(), 1980), 2107);
const month = date.getMonth() + 1;
@@ -62,6 +76,84 @@ function concatChunks(chunks: Uint8Array[]) {
return archive;
}
function sharedArchiveRoot(paths: string[]) {
if (paths.length === 0) return null;
const firstSegments = paths
.map((entry) => normalizeArchivePath(entry).split("/").filter(Boolean))
.filter((parts) => parts.length > 0);
if (firstSegments.length === 0) return null;
const candidate = firstSegments[0]![0]!;
return firstSegments.every((parts) => parts.length > 1 && parts[0] === candidate)
? candidate
: null;
}
export function readZipArchive(source: ArrayBuffer | Uint8Array): {
rootPath: string | null;
files: Record<string, string>;
} {
const bytes = source instanceof Uint8Array ? source : new Uint8Array(source);
const entries: Array<{ path: string; body: string }> = [];
let offset = 0;
while (offset + 4 <= bytes.length) {
const signature = readUint32(bytes, offset);
if (signature === 0x02014b50 || signature === 0x06054b50) break;
if (signature !== 0x04034b50) {
throw new Error("Invalid zip archive: unsupported local file header.");
}
if (offset + 30 > bytes.length) {
throw new Error("Invalid zip archive: truncated local file header.");
}
const generalPurposeFlag = readUint16(bytes, offset + 6);
const compressionMethod = readUint16(bytes, offset + 8);
const compressedSize = readUint32(bytes, offset + 18);
const fileNameLength = readUint16(bytes, offset + 26);
const extraFieldLength = readUint16(bytes, offset + 28);
if ((generalPurposeFlag & 0x0008) !== 0) {
throw new Error("Unsupported zip archive: data descriptors are not supported.");
}
if (compressionMethod !== 0) {
throw new Error("Unsupported zip archive: only uncompressed entries are supported.");
}
const nameOffset = offset + 30;
const bodyOffset = nameOffset + fileNameLength + extraFieldLength;
const bodyEnd = bodyOffset + compressedSize;
if (bodyEnd > bytes.length) {
throw new Error("Invalid zip archive: truncated file contents.");
}
const archivePath = normalizeArchivePath(
textDecoder.decode(bytes.slice(nameOffset, nameOffset + fileNameLength)),
);
if (archivePath && !archivePath.endsWith("/")) {
entries.push({
path: archivePath,
body: textDecoder.decode(bytes.slice(bodyOffset, bodyEnd)),
});
}
offset = bodyEnd;
}
const rootPath = sharedArchiveRoot(entries.map((entry) => entry.path));
const files: Record<string, string> = {};
for (const entry of entries) {
const normalizedPath =
rootPath && entry.path.startsWith(`${rootPath}/`)
? entry.path.slice(rootPath.length + 1)
: entry.path;
if (!normalizedPath) continue;
files[normalizedPath] = entry.body;
}
return { rootPath, files };
}
export function createZipArchive(files: Record<string, string>, rootPath: string): Uint8Array {
const normalizedRoot = normalizeArchivePath(rootPath);
const localChunks: Uint8Array[] = [];