aimeat 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/auth/jwt.d.ts +4 -0
- package/dist/src/auth/jwt.d.ts.map +1 -1
- package/dist/src/auth/jwt.js +5 -0
- package/dist/src/auth/jwt.js.map +1 -1
- package/dist/src/auth/node-keys.d.ts.map +1 -1
- package/dist/src/auth/node-keys.js +4 -1
- package/dist/src/auth/node-keys.js.map +1 -1
- package/dist/src/mcp/apps.d.ts.map +1 -1
- package/dist/src/mcp/apps.js +35 -12
- package/dist/src/mcp/apps.js.map +1 -1
- package/dist/src/mcp/core.d.ts.map +1 -1
- package/dist/src/mcp/core.js +38 -2
- package/dist/src/mcp/core.js.map +1 -1
- package/dist/src/mcp/cortex.d.ts.map +1 -1
- package/dist/src/mcp/cortex.js +37 -6
- package/dist/src/mcp/cortex.js.map +1 -1
- package/dist/src/mcp/extensions.d.ts.map +1 -1
- package/dist/src/mcp/extensions.js +37 -3
- package/dist/src/mcp/extensions.js.map +1 -1
- package/dist/src/routes/apps.d.ts.map +1 -1
- package/dist/src/routes/apps.js +25 -3
- package/dist/src/routes/apps.js.map +1 -1
- package/dist/src/routes/extensions.d.ts.map +1 -1
- package/dist/src/routes/extensions.js +3 -1
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/storage-files.d.ts.map +1 -1
- package/dist/src/routes/storage-files.js +26 -1
- package/dist/src/routes/storage-files.js.map +1 -1
- package/dist/src/routes/upload.d.ts +22 -0
- package/dist/src/routes/upload.d.ts.map +1 -0
- package/dist/src/routes/upload.js +219 -0
- package/dist/src/routes/upload.js.map +1 -0
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +3 -0
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +11 -3
- package/dist/src/server.js.map +1 -1
- package/dist/src/services/upload-token.d.ts +37 -0
- package/dist/src/services/upload-token.d.ts.map +1 -0
- package/dist/src/services/upload-token.js +98 -0
- package/dist/src/services/upload-token.js.map +1 -0
- package/dist/src/services/upload-zip.d.ts +31 -0
- package/dist/src/services/upload-zip.d.ts.map +1 -0
- package/dist/src/services/upload-zip.js +236 -0
- package/dist/src/services/upload-zip.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file upload-token.ts
|
|
3
|
+
* @description Presigned upload token generation and verification. Tokens are single-use,
|
|
4
|
+
* time-limited JWTs that authorize a single file upload to PUT /v1/upload/:token.
|
|
5
|
+
* Uses the existing node JWT signing infrastructure (no separate key management).
|
|
6
|
+
* @structure
|
|
7
|
+
* - generateUploadToken() — creates a signed upload JWT with metadata
|
|
8
|
+
* - verifyUploadToken() — validates signature, expiry, and single-use
|
|
9
|
+
* - UploadTokenError — typed error with code field
|
|
10
|
+
* @usage
|
|
11
|
+
* import { generateUploadToken, verifyUploadToken } from '../services/upload-token.js';
|
|
12
|
+
* @version-history
|
|
13
|
+
* v1.0.0 — 2026-05-02 — Initial implementation
|
|
14
|
+
*/
|
|
15
|
+
import { SignJWT, jwtVerify } from 'jose';
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
17
|
+
// ── Key management (initialized from jwt.ts node keys) ──
|
|
18
|
+
let _privateKey = null;
|
|
19
|
+
let _publicKey = null;
|
|
20
|
+
// Single-use tracking: token hash -> expiry timestamp
|
|
21
|
+
const usedTokens = new Map();
|
|
22
|
+
let _cleanupInterval = null;
|
|
23
|
+
export function initUploadTokenKeys(privateKey, publicKey) {
|
|
24
|
+
_privateKey = privateKey;
|
|
25
|
+
_publicKey = publicKey;
|
|
26
|
+
if (_cleanupInterval)
|
|
27
|
+
clearInterval(_cleanupInterval);
|
|
28
|
+
_cleanupInterval = setInterval(() => {
|
|
29
|
+
const now = Math.floor(Date.now() / 1000);
|
|
30
|
+
for (const [hash, exp] of usedTokens) {
|
|
31
|
+
if (exp < now)
|
|
32
|
+
usedTokens.delete(hash);
|
|
33
|
+
}
|
|
34
|
+
}, 5 * 60 * 1000);
|
|
35
|
+
}
|
|
36
|
+
export class UploadTokenError extends Error {
|
|
37
|
+
code;
|
|
38
|
+
constructor(code, message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'UploadTokenError';
|
|
41
|
+
this.code = code;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// ── Token generation ──
|
|
45
|
+
export async function generateUploadToken(payload, ttlSeconds = 3600) {
|
|
46
|
+
if (!_privateKey)
|
|
47
|
+
throw new Error('Upload token keys not initialized');
|
|
48
|
+
return new SignJWT({
|
|
49
|
+
typ: 'upload',
|
|
50
|
+
utype: payload.utype,
|
|
51
|
+
meta: payload.meta,
|
|
52
|
+
maxBytes: payload.maxBytes,
|
|
53
|
+
contentType: payload.contentType,
|
|
54
|
+
})
|
|
55
|
+
.setProtectedHeader({ alg: 'EdDSA', typ: 'JWT' })
|
|
56
|
+
.setSubject(payload.sub)
|
|
57
|
+
.setIssuedAt()
|
|
58
|
+
.setExpirationTime(`${ttlSeconds}s`)
|
|
59
|
+
.sign(_privateKey);
|
|
60
|
+
}
|
|
61
|
+
// ── Token verification ──
|
|
62
|
+
function hashToken(token) {
|
|
63
|
+
return createHash('sha256').update(token).digest('hex');
|
|
64
|
+
}
|
|
65
|
+
export async function verifyUploadToken(token) {
|
|
66
|
+
if (!_publicKey)
|
|
67
|
+
throw new Error('Upload token keys not initialized');
|
|
68
|
+
// Single-use check
|
|
69
|
+
const hash = hashToken(token);
|
|
70
|
+
if (usedTokens.has(hash)) {
|
|
71
|
+
throw new UploadTokenError('TOKEN_USED', 'Upload token has already been used (single-use)');
|
|
72
|
+
}
|
|
73
|
+
let payload;
|
|
74
|
+
try {
|
|
75
|
+
const result = await jwtVerify(token, _publicKey, { algorithms: ['EdDSA'] });
|
|
76
|
+
payload = result.payload;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
const msg = err.message;
|
|
80
|
+
if (msg.includes('expired') || msg.includes('exp')) {
|
|
81
|
+
throw new UploadTokenError('TOKEN_EXPIRED', 'Upload token has expired (60 min TTL)');
|
|
82
|
+
}
|
|
83
|
+
throw new UploadTokenError('TOKEN_INVALID', `Invalid upload token: ${msg}`);
|
|
84
|
+
}
|
|
85
|
+
if (payload.typ !== 'upload') {
|
|
86
|
+
throw new UploadTokenError('TOKEN_INVALID', 'Token is not an upload token');
|
|
87
|
+
}
|
|
88
|
+
// Mark as used (store until token expiry for dedup)
|
|
89
|
+
usedTokens.set(hash, payload.exp);
|
|
90
|
+
return {
|
|
91
|
+
sub: payload.sub,
|
|
92
|
+
utype: payload.utype,
|
|
93
|
+
meta: payload.meta,
|
|
94
|
+
maxBytes: payload.maxBytes,
|
|
95
|
+
contentType: payload.contentType,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=upload-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-token.js","sourceRoot":"","sources":["../../../src/services/upload-token.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file upload-zip.ts
|
|
3
|
+
* @description ZIP parsing for extension and cortex uploads via presigned URL.
|
|
4
|
+
* Validates ZIP structure, extracts manifest + scripts/libs, applies security checks
|
|
5
|
+
* (magic bytes, zip bomb detection, path traversal prevention).
|
|
6
|
+
* @structure
|
|
7
|
+
* - parseExtensionZip() — ZIP buffer -> ExtensionRecord
|
|
8
|
+
* - parseCortexZip() — ZIP buffer -> CortexExtensionRecord + libs
|
|
9
|
+
* - extractZipEntries() — internal secure ZIP extraction
|
|
10
|
+
* @usage
|
|
11
|
+
* import { parseExtensionZip, parseCortexZip } from '../services/upload-zip.js';
|
|
12
|
+
* @version-history
|
|
13
|
+
* v1.0.0 — 2026-05-02 — Initial implementation
|
|
14
|
+
*/
|
|
15
|
+
import type { AimeatConfig } from '../config.js';
|
|
16
|
+
import type { ExtensionRecord, CortexExtensionRecord } from '../storage/interface.js';
|
|
17
|
+
interface ExtensionZipResult {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
error?: string;
|
|
20
|
+
record?: ExtensionRecord;
|
|
21
|
+
}
|
|
22
|
+
export declare function parseExtensionZip(buffer: Buffer, config: AimeatConfig): Promise<ExtensionZipResult>;
|
|
23
|
+
interface CortexZipResult {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
extension?: CortexExtensionRecord;
|
|
27
|
+
libs?: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
export declare function parseCortexZip(buffer: Buffer, config: AimeatConfig, ownerName: string): Promise<CortexZipResult>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=upload-zip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-zip.d.ts","sourceRoot":"","sources":["../../../src/services/upload-zip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAUtF,UAAU,kBAAkB;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgIzG;AAID,UAAU,eAAe;IACrB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,wBAAsB,cAAc,CAChC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GACxD,OAAO,CAAC,eAAe,CAAC,CAkD1B"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file upload-zip.ts
|
|
3
|
+
* @description ZIP parsing for extension and cortex uploads via presigned URL.
|
|
4
|
+
* Validates ZIP structure, extracts manifest + scripts/libs, applies security checks
|
|
5
|
+
* (magic bytes, zip bomb detection, path traversal prevention).
|
|
6
|
+
* @structure
|
|
7
|
+
* - parseExtensionZip() — ZIP buffer -> ExtensionRecord
|
|
8
|
+
* - parseCortexZip() — ZIP buffer -> CortexExtensionRecord + libs
|
|
9
|
+
* - extractZipEntries() — internal secure ZIP extraction
|
|
10
|
+
* @usage
|
|
11
|
+
* import { parseExtensionZip, parseCortexZip } from '../services/upload-zip.js';
|
|
12
|
+
* @version-history
|
|
13
|
+
* v1.0.0 — 2026-05-02 — Initial implementation
|
|
14
|
+
*/
|
|
15
|
+
import yauzl from 'yauzl';
|
|
16
|
+
import YAML from 'yaml';
|
|
17
|
+
import { parseCortexManifest } from './cortex-manifest.js';
|
|
18
|
+
const ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);
|
|
19
|
+
const MAX_FILES = 50;
|
|
20
|
+
const MAX_DEPTH = 5;
|
|
21
|
+
const DECOMPRESSION_RATIO = 10;
|
|
22
|
+
export async function parseExtensionZip(buffer, config) {
|
|
23
|
+
if (buffer.length < 4 || !buffer.subarray(0, 4).equals(ZIP_MAGIC)) {
|
|
24
|
+
return { ok: false, error: 'Not a valid ZIP file (missing magic bytes)' };
|
|
25
|
+
}
|
|
26
|
+
const maxBytes = config.extensionMaxCodeSizeKb * 1024 * MAX_FILES;
|
|
27
|
+
if (buffer.length > maxBytes) {
|
|
28
|
+
return { ok: false, error: `ZIP exceeds maximum size of ${Math.round(maxBytes / 1024)}KB` };
|
|
29
|
+
}
|
|
30
|
+
let files;
|
|
31
|
+
try {
|
|
32
|
+
files = await extractZipEntries(buffer);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return { ok: false, error: `ZIP extraction failed: ${err.message}` };
|
|
36
|
+
}
|
|
37
|
+
const manifestBuf = files.get('manifest.yaml');
|
|
38
|
+
if (!manifestBuf) {
|
|
39
|
+
return { ok: false, error: 'ZIP must contain manifest.yaml at root' };
|
|
40
|
+
}
|
|
41
|
+
let manifest;
|
|
42
|
+
try {
|
|
43
|
+
manifest = YAML.parse(manifestBuf.toString('utf-8'));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return { ok: false, error: 'manifest.yaml is not valid YAML' };
|
|
47
|
+
}
|
|
48
|
+
const metadata = manifest.metadata;
|
|
49
|
+
if (!metadata?.name || !metadata?.version || !metadata?.description || !metadata?.author) {
|
|
50
|
+
return { ok: false, error: 'metadata.name, metadata.version, metadata.description, and metadata.author are required' };
|
|
51
|
+
}
|
|
52
|
+
const actions = manifest.actions;
|
|
53
|
+
if (!Array.isArray(actions) || actions.length === 0) {
|
|
54
|
+
return { ok: false, error: 'actions array is required and must not be empty' };
|
|
55
|
+
}
|
|
56
|
+
// Extract scripts from scripts/ directory
|
|
57
|
+
const scripts = {};
|
|
58
|
+
for (const [path, buf] of files) {
|
|
59
|
+
if (path.startsWith('scripts/') && path.length > 'scripts/'.length) {
|
|
60
|
+
const filename = path.slice('scripts/'.length);
|
|
61
|
+
scripts[filename] = buf.toString('utf-8');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const action of actions) {
|
|
65
|
+
if (!action.id || !action.method || !action.path || !action.script) {
|
|
66
|
+
return { ok: false, error: 'Each action must have id, method, path, and script fields' };
|
|
67
|
+
}
|
|
68
|
+
if (!scripts[action.script]) {
|
|
69
|
+
return { ok: false, error: `Script "${action.script}" referenced in action "${action.id}" not found in scripts/ directory` };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const [filename, content] of Object.entries(scripts)) {
|
|
73
|
+
const sizeKb = Buffer.byteLength(content, 'utf-8') / 1024;
|
|
74
|
+
if (sizeKb > config.extensionMaxCodeSizeKb) {
|
|
75
|
+
return { ok: false, error: `Script "${filename}" (${Math.round(sizeKb)}KB) exceeds limit of ${config.extensionMaxCodeSizeKb}KB` };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const name = metadata.name;
|
|
79
|
+
const manifestConfig = manifest.config;
|
|
80
|
+
const manifestLimits = manifest.limits;
|
|
81
|
+
const manifestFederation = manifest.federation;
|
|
82
|
+
const manifestSchedules = manifest.schedules;
|
|
83
|
+
const manifestInstances = manifest.instances;
|
|
84
|
+
const record = {
|
|
85
|
+
name,
|
|
86
|
+
version: metadata.version,
|
|
87
|
+
description: metadata.description,
|
|
88
|
+
author: metadata.author,
|
|
89
|
+
status: 'inactive',
|
|
90
|
+
requiredApis: manifest.required_apis ?? [],
|
|
91
|
+
actions: actions.map(a => ({
|
|
92
|
+
id: a.id,
|
|
93
|
+
method: a.method.toUpperCase(),
|
|
94
|
+
path: a.path,
|
|
95
|
+
inputSchema: a.input ?? {},
|
|
96
|
+
outputSchema: a.output ?? {},
|
|
97
|
+
scriptContent: scripts[a.script],
|
|
98
|
+
})),
|
|
99
|
+
config: {
|
|
100
|
+
...(manifestConfig
|
|
101
|
+
? Object.fromEntries(Object.entries(manifestConfig).map(([k, v]) => {
|
|
102
|
+
if (v && typeof v === 'object' && 'default' in v) {
|
|
103
|
+
return [k, v.default];
|
|
104
|
+
}
|
|
105
|
+
return [k, v];
|
|
106
|
+
}))
|
|
107
|
+
: {}),
|
|
108
|
+
...(manifestSchedules ? { __schedules: manifestSchedules } : {}),
|
|
109
|
+
},
|
|
110
|
+
limits: {
|
|
111
|
+
memoryMb: Math.min(manifestLimits?.memory_mb ?? config.extensionMaxMemoryMb, config.extensionMaxMemoryMb),
|
|
112
|
+
timeoutMs: Math.min(manifestLimits?.timeout_ms ?? config.extensionTimeoutMs, config.extensionTimeoutMs),
|
|
113
|
+
maxApiCalls: Math.min(manifestLimits?.max_api_calls ?? config.extensionMaxApiCalls, config.extensionMaxApiCalls),
|
|
114
|
+
},
|
|
115
|
+
federation: {
|
|
116
|
+
advertise: manifestFederation?.advertise ?? false,
|
|
117
|
+
capabilities: manifestFederation?.capabilities ?? [],
|
|
118
|
+
},
|
|
119
|
+
...(manifestInstances?.supported ? {
|
|
120
|
+
instances: {
|
|
121
|
+
supported: true,
|
|
122
|
+
configSchema: manifestInstances.config_per_instance ?? undefined,
|
|
123
|
+
},
|
|
124
|
+
} : {}),
|
|
125
|
+
installedBy: 'upload',
|
|
126
|
+
installedAt: new Date().toISOString(),
|
|
127
|
+
};
|
|
128
|
+
return { ok: true, record };
|
|
129
|
+
}
|
|
130
|
+
export async function parseCortexZip(buffer, config, ownerName) {
|
|
131
|
+
if (buffer.length < 4 || !buffer.subarray(0, 4).equals(ZIP_MAGIC)) {
|
|
132
|
+
return { ok: false, error: 'Not a valid ZIP file (missing magic bytes)' };
|
|
133
|
+
}
|
|
134
|
+
const maxBytes = config.cortexMaxLibSizeKb * 1024 * MAX_FILES;
|
|
135
|
+
if (buffer.length > maxBytes) {
|
|
136
|
+
return { ok: false, error: 'ZIP exceeds maximum size' };
|
|
137
|
+
}
|
|
138
|
+
let files;
|
|
139
|
+
try {
|
|
140
|
+
files = await extractZipEntries(buffer);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
return { ok: false, error: `ZIP extraction failed: ${err.message}` };
|
|
144
|
+
}
|
|
145
|
+
const manifestBuf = files.get('manifest.yaml');
|
|
146
|
+
if (!manifestBuf) {
|
|
147
|
+
return { ok: false, error: 'ZIP must contain manifest.yaml at root' };
|
|
148
|
+
}
|
|
149
|
+
const manifestStr = manifestBuf.toString('utf-8');
|
|
150
|
+
const manifestSizeKb = Buffer.byteLength(manifestStr, 'utf-8') / 1024;
|
|
151
|
+
if (manifestSizeKb > config.cortexMaxLibSizeKb) {
|
|
152
|
+
return { ok: false, error: `Manifest size ${Math.round(manifestSizeKb)}KB exceeds limit of ${config.cortexMaxLibSizeKb}KB` };
|
|
153
|
+
}
|
|
154
|
+
// Extract libs from libs/ directory
|
|
155
|
+
const libs = {};
|
|
156
|
+
for (const [path, buf] of files) {
|
|
157
|
+
if (path.startsWith('libs/') && path.length > 'libs/'.length) {
|
|
158
|
+
const filename = path.slice('libs/'.length);
|
|
159
|
+
const content = buf.toString('utf-8');
|
|
160
|
+
const sizeKb = Buffer.byteLength(content, 'utf-8') / 1024;
|
|
161
|
+
if (sizeKb > config.cortexMaxLibSizeKb) {
|
|
162
|
+
return { ok: false, error: `Lib "${filename}" is ${sizeKb.toFixed(1)}KB, max is ${config.cortexMaxLibSizeKb}KB` };
|
|
163
|
+
}
|
|
164
|
+
libs[filename] = content;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const result = parseCortexManifest(manifestStr, ownerName, Object.keys(libs).length > 0 ? libs : undefined);
|
|
168
|
+
if (!result.ok || !result.extension) {
|
|
169
|
+
return { ok: false, error: result.errors?.join('; ') ?? 'Manifest validation failed' };
|
|
170
|
+
}
|
|
171
|
+
return { ok: true, extension: result.extension, libs: Object.keys(libs).length > 0 ? libs : undefined };
|
|
172
|
+
}
|
|
173
|
+
// ── Internal ZIP extraction ──
|
|
174
|
+
function extractZipEntries(buffer) {
|
|
175
|
+
return new Promise((resolve, reject) => {
|
|
176
|
+
yauzl.fromBuffer(buffer, { lazyEntries: true }, (err, zipfile) => {
|
|
177
|
+
if (err)
|
|
178
|
+
return reject(err);
|
|
179
|
+
const files = new Map();
|
|
180
|
+
let totalDecompressed = 0;
|
|
181
|
+
const maxDecompressed = buffer.length * DECOMPRESSION_RATIO;
|
|
182
|
+
let fileCount = 0;
|
|
183
|
+
zipfile.on('error', reject);
|
|
184
|
+
zipfile.on('entry', (entry) => {
|
|
185
|
+
fileCount++;
|
|
186
|
+
if (fileCount > MAX_FILES) {
|
|
187
|
+
zipfile.close();
|
|
188
|
+
return reject(new Error(`ZIP contains more than ${MAX_FILES} files`));
|
|
189
|
+
}
|
|
190
|
+
const fileName = entry.fileName;
|
|
191
|
+
if (fileName.includes('../') || fileName.startsWith('/')) {
|
|
192
|
+
zipfile.close();
|
|
193
|
+
return reject(new Error(`Dangerous path detected: ${fileName}`));
|
|
194
|
+
}
|
|
195
|
+
// Symlink check (Unix external attributes)
|
|
196
|
+
const unixMode = (entry.externalFileAttributes >> 16) & 0xffff;
|
|
197
|
+
if ((unixMode & 0o170000) === 0o120000) {
|
|
198
|
+
zipfile.close();
|
|
199
|
+
return reject(new Error(`Symlink detected: ${fileName}`));
|
|
200
|
+
}
|
|
201
|
+
const depth = fileName.split('/').length - 1;
|
|
202
|
+
if (depth > MAX_DEPTH) {
|
|
203
|
+
zipfile.close();
|
|
204
|
+
return reject(new Error(`Directory depth exceeds ${MAX_DEPTH}: ${fileName}`));
|
|
205
|
+
}
|
|
206
|
+
if (fileName.endsWith('/')) {
|
|
207
|
+
zipfile.readEntry();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
zipfile.openReadStream(entry, (readErr, stream) => {
|
|
211
|
+
if (readErr) {
|
|
212
|
+
zipfile.close();
|
|
213
|
+
return reject(readErr);
|
|
214
|
+
}
|
|
215
|
+
const chunks = [];
|
|
216
|
+
stream.on('data', (chunk) => {
|
|
217
|
+
totalDecompressed += chunk.length;
|
|
218
|
+
if (totalDecompressed > maxDecompressed) {
|
|
219
|
+
zipfile.close();
|
|
220
|
+
return reject(new Error('Decompression ratio exceeded (possible zip bomb)'));
|
|
221
|
+
}
|
|
222
|
+
chunks.push(chunk);
|
|
223
|
+
});
|
|
224
|
+
stream.on('end', () => {
|
|
225
|
+
files.set(fileName, Buffer.concat(chunks));
|
|
226
|
+
zipfile.readEntry();
|
|
227
|
+
});
|
|
228
|
+
stream.on('error', (e) => { zipfile.close(); reject(e); });
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
zipfile.on('end', () => resolve(files));
|
|
232
|
+
zipfile.readEntry();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=upload-zip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-zip.js","sourceRoot":"","sources":["../../../src/services/upload-zip.ts"],"names":[],"mappings":"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"}
|