@shipstatic/types 0.5.3 → 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 +26 -84
- package/dist/index.js +41 -201
- package/package.json +1 -1
- package/src/index.ts +56 -250
package/dist/index.d.ts
CHANGED
|
@@ -156,10 +156,12 @@ export interface DomainValidateResponse {
|
|
|
156
156
|
* Deployment token for automated deployments
|
|
157
157
|
*/
|
|
158
158
|
export interface Token {
|
|
159
|
-
/**
|
|
159
|
+
/** 7-char management identifier */
|
|
160
160
|
readonly token: string;
|
|
161
161
|
/** The account this token belongs to */
|
|
162
162
|
readonly account: string;
|
|
163
|
+
/** SHA256 hash of the raw credential (auth lookups only, never exposed to users) */
|
|
164
|
+
readonly hash: string;
|
|
163
165
|
/** IP address locking for security, null if not locked */
|
|
164
166
|
readonly ip: string | null;
|
|
165
167
|
/** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
|
|
@@ -173,10 +175,10 @@ export interface Token {
|
|
|
173
175
|
}
|
|
174
176
|
/**
|
|
175
177
|
* Token as returned by the list endpoint.
|
|
176
|
-
*
|
|
178
|
+
* Shows 7-char management ID, omits account and hash.
|
|
177
179
|
*/
|
|
178
180
|
export interface TokenListItem {
|
|
179
|
-
/**
|
|
181
|
+
/** 7-char management identifier (e.g., "a1b2c3d") */
|
|
180
182
|
readonly token: string;
|
|
181
183
|
/** Labels for categorization and filtering. Always present, empty array when none. */
|
|
182
184
|
labels: string[];
|
|
@@ -200,8 +202,10 @@ export interface TokenListResponse {
|
|
|
200
202
|
* Response for token creation
|
|
201
203
|
*/
|
|
202
204
|
export interface TokenCreateResponse {
|
|
203
|
-
/**
|
|
205
|
+
/** 7-char management identifier */
|
|
204
206
|
token: string;
|
|
207
|
+
/** The raw credential value (shown once at creation, then never again) */
|
|
208
|
+
secret: string;
|
|
205
209
|
/** Labels for categorization and filtering. Always present, empty array when none. */
|
|
206
210
|
labels: string[];
|
|
207
211
|
/** Unix timestamp (seconds) when token expires, null for never */
|
|
@@ -341,99 +345,38 @@ export declare class ShipError extends Error {
|
|
|
341
345
|
*/
|
|
342
346
|
export declare function isShipError(error: unknown): error is ShipError;
|
|
343
347
|
/**
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
* Contains ONLY dynamic, runtime-specific values (plan-based limits).
|
|
347
|
-
* 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.
|
|
348
350
|
*/
|
|
349
351
|
export interface ConfigResponse {
|
|
350
|
-
/** Maximum individual file size in bytes */
|
|
351
352
|
maxFileSize: number;
|
|
352
|
-
/** Maximum number of files per deployment */
|
|
353
353
|
maxFilesCount: number;
|
|
354
|
-
/** Maximum total deployment size in bytes */
|
|
355
354
|
maxTotalSize: number;
|
|
356
355
|
}
|
|
357
356
|
/**
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
* This is a static platform constant, not per-user configuration.
|
|
361
|
-
* Safe to share across frontend/backend due to atomic deploys.
|
|
362
|
-
*
|
|
363
|
-
* Validation rules:
|
|
364
|
-
* - Exact match: 'application/json' allows only 'application/json'
|
|
365
|
-
* - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
|
|
366
|
-
*
|
|
367
|
-
* Coverage: 100% of browser-renderable web content
|
|
368
|
-
* - Core web (HTML, CSS, JS, WASM)
|
|
369
|
-
* - Media (images, audio, video, fonts)
|
|
370
|
-
* - Documents (PDF, Markdown, data formats)
|
|
371
|
-
* - Modern web (PWA, 3D, structured data)
|
|
372
|
-
*
|
|
373
|
-
* ============================================================================
|
|
374
|
-
* INTENTIONALLY EXCLUDED (Security & Platform Integrity)
|
|
375
|
-
* ============================================================================
|
|
376
|
-
*
|
|
377
|
-
* We are a WEB HOSTING platform, not a file distribution service.
|
|
378
|
-
* GitHub Pages-style parity for renderable content, more restrictive for downloads.
|
|
379
|
-
*
|
|
380
|
-
* 1. EXECUTABLES (Malware Distribution)
|
|
381
|
-
* → .exe, .msi, .dmg, .deb, .rpm, .app, .apk, .jar
|
|
382
|
-
* → Reason: Direct malware delivery vector
|
|
383
|
-
* → Alternative: Use GitHub Releases or dedicated software distribution CDN
|
|
357
|
+
* Blocked file extensions — files that cannot be uploaded.
|
|
384
358
|
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
* → 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`.
|
|
389
362
|
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
* → Reason: Source code exposure (database passwords, API keys, secrets)
|
|
393
|
-
* → Alternative: Static hosting only - use serverless functions for backends
|
|
394
|
-
*
|
|
395
|
-
* 4. SHELL SCRIPTS (OS Execution)
|
|
396
|
-
* → .sh, .bash, .bat, .cmd, .ps1, .vbs
|
|
397
|
-
* → Reason: Execute on user's OS outside browser sandbox, social engineering risk
|
|
398
|
-
* → Alternative: Embed code examples in HTML <pre><code> or link to GitHub repo
|
|
399
|
-
*
|
|
400
|
-
* 5. PROGRAMMING LANGUAGE SOURCE (Platform Scope)
|
|
401
|
-
* → .py, .rb, .pl, .java, .c, .cpp, .cs, .go, .rs
|
|
402
|
-
* → Reason: Not web-renderable, better served by GitHub/GitLab/Bitbucket
|
|
403
|
-
* → Alternative: Use GitHub for code hosting, link to repository
|
|
404
|
-
*
|
|
405
|
-
* 6. OFFICE DOCUMENTS (Macro Malware)
|
|
406
|
-
* → .doc, .docx, .xls, .xlsx, .ppt, .pptx
|
|
407
|
-
* → Reason: Can contain VBA macros, active exploits in the wild
|
|
408
|
-
* → Alternative: Use PDF for documents (fully supported)
|
|
409
|
-
*
|
|
410
|
-
* 7. GENERIC BINARIES (Unvalidatable)
|
|
411
|
-
* → application/octet-stream
|
|
412
|
-
* → Reason: Too broad - allows any binary format, cannot moderate effectively
|
|
413
|
-
* → Alternative: Use specific MIME types for known formats
|
|
414
|
-
*
|
|
415
|
-
* ============================================================================
|
|
416
|
-
* Security Model:
|
|
417
|
-
* - Browser sandbox (JS/WASM execute safely in controlled environment)
|
|
418
|
-
* - AI content moderation (scans text/image content for abuse)
|
|
419
|
-
* - No server-side execution (static files only)
|
|
420
|
-
* - Explicit allowlist (only approved formats, reject unknown)
|
|
421
|
-
* ============================================================================
|
|
363
|
+
* The blocklist targets file types that pose direct security risks when hosted:
|
|
364
|
+
* executables, disk images, malware vectors, dangerous scripts, and shortcuts.
|
|
422
365
|
*/
|
|
423
|
-
export declare const
|
|
366
|
+
export declare const BLOCKED_EXTENSIONS: ReadonlySet<string>;
|
|
424
367
|
/**
|
|
425
|
-
* Check if a
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* - 'application/json' matches 'application/json' exactly
|
|
429
|
-
* - '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.
|
|
430
371
|
*
|
|
431
372
|
* @example
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
373
|
+
* isBlockedExtension('virus.exe') // true
|
|
374
|
+
* isBlockedExtension('app.dmg') // true
|
|
375
|
+
* isBlockedExtension('style.css') // false
|
|
376
|
+
* isBlockedExtension('data.custom') // false
|
|
377
|
+
* isBlockedExtension('README') // false
|
|
435
378
|
*/
|
|
436
|
-
export declare function
|
|
379
|
+
export declare function isBlockedExtension(filename: string): boolean;
|
|
437
380
|
/**
|
|
438
381
|
* Simple ping response for health checks
|
|
439
382
|
*/
|
|
@@ -784,7 +727,6 @@ export interface ValidationIssue {
|
|
|
784
727
|
export interface ValidatableFile {
|
|
785
728
|
name: string;
|
|
786
729
|
size: number;
|
|
787
|
-
type: string;
|
|
788
730
|
status?: FileValidationStatusType;
|
|
789
731
|
statusMessage?: string;
|
|
790
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
|
@@ -182,10 +182,12 @@ export interface DomainValidateResponse {
|
|
|
182
182
|
* Deployment token for automated deployments
|
|
183
183
|
*/
|
|
184
184
|
export interface Token {
|
|
185
|
-
/**
|
|
185
|
+
/** 7-char management identifier */
|
|
186
186
|
readonly token: string;
|
|
187
187
|
/** The account this token belongs to */
|
|
188
188
|
readonly account: string;
|
|
189
|
+
/** SHA256 hash of the raw credential (auth lookups only, never exposed to users) */
|
|
190
|
+
readonly hash: string;
|
|
189
191
|
/** IP address locking for security, null if not locked */
|
|
190
192
|
readonly ip: string | null;
|
|
191
193
|
/** Labels for categorization and filtering (lowercase, alphanumeric with separators). Always present, empty array when none. */
|
|
@@ -200,10 +202,10 @@ export interface Token {
|
|
|
200
202
|
|
|
201
203
|
/**
|
|
202
204
|
* Token as returned by the list endpoint.
|
|
203
|
-
*
|
|
205
|
+
* Shows 7-char management ID, omits account and hash.
|
|
204
206
|
*/
|
|
205
207
|
export interface TokenListItem {
|
|
206
|
-
/**
|
|
208
|
+
/** 7-char management identifier (e.g., "a1b2c3d") */
|
|
207
209
|
readonly token: string;
|
|
208
210
|
/** Labels for categorization and filtering. Always present, empty array when none. */
|
|
209
211
|
labels: string[];
|
|
@@ -229,8 +231,10 @@ export interface TokenListResponse {
|
|
|
229
231
|
* Response for token creation
|
|
230
232
|
*/
|
|
231
233
|
export interface TokenCreateResponse {
|
|
232
|
-
/**
|
|
234
|
+
/** 7-char management identifier */
|
|
233
235
|
token: string;
|
|
236
|
+
/** The raw credential value (shown once at creation, then never again) */
|
|
237
|
+
secret: string;
|
|
234
238
|
/** Labels for categorization and filtering. Always present, empty array when none. */
|
|
235
239
|
labels: string[];
|
|
236
240
|
/** Unix timestamp (seconds) when token expires, null for never */
|
|
@@ -494,266 +498,69 @@ export function isShipError(error: unknown): error is ShipError {
|
|
|
494
498
|
// =============================================================================
|
|
495
499
|
|
|
496
500
|
/**
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
* Contains ONLY dynamic, runtime-specific values (plan-based limits).
|
|
500
|
-
* 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.
|
|
501
503
|
*/
|
|
502
504
|
export interface ConfigResponse {
|
|
503
|
-
/** Maximum individual file size in bytes */
|
|
504
505
|
maxFileSize: number;
|
|
505
|
-
/** Maximum number of files per deployment */
|
|
506
506
|
maxFilesCount: number;
|
|
507
|
-
/** Maximum total deployment size in bytes */
|
|
508
507
|
maxTotalSize: number;
|
|
509
508
|
}
|
|
510
509
|
|
|
510
|
+
// =============================================================================
|
|
511
|
+
// EXTENSION BLOCKLIST
|
|
512
|
+
// =============================================================================
|
|
513
|
+
|
|
511
514
|
/**
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
* This is a static platform constant, not per-user configuration.
|
|
515
|
-
* Safe to share across frontend/backend due to atomic deploys.
|
|
516
|
-
*
|
|
517
|
-
* Validation rules:
|
|
518
|
-
* - Exact match: 'application/json' allows only 'application/json'
|
|
519
|
-
* - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
|
|
520
|
-
*
|
|
521
|
-
* Coverage: 100% of browser-renderable web content
|
|
522
|
-
* - Core web (HTML, CSS, JS, WASM)
|
|
523
|
-
* - Media (images, audio, video, fonts)
|
|
524
|
-
* - Documents (PDF, Markdown, data formats)
|
|
525
|
-
* - Modern web (PWA, 3D, structured data)
|
|
526
|
-
*
|
|
527
|
-
* ============================================================================
|
|
528
|
-
* INTENTIONALLY EXCLUDED (Security & Platform Integrity)
|
|
529
|
-
* ============================================================================
|
|
530
|
-
*
|
|
531
|
-
* We are a WEB HOSTING platform, not a file distribution service.
|
|
532
|
-
* GitHub Pages-style parity for renderable content, more restrictive for downloads.
|
|
515
|
+
* Blocked file extensions — files that cannot be uploaded.
|
|
533
516
|
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
* → 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`.
|
|
538
520
|
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
* → application/octet-stream
|
|
566
|
-
* → Reason: Too broad - allows any binary format, cannot moderate effectively
|
|
567
|
-
* → Alternative: Use specific MIME types for known formats
|
|
568
|
-
*
|
|
569
|
-
* ============================================================================
|
|
570
|
-
* Security Model:
|
|
571
|
-
* - Browser sandbox (JS/WASM execute safely in controlled environment)
|
|
572
|
-
* - AI content moderation (scans text/image content for abuse)
|
|
573
|
-
* - No server-side execution (static files only)
|
|
574
|
-
* - Explicit allowlist (only approved formats, reject unknown)
|
|
575
|
-
* ============================================================================
|
|
576
|
-
*/
|
|
577
|
-
export const ALLOWED_MIME_TYPES = [
|
|
578
|
-
// =========================================================================
|
|
579
|
-
// TEXT CONTENT (explicit list - no prefix matching for security)
|
|
580
|
-
// =========================================================================
|
|
581
|
-
|
|
582
|
-
// Core web documents
|
|
583
|
-
'text/html', // HTML pages
|
|
584
|
-
'text/css', // Stylesheets
|
|
585
|
-
'text/plain', // Plain text (robots.txt, .well-known/*, LICENSE, README.txt)
|
|
586
|
-
'text/markdown', // Markdown files (.md)
|
|
587
|
-
'text/xml', // XML files
|
|
588
|
-
|
|
589
|
-
// Data formats
|
|
590
|
-
'text/csv', // CSV data files
|
|
591
|
-
'text/tab-separated-values', // TSV data files
|
|
592
|
-
'text/yaml', // YAML config files
|
|
593
|
-
'text/vcard', // VCard contact files (.vcf)
|
|
594
|
-
|
|
595
|
-
// Modern documentation formats
|
|
596
|
-
'text/mdx', // MDX (Markdown with JSX) - Next.js, Docusaurus, Nextra
|
|
597
|
-
'text/x-mdx', // MDX (alternative MIME type)
|
|
598
|
-
|
|
599
|
-
// Web-specific formats
|
|
600
|
-
'text/vtt', // WebVTT video subtitles/captions (accessibility)
|
|
601
|
-
'text/srt', // SRT subtitles (SubRip format, legacy video captions)
|
|
602
|
-
'text/calendar', // iCalendar (.ics) event files
|
|
603
|
-
|
|
604
|
-
// JavaScript (legacy MIME type, still widely used by ~50% of servers)
|
|
605
|
-
'text/javascript',
|
|
606
|
-
|
|
607
|
-
// Modern web development formats (uncompiled source)
|
|
608
|
-
'text/typescript', // TypeScript source (.ts)
|
|
609
|
-
'application/x-typescript', // TypeScript (alternative MIME type, .d.ts declarations)
|
|
610
|
-
'text/tsx', // TypeScript JSX (.tsx)
|
|
611
|
-
'text/jsx', // React JSX (.jsx)
|
|
612
|
-
'text/x-scss', // SCSS preprocessor
|
|
613
|
-
'text/x-sass', // Sass preprocessor
|
|
614
|
-
'text/less', // Less preprocessor
|
|
615
|
-
'text/x-less', // Less preprocessor (alternative)
|
|
616
|
-
'text/stylus', // Stylus preprocessor
|
|
617
|
-
'text/x-vue', // Vue single-file components (.vue)
|
|
618
|
-
'text/x-svelte', // Svelte components (.svelte)
|
|
619
|
-
|
|
620
|
-
// Developer documentation formats
|
|
621
|
-
'text/x-sql', // SQL files (database schemas, migrations)
|
|
622
|
-
'text/x-diff', // Diff files (code comparisons, patches)
|
|
623
|
-
'text/x-patch', // Patch files (version upgrades, migrations)
|
|
624
|
-
'text/x-protobuf', // Protocol Buffers text format (gRPC schemas)
|
|
625
|
-
'text/x-ini', // INI configuration files
|
|
626
|
-
|
|
627
|
-
// Academic/research formats
|
|
628
|
-
'text/x-tex', // LaTeX documents
|
|
629
|
-
'text/x-latex', // LaTeX documents (alternative)
|
|
630
|
-
'text/x-bibtex', // BibTeX citations
|
|
631
|
-
'text/x-r-markdown', // R Markdown (statistical documentation)
|
|
632
|
-
|
|
633
|
-
// =========================================================================
|
|
634
|
-
// MEDIA (prefix matching - covers all common subtypes)
|
|
635
|
-
// =========================================================================
|
|
636
|
-
|
|
637
|
-
// Images: PNG, JPEG, GIF, SVG, WebP, AVIF, HEIC, BMP, TIFF, ICO, etc.
|
|
638
|
-
'image/',
|
|
639
|
-
|
|
640
|
-
// Audio: MP3, OGG, WAV, WebM, AAC, FLAC, Opus, etc.
|
|
641
|
-
'audio/',
|
|
642
|
-
|
|
643
|
-
// Video: MP4, WebM, OGG, QuickTime, etc.
|
|
644
|
-
'video/',
|
|
645
|
-
|
|
646
|
-
// Modern fonts: WOFF2, WOFF, TTF, OTF
|
|
647
|
-
'font/',
|
|
648
|
-
|
|
649
|
-
// =========================================================================
|
|
650
|
-
// CORE WEB APPLICATION TYPES
|
|
651
|
-
// =========================================================================
|
|
652
|
-
|
|
653
|
-
// JavaScript (multiple MIME types for compatibility)
|
|
654
|
-
'application/javascript', // Modern standard (RFC 9239)
|
|
655
|
-
'application/ecmascript', // ECMAScript (legacy but still used)
|
|
656
|
-
'application/x-javascript', // Legacy variant (old CDNs, Apache configs)
|
|
657
|
-
|
|
658
|
-
// WebAssembly (modern web apps, games, compute-heavy workloads)
|
|
659
|
-
'application/wasm',
|
|
660
|
-
|
|
661
|
-
// JSON and structured data
|
|
662
|
-
'application/json',
|
|
663
|
-
'application/ld+json', // JSON-LD for structured data / SEO (Schema.org, Open Graph)
|
|
664
|
-
'application/geo+json', // GeoJSON for mapping (Leaflet, Mapbox)
|
|
665
|
-
'application/manifest+json', // PWA web app manifests
|
|
666
|
-
'application/x-ipynb+json', // Jupyter Notebooks (data science, ML tutorials)
|
|
667
|
-
|
|
668
|
-
// JSON variants (AI/ML, streaming data, configs)
|
|
669
|
-
'application/x-ndjson', // Newline-Delimited JSON (training datasets, logs)
|
|
670
|
-
'application/ndjson', // NDJSON (alternative MIME type)
|
|
671
|
-
'text/x-ndjson', // NDJSON (text variant)
|
|
672
|
-
'application/jsonl', // JSON Lines (Hugging Face, OpenAI fine-tuning)
|
|
673
|
-
'text/jsonl', // JSON Lines (text variant)
|
|
674
|
-
'application/json5', // JSON5 (JSON with comments, trailing commas)
|
|
675
|
-
'text/json5', // JSON5 (text variant)
|
|
676
|
-
'application/schema+json', // JSON Schema (API specs, model definitions)
|
|
677
|
-
|
|
678
|
-
// Development tools
|
|
679
|
-
'application/source-map', // Source maps (.js.map, .css.map) for debugging
|
|
680
|
-
|
|
681
|
-
// XML and feeds
|
|
682
|
-
'application/xml',
|
|
683
|
-
'application/xhtml+xml', // XHTML - XML-compliant HTML (legacy sites)
|
|
684
|
-
'application/rss+xml', // RSS feeds (blogs, podcasts)
|
|
685
|
-
'application/atom+xml', // Atom feeds
|
|
686
|
-
'application/feed+json', // JSON Feed (modern RSS alternative)
|
|
687
|
-
'application/vnd.google-earth.kml+xml', // KML for mapping (Google Earth, GIS)
|
|
688
|
-
|
|
689
|
-
// Configuration formats
|
|
690
|
-
'application/yaml', // YAML configs (static site generators)
|
|
691
|
-
'application/toml', // TOML configs (Cargo, Netlify, Rust projects)
|
|
692
|
-
|
|
693
|
-
// Documents
|
|
694
|
-
'application/pdf', // PDF documents
|
|
695
|
-
|
|
696
|
-
// Media metadata
|
|
697
|
-
'application/x-subrip', // SRT subtitles (SubRip format)
|
|
698
|
-
|
|
699
|
-
// Developer tools and schemas
|
|
700
|
-
'application/sql', // SQL files (database schemas, queries)
|
|
701
|
-
'application/graphql', // GraphQL schemas (API documentation)
|
|
702
|
-
'application/graphql+json', // GraphQL with JSON encoding
|
|
703
|
-
'application/x-protobuf', // Protocol Buffers binary (gRPC)
|
|
704
|
-
'application/x-ini', // INI configuration files
|
|
705
|
-
|
|
706
|
-
// Academic formats
|
|
707
|
-
'application/x-tex', // LaTeX documents
|
|
708
|
-
'application/x-bibtex', // BibTeX citations
|
|
709
|
-
|
|
710
|
-
// =========================================================================
|
|
711
|
-
// 3D FORMATS (industry standard only)
|
|
712
|
-
// =========================================================================
|
|
713
|
-
|
|
714
|
-
// glTF - Khronos standard for 3D web content
|
|
715
|
-
'model/gltf+json', // glTF JSON format
|
|
716
|
-
'model/gltf-binary', // GLB binary format
|
|
717
|
-
|
|
718
|
-
// =========================================================================
|
|
719
|
-
// LEGACY COMPATIBILITY
|
|
720
|
-
// =========================================================================
|
|
721
|
-
|
|
722
|
-
// Video (some tools detect MP4 as application/mp4)
|
|
723
|
-
'application/mp4',
|
|
724
|
-
|
|
725
|
-
// Legacy font MIME types (Bootstrap, Font Awesome, IE compatibility)
|
|
726
|
-
'application/font-woff',
|
|
727
|
-
'application/font-woff2',
|
|
728
|
-
'application/x-font-woff',
|
|
729
|
-
'application/x-woff',
|
|
730
|
-
'application/vnd.ms-fontobject', // EOT files (Internet Explorer)
|
|
731
|
-
'application/x-font-ttf',
|
|
732
|
-
'application/x-font-truetype',
|
|
733
|
-
'application/x-font-otf',
|
|
734
|
-
'application/x-font-opentype',
|
|
735
|
-
] as const;
|
|
736
|
-
|
|
737
|
-
/**
|
|
738
|
-
* Check if a MIME type is allowed for upload.
|
|
739
|
-
*
|
|
740
|
-
* Supports both exact matches and prefix matches:
|
|
741
|
-
* - 'application/json' matches 'application/json' exactly
|
|
742
|
-
* - '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.
|
|
743
547
|
*
|
|
744
548
|
* @example
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
);
|
|
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);
|
|
753
560
|
}
|
|
754
561
|
|
|
755
562
|
// =============================================================================
|
|
756
|
-
// COMMON RESPONSE PATTERNS
|
|
563
|
+
// COMMON RESPONSE PATTERNS
|
|
757
564
|
// =============================================================================
|
|
758
565
|
|
|
759
566
|
/**
|
|
@@ -1290,7 +1097,6 @@ export interface ValidationIssue {
|
|
|
1290
1097
|
export interface ValidatableFile {
|
|
1291
1098
|
name: string;
|
|
1292
1099
|
size: number;
|
|
1293
|
-
type: string;
|
|
1294
1100
|
status?: FileValidationStatusType;
|
|
1295
1101
|
statusMessage?: string;
|
|
1296
1102
|
}
|