@shipstatic/types 0.5.4 → 0.6.0
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/index.d.ts +18 -80
- package/dist/index.js +41 -201
- package/package.json +1 -1
- package/src/index.ts +48 -246
package/dist/index.d.ts
CHANGED
|
@@ -345,99 +345,38 @@ export declare class ShipError extends Error {
|
|
|
345
345
|
*/
|
|
346
346
|
export declare function isShipError(error: unknown): error is ShipError;
|
|
347
347
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
* Contains ONLY dynamic, runtime-specific values (plan-based limits).
|
|
351
|
-
* Static constants (MIME types, validation rules) live as exported constants.
|
|
348
|
+
* Dynamic platform configuration returned by the /config endpoint.
|
|
349
|
+
* Contains plan-based limits that vary by account.
|
|
352
350
|
*/
|
|
353
351
|
export interface ConfigResponse {
|
|
354
|
-
/** Maximum individual file size in bytes */
|
|
355
352
|
maxFileSize: number;
|
|
356
|
-
/** Maximum number of files per deployment */
|
|
357
353
|
maxFilesCount: number;
|
|
358
|
-
/** Maximum total deployment size in bytes */
|
|
359
354
|
maxTotalSize: number;
|
|
360
355
|
}
|
|
361
356
|
/**
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
* This is a static platform constant, not per-user configuration.
|
|
365
|
-
* Safe to share across frontend/backend due to atomic deploys.
|
|
366
|
-
*
|
|
367
|
-
* Validation rules:
|
|
368
|
-
* - Exact match: 'application/json' allows only 'application/json'
|
|
369
|
-
* - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
|
|
370
|
-
*
|
|
371
|
-
* Coverage: 100% of browser-renderable web content
|
|
372
|
-
* - Core web (HTML, CSS, JS, WASM)
|
|
373
|
-
* - Media (images, audio, video, fonts)
|
|
374
|
-
* - Documents (PDF, Markdown, data formats)
|
|
375
|
-
* - Modern web (PWA, 3D, structured data)
|
|
376
|
-
*
|
|
377
|
-
* ============================================================================
|
|
378
|
-
* INTENTIONALLY EXCLUDED (Security & Platform Integrity)
|
|
379
|
-
* ============================================================================
|
|
380
|
-
*
|
|
381
|
-
* We are a WEB HOSTING platform, not a file distribution service.
|
|
382
|
-
* GitHub Pages-style parity for renderable content, more restrictive for downloads.
|
|
383
|
-
*
|
|
384
|
-
* 1. EXECUTABLES (Malware Distribution)
|
|
385
|
-
* → .exe, .msi, .dmg, .deb, .rpm, .app, .apk, .jar
|
|
386
|
-
* → Reason: Direct malware delivery vector
|
|
387
|
-
* → Alternative: Use GitHub Releases or dedicated software distribution CDN
|
|
357
|
+
* Blocked file extensions — files that cannot be uploaded.
|
|
388
358
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
* → Alternative: Use file hosting service (Dropbox, Google Drive) or GitHub Releases
|
|
359
|
+
* We accept any file type by default and derive Content-Type from the
|
|
360
|
+
* extension at serve time (via mime-db in the API worker). Unknown extensions
|
|
361
|
+
* are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.
|
|
393
362
|
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
* → Reason: Source code exposure (database passwords, API keys, secrets)
|
|
397
|
-
* → Alternative: Static hosting only - use serverless functions for backends
|
|
398
|
-
*
|
|
399
|
-
* 4. SHELL SCRIPTS (OS Execution)
|
|
400
|
-
* → .sh, .bash, .bat, .cmd, .ps1, .vbs
|
|
401
|
-
* → Reason: Execute on user's OS outside browser sandbox, social engineering risk
|
|
402
|
-
* → Alternative: Embed code examples in HTML <pre><code> or link to GitHub repo
|
|
403
|
-
*
|
|
404
|
-
* 5. PROGRAMMING LANGUAGE SOURCE (Platform Scope)
|
|
405
|
-
* → .py, .rb, .pl, .java, .c, .cpp, .cs, .go, .rs
|
|
406
|
-
* → Reason: Not web-renderable, better served by GitHub/GitLab/Bitbucket
|
|
407
|
-
* → Alternative: Use GitHub for code hosting, link to repository
|
|
408
|
-
*
|
|
409
|
-
* 6. OFFICE DOCUMENTS (Macro Malware)
|
|
410
|
-
* → .doc, .docx, .xls, .xlsx, .ppt, .pptx
|
|
411
|
-
* → Reason: Can contain VBA macros, active exploits in the wild
|
|
412
|
-
* → Alternative: Use PDF for documents (fully supported)
|
|
413
|
-
*
|
|
414
|
-
* 7. GENERIC BINARIES (Unvalidatable)
|
|
415
|
-
* → application/octet-stream
|
|
416
|
-
* → Reason: Too broad - allows any binary format, cannot moderate effectively
|
|
417
|
-
* → Alternative: Use specific MIME types for known formats
|
|
418
|
-
*
|
|
419
|
-
* ============================================================================
|
|
420
|
-
* Security Model:
|
|
421
|
-
* - Browser sandbox (JS/WASM execute safely in controlled environment)
|
|
422
|
-
* - AI content moderation (scans text/image content for abuse)
|
|
423
|
-
* - No server-side execution (static files only)
|
|
424
|
-
* - Explicit allowlist (only approved formats, reject unknown)
|
|
425
|
-
* ============================================================================
|
|
363
|
+
* The blocklist targets file types that pose direct security risks when hosted:
|
|
364
|
+
* executables, disk images, malware vectors, dangerous scripts, and shortcuts.
|
|
426
365
|
*/
|
|
427
|
-
export declare const
|
|
366
|
+
export declare const BLOCKED_EXTENSIONS: ReadonlySet<string>;
|
|
428
367
|
/**
|
|
429
|
-
* Check if a
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* - 'application/json' matches 'application/json' exactly
|
|
433
|
-
* - 'text/' matches 'text/plain', 'text/html', etc.
|
|
368
|
+
* Check if a filename has a blocked extension.
|
|
369
|
+
* Extracts the extension from the filename and checks against the blocklist.
|
|
370
|
+
* Case-insensitive. Returns false for files without extensions.
|
|
434
371
|
*
|
|
435
372
|
* @example
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
373
|
+
* isBlockedExtension('virus.exe') // true
|
|
374
|
+
* isBlockedExtension('app.dmg') // true
|
|
375
|
+
* isBlockedExtension('style.css') // false
|
|
376
|
+
* isBlockedExtension('data.custom') // false
|
|
377
|
+
* isBlockedExtension('README') // false
|
|
439
378
|
*/
|
|
440
|
-
export declare function
|
|
379
|
+
export declare function isBlockedExtension(filename: string): boolean;
|
|
441
380
|
/**
|
|
442
381
|
* Simple ping response for health checks
|
|
443
382
|
*/
|
|
@@ -788,7 +727,6 @@ export interface ValidationIssue {
|
|
|
788
727
|
export interface ValidatableFile {
|
|
789
728
|
name: string;
|
|
790
729
|
size: number;
|
|
791
|
-
type: string;
|
|
792
730
|
status?: FileValidationStatusType;
|
|
793
731
|
statusMessage?: string;
|
|
794
732
|
}
|
package/dist/index.js
CHANGED
|
@@ -199,215 +199,55 @@ export function isShipError(error) {
|
|
|
199
199
|
error.name === 'ShipError' &&
|
|
200
200
|
'status' in error);
|
|
201
201
|
}
|
|
202
|
+
// =============================================================================
|
|
203
|
+
// EXTENSION BLOCKLIST
|
|
204
|
+
// =============================================================================
|
|
202
205
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* This is a static platform constant, not per-user configuration.
|
|
206
|
-
* Safe to share across frontend/backend due to atomic deploys.
|
|
207
|
-
*
|
|
208
|
-
* Validation rules:
|
|
209
|
-
* - Exact match: 'application/json' allows only 'application/json'
|
|
210
|
-
* - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
|
|
211
|
-
*
|
|
212
|
-
* Coverage: 100% of browser-renderable web content
|
|
213
|
-
* - Core web (HTML, CSS, JS, WASM)
|
|
214
|
-
* - Media (images, audio, video, fonts)
|
|
215
|
-
* - Documents (PDF, Markdown, data formats)
|
|
216
|
-
* - Modern web (PWA, 3D, structured data)
|
|
217
|
-
*
|
|
218
|
-
* ============================================================================
|
|
219
|
-
* INTENTIONALLY EXCLUDED (Security & Platform Integrity)
|
|
220
|
-
* ============================================================================
|
|
221
|
-
*
|
|
222
|
-
* We are a WEB HOSTING platform, not a file distribution service.
|
|
223
|
-
* GitHub Pages-style parity for renderable content, more restrictive for downloads.
|
|
206
|
+
* Blocked file extensions — files that cannot be uploaded.
|
|
224
207
|
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* → Alternative: Use GitHub Releases or dedicated software distribution CDN
|
|
208
|
+
* We accept any file type by default and derive Content-Type from the
|
|
209
|
+
* extension at serve time (via mime-db in the API worker). Unknown extensions
|
|
210
|
+
* are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.
|
|
229
211
|
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* → Reason: File sharing abuse, can contain executables, no web rendering
|
|
233
|
-
* → Alternative: Use file hosting service (Dropbox, Google Drive) or GitHub Releases
|
|
234
|
-
*
|
|
235
|
-
* 3. SERVER-SIDE SCRIPTS (Credential Leakage)
|
|
236
|
-
* → .php, .asp, .jsp, .cgi
|
|
237
|
-
* → Reason: Source code exposure (database passwords, API keys, secrets)
|
|
238
|
-
* → Alternative: Static hosting only - use serverless functions for backends
|
|
239
|
-
*
|
|
240
|
-
* 4. SHELL SCRIPTS (OS Execution)
|
|
241
|
-
* → .sh, .bash, .bat, .cmd, .ps1, .vbs
|
|
242
|
-
* → Reason: Execute on user's OS outside browser sandbox, social engineering risk
|
|
243
|
-
* → Alternative: Embed code examples in HTML <pre><code> or link to GitHub repo
|
|
244
|
-
*
|
|
245
|
-
* 5. PROGRAMMING LANGUAGE SOURCE (Platform Scope)
|
|
246
|
-
* → .py, .rb, .pl, .java, .c, .cpp, .cs, .go, .rs
|
|
247
|
-
* → Reason: Not web-renderable, better served by GitHub/GitLab/Bitbucket
|
|
248
|
-
* → Alternative: Use GitHub for code hosting, link to repository
|
|
249
|
-
*
|
|
250
|
-
* 6. OFFICE DOCUMENTS (Macro Malware)
|
|
251
|
-
* → .doc, .docx, .xls, .xlsx, .ppt, .pptx
|
|
252
|
-
* → Reason: Can contain VBA macros, active exploits in the wild
|
|
253
|
-
* → Alternative: Use PDF for documents (fully supported)
|
|
254
|
-
*
|
|
255
|
-
* 7. GENERIC BINARIES (Unvalidatable)
|
|
256
|
-
* → application/octet-stream
|
|
257
|
-
* → Reason: Too broad - allows any binary format, cannot moderate effectively
|
|
258
|
-
* → Alternative: Use specific MIME types for known formats
|
|
259
|
-
*
|
|
260
|
-
* ============================================================================
|
|
261
|
-
* Security Model:
|
|
262
|
-
* - Browser sandbox (JS/WASM execute safely in controlled environment)
|
|
263
|
-
* - AI content moderation (scans text/image content for abuse)
|
|
264
|
-
* - No server-side execution (static files only)
|
|
265
|
-
* - Explicit allowlist (only approved formats, reject unknown)
|
|
266
|
-
* ============================================================================
|
|
212
|
+
* The blocklist targets file types that pose direct security risks when hosted:
|
|
213
|
+
* executables, disk images, malware vectors, dangerous scripts, and shortcuts.
|
|
267
214
|
*/
|
|
268
|
-
export const
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
'
|
|
275
|
-
|
|
276
|
-
'
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
'
|
|
281
|
-
|
|
282
|
-
'
|
|
283
|
-
//
|
|
284
|
-
'
|
|
285
|
-
|
|
286
|
-
// Web-specific formats
|
|
287
|
-
'text/vtt', // WebVTT video subtitles/captions (accessibility)
|
|
288
|
-
'text/srt', // SRT subtitles (SubRip format, legacy video captions)
|
|
289
|
-
'text/calendar', // iCalendar (.ics) event files
|
|
290
|
-
// JavaScript (legacy MIME type, still widely used by ~50% of servers)
|
|
291
|
-
'text/javascript',
|
|
292
|
-
// Modern web development formats (uncompiled source)
|
|
293
|
-
'text/typescript', // TypeScript source (.ts)
|
|
294
|
-
'application/x-typescript', // TypeScript (alternative MIME type, .d.ts declarations)
|
|
295
|
-
'text/tsx', // TypeScript JSX (.tsx)
|
|
296
|
-
'text/jsx', // React JSX (.jsx)
|
|
297
|
-
'text/x-scss', // SCSS preprocessor
|
|
298
|
-
'text/x-sass', // Sass preprocessor
|
|
299
|
-
'text/less', // Less preprocessor
|
|
300
|
-
'text/x-less', // Less preprocessor (alternative)
|
|
301
|
-
'text/stylus', // Stylus preprocessor
|
|
302
|
-
'text/x-vue', // Vue single-file components (.vue)
|
|
303
|
-
'text/x-svelte', // Svelte components (.svelte)
|
|
304
|
-
// Developer documentation formats
|
|
305
|
-
'text/x-sql', // SQL files (database schemas, migrations)
|
|
306
|
-
'text/x-diff', // Diff files (code comparisons, patches)
|
|
307
|
-
'text/x-patch', // Patch files (version upgrades, migrations)
|
|
308
|
-
'text/x-protobuf', // Protocol Buffers text format (gRPC schemas)
|
|
309
|
-
'text/x-ini', // INI configuration files
|
|
310
|
-
// Academic/research formats
|
|
311
|
-
'text/x-tex', // LaTeX documents
|
|
312
|
-
'text/x-latex', // LaTeX documents (alternative)
|
|
313
|
-
'text/x-bibtex', // BibTeX citations
|
|
314
|
-
'text/x-r-markdown', // R Markdown (statistical documentation)
|
|
315
|
-
// =========================================================================
|
|
316
|
-
// MEDIA (prefix matching - covers all common subtypes)
|
|
317
|
-
// =========================================================================
|
|
318
|
-
// Images: PNG, JPEG, GIF, SVG, WebP, AVIF, HEIC, BMP, TIFF, ICO, etc.
|
|
319
|
-
'image/',
|
|
320
|
-
// Audio: MP3, OGG, WAV, WebM, AAC, FLAC, Opus, etc.
|
|
321
|
-
'audio/',
|
|
322
|
-
// Video: MP4, WebM, OGG, QuickTime, etc.
|
|
323
|
-
'video/',
|
|
324
|
-
// Modern fonts: WOFF2, WOFF, TTF, OTF
|
|
325
|
-
'font/',
|
|
326
|
-
// =========================================================================
|
|
327
|
-
// CORE WEB APPLICATION TYPES
|
|
328
|
-
// =========================================================================
|
|
329
|
-
// JavaScript (multiple MIME types for compatibility)
|
|
330
|
-
'application/javascript', // Modern standard (RFC 9239)
|
|
331
|
-
'application/ecmascript', // ECMAScript (legacy but still used)
|
|
332
|
-
'application/x-javascript', // Legacy variant (old CDNs, Apache configs)
|
|
333
|
-
// WebAssembly (modern web apps, games, compute-heavy workloads)
|
|
334
|
-
'application/wasm',
|
|
335
|
-
// JSON and structured data
|
|
336
|
-
'application/json',
|
|
337
|
-
'application/ld+json', // JSON-LD for structured data / SEO (Schema.org, Open Graph)
|
|
338
|
-
'application/geo+json', // GeoJSON for mapping (Leaflet, Mapbox)
|
|
339
|
-
'application/manifest+json', // PWA web app manifests
|
|
340
|
-
'application/x-ipynb+json', // Jupyter Notebooks (data science, ML tutorials)
|
|
341
|
-
// JSON variants (AI/ML, streaming data, configs)
|
|
342
|
-
'application/x-ndjson', // Newline-Delimited JSON (training datasets, logs)
|
|
343
|
-
'application/ndjson', // NDJSON (alternative MIME type)
|
|
344
|
-
'text/x-ndjson', // NDJSON (text variant)
|
|
345
|
-
'application/jsonl', // JSON Lines (Hugging Face, OpenAI fine-tuning)
|
|
346
|
-
'text/jsonl', // JSON Lines (text variant)
|
|
347
|
-
'application/json5', // JSON5 (JSON with comments, trailing commas)
|
|
348
|
-
'text/json5', // JSON5 (text variant)
|
|
349
|
-
'application/schema+json', // JSON Schema (API specs, model definitions)
|
|
350
|
-
// Development tools
|
|
351
|
-
'application/source-map', // Source maps (.js.map, .css.map) for debugging
|
|
352
|
-
// XML and feeds
|
|
353
|
-
'application/xml',
|
|
354
|
-
'application/xhtml+xml', // XHTML - XML-compliant HTML (legacy sites)
|
|
355
|
-
'application/rss+xml', // RSS feeds (blogs, podcasts)
|
|
356
|
-
'application/atom+xml', // Atom feeds
|
|
357
|
-
'application/feed+json', // JSON Feed (modern RSS alternative)
|
|
358
|
-
'application/vnd.google-earth.kml+xml', // KML for mapping (Google Earth, GIS)
|
|
359
|
-
// Configuration formats
|
|
360
|
-
'application/yaml', // YAML configs (static site generators)
|
|
361
|
-
'application/toml', // TOML configs (Cargo, Netlify, Rust projects)
|
|
362
|
-
// Documents
|
|
363
|
-
'application/pdf', // PDF documents
|
|
364
|
-
// Media metadata
|
|
365
|
-
'application/x-subrip', // SRT subtitles (SubRip format)
|
|
366
|
-
// Developer tools and schemas
|
|
367
|
-
'application/sql', // SQL files (database schemas, queries)
|
|
368
|
-
'application/graphql', // GraphQL schemas (API documentation)
|
|
369
|
-
'application/graphql+json', // GraphQL with JSON encoding
|
|
370
|
-
'application/x-protobuf', // Protocol Buffers binary (gRPC)
|
|
371
|
-
'application/x-ini', // INI configuration files
|
|
372
|
-
// Academic formats
|
|
373
|
-
'application/x-tex', // LaTeX documents
|
|
374
|
-
'application/x-bibtex', // BibTeX citations
|
|
375
|
-
// =========================================================================
|
|
376
|
-
// 3D FORMATS (industry standard only)
|
|
377
|
-
// =========================================================================
|
|
378
|
-
// glTF - Khronos standard for 3D web content
|
|
379
|
-
'model/gltf+json', // glTF JSON format
|
|
380
|
-
'model/gltf-binary', // GLB binary format
|
|
381
|
-
// =========================================================================
|
|
382
|
-
// LEGACY COMPATIBILITY
|
|
383
|
-
// =========================================================================
|
|
384
|
-
// Video (some tools detect MP4 as application/mp4)
|
|
385
|
-
'application/mp4',
|
|
386
|
-
// Legacy font MIME types (Bootstrap, Font Awesome, IE compatibility)
|
|
387
|
-
'application/font-woff',
|
|
388
|
-
'application/font-woff2',
|
|
389
|
-
'application/x-font-woff',
|
|
390
|
-
'application/x-woff',
|
|
391
|
-
'application/vnd.ms-fontobject', // EOT files (Internet Explorer)
|
|
392
|
-
'application/x-font-ttf',
|
|
393
|
-
'application/x-font-truetype',
|
|
394
|
-
'application/x-font-otf',
|
|
395
|
-
'application/x-font-opentype',
|
|
396
|
-
];
|
|
215
|
+
export const BLOCKED_EXTENSIONS = new Set([
|
|
216
|
+
// Executables
|
|
217
|
+
'exe', 'msi', 'dll', 'scr', 'bat', 'cmd', 'com', 'pif', 'app', 'deb', 'rpm',
|
|
218
|
+
// Installers
|
|
219
|
+
'pkg', 'mpkg',
|
|
220
|
+
// Disk images
|
|
221
|
+
'dmg', 'iso', 'img',
|
|
222
|
+
// Malware vectors
|
|
223
|
+
'cab', 'cpl', 'chm',
|
|
224
|
+
// Dangerous scripts
|
|
225
|
+
'ps1', 'vbs', 'vbe', 'ws', 'wsf', 'wsc', 'wsh', 'reg',
|
|
226
|
+
// Java
|
|
227
|
+
'jar', 'jnlp',
|
|
228
|
+
// Mobile/browser packages
|
|
229
|
+
'apk', 'crx',
|
|
230
|
+
// Shortcut/link
|
|
231
|
+
'lnk', 'inf', 'hta',
|
|
232
|
+
]);
|
|
397
233
|
/**
|
|
398
|
-
* Check if a
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
* - 'application/json' matches 'application/json' exactly
|
|
402
|
-
* - 'text/' matches 'text/plain', 'text/html', etc.
|
|
234
|
+
* Check if a filename has a blocked extension.
|
|
235
|
+
* Extracts the extension from the filename and checks against the blocklist.
|
|
236
|
+
* Case-insensitive. Returns false for files without extensions.
|
|
403
237
|
*
|
|
404
238
|
* @example
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
239
|
+
* isBlockedExtension('virus.exe') // true
|
|
240
|
+
* isBlockedExtension('app.dmg') // true
|
|
241
|
+
* isBlockedExtension('style.css') // false
|
|
242
|
+
* isBlockedExtension('data.custom') // false
|
|
243
|
+
* isBlockedExtension('README') // false
|
|
408
244
|
*/
|
|
409
|
-
export function
|
|
410
|
-
|
|
245
|
+
export function isBlockedExtension(filename) {
|
|
246
|
+
const dotIndex = filename.lastIndexOf('.');
|
|
247
|
+
if (dotIndex === -1 || dotIndex === filename.length - 1)
|
|
248
|
+
return false;
|
|
249
|
+
const ext = filename.slice(dotIndex + 1).toLowerCase();
|
|
250
|
+
return BLOCKED_EXTENSIONS.has(ext);
|
|
411
251
|
}
|
|
412
252
|
// API Key Configuration
|
|
413
253
|
export const API_KEY_PREFIX = 'ship-';
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -498,266 +498,69 @@ export function isShipError(error: unknown): error is ShipError {
|
|
|
498
498
|
// =============================================================================
|
|
499
499
|
|
|
500
500
|
/**
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
* Contains ONLY dynamic, runtime-specific values (plan-based limits).
|
|
504
|
-
* Static constants (MIME types, validation rules) live as exported constants.
|
|
501
|
+
* Dynamic platform configuration returned by the /config endpoint.
|
|
502
|
+
* Contains plan-based limits that vary by account.
|
|
505
503
|
*/
|
|
506
504
|
export interface ConfigResponse {
|
|
507
|
-
/** Maximum individual file size in bytes */
|
|
508
505
|
maxFileSize: number;
|
|
509
|
-
/** Maximum number of files per deployment */
|
|
510
506
|
maxFilesCount: number;
|
|
511
|
-
/** Maximum total deployment size in bytes */
|
|
512
507
|
maxTotalSize: number;
|
|
513
508
|
}
|
|
514
509
|
|
|
510
|
+
// =============================================================================
|
|
511
|
+
// EXTENSION BLOCKLIST
|
|
512
|
+
// =============================================================================
|
|
513
|
+
|
|
515
514
|
/**
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
* This is a static platform constant, not per-user configuration.
|
|
519
|
-
* Safe to share across frontend/backend due to atomic deploys.
|
|
520
|
-
*
|
|
521
|
-
* Validation rules:
|
|
522
|
-
* - Exact match: 'application/json' allows only 'application/json'
|
|
523
|
-
* - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
|
|
524
|
-
*
|
|
525
|
-
* Coverage: 100% of browser-renderable web content
|
|
526
|
-
* - Core web (HTML, CSS, JS, WASM)
|
|
527
|
-
* - Media (images, audio, video, fonts)
|
|
528
|
-
* - Documents (PDF, Markdown, data formats)
|
|
529
|
-
* - Modern web (PWA, 3D, structured data)
|
|
530
|
-
*
|
|
531
|
-
* ============================================================================
|
|
532
|
-
* INTENTIONALLY EXCLUDED (Security & Platform Integrity)
|
|
533
|
-
* ============================================================================
|
|
534
|
-
*
|
|
535
|
-
* We are a WEB HOSTING platform, not a file distribution service.
|
|
536
|
-
* GitHub Pages-style parity for renderable content, more restrictive for downloads.
|
|
515
|
+
* Blocked file extensions — files that cannot be uploaded.
|
|
537
516
|
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
* → Alternative: Use GitHub Releases or dedicated software distribution CDN
|
|
517
|
+
* We accept any file type by default and derive Content-Type from the
|
|
518
|
+
* extension at serve time (via mime-db in the API worker). Unknown extensions
|
|
519
|
+
* are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.
|
|
542
520
|
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
* → application/octet-stream
|
|
570
|
-
* → Reason: Too broad - allows any binary format, cannot moderate effectively
|
|
571
|
-
* → Alternative: Use specific MIME types for known formats
|
|
572
|
-
*
|
|
573
|
-
* ============================================================================
|
|
574
|
-
* Security Model:
|
|
575
|
-
* - Browser sandbox (JS/WASM execute safely in controlled environment)
|
|
576
|
-
* - AI content moderation (scans text/image content for abuse)
|
|
577
|
-
* - No server-side execution (static files only)
|
|
578
|
-
* - Explicit allowlist (only approved formats, reject unknown)
|
|
579
|
-
* ============================================================================
|
|
580
|
-
*/
|
|
581
|
-
export const ALLOWED_MIME_TYPES = [
|
|
582
|
-
// =========================================================================
|
|
583
|
-
// TEXT CONTENT (explicit list - no prefix matching for security)
|
|
584
|
-
// =========================================================================
|
|
585
|
-
|
|
586
|
-
// Core web documents
|
|
587
|
-
'text/html', // HTML pages
|
|
588
|
-
'text/css', // Stylesheets
|
|
589
|
-
'text/plain', // Plain text (robots.txt, .well-known/*, LICENSE, README.txt)
|
|
590
|
-
'text/markdown', // Markdown files (.md)
|
|
591
|
-
'text/xml', // XML files
|
|
592
|
-
|
|
593
|
-
// Data formats
|
|
594
|
-
'text/csv', // CSV data files
|
|
595
|
-
'text/tab-separated-values', // TSV data files
|
|
596
|
-
'text/yaml', // YAML config files
|
|
597
|
-
'text/vcard', // VCard contact files (.vcf)
|
|
598
|
-
|
|
599
|
-
// Modern documentation formats
|
|
600
|
-
'text/mdx', // MDX (Markdown with JSX) - Next.js, Docusaurus, Nextra
|
|
601
|
-
'text/x-mdx', // MDX (alternative MIME type)
|
|
602
|
-
|
|
603
|
-
// Web-specific formats
|
|
604
|
-
'text/vtt', // WebVTT video subtitles/captions (accessibility)
|
|
605
|
-
'text/srt', // SRT subtitles (SubRip format, legacy video captions)
|
|
606
|
-
'text/calendar', // iCalendar (.ics) event files
|
|
607
|
-
|
|
608
|
-
// JavaScript (legacy MIME type, still widely used by ~50% of servers)
|
|
609
|
-
'text/javascript',
|
|
610
|
-
|
|
611
|
-
// Modern web development formats (uncompiled source)
|
|
612
|
-
'text/typescript', // TypeScript source (.ts)
|
|
613
|
-
'application/x-typescript', // TypeScript (alternative MIME type, .d.ts declarations)
|
|
614
|
-
'text/tsx', // TypeScript JSX (.tsx)
|
|
615
|
-
'text/jsx', // React JSX (.jsx)
|
|
616
|
-
'text/x-scss', // SCSS preprocessor
|
|
617
|
-
'text/x-sass', // Sass preprocessor
|
|
618
|
-
'text/less', // Less preprocessor
|
|
619
|
-
'text/x-less', // Less preprocessor (alternative)
|
|
620
|
-
'text/stylus', // Stylus preprocessor
|
|
621
|
-
'text/x-vue', // Vue single-file components (.vue)
|
|
622
|
-
'text/x-svelte', // Svelte components (.svelte)
|
|
623
|
-
|
|
624
|
-
// Developer documentation formats
|
|
625
|
-
'text/x-sql', // SQL files (database schemas, migrations)
|
|
626
|
-
'text/x-diff', // Diff files (code comparisons, patches)
|
|
627
|
-
'text/x-patch', // Patch files (version upgrades, migrations)
|
|
628
|
-
'text/x-protobuf', // Protocol Buffers text format (gRPC schemas)
|
|
629
|
-
'text/x-ini', // INI configuration files
|
|
630
|
-
|
|
631
|
-
// Academic/research formats
|
|
632
|
-
'text/x-tex', // LaTeX documents
|
|
633
|
-
'text/x-latex', // LaTeX documents (alternative)
|
|
634
|
-
'text/x-bibtex', // BibTeX citations
|
|
635
|
-
'text/x-r-markdown', // R Markdown (statistical documentation)
|
|
636
|
-
|
|
637
|
-
// =========================================================================
|
|
638
|
-
// MEDIA (prefix matching - covers all common subtypes)
|
|
639
|
-
// =========================================================================
|
|
640
|
-
|
|
641
|
-
// Images: PNG, JPEG, GIF, SVG, WebP, AVIF, HEIC, BMP, TIFF, ICO, etc.
|
|
642
|
-
'image/',
|
|
643
|
-
|
|
644
|
-
// Audio: MP3, OGG, WAV, WebM, AAC, FLAC, Opus, etc.
|
|
645
|
-
'audio/',
|
|
646
|
-
|
|
647
|
-
// Video: MP4, WebM, OGG, QuickTime, etc.
|
|
648
|
-
'video/',
|
|
649
|
-
|
|
650
|
-
// Modern fonts: WOFF2, WOFF, TTF, OTF
|
|
651
|
-
'font/',
|
|
652
|
-
|
|
653
|
-
// =========================================================================
|
|
654
|
-
// CORE WEB APPLICATION TYPES
|
|
655
|
-
// =========================================================================
|
|
656
|
-
|
|
657
|
-
// JavaScript (multiple MIME types for compatibility)
|
|
658
|
-
'application/javascript', // Modern standard (RFC 9239)
|
|
659
|
-
'application/ecmascript', // ECMAScript (legacy but still used)
|
|
660
|
-
'application/x-javascript', // Legacy variant (old CDNs, Apache configs)
|
|
661
|
-
|
|
662
|
-
// WebAssembly (modern web apps, games, compute-heavy workloads)
|
|
663
|
-
'application/wasm',
|
|
664
|
-
|
|
665
|
-
// JSON and structured data
|
|
666
|
-
'application/json',
|
|
667
|
-
'application/ld+json', // JSON-LD for structured data / SEO (Schema.org, Open Graph)
|
|
668
|
-
'application/geo+json', // GeoJSON for mapping (Leaflet, Mapbox)
|
|
669
|
-
'application/manifest+json', // PWA web app manifests
|
|
670
|
-
'application/x-ipynb+json', // Jupyter Notebooks (data science, ML tutorials)
|
|
671
|
-
|
|
672
|
-
// JSON variants (AI/ML, streaming data, configs)
|
|
673
|
-
'application/x-ndjson', // Newline-Delimited JSON (training datasets, logs)
|
|
674
|
-
'application/ndjson', // NDJSON (alternative MIME type)
|
|
675
|
-
'text/x-ndjson', // NDJSON (text variant)
|
|
676
|
-
'application/jsonl', // JSON Lines (Hugging Face, OpenAI fine-tuning)
|
|
677
|
-
'text/jsonl', // JSON Lines (text variant)
|
|
678
|
-
'application/json5', // JSON5 (JSON with comments, trailing commas)
|
|
679
|
-
'text/json5', // JSON5 (text variant)
|
|
680
|
-
'application/schema+json', // JSON Schema (API specs, model definitions)
|
|
681
|
-
|
|
682
|
-
// Development tools
|
|
683
|
-
'application/source-map', // Source maps (.js.map, .css.map) for debugging
|
|
684
|
-
|
|
685
|
-
// XML and feeds
|
|
686
|
-
'application/xml',
|
|
687
|
-
'application/xhtml+xml', // XHTML - XML-compliant HTML (legacy sites)
|
|
688
|
-
'application/rss+xml', // RSS feeds (blogs, podcasts)
|
|
689
|
-
'application/atom+xml', // Atom feeds
|
|
690
|
-
'application/feed+json', // JSON Feed (modern RSS alternative)
|
|
691
|
-
'application/vnd.google-earth.kml+xml', // KML for mapping (Google Earth, GIS)
|
|
692
|
-
|
|
693
|
-
// Configuration formats
|
|
694
|
-
'application/yaml', // YAML configs (static site generators)
|
|
695
|
-
'application/toml', // TOML configs (Cargo, Netlify, Rust projects)
|
|
696
|
-
|
|
697
|
-
// Documents
|
|
698
|
-
'application/pdf', // PDF documents
|
|
699
|
-
|
|
700
|
-
// Media metadata
|
|
701
|
-
'application/x-subrip', // SRT subtitles (SubRip format)
|
|
702
|
-
|
|
703
|
-
// Developer tools and schemas
|
|
704
|
-
'application/sql', // SQL files (database schemas, queries)
|
|
705
|
-
'application/graphql', // GraphQL schemas (API documentation)
|
|
706
|
-
'application/graphql+json', // GraphQL with JSON encoding
|
|
707
|
-
'application/x-protobuf', // Protocol Buffers binary (gRPC)
|
|
708
|
-
'application/x-ini', // INI configuration files
|
|
709
|
-
|
|
710
|
-
// Academic formats
|
|
711
|
-
'application/x-tex', // LaTeX documents
|
|
712
|
-
'application/x-bibtex', // BibTeX citations
|
|
713
|
-
|
|
714
|
-
// =========================================================================
|
|
715
|
-
// 3D FORMATS (industry standard only)
|
|
716
|
-
// =========================================================================
|
|
717
|
-
|
|
718
|
-
// glTF - Khronos standard for 3D web content
|
|
719
|
-
'model/gltf+json', // glTF JSON format
|
|
720
|
-
'model/gltf-binary', // GLB binary format
|
|
721
|
-
|
|
722
|
-
// =========================================================================
|
|
723
|
-
// LEGACY COMPATIBILITY
|
|
724
|
-
// =========================================================================
|
|
725
|
-
|
|
726
|
-
// Video (some tools detect MP4 as application/mp4)
|
|
727
|
-
'application/mp4',
|
|
728
|
-
|
|
729
|
-
// Legacy font MIME types (Bootstrap, Font Awesome, IE compatibility)
|
|
730
|
-
'application/font-woff',
|
|
731
|
-
'application/font-woff2',
|
|
732
|
-
'application/x-font-woff',
|
|
733
|
-
'application/x-woff',
|
|
734
|
-
'application/vnd.ms-fontobject', // EOT files (Internet Explorer)
|
|
735
|
-
'application/x-font-ttf',
|
|
736
|
-
'application/x-font-truetype',
|
|
737
|
-
'application/x-font-otf',
|
|
738
|
-
'application/x-font-opentype',
|
|
739
|
-
] as const;
|
|
740
|
-
|
|
741
|
-
/**
|
|
742
|
-
* Check if a MIME type is allowed for upload.
|
|
743
|
-
*
|
|
744
|
-
* Supports both exact matches and prefix matches:
|
|
745
|
-
* - 'application/json' matches 'application/json' exactly
|
|
746
|
-
* - 'text/' matches 'text/plain', 'text/html', etc.
|
|
521
|
+
* The blocklist targets file types that pose direct security risks when hosted:
|
|
522
|
+
* executables, disk images, malware vectors, dangerous scripts, and shortcuts.
|
|
523
|
+
*/
|
|
524
|
+
export const BLOCKED_EXTENSIONS: ReadonlySet<string> = new Set([
|
|
525
|
+
// Executables
|
|
526
|
+
'exe', 'msi', 'dll', 'scr', 'bat', 'cmd', 'com', 'pif', 'app', 'deb', 'rpm',
|
|
527
|
+
// Installers
|
|
528
|
+
'pkg', 'mpkg',
|
|
529
|
+
// Disk images
|
|
530
|
+
'dmg', 'iso', 'img',
|
|
531
|
+
// Malware vectors
|
|
532
|
+
'cab', 'cpl', 'chm',
|
|
533
|
+
// Dangerous scripts
|
|
534
|
+
'ps1', 'vbs', 'vbe', 'ws', 'wsf', 'wsc', 'wsh', 'reg',
|
|
535
|
+
// Java
|
|
536
|
+
'jar', 'jnlp',
|
|
537
|
+
// Mobile/browser packages
|
|
538
|
+
'apk', 'crx',
|
|
539
|
+
// Shortcut/link
|
|
540
|
+
'lnk', 'inf', 'hta',
|
|
541
|
+
]);
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Check if a filename has a blocked extension.
|
|
545
|
+
* Extracts the extension from the filename and checks against the blocklist.
|
|
546
|
+
* Case-insensitive. Returns false for files without extensions.
|
|
747
547
|
*
|
|
748
548
|
* @example
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
*
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
);
|
|
549
|
+
* isBlockedExtension('virus.exe') // true
|
|
550
|
+
* isBlockedExtension('app.dmg') // true
|
|
551
|
+
* isBlockedExtension('style.css') // false
|
|
552
|
+
* isBlockedExtension('data.custom') // false
|
|
553
|
+
* isBlockedExtension('README') // false
|
|
554
|
+
*/
|
|
555
|
+
export function isBlockedExtension(filename: string): boolean {
|
|
556
|
+
const dotIndex = filename.lastIndexOf('.');
|
|
557
|
+
if (dotIndex === -1 || dotIndex === filename.length - 1) return false;
|
|
558
|
+
const ext = filename.slice(dotIndex + 1).toLowerCase();
|
|
559
|
+
return BLOCKED_EXTENSIONS.has(ext);
|
|
757
560
|
}
|
|
758
561
|
|
|
759
562
|
// =============================================================================
|
|
760
|
-
// COMMON RESPONSE PATTERNS
|
|
563
|
+
// COMMON RESPONSE PATTERNS
|
|
761
564
|
// =============================================================================
|
|
762
565
|
|
|
763
566
|
/**
|
|
@@ -1294,7 +1097,6 @@ export interface ValidationIssue {
|
|
|
1294
1097
|
export interface ValidatableFile {
|
|
1295
1098
|
name: string;
|
|
1296
1099
|
size: number;
|
|
1297
|
-
type: string;
|
|
1298
1100
|
status?: FileValidationStatusType;
|
|
1299
1101
|
statusMessage?: string;
|
|
1300
1102
|
}
|