@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 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
- * Platform configuration response from API
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
- * Allowed MIME types for static web hosting.
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
- * 2. ARCHIVES (Piracy & Abuse)
390
- * .zip, .rar, .tar, .gz, .7z, .bz2
391
- * Reason: File sharing abuse, can contain executables, no web rendering
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
- * 3. SERVER-SIDE SCRIPTS (Credential Leakage)
395
- * .php, .asp, .jsp, .cgi
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 ALLOWED_MIME_TYPES: readonly ["text/html", "text/css", "text/plain", "text/markdown", "text/xml", "text/csv", "text/tab-separated-values", "text/yaml", "text/vcard", "text/mdx", "text/x-mdx", "text/vtt", "text/srt", "text/calendar", "text/javascript", "text/typescript", "application/x-typescript", "text/tsx", "text/jsx", "text/x-scss", "text/x-sass", "text/less", "text/x-less", "text/stylus", "text/x-vue", "text/x-svelte", "text/x-sql", "text/x-diff", "text/x-patch", "text/x-protobuf", "text/x-ini", "text/x-tex", "text/x-latex", "text/x-bibtex", "text/x-r-markdown", "image/", "audio/", "video/", "font/", "application/javascript", "application/ecmascript", "application/x-javascript", "application/wasm", "application/json", "application/ld+json", "application/geo+json", "application/manifest+json", "application/x-ipynb+json", "application/x-ndjson", "application/ndjson", "text/x-ndjson", "application/jsonl", "text/jsonl", "application/json5", "text/json5", "application/schema+json", "application/source-map", "application/xml", "application/xhtml+xml", "application/rss+xml", "application/atom+xml", "application/feed+json", "application/vnd.google-earth.kml+xml", "application/yaml", "application/toml", "application/pdf", "application/x-subrip", "application/sql", "application/graphql", "application/graphql+json", "application/x-protobuf", "application/x-ini", "application/x-tex", "application/x-bibtex", "model/gltf+json", "model/gltf-binary", "application/mp4", "application/font-woff", "application/font-woff2", "application/x-font-woff", "application/x-woff", "application/vnd.ms-fontobject", "application/x-font-ttf", "application/x-font-truetype", "application/x-font-otf", "application/x-font-opentype"];
366
+ export declare const BLOCKED_EXTENSIONS: ReadonlySet<string>;
428
367
  /**
429
- * Check if a MIME type is allowed for upload.
430
- *
431
- * Supports both exact matches and prefix matches:
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
- * isAllowedMimeType('text/plain') // true (prefix match)
437
- * isAllowedMimeType('application/json') // true (exact match)
438
- * isAllowedMimeType('application/wasm') // false (not allowed)
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 isAllowedMimeType(mimeType: string): boolean;
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
- * Allowed MIME types for static web hosting.
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
- * 1. EXECUTABLES (Malware Distribution)
226
- * .exe, .msi, .dmg, .deb, .rpm, .app, .apk, .jar
227
- * Reason: Direct malware delivery vector
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
- * 2. ARCHIVES (Piracy & Abuse)
231
- * .zip, .rar, .tar, .gz, .7z, .bz2
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 ALLOWED_MIME_TYPES = [
269
- // =========================================================================
270
- // TEXT CONTENT (explicit list - no prefix matching for security)
271
- // =========================================================================
272
- // Core web documents
273
- 'text/html', // HTML pages
274
- 'text/css', // Stylesheets
275
- 'text/plain', // Plain text (robots.txt, .well-known/*, LICENSE, README.txt)
276
- 'text/markdown', // Markdown files (.md)
277
- 'text/xml', // XML files
278
- // Data formats
279
- 'text/csv', // CSV data files
280
- 'text/tab-separated-values', // TSV data files
281
- 'text/yaml', // YAML config files
282
- 'text/vcard', // VCard contact files (.vcf)
283
- // Modern documentation formats
284
- 'text/mdx', // MDX (Markdown with JSX) - Next.js, Docusaurus, Nextra
285
- 'text/x-mdx', // MDX (alternative MIME type)
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 MIME type is allowed for upload.
399
- *
400
- * Supports both exact matches and prefix matches:
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
- * isAllowedMimeType('text/plain') // true (prefix match)
406
- * isAllowedMimeType('application/json') // true (exact match)
407
- * isAllowedMimeType('application/wasm') // false (not allowed)
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 isAllowedMimeType(mimeType) {
410
- return ALLOWED_MIME_TYPES.some(allowed => mimeType === allowed || mimeType.startsWith(allowed));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "description": "Shared types for Shipstatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -498,266 +498,69 @@ export function isShipError(error: unknown): error is ShipError {
498
498
  // =============================================================================
499
499
 
500
500
  /**
501
- * Platform configuration response from API
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
- * Allowed MIME types for static web hosting.
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
- * 1. EXECUTABLES (Malware Distribution)
539
- * .exe, .msi, .dmg, .deb, .rpm, .app, .apk, .jar
540
- * Reason: Direct malware delivery vector
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
- * 2. ARCHIVES (Piracy & Abuse)
544
- * .zip, .rar, .tar, .gz, .7z, .bz2
545
- * → Reason: File sharing abuse, can contain executables, no web rendering
546
- * → Alternative: Use file hosting service (Dropbox, Google Drive) or GitHub Releases
547
- *
548
- * 3. SERVER-SIDE SCRIPTS (Credential Leakage)
549
- * → .php, .asp, .jsp, .cgi
550
- * → Reason: Source code exposure (database passwords, API keys, secrets)
551
- * → Alternative: Static hosting only - use serverless functions for backends
552
- *
553
- * 4. SHELL SCRIPTS (OS Execution)
554
- * → .sh, .bash, .bat, .cmd, .ps1, .vbs
555
- * → Reason: Execute on user's OS outside browser sandbox, social engineering risk
556
- * → Alternative: Embed code examples in HTML <pre><code> or link to GitHub repo
557
- *
558
- * 5. PROGRAMMING LANGUAGE SOURCE (Platform Scope)
559
- * → .py, .rb, .pl, .java, .c, .cpp, .cs, .go, .rs
560
- * → Reason: Not web-renderable, better served by GitHub/GitLab/Bitbucket
561
- * → Alternative: Use GitHub for code hosting, link to repository
562
- *
563
- * 6. OFFICE DOCUMENTS (Macro Malware)
564
- * → .doc, .docx, .xls, .xlsx, .ppt, .pptx
565
- * → Reason: Can contain VBA macros, active exploits in the wild
566
- * Alternative: Use PDF for documents (fully supported)
567
- *
568
- * 7. GENERIC BINARIES (Unvalidatable)
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
- * isAllowedMimeType('text/plain') // true (prefix match)
750
- * isAllowedMimeType('application/json') // true (exact match)
751
- * isAllowedMimeType('application/wasm') // false (not allowed)
752
- */
753
- export function isAllowedMimeType(mimeType: string): boolean {
754
- return ALLOWED_MIME_TYPES.some(allowed =>
755
- mimeType === allowed || mimeType.startsWith(allowed)
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
  }